Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / forms.py @ 53bf2659

History | View | Annotate | Download (12.5 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
# 
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
# 
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
# 
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
# 
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
# 
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33
from urlparse import urljoin
34
from datetime import datetime
35

    
36
from django import forms
37
from django.utils.translation import ugettext as _
38
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, PasswordResetForm
39
from django.core.mail import send_mail
40
from django.contrib.auth.tokens import default_token_generator
41
from django.template import Context, loader
42
from django.utils.http import int_to_base36
43
from django.core.urlresolvers import reverse
44
from django.utils.functional import lazy
45

    
46
from astakos.im.models import AstakosUser
47
from astakos.im.settings import INVITATIONS_PER_LEVEL, DEFAULT_FROM_EMAIL, BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY, DEFAULT_CONTACT_EMAIL, RECAPTCHA_ENABLED
48
from astakos.im.widgets import DummyWidget, RecaptchaWidget, ApprovalTermsWidget
49

    
50
# since Django 1.4 use django.core.urlresolvers.reverse_lazy instead
51
from astakos.im.util import reverse_lazy
52

    
53
import logging
54
import recaptcha.client.captcha as captcha
55

    
56
logger = logging.getLogger(__name__)
57

    
58
class LocalUserCreationForm(UserCreationForm):
59
    """
60
    Extends the built in UserCreationForm in several ways:
61
    
62
    * Adds email, first_name, last_name, recaptcha_challenge_field, recaptcha_response_field field.
63
    * The username field isn't visible and it is assigned a generated id.
64
    * User created is not active. 
65
    """
66
    recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
67
    recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='')
68
    
69
    class Meta:
70
        model = AstakosUser
71
        fields = ("email", "first_name", "last_name", "has_signed_terms")
72
        widgets = {"has_signed_terms":ApprovalTermsWidget(terms_uri=reverse_lazy('latest_terms'))}
73
    
74
    def __init__(self, *args, **kwargs):
75
        """
76
        Changes the order of fields, and removes the username field.
77
        """
78
        if 'ip' in kwargs:
79
            self.ip = kwargs['ip']
80
            kwargs.pop('ip')
81
        super(LocalUserCreationForm, self).__init__(*args, **kwargs)
82
        self.fields.keyOrder = ['email', 'first_name', 'last_name',
83
                                'password1', 'password2',
84
                                'has_signed_terms']
85
        if RECAPTCHA_ENABLED:
86
            self.fields.keyOrder.extend(['recaptcha_challenge_field',
87
                                         'recaptcha_response_field',])
88
    
89
    def clean_email(self):
90
        email = self.cleaned_data['email']
91
        if not email:
92
            raise forms.ValidationError(_("This field is required"))
93
        try:
94
            AstakosUser.objects.get(email = email)
95
            raise forms.ValidationError(_("This email is already used"))
96
        except AstakosUser.DoesNotExist:
97
            return email
98
    
99
    def clean_has_signed_terms(self):
100
        has_signed_terms = self.cleaned_data['has_signed_terms']
101
        if not has_signed_terms:
102
            raise forms.ValidationError(_('You have to agree with the terms'))
103
        return has_signed_terms
104
    
105
    def clean_recaptcha_response_field(self):
106
        if 'recaptcha_challenge_field' in self.cleaned_data:
107
            self.validate_captcha()
108
        return self.cleaned_data['recaptcha_response_field']
109

    
110
    def clean_recaptcha_challenge_field(self):
111
        if 'recaptcha_response_field' in self.cleaned_data:
112
            self.validate_captcha()
113
        return self.cleaned_data['recaptcha_challenge_field']
114

    
115
    def validate_captcha(self):
116
        rcf = self.cleaned_data['recaptcha_challenge_field']
117
        rrf = self.cleaned_data['recaptcha_response_field']
118
        check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip)
119
        if not check.is_valid:
120
            raise forms.ValidationError(_('You have not entered the correct words'))
121
    
122
    def save(self, commit=True):
123
        """
124
        Saves the email, first_name and last_name properties, after the normal
125
        save behavior is complete.
126
        """
127
        user = super(LocalUserCreationForm, self).save(commit=False)
128
        user.renew_token()
129
        user.date_signed_terms = datetime.now()
130
        if commit:
131
            user.save()
132
        logger.info('Created user %s', user)
133
        return user
134

    
135
class InvitedLocalUserCreationForm(LocalUserCreationForm):
136
    """
137
    Extends the LocalUserCreationForm: adds an inviter readonly field.
138
    """
139
    
140
    inviter = forms.CharField(widget=forms.TextInput(), label=_('Inviter Real Name'))
141
    
142
    class Meta:
143
        model = AstakosUser
144
        fields = ("email", "first_name", "last_name", "has_signed_terms")
145
        widgets = {"has_signed_terms":ApprovalTermsWidget(terms_uri=reverse_lazy('latest_terms'))}
146
    
147
    def __init__(self, *args, **kwargs):
148
        """
149
        Changes the order of fields, and removes the username field.
150
        """
151
        super(InvitedLocalUserCreationForm, self).__init__(*args, **kwargs)
152
        self.fields.keyOrder = ['email', 'inviter', 'first_name',
153
                                'last_name', 'password1', 'password2',
154
                                'has_signed_terms']
155
        if RECAPTCHA_ENABLED:
156
            self.fields.keyOrder.extend(['recaptcha_challenge_field',
157
                                         'recaptcha_response_field',])
158
        
159
        #set readonly form fields
160
        self.fields['inviter'].widget.attrs['readonly'] = True
161
        self.fields['email'].widget.attrs['readonly'] = True
162
        self.fields['username'].widget.attrs['readonly'] = True
163
    
164
    def save(self, commit=True):
165
        user = super(InvitedLocalUserCreationForm, self).save(commit=False)
166
        level = user.invitation.inviter.level + 1
167
        user.level = level
168
        user.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
169
        user.email_verified = True
170
        if commit:
171
            user.save()
172
        return user
173

    
174
class LoginForm(AuthenticationForm):
175
    username = forms.EmailField(label=_("Email"))
176

    
177
class ProfileForm(forms.ModelForm):
178
    """
179
    Subclass of ``ModelForm`` for permiting user to edit his/her profile.
180
    Most of the fields are readonly since the user is not allowed to change them.
181
    
182
    The class defines a save method which sets ``is_verified`` to True so as the user
183
    during the next login will not to be redirected to profile page.
184
    """
185
    renew = forms.BooleanField(label='Renew token', required=False)
186
    
187
    class Meta:
188
        model = AstakosUser
189
        fields = ('email', 'first_name', 'last_name', 'auth_token', 'auth_token_expires')
190
    
191
    def __init__(self, *args, **kwargs):
192
        super(ProfileForm, self).__init__(*args, **kwargs)
193
        instance = getattr(self, 'instance', None)
194
        ro_fields = ('auth_token', 'auth_token_expires', 'email')
195
        if instance and instance.id:
196
            for field in ro_fields:
197
                self.fields[field].widget.attrs['readonly'] = True
198
    
199
    def save(self, commit=True):
200
        user = super(ProfileForm, self).save(commit=False)
201
        user.is_verified = True
202
        if self.cleaned_data.get('renew'):
203
            user.renew_token()
204
        if commit:
205
            user.save()
206
        return user
207

    
208
class ThirdPartyUserCreationForm(ProfileForm):
209
    class Meta:
210
        model = AstakosUser
211
        fields = ('email', 'last_name', 'first_name', 'affiliation', 'provider', 'third_party_identifier')
212
    
213
    def __init__(self, *args, **kwargs):
214
        if 'ip' in kwargs:
215
            self.ip = kwargs['ip']
216
            kwargs.pop('ip')
217
        super(ThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
218
        self.fields.keyOrder = ['email']
219
    
220
    def clean_email(self):
221
        email = self.cleaned_data['email']
222
        if not email:
223
            raise forms.ValidationError(_("This field is required"))
224
        try:
225
            user = AstakosUser.objects.get(email = email)
226
            raise forms.ValidationError(_("This email is already used"))
227
        except AstakosUser.DoesNotExist:
228
            return email
229
    
230
    def save(self, commit=True):
231
        user = super(ThirdPartyUserCreationForm, self).save(commit=False)
232
        user.verified = False
233
        user.renew_token()
234
        if commit:
235
            user.save()
236
        logger.info('Created user %s', user)
237
        return user
238

    
239
class InvitedThirdPartyUserCreationForm(ThirdPartyUserCreationForm):
240
    def __init__(self, *args, **kwargs):
241
        super(InvitedThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
242
        #set readonly form fields
243
        self.fields['email'].widget.attrs['readonly'] = True
244

    
245
class FeedbackForm(forms.Form):
246
    """
247
    Form for writing feedback.
248
    """
249
    feedback_msg = forms.CharField(widget=forms.Textarea(),
250
                                label=u'Message', required=False)
251
    feedback_data = forms.CharField(widget=forms.HiddenInput(),
252
                                label='', required=False)
253

    
254
class SendInvitationForm(forms.Form):
255
    """
256
    Form for sending an invitations
257
    """
258
    
259
    email = forms.EmailField(required = True, label = 'Email address')
260
    first_name = forms.EmailField(label = 'First name')
261
    last_name = forms.EmailField(label = 'Last name')
262

    
263
class ExtendedPasswordResetForm(PasswordResetForm):
264
    """
265
    Extends PasswordResetForm by overriding save method:
266
    passes a custom from_email in send_mail.
267
    
268
    Since Django 1.3 this is useless since ``django.contrib.auth.views.reset_password``
269
    accepts a from_email argument.
270
    """
271
    def save(self, domain_override=None, email_template_name='registration/password_reset_email.html',
272
             use_https=False, token_generator=default_token_generator, request=None):
273
        """
274
        Generates a one-use only link for resetting password and sends to the user.
275
        """
276
        for user in self.users_cache:
277
            url = urljoin(BASEURL,
278
                          '/im/local/reset/confirm/%s-%s' %(int_to_base36(user.id),
279
                                                            token_generator.make_token(user)))
280
            t = loader.get_template(email_template_name)
281
            c = {
282
                'email': user.email,
283
                'url': url,
284
                'site_name': SITENAME,
285
                'user': user,
286
                'baseurl': BASEURL,
287
                'support': DEFAULT_CONTACT_EMAIL
288
            }
289
            from_email = DEFAULT_FROM_EMAIL
290
            send_mail(_("Password reset on %s alpha2 testing") % SITENAME,
291
                t.render(Context(c)), from_email, [user.email])
292

    
293
class SignApprovalTermsForm(forms.ModelForm):
294
    class Meta:
295
        model = AstakosUser
296
        fields = ("has_signed_terms",)
297
    
298
    def __init__(self, *args, **kwargs):
299
        super(SignApprovalTermsForm, self).__init__(*args, **kwargs)
300
    
301
    def clean_has_signed_terms(self):
302
        has_signed_terms = self.cleaned_data['has_signed_terms']
303
        if not has_signed_terms:
304
            raise forms.ValidationError(_('You have to agree with the terms'))
305
        return has_signed_terms
306
    
307
    def save(self, commit=True):
308
        """
309
        Saves the , after the normal
310
        save behavior is complete.
311
        """
312
        user = super(SignApprovalTermsForm, self).save(commit=False)
313
        user.date_signed_terms = datetime.now()
314
        if commit:
315
            user.save()
316
        return user