Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / forms.py @ 28252c7f

History | View | Annotate | Download (21.9 kB)

1 aba1e498 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 18ffbee1 Sofia Papagiannaki
#
3 64cd4730 Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 64cd4730 Antony Chazapis
# without modification, are permitted provided that the following
5 64cd4730 Antony Chazapis
# conditions are met:
6 18ffbee1 Sofia Papagiannaki
#
7 64cd4730 Antony Chazapis
#   1. Redistributions of source code must retain the above
8 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
9 64cd4730 Antony Chazapis
#      disclaimer.
10 18ffbee1 Sofia Papagiannaki
#
11 64cd4730 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 64cd4730 Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 64cd4730 Antony Chazapis
#      provided with the distribution.
15 18ffbee1 Sofia Papagiannaki
#
16 64cd4730 Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 64cd4730 Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 64cd4730 Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 64cd4730 Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 64cd4730 Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 64cd4730 Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 64cd4730 Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 64cd4730 Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 64cd4730 Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 64cd4730 Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 64cd4730 Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 64cd4730 Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 18ffbee1 Sofia Papagiannaki
#
29 64cd4730 Antony Chazapis
# The views and conclusions contained in the software and
30 64cd4730 Antony Chazapis
# documentation are those of the authors and should not be
31 64cd4730 Antony Chazapis
# interpreted as representing official policies, either expressed
32 64cd4730 Antony Chazapis
# or implied, of GRNET S.A.
33 8f378756 Sofia Papagiannaki
from urlparse import urljoin
34 270dd48d Sofia Papagiannaki
from datetime import datetime
35 64cd4730 Antony Chazapis
36 64cd4730 Antony Chazapis
from django import forms
37 64cd4730 Antony Chazapis
from django.utils.translation import ugettext as _
38 1039bab1 Sofia Papagiannaki
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, \
39 1039bab1 Sofia Papagiannaki
    PasswordResetForm, PasswordChangeForm
40 e2125441 Sofia Papagiannaki
from django.core.mail import send_mail
41 e2125441 Sofia Papagiannaki
from django.contrib.auth.tokens import default_token_generator
42 e2125441 Sofia Papagiannaki
from django.template import Context, loader
43 e2125441 Sofia Papagiannaki
from django.utils.http import int_to_base36
44 374611bc Sofia Papagiannaki
from django.core.urlresolvers import reverse
45 270dd48d Sofia Papagiannaki
from django.utils.functional import lazy
46 18ffbee1 Sofia Papagiannaki
from django.utils.safestring import mark_safe
47 0a569195 Sofia Papagiannaki
from django.contrib import messages
48 49790d9d Sofia Papagiannaki
from django.utils.encoding import smart_str
49 8e45d6fd Sofia Papagiannaki
from django.forms.extras.widgets import SelectDateWidget
50 8e45d6fd Sofia Papagiannaki
from django.db.models import Q
51 01ac12d5 Sofia Papagiannaki
from django.db.models.query import EmptyQuerySet
52 5ed6816e Sofia Papagiannaki
53 8e45d6fd Sofia Papagiannaki
from astakos.im.models import *
54 111f3da6 Sofia Papagiannaki
from astakos.im.settings import INVITATIONS_PER_LEVEL, DEFAULT_FROM_EMAIL, \
55 111f3da6 Sofia Papagiannaki
    BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY, DEFAULT_CONTACT_EMAIL, \
56 111f3da6 Sofia Papagiannaki
    RECAPTCHA_ENABLED, LOGGING_LEVEL
57 45f8d9ff Sofia Papagiannaki
from astakos.im.widgets import DummyWidget, RecaptchaWidget
58 49790d9d Sofia Papagiannaki
from astakos.im.functions import send_change_email
59 270dd48d Sofia Papagiannaki
60 270dd48d Sofia Papagiannaki
# since Django 1.4 use django.core.urlresolvers.reverse_lazy instead
61 09e7393c Sofia Papagiannaki
from astakos.im.util import reverse_lazy, reserved_email, get_query
62 64cd4730 Antony Chazapis
63 3bf924ec Sofia Papagiannaki
import logging
64 49790d9d Sofia Papagiannaki
import hashlib
65 db7fecd9 Sofia Papagiannaki
import recaptcha.client.captcha as captcha
66 49790d9d Sofia Papagiannaki
from random import random
67 64cd4730 Antony Chazapis
68 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
69 e015e9e6 Sofia Papagiannaki
70 15efc749 Sofia Papagiannaki
class LocalUserCreationForm(UserCreationForm):
71 890b0eaf Sofia Papagiannaki
    """
72 890b0eaf Sofia Papagiannaki
    Extends the built in UserCreationForm in several ways:
73 18ffbee1 Sofia Papagiannaki

74 8316698a Sofia Papagiannaki
    * Adds email, first_name, last_name, recaptcha_challenge_field, recaptcha_response_field field.
75 5ed6816e Sofia Papagiannaki
    * The username field isn't visible and it is assigned a generated id.
76 18ffbee1 Sofia Papagiannaki
    * User created is not active.
77 890b0eaf Sofia Papagiannaki
    """
78 db7fecd9 Sofia Papagiannaki
    recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
79 db7fecd9 Sofia Papagiannaki
    recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='')
80 18ffbee1 Sofia Papagiannaki
81 794852f2 Sofia Papagiannaki
    class Meta:
82 794852f2 Sofia Papagiannaki
        model = AstakosUser
83 45f8d9ff Sofia Papagiannaki
        fields = ("email", "first_name", "last_name", "has_signed_terms", "has_signed_terms")
84 18ffbee1 Sofia Papagiannaki
85 64cd4730 Antony Chazapis
    def __init__(self, *args, **kwargs):
86 64cd4730 Antony Chazapis
        """
87 890b0eaf Sofia Papagiannaki
        Changes the order of fields, and removes the username field.
88 64cd4730 Antony Chazapis
        """
89 672d445a Sofia Papagiannaki
        request = kwargs.get('request', None)
90 672d445a Sofia Papagiannaki
        if request:
91 672d445a Sofia Papagiannaki
            kwargs.pop('request')
92 672d445a Sofia Papagiannaki
            self.ip = request.META.get('REMOTE_ADDR',
93 672d445a Sofia Papagiannaki
                                       request.META.get('HTTP_X_REAL_IP', None))
94 672d445a Sofia Papagiannaki
        
95 15efc749 Sofia Papagiannaki
        super(LocalUserCreationForm, self).__init__(*args, **kwargs)
96 890b0eaf Sofia Papagiannaki
        self.fields.keyOrder = ['email', 'first_name', 'last_name',
97 d8f63346 Sofia Papagiannaki
                                'password1', 'password2']
98 1b3398a0 Olga Brani
99 53bf2659 Sofia Papagiannaki
        if RECAPTCHA_ENABLED:
100 53bf2659 Sofia Papagiannaki
            self.fields.keyOrder.extend(['recaptcha_challenge_field',
101 53bf2659 Sofia Papagiannaki
                                         'recaptcha_response_field',])
102 1b3398a0 Olga Brani
        if get_latest_terms():
103 1b3398a0 Olga Brani
            self.fields.keyOrder.append('has_signed_terms')
104 1b3398a0 Olga Brani
            
105 18ffbee1 Sofia Papagiannaki
        if 'has_signed_terms' in self.fields:
106 18ffbee1 Sofia Papagiannaki
            # Overriding field label since we need to apply a link
107 18ffbee1 Sofia Papagiannaki
            # to the terms within the label
108 18ffbee1 Sofia Papagiannaki
            terms_link_html = '<a href="%s" target="_blank">%s</a>' \
109 18ffbee1 Sofia Papagiannaki
                    % (reverse('latest_terms'), _("the terms"))
110 18ffbee1 Sofia Papagiannaki
            self.fields['has_signed_terms'].label = \
111 18ffbee1 Sofia Papagiannaki
                    mark_safe("I agree with %s" % terms_link_html)
112 18ffbee1 Sofia Papagiannaki
113 af4eb974 Sofia Papagiannaki
    def clean_email(self):
114 af4eb974 Sofia Papagiannaki
        email = self.cleaned_data['email']
115 881c856c Sofia Papagiannaki
        if not email:
116 881c856c Sofia Papagiannaki
            raise forms.ValidationError(_("This field is required"))
117 0a569195 Sofia Papagiannaki
        if reserved_email(email):
118 c37263ea Sofia Papagiannaki
            raise forms.ValidationError(_("This email is already used"))
119 0a569195 Sofia Papagiannaki
        return email
120 18ffbee1 Sofia Papagiannaki
121 270dd48d Sofia Papagiannaki
    def clean_has_signed_terms(self):
122 270dd48d Sofia Papagiannaki
        has_signed_terms = self.cleaned_data['has_signed_terms']
123 270dd48d Sofia Papagiannaki
        if not has_signed_terms:
124 270dd48d Sofia Papagiannaki
            raise forms.ValidationError(_('You have to agree with the terms'))
125 270dd48d Sofia Papagiannaki
        return has_signed_terms
126 18ffbee1 Sofia Papagiannaki
127 db7fecd9 Sofia Papagiannaki
    def clean_recaptcha_response_field(self):
128 db7fecd9 Sofia Papagiannaki
        if 'recaptcha_challenge_field' in self.cleaned_data:
129 db7fecd9 Sofia Papagiannaki
            self.validate_captcha()
130 db7fecd9 Sofia Papagiannaki
        return self.cleaned_data['recaptcha_response_field']
131 db7fecd9 Sofia Papagiannaki
132 db7fecd9 Sofia Papagiannaki
    def clean_recaptcha_challenge_field(self):
133 db7fecd9 Sofia Papagiannaki
        if 'recaptcha_response_field' in self.cleaned_data:
134 db7fecd9 Sofia Papagiannaki
            self.validate_captcha()
135 db7fecd9 Sofia Papagiannaki
        return self.cleaned_data['recaptcha_challenge_field']
136 db7fecd9 Sofia Papagiannaki
137 db7fecd9 Sofia Papagiannaki
    def validate_captcha(self):
138 db7fecd9 Sofia Papagiannaki
        rcf = self.cleaned_data['recaptcha_challenge_field']
139 db7fecd9 Sofia Papagiannaki
        rrf = self.cleaned_data['recaptcha_response_field']
140 db7fecd9 Sofia Papagiannaki
        check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip)
141 db7fecd9 Sofia Papagiannaki
        if not check.is_valid:
142 db7fecd9 Sofia Papagiannaki
            raise forms.ValidationError(_('You have not entered the correct words'))
143 18ffbee1 Sofia Papagiannaki
144 890b0eaf Sofia Papagiannaki
    def save(self, commit=True):
145 64cd4730 Antony Chazapis
        """
146 890b0eaf Sofia Papagiannaki
        Saves the email, first_name and last_name properties, after the normal
147 890b0eaf Sofia Papagiannaki
        save behavior is complete.
148 890b0eaf Sofia Papagiannaki
        """
149 15efc749 Sofia Papagiannaki
        user = super(LocalUserCreationForm, self).save(commit=False)
150 3bf924ec Sofia Papagiannaki
        user.renew_token()
151 9fb8e808 Sofia Papagiannaki
        if commit:
152 9fb8e808 Sofia Papagiannaki
            user.save()
153 111f3da6 Sofia Papagiannaki
            logger._log(LOGGING_LEVEL, 'Created user %s' % user.email, [])
154 890b0eaf Sofia Papagiannaki
        return user
155 64cd4730 Antony Chazapis
156 15efc749 Sofia Papagiannaki
class InvitedLocalUserCreationForm(LocalUserCreationForm):
157 890b0eaf Sofia Papagiannaki
    """
158 062c970c Sofia Papagiannaki
    Extends the LocalUserCreationForm: email is readonly.
159 890b0eaf Sofia Papagiannaki
    """
160 794852f2 Sofia Papagiannaki
    class Meta:
161 794852f2 Sofia Papagiannaki
        model = AstakosUser
162 270dd48d Sofia Papagiannaki
        fields = ("email", "first_name", "last_name", "has_signed_terms")
163 18ffbee1 Sofia Papagiannaki
164 64cd4730 Antony Chazapis
    def __init__(self, *args, **kwargs):
165 64cd4730 Antony Chazapis
        """
166 3bf924ec Sofia Papagiannaki
        Changes the order of fields, and removes the username field.
167 64cd4730 Antony Chazapis
        """
168 15efc749 Sofia Papagiannaki
        super(InvitedLocalUserCreationForm, self).__init__(*args, **kwargs)
169 18ffbee1 Sofia Papagiannaki
170 64cd4730 Antony Chazapis
        #set readonly form fields
171 062c970c Sofia Papagiannaki
        ro = ('email', 'username',)
172 4e30244e Sofia Papagiannaki
        for f in ro:
173 4e30244e Sofia Papagiannaki
            self.fields[f].widget.attrs['readonly'] = True
174 4e30244e Sofia Papagiannaki
        
175 18ffbee1 Sofia Papagiannaki
176 9fb8e808 Sofia Papagiannaki
    def save(self, commit=True):
177 15efc749 Sofia Papagiannaki
        user = super(InvitedLocalUserCreationForm, self).save(commit=False)
178 881c856c Sofia Papagiannaki
        level = user.invitation.inviter.level + 1
179 881c856c Sofia Papagiannaki
        user.level = level
180 8316698a Sofia Papagiannaki
        user.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
181 8316698a Sofia Papagiannaki
        user.email_verified = True
182 9fb8e808 Sofia Papagiannaki
        if commit:
183 9fb8e808 Sofia Papagiannaki
            user.save()
184 9fb8e808 Sofia Papagiannaki
        return user
185 5ed6816e Sofia Papagiannaki
186 18ffbee1 Sofia Papagiannaki
class ThirdPartyUserCreationForm(forms.ModelForm):
187 8f5a3a06 Sofia Papagiannaki
    class Meta:
188 8f5a3a06 Sofia Papagiannaki
        model = AstakosUser
189 45f8d9ff Sofia Papagiannaki
        fields = ("email", "first_name", "last_name", "third_party_identifier", "has_signed_terms")
190 8f5a3a06 Sofia Papagiannaki
    
191 8f5a3a06 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
192 8f5a3a06 Sofia Papagiannaki
        """
193 8f5a3a06 Sofia Papagiannaki
        Changes the order of fields, and removes the username field.
194 8f5a3a06 Sofia Papagiannaki
        """
195 0a569195 Sofia Papagiannaki
        self.request = kwargs.get('request', None)
196 0a569195 Sofia Papagiannaki
        if self.request:
197 0a569195 Sofia Papagiannaki
            kwargs.pop('request')
198 8f5a3a06 Sofia Papagiannaki
        super(ThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
199 0a569195 Sofia Papagiannaki
        self.fields.keyOrder = ['email', 'first_name', 'last_name', 'third_party_identifier']
200 8f5a3a06 Sofia Papagiannaki
        if get_latest_terms():
201 8f5a3a06 Sofia Papagiannaki
            self.fields.keyOrder.append('has_signed_terms')
202 18ffbee1 Sofia Papagiannaki
        #set readonly form fields
203 ca828a10 Sofia Papagiannaki
        ro = ["third_party_identifier"]
204 18ffbee1 Sofia Papagiannaki
        for f in ro:
205 18ffbee1 Sofia Papagiannaki
            self.fields[f].widget.attrs['readonly'] = True
206 18ffbee1 Sofia Papagiannaki
        
207 18ffbee1 Sofia Papagiannaki
        if 'has_signed_terms' in self.fields:
208 18ffbee1 Sofia Papagiannaki
            # Overriding field label since we need to apply a link
209 18ffbee1 Sofia Papagiannaki
            # to the terms within the label
210 18ffbee1 Sofia Papagiannaki
            terms_link_html = '<a href="%s" target="_blank">%s</a>' \
211 18ffbee1 Sofia Papagiannaki
                    % (reverse('latest_terms'), _("the terms"))
212 18ffbee1 Sofia Papagiannaki
            self.fields['has_signed_terms'].label = \
213 18ffbee1 Sofia Papagiannaki
                    mark_safe("I agree with %s" % terms_link_html)
214 18ffbee1 Sofia Papagiannaki
    
215 18ffbee1 Sofia Papagiannaki
    def clean_email(self):
216 18ffbee1 Sofia Papagiannaki
        email = self.cleaned_data['email']
217 18ffbee1 Sofia Papagiannaki
        if not email:
218 18ffbee1 Sofia Papagiannaki
            raise forms.ValidationError(_("This field is required"))
219 0a569195 Sofia Papagiannaki
        return email
220 18ffbee1 Sofia Papagiannaki
    
221 18ffbee1 Sofia Papagiannaki
    def clean_has_signed_terms(self):
222 18ffbee1 Sofia Papagiannaki
        has_signed_terms = self.cleaned_data['has_signed_terms']
223 18ffbee1 Sofia Papagiannaki
        if not has_signed_terms:
224 18ffbee1 Sofia Papagiannaki
            raise forms.ValidationError(_('You have to agree with the terms'))
225 18ffbee1 Sofia Papagiannaki
        return has_signed_terms
226 8f5a3a06 Sofia Papagiannaki
    
227 8f5a3a06 Sofia Papagiannaki
    def save(self, commit=True):
228 8f5a3a06 Sofia Papagiannaki
        user = super(ThirdPartyUserCreationForm, self).save(commit=False)
229 8f5a3a06 Sofia Papagiannaki
        user.set_unusable_password()
230 18ffbee1 Sofia Papagiannaki
        user.renew_token()
231 0a569195 Sofia Papagiannaki
        user.provider = get_query(self.request).get('provider')
232 8f5a3a06 Sofia Papagiannaki
        if commit:
233 8f5a3a06 Sofia Papagiannaki
            user.save()
234 111f3da6 Sofia Papagiannaki
            logger._log(LOGGING_LEVEL, 'Created user %s' % user.email, [])
235 8f5a3a06 Sofia Papagiannaki
        return user
236 8f5a3a06 Sofia Papagiannaki
237 8f5a3a06 Sofia Papagiannaki
class InvitedThirdPartyUserCreationForm(ThirdPartyUserCreationForm):
238 4e30244e Sofia Papagiannaki
    """
239 062c970c Sofia Papagiannaki
    Extends the ThirdPartyUserCreationForm: email is readonly.
240 4e30244e Sofia Papagiannaki
    """
241 8f5a3a06 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
242 4e30244e Sofia Papagiannaki
        """
243 4e30244e Sofia Papagiannaki
        Changes the order of fields, and removes the username field.
244 4e30244e Sofia Papagiannaki
        """
245 8f5a3a06 Sofia Papagiannaki
        super(InvitedThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
246 4e30244e Sofia Papagiannaki
247 8f5a3a06 Sofia Papagiannaki
        #set readonly form fields
248 062c970c Sofia Papagiannaki
        ro = ('email',)
249 4e30244e Sofia Papagiannaki
        for f in ro:
250 4e30244e Sofia Papagiannaki
            self.fields[f].widget.attrs['readonly'] = True
251 4e30244e Sofia Papagiannaki
    
252 4e30244e Sofia Papagiannaki
    def save(self, commit=True):
253 4e30244e Sofia Papagiannaki
        user = super(InvitedThirdPartyUserCreationForm, self).save(commit=False)
254 4e30244e Sofia Papagiannaki
        level = user.invitation.inviter.level + 1
255 4e30244e Sofia Papagiannaki
        user.level = level
256 4e30244e Sofia Papagiannaki
        user.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
257 4e30244e Sofia Papagiannaki
        user.email_verified = True
258 4e30244e Sofia Papagiannaki
        if commit:
259 4e30244e Sofia Papagiannaki
            user.save()
260 4e30244e Sofia Papagiannaki
        return user
261 8f5a3a06 Sofia Papagiannaki
262 18ffbee1 Sofia Papagiannaki
class ShibbolethUserCreationForm(ThirdPartyUserCreationForm):
263 ca828a10 Sofia Papagiannaki
    additional_email = forms.CharField(widget=forms.HiddenInput(), label='', required = False)
264 ca828a10 Sofia Papagiannaki
    
265 ca828a10 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
266 ca828a10 Sofia Papagiannaki
        super(ShibbolethUserCreationForm, self).__init__(*args, **kwargs)
267 ca828a10 Sofia Papagiannaki
        self.fields.keyOrder.append('additional_email')
268 ca828a10 Sofia Papagiannaki
        # copy email value to additional_mail in case user will change it
269 ca828a10 Sofia Papagiannaki
        name = 'email'
270 ca828a10 Sofia Papagiannaki
        field = self.fields[name]
271 ca828a10 Sofia Papagiannaki
        self.initial['additional_email'] = self.initial.get(name, field.initial)
272 ca828a10 Sofia Papagiannaki
    
273 18ffbee1 Sofia Papagiannaki
    def clean_email(self):
274 18ffbee1 Sofia Papagiannaki
        email = self.cleaned_data['email']
275 0a569195 Sofia Papagiannaki
        for user in AstakosUser.objects.filter(email = email):
276 0a569195 Sofia Papagiannaki
            if user.provider == 'shibboleth':
277 18ffbee1 Sofia Papagiannaki
                raise forms.ValidationError(_("This email is already associated with another shibboleth account."))
278 591d0505 Sofia Papagiannaki
            elif not user.is_active:
279 591d0505 Sofia Papagiannaki
                raise forms.ValidationError(_("This email is already associated with an inactive account. \
280 591d0505 Sofia Papagiannaki
                                              You need to wait to be activated before being able to switch to a shibboleth account."))
281 ab8f7956 Sofia Papagiannaki
        super(ShibbolethUserCreationForm, self).clean_email()
282 0a569195 Sofia Papagiannaki
        return email
283 4e30244e Sofia Papagiannaki
284 0a569195 Sofia Papagiannaki
class InvitedShibbolethUserCreationForm(ShibbolethUserCreationForm, InvitedThirdPartyUserCreationForm):
285 4e30244e Sofia Papagiannaki
    pass
286 18ffbee1 Sofia Papagiannaki
    
287 5ed6816e Sofia Papagiannaki
class LoginForm(AuthenticationForm):
288 5ed6816e Sofia Papagiannaki
    username = forms.EmailField(label=_("Email"))
289 672d445a Sofia Papagiannaki
    recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
290 672d445a Sofia Papagiannaki
    recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='')
291 672d445a Sofia Papagiannaki
    
292 672d445a Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
293 672d445a Sofia Papagiannaki
        was_limited = kwargs.get('was_limited', False)
294 672d445a Sofia Papagiannaki
        request = kwargs.get('request', None)
295 672d445a Sofia Papagiannaki
        if request:
296 672d445a Sofia Papagiannaki
            self.ip = request.META.get('REMOTE_ADDR',
297 672d445a Sofia Papagiannaki
                                       request.META.get('HTTP_X_REAL_IP', None))
298 672d445a Sofia Papagiannaki
        
299 672d445a Sofia Papagiannaki
        t = ('request', 'was_limited')
300 672d445a Sofia Papagiannaki
        for elem in t:
301 672d445a Sofia Papagiannaki
            if elem in kwargs.keys():
302 672d445a Sofia Papagiannaki
                kwargs.pop(elem)
303 672d445a Sofia Papagiannaki
        super(LoginForm, self).__init__(*args, **kwargs)
304 672d445a Sofia Papagiannaki
        
305 672d445a Sofia Papagiannaki
        self.fields.keyOrder = ['username', 'password']
306 672d445a Sofia Papagiannaki
        if was_limited and RECAPTCHA_ENABLED:
307 672d445a Sofia Papagiannaki
            self.fields.keyOrder.extend(['recaptcha_challenge_field',
308 672d445a Sofia Papagiannaki
                                         'recaptcha_response_field',])
309 672d445a Sofia Papagiannaki
    
310 672d445a Sofia Papagiannaki
    def clean_recaptcha_response_field(self):
311 672d445a Sofia Papagiannaki
        if 'recaptcha_challenge_field' in self.cleaned_data:
312 672d445a Sofia Papagiannaki
            self.validate_captcha()
313 672d445a Sofia Papagiannaki
        return self.cleaned_data['recaptcha_response_field']
314 672d445a Sofia Papagiannaki
315 672d445a Sofia Papagiannaki
    def clean_recaptcha_challenge_field(self):
316 672d445a Sofia Papagiannaki
        if 'recaptcha_response_field' in self.cleaned_data:
317 672d445a Sofia Papagiannaki
            self.validate_captcha()
318 672d445a Sofia Papagiannaki
        return self.cleaned_data['recaptcha_challenge_field']
319 672d445a Sofia Papagiannaki
320 672d445a Sofia Papagiannaki
    def validate_captcha(self):
321 672d445a Sofia Papagiannaki
        rcf = self.cleaned_data['recaptcha_challenge_field']
322 672d445a Sofia Papagiannaki
        rrf = self.cleaned_data['recaptcha_response_field']
323 672d445a Sofia Papagiannaki
        check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip)
324 672d445a Sofia Papagiannaki
        if not check.is_valid:
325 672d445a Sofia Papagiannaki
            raise forms.ValidationError(_('You have not entered the correct words'))
326 eedb3923 Sofia Papagiannaki
    
327 eedb3923 Sofia Papagiannaki
    def clean(self):
328 eedb3923 Sofia Papagiannaki
        super(LoginForm, self).clean()
329 21f89374 Sofia Papagiannaki
        if self.user_cache and self.user_cache.provider not in ('local', ''):
330 eedb3923 Sofia Papagiannaki
            raise forms.ValidationError(_('Local login is not the current authentication method for this account.'))
331 eedb3923 Sofia Papagiannaki
        return self.cleaned_data
332 64cd4730 Antony Chazapis
333 890b0eaf Sofia Papagiannaki
class ProfileForm(forms.ModelForm):
334 890b0eaf Sofia Papagiannaki
    """
335 890b0eaf Sofia Papagiannaki
    Subclass of ``ModelForm`` for permiting user to edit his/her profile.
336 890b0eaf Sofia Papagiannaki
    Most of the fields are readonly since the user is not allowed to change them.
337 18ffbee1 Sofia Papagiannaki

338 890b0eaf Sofia Papagiannaki
    The class defines a save method which sets ``is_verified`` to True so as the user
339 890b0eaf Sofia Papagiannaki
    during the next login will not to be redirected to profile page.
340 890b0eaf Sofia Papagiannaki
    """
341 c301698f Sofia Papagiannaki
    renew = forms.BooleanField(label='Renew token', required=False)
342 18ffbee1 Sofia Papagiannaki
343 890b0eaf Sofia Papagiannaki
    class Meta:
344 890b0eaf Sofia Papagiannaki
        model = AstakosUser
345 0a569195 Sofia Papagiannaki
        fields = ('email', 'first_name', 'last_name', 'auth_token', 'auth_token_expires')
346 18ffbee1 Sofia Papagiannaki
347 64cd4730 Antony Chazapis
    def __init__(self, *args, **kwargs):
348 890b0eaf Sofia Papagiannaki
        super(ProfileForm, self).__init__(*args, **kwargs)
349 890b0eaf Sofia Papagiannaki
        instance = getattr(self, 'instance', None)
350 0a569195 Sofia Papagiannaki
        ro_fields = ('email', 'auth_token', 'auth_token_expires')
351 890b0eaf Sofia Papagiannaki
        if instance and instance.id:
352 890b0eaf Sofia Papagiannaki
            for field in ro_fields:
353 890b0eaf Sofia Papagiannaki
                self.fields[field].widget.attrs['readonly'] = True
354 18ffbee1 Sofia Papagiannaki
355 890b0eaf Sofia Papagiannaki
    def save(self, commit=True):
356 890b0eaf Sofia Papagiannaki
        user = super(ProfileForm, self).save(commit=False)
357 890b0eaf Sofia Papagiannaki
        user.is_verified = True
358 c301698f Sofia Papagiannaki
        if self.cleaned_data.get('renew'):
359 c301698f Sofia Papagiannaki
            user.renew_token()
360 890b0eaf Sofia Papagiannaki
        if commit:
361 890b0eaf Sofia Papagiannaki
            user.save()
362 890b0eaf Sofia Papagiannaki
        return user
363 64cd4730 Antony Chazapis
364 890b0eaf Sofia Papagiannaki
class FeedbackForm(forms.Form):
365 890b0eaf Sofia Papagiannaki
    """
366 890b0eaf Sofia Papagiannaki
    Form for writing feedback.
367 890b0eaf Sofia Papagiannaki
    """
368 0a569195 Sofia Papagiannaki
    feedback_msg = forms.CharField(widget=forms.Textarea, label=u'Message')
369 8f5a3a06 Sofia Papagiannaki
    feedback_data = forms.CharField(widget=forms.HiddenInput(), label='',
370 8f5a3a06 Sofia Papagiannaki
                                    required=False)
371 5ed6816e Sofia Papagiannaki
372 5ed6816e Sofia Papagiannaki
class SendInvitationForm(forms.Form):
373 5ed6816e Sofia Papagiannaki
    """
374 5ed6816e Sofia Papagiannaki
    Form for sending an invitations
375 5ed6816e Sofia Papagiannaki
    """
376 18ffbee1 Sofia Papagiannaki
377 5ed6816e Sofia Papagiannaki
    email = forms.EmailField(required = True, label = 'Email address')
378 5ed6816e Sofia Papagiannaki
    first_name = forms.EmailField(label = 'First name')
379 5ed6816e Sofia Papagiannaki
    last_name = forms.EmailField(label = 'Last name')
380 e2125441 Sofia Papagiannaki
381 e2125441 Sofia Papagiannaki
class ExtendedPasswordResetForm(PasswordResetForm):
382 e2125441 Sofia Papagiannaki
    """
383 e2125441 Sofia Papagiannaki
    Extends PasswordResetForm by overriding save method:
384 e2125441 Sofia Papagiannaki
    passes a custom from_email in send_mail.
385 18ffbee1 Sofia Papagiannaki

386 e2125441 Sofia Papagiannaki
    Since Django 1.3 this is useless since ``django.contrib.auth.views.reset_password``
387 e2125441 Sofia Papagiannaki
    accepts a from_email argument.
388 e2125441 Sofia Papagiannaki
    """
389 23c271b3 Sofia Papagiannaki
    def clean_email(self):
390 23c271b3 Sofia Papagiannaki
        email = super(ExtendedPasswordResetForm, self).clean_email()
391 23c271b3 Sofia Papagiannaki
        try:
392 0a569195 Sofia Papagiannaki
            user = AstakosUser.objects.get(email=email, is_active=True)
393 23c271b3 Sofia Papagiannaki
            if not user.has_usable_password():
394 23c271b3 Sofia Papagiannaki
                raise forms.ValidationError(_("This account has not a usable password."))
395 23c271b3 Sofia Papagiannaki
        except AstakosUser.DoesNotExist, e:
396 23c271b3 Sofia Papagiannaki
            raise forms.ValidationError(_('That e-mail address doesn\'t have an associated user account. Are you sure you\'ve registered?'))
397 23c271b3 Sofia Papagiannaki
        return email
398 23c271b3 Sofia Papagiannaki
    
399 e2125441 Sofia Papagiannaki
    def save(self, domain_override=None, email_template_name='registration/password_reset_email.html',
400 e2125441 Sofia Papagiannaki
             use_https=False, token_generator=default_token_generator, request=None):
401 e2125441 Sofia Papagiannaki
        """
402 e2125441 Sofia Papagiannaki
        Generates a one-use only link for resetting password and sends to the user.
403 e2125441 Sofia Papagiannaki
        """
404 e2125441 Sofia Papagiannaki
        for user in self.users_cache:
405 18ffbee1 Sofia Papagiannaki
            url = reverse('django.contrib.auth.views.password_reset_confirm',
406 18ffbee1 Sofia Papagiannaki
                          kwargs={'uidb36':int_to_base36(user.id),
407 18ffbee1 Sofia Papagiannaki
                                  'token':token_generator.make_token(user)})
408 a53b19da Sofia Papagiannaki
            url = urljoin(BASEURL, url)
409 e2125441 Sofia Papagiannaki
            t = loader.get_template(email_template_name)
410 e2125441 Sofia Papagiannaki
            c = {
411 e2125441 Sofia Papagiannaki
                'email': user.email,
412 8f378756 Sofia Papagiannaki
                'url': url,
413 374611bc Sofia Papagiannaki
                'site_name': SITENAME,
414 e2125441 Sofia Papagiannaki
                'user': user,
415 a53b19da Sofia Papagiannaki
                'baseurl': BASEURL,
416 09122dd8 Sofia Papagiannaki
                'support': DEFAULT_CONTACT_EMAIL
417 e2125441 Sofia Papagiannaki
            }
418 d552ecb7 Antony Chazapis
            from_email = DEFAULT_FROM_EMAIL
419 4abc7b29 Sofia Papagiannaki
            send_mail(_("Password reset on %s alpha2 testing") % SITENAME,
420 e2125441 Sofia Papagiannaki
                t.render(Context(c)), from_email, [user.email])
421 270dd48d Sofia Papagiannaki
422 49790d9d Sofia Papagiannaki
class EmailChangeForm(forms.ModelForm):
423 49790d9d Sofia Papagiannaki
    class Meta:
424 49790d9d Sofia Papagiannaki
        model = EmailChange
425 49790d9d Sofia Papagiannaki
        fields = ('new_email_address',)
426 49790d9d Sofia Papagiannaki
            
427 49790d9d Sofia Papagiannaki
    def clean_new_email_address(self):
428 49790d9d Sofia Papagiannaki
        addr = self.cleaned_data['new_email_address']
429 49790d9d Sofia Papagiannaki
        if AstakosUser.objects.filter(email__iexact=addr):
430 49790d9d Sofia Papagiannaki
            raise forms.ValidationError(_(u'This email address is already in use. Please supply a different email address.'))
431 49790d9d Sofia Papagiannaki
        return addr
432 49790d9d Sofia Papagiannaki
    
433 49790d9d Sofia Papagiannaki
    def save(self, email_template_name, request, commit=True):
434 49790d9d Sofia Papagiannaki
        ec = super(EmailChangeForm, self).save(commit=False)
435 49790d9d Sofia Papagiannaki
        ec.user = request.user
436 49790d9d Sofia Papagiannaki
        activation_key = hashlib.sha1(str(random()) + smart_str(ec.new_email_address))
437 49790d9d Sofia Papagiannaki
        ec.activation_key=activation_key.hexdigest()
438 49790d9d Sofia Papagiannaki
        if commit:
439 49790d9d Sofia Papagiannaki
            ec.save()
440 49790d9d Sofia Papagiannaki
        send_change_email(ec, request, email_template_name=email_template_name)
441 49790d9d Sofia Papagiannaki
442 270dd48d Sofia Papagiannaki
class SignApprovalTermsForm(forms.ModelForm):
443 270dd48d Sofia Papagiannaki
    class Meta:
444 270dd48d Sofia Papagiannaki
        model = AstakosUser
445 270dd48d Sofia Papagiannaki
        fields = ("has_signed_terms",)
446 18ffbee1 Sofia Papagiannaki
447 270dd48d Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
448 270dd48d Sofia Papagiannaki
        super(SignApprovalTermsForm, self).__init__(*args, **kwargs)
449 18ffbee1 Sofia Papagiannaki
450 270dd48d Sofia Papagiannaki
    def clean_has_signed_terms(self):
451 270dd48d Sofia Papagiannaki
        has_signed_terms = self.cleaned_data['has_signed_terms']
452 270dd48d Sofia Papagiannaki
        if not has_signed_terms:
453 270dd48d Sofia Papagiannaki
            raise forms.ValidationError(_('You have to agree with the terms'))
454 270dd48d Sofia Papagiannaki
        return has_signed_terms
455 8f5a3a06 Sofia Papagiannaki
456 8f5a3a06 Sofia Papagiannaki
class InvitationForm(forms.ModelForm):
457 8f5a3a06 Sofia Papagiannaki
    username = forms.EmailField(label=_("Email"))
458 8f5a3a06 Sofia Papagiannaki
    
459 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
460 18ffbee1 Sofia Papagiannaki
        super(InvitationForm, self).__init__(*args, **kwargs)
461 18ffbee1 Sofia Papagiannaki
    
462 8f5a3a06 Sofia Papagiannaki
    class Meta:
463 8f5a3a06 Sofia Papagiannaki
        model = Invitation
464 8f5a3a06 Sofia Papagiannaki
        fields = ('username', 'realname')
465 8f5a3a06 Sofia Papagiannaki
    
466 8f5a3a06 Sofia Papagiannaki
    def clean_username(self):
467 8f5a3a06 Sofia Papagiannaki
        username = self.cleaned_data['username']
468 8f5a3a06 Sofia Papagiannaki
        try:
469 8f5a3a06 Sofia Papagiannaki
            Invitation.objects.get(username = username)
470 8f5a3a06 Sofia Papagiannaki
            raise forms.ValidationError(_('There is already invitation for this email.'))
471 8f5a3a06 Sofia Papagiannaki
        except Invitation.DoesNotExist:
472 8f5a3a06 Sofia Papagiannaki
            pass
473 18ffbee1 Sofia Papagiannaki
        return username
474 1039bab1 Sofia Papagiannaki
475 1039bab1 Sofia Papagiannaki
class ExtendedPasswordChangeForm(PasswordChangeForm):
476 1039bab1 Sofia Papagiannaki
    """
477 1039bab1 Sofia Papagiannaki
    Extends PasswordChangeForm by enabling user
478 1039bab1 Sofia Papagiannaki
    to optionally renew also the token.
479 1039bab1 Sofia Papagiannaki
    """
480 1039bab1 Sofia Papagiannaki
    renew = forms.BooleanField(label='Renew token', required=False)
481 1039bab1 Sofia Papagiannaki
    
482 1039bab1 Sofia Papagiannaki
    def __init__(self, user, *args, **kwargs):
483 1039bab1 Sofia Papagiannaki
        super(ExtendedPasswordChangeForm, self).__init__(user, *args, **kwargs)
484 1039bab1 Sofia Papagiannaki
    
485 1039bab1 Sofia Papagiannaki
    def save(self, commit=True):
486 1039bab1 Sofia Papagiannaki
        user = super(ExtendedPasswordChangeForm, self).save(commit=False)
487 1039bab1 Sofia Papagiannaki
        if self.cleaned_data.get('renew'):
488 1039bab1 Sofia Papagiannaki
            user.renew_token()
489 1039bab1 Sofia Papagiannaki
        if commit:
490 1039bab1 Sofia Papagiannaki
            user.save()
491 8e45d6fd Sofia Papagiannaki
        return user
492 8e45d6fd Sofia Papagiannaki
493 8e45d6fd Sofia Papagiannaki
def get_astakos_group_creation_form(request):
494 8e45d6fd Sofia Papagiannaki
    class AstakosGroupCreationForm(forms.ModelForm):
495 373daf6a Sofia Papagiannaki
        issue_date = forms.DateField(widget=SelectDateWidget(), initial=datetime.now())
496 373daf6a Sofia Papagiannaki
        # TODO set initial in exact one month
497 373daf6a Sofia Papagiannaki
        expiration_date = forms.DateField(widget=SelectDateWidget(), initial = datetime.now() + timedelta(days=30))
498 8e45d6fd Sofia Papagiannaki
        kind = forms.ModelChoiceField(queryset=GroupKind.objects.all(), empty_label=None)
499 76c68fd6 Sofia Papagiannaki
        name = forms.URLField()
500 8e45d6fd Sofia Papagiannaki
        
501 8e45d6fd Sofia Papagiannaki
        class Meta:
502 8e45d6fd Sofia Papagiannaki
            model = AstakosGroup
503 8e45d6fd Sofia Papagiannaki
        
504 8e45d6fd Sofia Papagiannaki
        def __init__(self, *args, **kwargs):
505 8e45d6fd Sofia Papagiannaki
            super(AstakosGroupCreationForm, self).__init__(*args, **kwargs)
506 76c68fd6 Sofia Papagiannaki
            self.fields.keyOrder = ['kind', 'name', 'desc', 'issue_date',
507 8e45d6fd Sofia Papagiannaki
                                    'expiration_date', 'estimated_participants',
508 28252c7f Sofia Papagiannaki
                                    'moderation_enabled']
509 8e45d6fd Sofia Papagiannaki
        
510 8e45d6fd Sofia Papagiannaki
        def save(self, commit=True):
511 8e45d6fd Sofia Papagiannaki
            g = super(AstakosGroupCreationForm, self).save(commit=False)
512 8e45d6fd Sofia Papagiannaki
            if commit: 
513 8e45d6fd Sofia Papagiannaki
                g.save()
514 8e45d6fd Sofia Papagiannaki
                g.owner = [request.user]
515 373daf6a Sofia Papagiannaki
                g.approve_member(request.user)
516 8e45d6fd Sofia Papagiannaki
            return g
517 8e45d6fd Sofia Papagiannaki
    
518 8e45d6fd Sofia Papagiannaki
    return AstakosGroupCreationForm
519 8e45d6fd Sofia Papagiannaki
520 373daf6a Sofia Papagiannaki
def get_astakos_group_policy_creation_form(astakosgroup):
521 8e45d6fd Sofia Papagiannaki
    class AstakosGroupPolicyCreationForm(forms.ModelForm):
522 373daf6a Sofia Papagiannaki
        choices = Resource.objects.filter(~Q(astakosgroup=astakosgroup))
523 8e45d6fd Sofia Papagiannaki
        resource = forms.ModelChoiceField(queryset=choices, empty_label=None)
524 373daf6a Sofia Papagiannaki
        # TODO check that it does not hit the db
525 373daf6a Sofia Papagiannaki
        group = forms.ModelChoiceField(queryset=AstakosGroup.objects.all(), initial=astakosgroup, widget=forms.HiddenInput())
526 8e45d6fd Sofia Papagiannaki
        
527 8e45d6fd Sofia Papagiannaki
        class Meta:
528 8e45d6fd Sofia Papagiannaki
            model = AstakosGroupQuota
529 8e45d6fd Sofia Papagiannaki
    
530 7cb372c7 Sofia Papagiannaki
    return AstakosGroupPolicyCreationForm
531 01ac12d5 Sofia Papagiannaki
532 01ac12d5 Sofia Papagiannaki
class AstakosGroupSearchForm(forms.Form):
533 01ac12d5 Sofia Papagiannaki
    q = forms.CharField(max_length=200, label='')
534 01ac12d5 Sofia Papagiannaki
535 01ac12d5 Sofia Papagiannaki
class MembershipCreationForm(forms.ModelForm):
536 01ac12d5 Sofia Papagiannaki
    # TODO check not to hit the db
537 01ac12d5 Sofia Papagiannaki
    group = forms.ModelChoiceField(queryset=AstakosGroup.objects.all(), widget=forms.HiddenInput())
538 01ac12d5 Sofia Papagiannaki
    person = forms.ModelChoiceField(queryset=AstakosUser.objects.all(), widget=forms.HiddenInput())
539 01ac12d5 Sofia Papagiannaki
    date_requested = forms.DateField(widget=forms.HiddenInput(), input_formats="%d/%m/%Y")
540 01ac12d5 Sofia Papagiannaki
    
541 01ac12d5 Sofia Papagiannaki
    class Meta:
542 01ac12d5 Sofia Papagiannaki
        model = Membership
543 01ac12d5 Sofia Papagiannaki
        exclude = ('date_joined',)
544 01ac12d5 Sofia Papagiannaki
    
545 01ac12d5 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
546 01ac12d5 Sofia Papagiannaki
        super(MembershipCreationForm, self).__init__(*args, **kwargs)