Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / forms.py @ f289215d

History | View | Annotate | Download (10.4 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

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

    
44
from astakos.im.models import AstakosUser
45
from astakos.im.settings import INVITATIONS_PER_LEVEL, DEFAULT_FROM_EMAIL, BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY
46
from astakos.im.widgets import DummyWidget, RecaptchaWidget
47

    
48
import logging
49
import recaptcha.client.captcha as captcha
50

    
51
logger = logging.getLogger(__name__)
52

    
53
class LocalUserCreationForm(UserCreationForm):
54
    """
55
    Extends the built in UserCreationForm in several ways:
56
    
57
    * Adds email, first_name and last_name field.
58
    * The username field isn't visible and it is assigned a generated id.
59
    * User created is not active. 
60
    """
61
    recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
62
    recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='')
63
    
64
    class Meta:
65
        model = AstakosUser
66
        fields = ("email", "first_name", "last_name")
67
    
68
    def __init__(self, *args, **kwargs):
69
        """
70
        Changes the order of fields, and removes the username field.
71
        """
72
        if 'ip' in kwargs:
73
            self.ip = kwargs['ip']
74
            kwargs.pop('ip')
75
        super(LocalUserCreationForm, self).__init__(*args, **kwargs)
76
        self.fields.keyOrder = ['email', 'first_name', 'last_name',
77
                                'password1', 'password2',
78
                                'recaptcha_challenge_field',
79
                                'recaptcha_response_field']
80
    
81
    def clean_email(self):
82
        email = self.cleaned_data['email']
83
        if not email:
84
            raise forms.ValidationError(_("This field is required"))
85
        try:
86
            AstakosUser.objects.get(email = email)
87
            raise forms.ValidationError(_("Email is reserved"))
88
        except AstakosUser.DoesNotExist:
89
            return email
90
    
91
    def clean_recaptcha_response_field(self):
92
        if 'recaptcha_challenge_field' in self.cleaned_data:
93
            self.validate_captcha()
94
        return self.cleaned_data['recaptcha_response_field']
95

    
96
    def clean_recaptcha_challenge_field(self):
97
        if 'recaptcha_response_field' in self.cleaned_data:
98
            self.validate_captcha()
99
        return self.cleaned_data['recaptcha_challenge_field']
100

    
101
    def validate_captcha(self):
102
        rcf = self.cleaned_data['recaptcha_challenge_field']
103
        rrf = self.cleaned_data['recaptcha_response_field']
104
        check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip)
105
        if not check.is_valid:
106
            raise forms.ValidationError(_('You have not entered the correct words'))
107
    
108
    def save(self, commit=True):
109
        """
110
        Saves the email, first_name and last_name properties, after the normal
111
        save behavior is complete.
112
        """
113
        user = super(LocalUserCreationForm, self).save(commit=False)
114
        user.renew_token()
115
        if commit:
116
            user.save()
117
        logger.info('Created user %s', user)
118
        return user
119

    
120
class InvitedLocalUserCreationForm(LocalUserCreationForm):
121
    """
122
    Extends the LocalUserCreationForm: adds an inviter readonly field.
123
    """
124
    
125
    inviter = forms.CharField(widget=forms.TextInput(), label=_('Inviter Real Name'))
126
    
127
    class Meta:
128
        model = AstakosUser
129
        fields = ("email", "first_name", "last_name")
130
    
131
    def __init__(self, *args, **kwargs):
132
        """
133
        Changes the order of fields, and removes the username field.
134
        """
135
        super(InvitedLocalUserCreationForm, self).__init__(*args, **kwargs)
136
        self.fields.keyOrder = ['email', 'inviter', 'first_name',
137
                                'last_name', 'password1', 'password2']
138
        #set readonly form fields
139
        self.fields['inviter'].widget.attrs['readonly'] = True
140
        self.fields['email'].widget.attrs['readonly'] = True
141
        self.fields['username'].widget.attrs['readonly'] = True
142
    
143
    def save(self, commit=True):
144
        user = super(InvitedLocalUserCreationForm, self).save(commit=False)
145
        level = user.invitation.inviter.level + 1
146
        user.level = level
147
        user.invitations = INVITATIONS_PER_LEVEL[level]
148
        if commit:
149
            user.save()
150
        return user
151

    
152
class LoginForm(AuthenticationForm):
153
    username = forms.EmailField(label=_("Email"))
154

    
155
class ProfileForm(forms.ModelForm):
156
    """
157
    Subclass of ``ModelForm`` for permiting user to edit his/her profile.
158
    Most of the fields are readonly since the user is not allowed to change them.
159
    
160
    The class defines a save method which sets ``is_verified`` to True so as the user
161
    during the next login will not to be redirected to profile page.
162
    """
163
    renew = forms.BooleanField(label='Renew token', required=False)
164
    
165
    class Meta:
166
        model = AstakosUser
167
        fields = ('email', 'first_name', 'last_name', 'auth_token', 'auth_token_expires')
168
    
169
    def __init__(self, *args, **kwargs):
170
        super(ProfileForm, self).__init__(*args, **kwargs)
171
        instance = getattr(self, 'instance', None)
172
        ro_fields = ('auth_token', 'auth_token_expires', 'email')
173
        if instance and instance.id:
174
            for field in ro_fields:
175
                self.fields[field].widget.attrs['readonly'] = True
176
    
177
    def save(self, commit=True):
178
        user = super(ProfileForm, self).save(commit=False)
179
        user.is_verified = True
180
        if self.cleaned_data.get('renew'):
181
            user.renew_token()
182
        if commit:
183
            user.save()
184
        return user
185

    
186
class ThirdPartyUserCreationForm(ProfileForm):
187
    class Meta:
188
        model = AstakosUser
189
        fields = ('email', 'last_name', 'first_name', 'affiliation', 'provider', 'third_party_identifier')
190
    
191
    def __init__(self, *args, **kwargs):
192
        if 'ip' in kwargs:
193
            self.ip = kwargs['ip']
194
            kwargs.pop('ip')
195
        super(ThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
196
        self.fields.keyOrder = ['email']
197
    
198
    def clean_email(self):
199
        email = self.cleaned_data['email']
200
        if not email:
201
            raise forms.ValidationError(_("This field is required"))
202
        try:
203
            user = AstakosUser.objects.get(email = email)
204
            raise forms.ValidationError(_("Email is reserved"))
205
        except AstakosUser.DoesNotExist:
206
            return email
207
    
208
    def save(self, commit=True):
209
        user = super(ThirdPartyUserCreationForm, self).save(commit=False)
210
        user.verified = False
211
        user.renew_token()
212
        if commit:
213
            user.save()
214
        logger.info('Created user %s', user)
215
        return user
216

    
217
class InvitedThirdPartyUserCreationForm(ThirdPartyUserCreationForm):
218
    def __init__(self, *args, **kwargs):
219
        super(InvitedThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
220
        #set readonly form fields
221
        self.fields['email'].widget.attrs['readonly'] = True
222

    
223
class FeedbackForm(forms.Form):
224
    """
225
    Form for writing feedback.
226
    """
227
    feedback_msg = forms.CharField(widget=forms.Textarea(),
228
                                label=u'Message', required=False)
229
    feedback_data = forms.CharField(widget=forms.HiddenInput(),
230
                                label='', required=False)
231

    
232
class SendInvitationForm(forms.Form):
233
    """
234
    Form for sending an invitations
235
    """
236
    
237
    email = forms.EmailField(required = True, label = 'Email address')
238
    first_name = forms.EmailField(label = 'First name')
239
    last_name = forms.EmailField(label = 'Last name')
240

    
241
class ExtendedPasswordResetForm(PasswordResetForm):
242
    """
243
    Extends PasswordResetForm by overriding save method:
244
    passes a custom from_email in send_mail.
245
    
246
    Since Django 1.3 this is useless since ``django.contrib.auth.views.reset_password``
247
    accepts a from_email argument.
248
    """
249
    def save(self, domain_override=None, email_template_name='registration/password_reset_email.html',
250
             use_https=False, token_generator=default_token_generator, request=None):
251
        """
252
        Generates a one-use only link for resetting password and sends to the user.
253
        """
254
        for user in self.users_cache:
255
            url = urljoin(BASEURL,
256
                          '/im/local/reset/confirm/%s-%s' %(int_to_base36(user.id),
257
                                                            token_generator.make_token(user)))
258
            t = loader.get_template(email_template_name)
259
            c = {
260
                'email': user.email,
261
                'url': url,
262
                'site_name': SITENAME,
263
                'user': user,
264
                'baseurl': BASEURL
265
            }
266
            from_email = DEFAULT_FROM_EMAIL
267
            send_mail(_("Password reset on %s") % SITENAME,
268
                t.render(Context(c)), from_email, [user.email])