Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.7 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, DEFAULT_CONTACT_EMAIL
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, last_name, recaptcha_challenge_field, recaptcha_response_field 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(_("This email is already used"))
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
                                'recaptcha_challenge_field',
139
                                'recaptcha_response_field']
140
        #set readonly form fields
141
        self.fields['inviter'].widget.attrs['readonly'] = True
142
        self.fields['email'].widget.attrs['readonly'] = True
143
        self.fields['username'].widget.attrs['readonly'] = True
144
    
145
    def save(self, commit=True):
146
        user = super(InvitedLocalUserCreationForm, self).save(commit=False)
147
        level = user.invitation.inviter.level + 1
148
        user.level = level
149
        user.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
150
        user.email_verified = True
151
        if commit:
152
            user.save()
153
        return user
154

    
155
class LoginForm(AuthenticationForm):
156
    username = forms.EmailField(label=_("Email"))
157

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

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

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

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

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

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