Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / forms.py @ 01223f04

History | View | Annotate | Download (33.7 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 caf70869 Sofia Papagiannaki
from random import random
35 69b26576 Sofia Papagiannaki
from datetime import datetime, timedelta
36 64cd4730 Antony Chazapis
37 64cd4730 Antony Chazapis
from django import forms
38 64cd4730 Antony Chazapis
from django.utils.translation import ugettext as _
39 e1a80257 Sofia Papagiannaki
from django.contrib.auth.forms import (
40 e1a80257 Sofia Papagiannaki
    UserCreationForm, AuthenticationForm,
41 e1a80257 Sofia Papagiannaki
    PasswordResetForm, PasswordChangeForm,
42 73fbaec4 Sofia Papagiannaki
    SetPasswordForm)
43 e2125441 Sofia Papagiannaki
from django.core.mail import send_mail
44 e2125441 Sofia Papagiannaki
from django.contrib.auth.tokens import default_token_generator
45 e2125441 Sofia Papagiannaki
from django.template import Context, loader
46 e2125441 Sofia Papagiannaki
from django.utils.http import int_to_base36
47 374611bc Sofia Papagiannaki
from django.core.urlresolvers import reverse
48 18ffbee1 Sofia Papagiannaki
from django.utils.safestring import mark_safe
49 49790d9d Sofia Papagiannaki
from django.utils.encoding import smart_str
50 1cbce16f Sofia Papagiannaki
from django.conf import settings
51 678b2236 Sofia Papagiannaki
from django.forms.models import fields_for_model
52 d2633501 Kostas Papadimitriou
from django.db import transaction
53 caf70869 Sofia Papagiannaki
from django.utils.encoding import smart_unicode
54 caf70869 Sofia Papagiannaki
from django.core import validators
55 e1a80257 Sofia Papagiannaki
from django.contrib.auth.models import AnonymousUser
56 e47fb17a Sofia Papagiannaki
from django.core.exceptions import PermissionDenied
57 5ed6816e Sofia Papagiannaki
58 678b2236 Sofia Papagiannaki
from astakos.im.models import (
59 73fbaec4 Sofia Papagiannaki
    AstakosUser, EmailChange, Invitation,
60 e1a80257 Sofia Papagiannaki
    Resource, PendingThirdPartyUser, get_latest_terms, RESOURCE_SEPARATOR,
61 e47fb17a Sofia Papagiannaki
    ProjectApplication, Project)
62 c0b26605 Sofia Papagiannaki
from astakos.im.settings import (
63 c0b26605 Sofia Papagiannaki
    INVITATIONS_PER_LEVEL, BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY,
64 c0b26605 Sofia Papagiannaki
    RECAPTCHA_ENABLED, DEFAULT_CONTACT_EMAIL, LOGGING_LEVEL,
65 2e90e3ec Kostas Papadimitriou
    PASSWORD_RESET_EMAIL_SUBJECT, NEWPASSWD_INVALIDATE_TOKEN,
66 272cf735 Sofia Papagiannaki
    MODERATION_ENABLED, PROJECT_MEMBER_JOIN_POLICIES,
67 272cf735 Sofia Papagiannaki
    PROJECT_MEMBER_LEAVE_POLICIES)
68 45f8d9ff Sofia Papagiannaki
from astakos.im.widgets import DummyWidget, RecaptchaWidget
69 e47fb17a Sofia Papagiannaki
from astakos.im.functions import (
70 e47fb17a Sofia Papagiannaki
    send_change_email, submit_application, do_accept_membership_checks)
71 270dd48d Sofia Papagiannaki
72 43332a76 Kostas Papadimitriou
from astakos.im.util import reserved_email, reserved_verified_email, \
73 43332a76 Kostas Papadimitriou
                            get_query, model_to_dict
74 c4b1a172 Kostas Papadimitriou
from astakos.im import auth_providers
75 64cd4730 Antony Chazapis
76 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
77 64cd4730 Antony Chazapis
78 3bf924ec Sofia Papagiannaki
import logging
79 49790d9d Sofia Papagiannaki
import hashlib
80 db7fecd9 Sofia Papagiannaki
import recaptcha.client.captcha as captcha
81 caf70869 Sofia Papagiannaki
import re
82 64cd4730 Antony Chazapis
83 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
84 e015e9e6 Sofia Papagiannaki
85 caf70869 Sofia Papagiannaki
DOMAIN_VALUE_REGEX = re.compile(
86 892410d3 Sofia Papagiannaki
    r'^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$',
87 73fbaec4 Sofia Papagiannaki
    re.IGNORECASE)
88 d2633501 Kostas Papadimitriou
89 01223f04 Sofia Papagiannaki
today = datetime.now()
90 01223f04 Sofia Papagiannaki
today = datetime(today.year, today.month, today.day)
91 01223f04 Sofia Papagiannaki
92 caf70869 Sofia Papagiannaki
class StoreUserMixin(object):
93 e5966bd9 Kostas Papadimitriou
94 d2633501 Kostas Papadimitriou
    def store_user(self, user, request):
95 43332a76 Kostas Papadimitriou
        """
96 43332a76 Kostas Papadimitriou
        WARNING: this should be wrapped inside a transactional view/method.
97 43332a76 Kostas Papadimitriou
        """
98 d2633501 Kostas Papadimitriou
        user.save()
99 d2633501 Kostas Papadimitriou
        self.post_store_user(user, request)
100 d2633501 Kostas Papadimitriou
        return user
101 d2633501 Kostas Papadimitriou
102 d2633501 Kostas Papadimitriou
    def post_store_user(self, user, request):
103 d2633501 Kostas Papadimitriou
        """
104 d2633501 Kostas Papadimitriou
        Interface method for descendant backends to be able to do stuff within
105 d2633501 Kostas Papadimitriou
        the transaction enabled by store_user.
106 d2633501 Kostas Papadimitriou
        """
107 d2633501 Kostas Papadimitriou
        pass
108 d2633501 Kostas Papadimitriou
109 d2633501 Kostas Papadimitriou
110 d2633501 Kostas Papadimitriou
class LocalUserCreationForm(UserCreationForm, StoreUserMixin):
111 890b0eaf Sofia Papagiannaki
    """
112 890b0eaf Sofia Papagiannaki
    Extends the built in UserCreationForm in several ways:
113 18ffbee1 Sofia Papagiannaki

114 e47fb17a Sofia Papagiannaki
    * Adds email, first_name, last_name, recaptcha_challenge_field,
115 e47fb17a Sofia Papagiannaki
    * recaptcha_response_field field.
116 5ed6816e Sofia Papagiannaki
    * The username field isn't visible and it is assigned a generated id.
117 18ffbee1 Sofia Papagiannaki
    * User created is not active.
118 890b0eaf Sofia Papagiannaki
    """
119 db7fecd9 Sofia Papagiannaki
    recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
120 5ce3ce4f Sofia Papagiannaki
    recaptcha_response_field = forms.CharField(
121 5ce3ce4f Sofia Papagiannaki
        widget=RecaptchaWidget, label='')
122 18ffbee1 Sofia Papagiannaki
123 794852f2 Sofia Papagiannaki
    class Meta:
124 794852f2 Sofia Papagiannaki
        model = AstakosUser
125 5ce3ce4f Sofia Papagiannaki
        fields = ("email", "first_name", "last_name",
126 5ce3ce4f Sofia Papagiannaki
                  "has_signed_terms", "has_signed_terms")
127 18ffbee1 Sofia Papagiannaki
128 64cd4730 Antony Chazapis
    def __init__(self, *args, **kwargs):
129 64cd4730 Antony Chazapis
        """
130 890b0eaf Sofia Papagiannaki
        Changes the order of fields, and removes the username field.
131 64cd4730 Antony Chazapis
        """
132 bf0c6de5 Sofia Papagiannaki
        request = kwargs.pop('request', None)
133 672d445a Sofia Papagiannaki
        if request:
134 672d445a Sofia Papagiannaki
            self.ip = request.META.get('REMOTE_ADDR',
135 672d445a Sofia Papagiannaki
                                       request.META.get('HTTP_X_REAL_IP', None))
136 ab8bfb29 Kostas Papadimitriou
137 15efc749 Sofia Papagiannaki
        super(LocalUserCreationForm, self).__init__(*args, **kwargs)
138 890b0eaf Sofia Papagiannaki
        self.fields.keyOrder = ['email', 'first_name', 'last_name',
139 d8f63346 Sofia Papagiannaki
                                'password1', 'password2']
140 1b3398a0 Olga Brani
141 53bf2659 Sofia Papagiannaki
        if RECAPTCHA_ENABLED:
142 53bf2659 Sofia Papagiannaki
            self.fields.keyOrder.extend(['recaptcha_challenge_field',
143 5ce3ce4f Sofia Papagiannaki
                                         'recaptcha_response_field', ])
144 1b3398a0 Olga Brani
        if get_latest_terms():
145 1b3398a0 Olga Brani
            self.fields.keyOrder.append('has_signed_terms')
146 18ffbee1 Sofia Papagiannaki
147 18ffbee1 Sofia Papagiannaki
        if 'has_signed_terms' in self.fields:
148 18ffbee1 Sofia Papagiannaki
            # Overriding field label since we need to apply a link
149 18ffbee1 Sofia Papagiannaki
            # to the terms within the label
150 18ffbee1 Sofia Papagiannaki
            terms_link_html = '<a href="%s" target="_blank">%s</a>' \
151 5ce3ce4f Sofia Papagiannaki
                % (reverse('latest_terms'), _("the terms"))
152 18ffbee1 Sofia Papagiannaki
            self.fields['has_signed_terms'].label = \
153 5ce3ce4f Sofia Papagiannaki
                mark_safe("I agree with %s" % terms_link_html)
154 18ffbee1 Sofia Papagiannaki
155 af4eb974 Sofia Papagiannaki
    def clean_email(self):
156 e5966bd9 Kostas Papadimitriou
        email = self.cleaned_data['email']
157 881c856c Sofia Papagiannaki
        if not email:
158 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.REQUIRED_FIELD))
159 43332a76 Kostas Papadimitriou
        if reserved_verified_email(email):
160 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.EMAIL_USED))
161 0a569195 Sofia Papagiannaki
        return email
162 18ffbee1 Sofia Papagiannaki
163 270dd48d Sofia Papagiannaki
    def clean_has_signed_terms(self):
164 270dd48d Sofia Papagiannaki
        has_signed_terms = self.cleaned_data['has_signed_terms']
165 270dd48d Sofia Papagiannaki
        if not has_signed_terms:
166 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.SIGN_TERMS))
167 270dd48d Sofia Papagiannaki
        return has_signed_terms
168 18ffbee1 Sofia Papagiannaki
169 db7fecd9 Sofia Papagiannaki
    def clean_recaptcha_response_field(self):
170 db7fecd9 Sofia Papagiannaki
        if 'recaptcha_challenge_field' in self.cleaned_data:
171 db7fecd9 Sofia Papagiannaki
            self.validate_captcha()
172 db7fecd9 Sofia Papagiannaki
        return self.cleaned_data['recaptcha_response_field']
173 db7fecd9 Sofia Papagiannaki
174 db7fecd9 Sofia Papagiannaki
    def clean_recaptcha_challenge_field(self):
175 db7fecd9 Sofia Papagiannaki
        if 'recaptcha_response_field' in self.cleaned_data:
176 db7fecd9 Sofia Papagiannaki
            self.validate_captcha()
177 db7fecd9 Sofia Papagiannaki
        return self.cleaned_data['recaptcha_challenge_field']
178 db7fecd9 Sofia Papagiannaki
179 db7fecd9 Sofia Papagiannaki
    def validate_captcha(self):
180 db7fecd9 Sofia Papagiannaki
        rcf = self.cleaned_data['recaptcha_challenge_field']
181 db7fecd9 Sofia Papagiannaki
        rrf = self.cleaned_data['recaptcha_response_field']
182 db7fecd9 Sofia Papagiannaki
        check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip)
183 db7fecd9 Sofia Papagiannaki
        if not check.is_valid:
184 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.CAPTCHA_VALIDATION_ERR))
185 18ffbee1 Sofia Papagiannaki
186 d2633501 Kostas Papadimitriou
    def post_store_user(self, user, request):
187 d2633501 Kostas Papadimitriou
        """
188 d2633501 Kostas Papadimitriou
        Interface method for descendant backends to be able to do stuff within
189 d2633501 Kostas Papadimitriou
        the transaction enabled by store_user.
190 d2633501 Kostas Papadimitriou
        """
191 d2633501 Kostas Papadimitriou
        user.add_auth_provider('local', auth_backend='astakos')
192 d2633501 Kostas Papadimitriou
        user.set_password(self.cleaned_data['password1'])
193 d2633501 Kostas Papadimitriou
194 890b0eaf Sofia Papagiannaki
    def save(self, commit=True):
195 64cd4730 Antony Chazapis
        """
196 890b0eaf Sofia Papagiannaki
        Saves the email, first_name and last_name properties, after the normal
197 890b0eaf Sofia Papagiannaki
        save behavior is complete.
198 890b0eaf Sofia Papagiannaki
        """
199 15efc749 Sofia Papagiannaki
        user = super(LocalUserCreationForm, self).save(commit=False)
200 d2633501 Kostas Papadimitriou
        user.renew_token()
201 9fb8e808 Sofia Papagiannaki
        if commit:
202 9fb8e808 Sofia Papagiannaki
            user.save()
203 aab4d540 Sofia Papagiannaki
            logger.log(LOGGING_LEVEL, 'Created user %s' % user.email)
204 890b0eaf Sofia Papagiannaki
        return user
205 64cd4730 Antony Chazapis
206 5ce3ce4f Sofia Papagiannaki
207 15efc749 Sofia Papagiannaki
class InvitedLocalUserCreationForm(LocalUserCreationForm):
208 890b0eaf Sofia Papagiannaki
    """
209 062c970c Sofia Papagiannaki
    Extends the LocalUserCreationForm: email is readonly.
210 890b0eaf Sofia Papagiannaki
    """
211 794852f2 Sofia Papagiannaki
    class Meta:
212 794852f2 Sofia Papagiannaki
        model = AstakosUser
213 270dd48d Sofia Papagiannaki
        fields = ("email", "first_name", "last_name", "has_signed_terms")
214 18ffbee1 Sofia Papagiannaki
215 64cd4730 Antony Chazapis
    def __init__(self, *args, **kwargs):
216 64cd4730 Antony Chazapis
        """
217 3bf924ec Sofia Papagiannaki
        Changes the order of fields, and removes the username field.
218 64cd4730 Antony Chazapis
        """
219 15efc749 Sofia Papagiannaki
        super(InvitedLocalUserCreationForm, self).__init__(*args, **kwargs)
220 18ffbee1 Sofia Papagiannaki
221 64cd4730 Antony Chazapis
        #set readonly form fields
222 062c970c Sofia Papagiannaki
        ro = ('email', 'username',)
223 4e30244e Sofia Papagiannaki
        for f in ro:
224 4e30244e Sofia Papagiannaki
            self.fields[f].widget.attrs['readonly'] = True
225 ab8bfb29 Kostas Papadimitriou
226 9fb8e808 Sofia Papagiannaki
    def save(self, commit=True):
227 15efc749 Sofia Papagiannaki
        user = super(InvitedLocalUserCreationForm, self).save(commit=False)
228 74836e50 Sofia Papagiannaki
        user.set_invitations_level()
229 8316698a Sofia Papagiannaki
        user.email_verified = True
230 9fb8e808 Sofia Papagiannaki
        if commit:
231 9fb8e808 Sofia Papagiannaki
            user.save()
232 9fb8e808 Sofia Papagiannaki
        return user
233 5ed6816e Sofia Papagiannaki
234 d2633501 Kostas Papadimitriou
235 d2633501 Kostas Papadimitriou
class ThirdPartyUserCreationForm(forms.ModelForm, StoreUserMixin):
236 6c8a3f7c Sofia Papagiannaki
    id = forms.CharField(
237 6c8a3f7c Sofia Papagiannaki
        widget=forms.HiddenInput(),
238 6c8a3f7c Sofia Papagiannaki
        label='',
239 6c8a3f7c Sofia Papagiannaki
        required=False
240 6c8a3f7c Sofia Papagiannaki
    )
241 ef20ea07 Sofia Papagiannaki
    third_party_identifier = forms.CharField(
242 ef20ea07 Sofia Papagiannaki
        widget=forms.HiddenInput(),
243 ef20ea07 Sofia Papagiannaki
        label=''
244 ef20ea07 Sofia Papagiannaki
    )
245 43332a76 Kostas Papadimitriou
246 8f5a3a06 Sofia Papagiannaki
    class Meta:
247 8f5a3a06 Sofia Papagiannaki
        model = AstakosUser
248 6c8a3f7c Sofia Papagiannaki
        fields = ['id', 'email', 'third_party_identifier', 'first_name', 'last_name']
249 ab8bfb29 Kostas Papadimitriou
250 8f5a3a06 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
251 8f5a3a06 Sofia Papagiannaki
        """
252 8f5a3a06 Sofia Papagiannaki
        Changes the order of fields, and removes the username field.
253 8f5a3a06 Sofia Papagiannaki
        """
254 0a569195 Sofia Papagiannaki
        self.request = kwargs.get('request', None)
255 0a569195 Sofia Papagiannaki
        if self.request:
256 0a569195 Sofia Papagiannaki
            kwargs.pop('request')
257 2e90e3ec Kostas Papadimitriou
258 ef20ea07 Sofia Papagiannaki
        latest_terms = get_latest_terms()
259 ef20ea07 Sofia Papagiannaki
        if latest_terms:
260 ef20ea07 Sofia Papagiannaki
            self._meta.fields.append('has_signed_terms')
261 2e90e3ec Kostas Papadimitriou
262 8f5a3a06 Sofia Papagiannaki
        super(ThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
263 2e90e3ec Kostas Papadimitriou
264 ef20ea07 Sofia Papagiannaki
        if latest_terms:
265 8f5a3a06 Sofia Papagiannaki
            self.fields.keyOrder.append('has_signed_terms')
266 2e90e3ec Kostas Papadimitriou
267 18ffbee1 Sofia Papagiannaki
        if 'has_signed_terms' in self.fields:
268 18ffbee1 Sofia Papagiannaki
            # Overriding field label since we need to apply a link
269 18ffbee1 Sofia Papagiannaki
            # to the terms within the label
270 18ffbee1 Sofia Papagiannaki
            terms_link_html = '<a href="%s" target="_blank">%s</a>' \
271 5ce3ce4f Sofia Papagiannaki
                % (reverse('latest_terms'), _("the terms"))
272 18ffbee1 Sofia Papagiannaki
            self.fields['has_signed_terms'].label = \
273 18ffbee1 Sofia Papagiannaki
                    mark_safe("I agree with %s" % terms_link_html)
274 2e90e3ec Kostas Papadimitriou
275 18ffbee1 Sofia Papagiannaki
    def clean_email(self):
276 e5966bd9 Kostas Papadimitriou
        email = self.cleaned_data['email']
277 18ffbee1 Sofia Papagiannaki
        if not email:
278 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.REQUIRED_FIELD))
279 43332a76 Kostas Papadimitriou
        if reserved_verified_email(email):
280 564a2292 Kostas Papadimitriou
            provider = auth_providers.get_provider(self.request.REQUEST.get('provider', 'local'))
281 564a2292 Kostas Papadimitriou
            extra_message = _(astakos_messages.EXISTING_EMAIL_THIRD_PARTY_NOTIFICATION) % \
282 564a2292 Kostas Papadimitriou
                    (provider.get_title_display, reverse('edit_profile'))
283 564a2292 Kostas Papadimitriou
284 564a2292 Kostas Papadimitriou
            raise forms.ValidationError(_(astakos_messages.EMAIL_USED) + ' ' + \
285 564a2292 Kostas Papadimitriou
                                        extra_message)
286 0a569195 Sofia Papagiannaki
        return email
287 ab8bfb29 Kostas Papadimitriou
288 18ffbee1 Sofia Papagiannaki
    def clean_has_signed_terms(self):
289 18ffbee1 Sofia Papagiannaki
        has_signed_terms = self.cleaned_data['has_signed_terms']
290 18ffbee1 Sofia Papagiannaki
        if not has_signed_terms:
291 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.SIGN_TERMS))
292 18ffbee1 Sofia Papagiannaki
        return has_signed_terms
293 ab8bfb29 Kostas Papadimitriou
294 d2633501 Kostas Papadimitriou
    def post_store_user(self, user, request):
295 d2633501 Kostas Papadimitriou
        pending = PendingThirdPartyUser.objects.get(
296 d2633501 Kostas Papadimitriou
                                token=request.POST.get('third_party_token'),
297 d2633501 Kostas Papadimitriou
                                third_party_identifier= \
298 43332a76 Kostas Papadimitriou
                            self.cleaned_data.get('third_party_identifier'))
299 d2633501 Kostas Papadimitriou
        return user.add_pending_auth_provider(pending)
300 d2633501 Kostas Papadimitriou
301 8f5a3a06 Sofia Papagiannaki
    def save(self, commit=True):
302 8f5a3a06 Sofia Papagiannaki
        user = super(ThirdPartyUserCreationForm, self).save(commit=False)
303 8f5a3a06 Sofia Papagiannaki
        user.set_unusable_password()
304 d2633501 Kostas Papadimitriou
        user.renew_token()
305 8f5a3a06 Sofia Papagiannaki
        if commit:
306 8f5a3a06 Sofia Papagiannaki
            user.save()
307 aab4d540 Sofia Papagiannaki
            logger.log(LOGGING_LEVEL, 'Created user %s' % user.email)
308 8f5a3a06 Sofia Papagiannaki
        return user
309 8f5a3a06 Sofia Papagiannaki
310 5ce3ce4f Sofia Papagiannaki
311 8f5a3a06 Sofia Papagiannaki
class InvitedThirdPartyUserCreationForm(ThirdPartyUserCreationForm):
312 4e30244e Sofia Papagiannaki
    """
313 062c970c Sofia Papagiannaki
    Extends the ThirdPartyUserCreationForm: email is readonly.
314 4e30244e Sofia Papagiannaki
    """
315 8f5a3a06 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
316 4e30244e Sofia Papagiannaki
        """
317 4e30244e Sofia Papagiannaki
        Changes the order of fields, and removes the username field.
318 4e30244e Sofia Papagiannaki
        """
319 5ce3ce4f Sofia Papagiannaki
        super(
320 5ce3ce4f Sofia Papagiannaki
            InvitedThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
321 4e30244e Sofia Papagiannaki
322 8f5a3a06 Sofia Papagiannaki
        #set readonly form fields
323 062c970c Sofia Papagiannaki
        ro = ('email',)
324 4e30244e Sofia Papagiannaki
        for f in ro:
325 4e30244e Sofia Papagiannaki
            self.fields[f].widget.attrs['readonly'] = True
326 ab8bfb29 Kostas Papadimitriou
327 4e30244e Sofia Papagiannaki
    def save(self, commit=True):
328 4e30244e Sofia Papagiannaki
        user = super(InvitedThirdPartyUserCreationForm, self).save(commit=False)
329 d2633501 Kostas Papadimitriou
        user.set_invitation_level()
330 4e30244e Sofia Papagiannaki
        user.email_verified = True
331 4e30244e Sofia Papagiannaki
        if commit:
332 4e30244e Sofia Papagiannaki
            user.save()
333 4e30244e Sofia Papagiannaki
        return user
334 8f5a3a06 Sofia Papagiannaki
335 5ce3ce4f Sofia Papagiannaki
336 18ffbee1 Sofia Papagiannaki
class ShibbolethUserCreationForm(ThirdPartyUserCreationForm):
337 5ce3ce4f Sofia Papagiannaki
    additional_email = forms.CharField(
338 5ce3ce4f Sofia Papagiannaki
        widget=forms.HiddenInput(), label='', required=False)
339 ab8bfb29 Kostas Papadimitriou
340 ca828a10 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
341 ca828a10 Sofia Papagiannaki
        super(ShibbolethUserCreationForm, self).__init__(*args, **kwargs)
342 ca828a10 Sofia Papagiannaki
        # copy email value to additional_mail in case user will change it
343 ca828a10 Sofia Papagiannaki
        name = 'email'
344 ca828a10 Sofia Papagiannaki
        field = self.fields[name]
345 ca828a10 Sofia Papagiannaki
        self.initial['additional_email'] = self.initial.get(name, field.initial)
346 ef20ea07 Sofia Papagiannaki
        self.initial['email'] = None
347 d2633501 Kostas Papadimitriou
348 4e30244e Sofia Papagiannaki
349 9a06d96f Olga Brani
class InvitedShibbolethUserCreationForm(ShibbolethUserCreationForm,
350 9a06d96f Olga Brani
                                        InvitedThirdPartyUserCreationForm):
351 4e30244e Sofia Papagiannaki
    pass
352 ab8bfb29 Kostas Papadimitriou
353 5ce3ce4f Sofia Papagiannaki
354 5ed6816e Sofia Papagiannaki
class LoginForm(AuthenticationForm):
355 5ed6816e Sofia Papagiannaki
    username = forms.EmailField(label=_("Email"))
356 672d445a Sofia Papagiannaki
    recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
357 5ce3ce4f Sofia Papagiannaki
    recaptcha_response_field = forms.CharField(
358 5ce3ce4f Sofia Papagiannaki
        widget=RecaptchaWidget, label='')
359 ab8bfb29 Kostas Papadimitriou
360 672d445a Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
361 672d445a Sofia Papagiannaki
        was_limited = kwargs.get('was_limited', False)
362 672d445a Sofia Papagiannaki
        request = kwargs.get('request', None)
363 672d445a Sofia Papagiannaki
        if request:
364 672d445a Sofia Papagiannaki
            self.ip = request.META.get('REMOTE_ADDR',
365 672d445a Sofia Papagiannaki
                                       request.META.get('HTTP_X_REAL_IP', None))
366 ab8bfb29 Kostas Papadimitriou
367 672d445a Sofia Papagiannaki
        t = ('request', 'was_limited')
368 672d445a Sofia Papagiannaki
        for elem in t:
369 672d445a Sofia Papagiannaki
            if elem in kwargs.keys():
370 672d445a Sofia Papagiannaki
                kwargs.pop(elem)
371 672d445a Sofia Papagiannaki
        super(LoginForm, self).__init__(*args, **kwargs)
372 ab8bfb29 Kostas Papadimitriou
373 672d445a Sofia Papagiannaki
        self.fields.keyOrder = ['username', 'password']
374 672d445a Sofia Papagiannaki
        if was_limited and RECAPTCHA_ENABLED:
375 672d445a Sofia Papagiannaki
            self.fields.keyOrder.extend(['recaptcha_challenge_field',
376 5ce3ce4f Sofia Papagiannaki
                                         'recaptcha_response_field', ])
377 9a06d96f Olga Brani
378 9a06d96f Olga Brani
    def clean_username(self):
379 4bdd7e3d Kostas Papadimitriou
        return self.cleaned_data['username'].lower()
380 ab8bfb29 Kostas Papadimitriou
381 672d445a Sofia Papagiannaki
    def clean_recaptcha_response_field(self):
382 672d445a Sofia Papagiannaki
        if 'recaptcha_challenge_field' in self.cleaned_data:
383 672d445a Sofia Papagiannaki
            self.validate_captcha()
384 672d445a Sofia Papagiannaki
        return self.cleaned_data['recaptcha_response_field']
385 672d445a Sofia Papagiannaki
386 672d445a Sofia Papagiannaki
    def clean_recaptcha_challenge_field(self):
387 672d445a Sofia Papagiannaki
        if 'recaptcha_response_field' in self.cleaned_data:
388 672d445a Sofia Papagiannaki
            self.validate_captcha()
389 672d445a Sofia Papagiannaki
        return self.cleaned_data['recaptcha_challenge_field']
390 672d445a Sofia Papagiannaki
391 672d445a Sofia Papagiannaki
    def validate_captcha(self):
392 672d445a Sofia Papagiannaki
        rcf = self.cleaned_data['recaptcha_challenge_field']
393 672d445a Sofia Papagiannaki
        rrf = self.cleaned_data['recaptcha_response_field']
394 672d445a Sofia Papagiannaki
        check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip)
395 672d445a Sofia Papagiannaki
        if not check.is_valid:
396 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.CAPTCHA_VALIDATION_ERR))
397 2e90e3ec Kostas Papadimitriou
398 eedb3923 Sofia Papagiannaki
    def clean(self):
399 1f3b4b39 Sofia Papagiannaki
        """
400 1f3b4b39 Sofia Papagiannaki
        Override default behavior in order to check user's activation later
401 1f3b4b39 Sofia Papagiannaki
        """
402 c4b1a172 Kostas Papadimitriou
        username = self.cleaned_data.get('username')
403 c4b1a172 Kostas Papadimitriou
404 bea8a810 Kostas Papadimitriou
        if username:
405 bea8a810 Kostas Papadimitriou
            try:
406 bea8a810 Kostas Papadimitriou
                user = AstakosUser.objects.get_by_identifier(username)
407 bea8a810 Kostas Papadimitriou
                if not user.has_auth_provider('local'):
408 bea8a810 Kostas Papadimitriou
                    provider = auth_providers.get_provider('local')
409 bea8a810 Kostas Papadimitriou
                    raise forms.ValidationError(
410 bea8a810 Kostas Papadimitriou
                        _(provider.get_message('NOT_ACTIVE_FOR_USER')))
411 bea8a810 Kostas Papadimitriou
            except AstakosUser.DoesNotExist:
412 bea8a810 Kostas Papadimitriou
                pass
413 c4b1a172 Kostas Papadimitriou
414 1f3b4b39 Sofia Papagiannaki
        try:
415 1f3b4b39 Sofia Papagiannaki
            super(LoginForm, self).clean()
416 1f3b4b39 Sofia Papagiannaki
        except forms.ValidationError, e:
417 c4b1a172 Kostas Papadimitriou
            if self.user_cache is None:
418 c4b1a172 Kostas Papadimitriou
                raise
419 c4b1a172 Kostas Papadimitriou
            if not self.user_cache.is_active:
420 c4b1a172 Kostas Papadimitriou
                raise forms.ValidationError(self.user_cache.get_inactive_message())
421 1f3b4b39 Sofia Papagiannaki
            if self.request:
422 1f3b4b39 Sofia Papagiannaki
                if not self.request.session.test_cookie_worked():
423 1f3b4b39 Sofia Papagiannaki
                    raise
424 eedb3923 Sofia Papagiannaki
        return self.cleaned_data
425 64cd4730 Antony Chazapis
426 5ce3ce4f Sofia Papagiannaki
427 890b0eaf Sofia Papagiannaki
class ProfileForm(forms.ModelForm):
428 890b0eaf Sofia Papagiannaki
    """
429 890b0eaf Sofia Papagiannaki
    Subclass of ``ModelForm`` for permiting user to edit his/her profile.
430 9a06d96f Olga Brani
    Most of the fields are readonly since the user is not allowed to change
431 9a06d96f Olga Brani
    them.
432 18ffbee1 Sofia Papagiannaki

433 9a06d96f Olga Brani
    The class defines a save method which sets ``is_verified`` to True so as the
434 9a06d96f Olga Brani
    user during the next login will not to be redirected to profile page.
435 890b0eaf Sofia Papagiannaki
    """
436 c301698f Sofia Papagiannaki
    renew = forms.BooleanField(label='Renew token', required=False)
437 18ffbee1 Sofia Papagiannaki
438 890b0eaf Sofia Papagiannaki
    class Meta:
439 890b0eaf Sofia Papagiannaki
        model = AstakosUser
440 5ce3ce4f Sofia Papagiannaki
        fields = ('email', 'first_name', 'last_name', 'auth_token',
441 5ce3ce4f Sofia Papagiannaki
                  'auth_token_expires')
442 18ffbee1 Sofia Papagiannaki
443 64cd4730 Antony Chazapis
    def __init__(self, *args, **kwargs):
444 bf0c6de5 Sofia Papagiannaki
        self.session_key = kwargs.pop('session_key', None)
445 890b0eaf Sofia Papagiannaki
        super(ProfileForm, self).__init__(*args, **kwargs)
446 890b0eaf Sofia Papagiannaki
        instance = getattr(self, 'instance', None)
447 0a569195 Sofia Papagiannaki
        ro_fields = ('email', 'auth_token', 'auth_token_expires')
448 890b0eaf Sofia Papagiannaki
        if instance and instance.id:
449 890b0eaf Sofia Papagiannaki
            for field in ro_fields:
450 890b0eaf Sofia Papagiannaki
                self.fields[field].widget.attrs['readonly'] = True
451 18ffbee1 Sofia Papagiannaki
452 890b0eaf Sofia Papagiannaki
    def save(self, commit=True):
453 890b0eaf Sofia Papagiannaki
        user = super(ProfileForm, self).save(commit=False)
454 890b0eaf Sofia Papagiannaki
        user.is_verified = True
455 c301698f Sofia Papagiannaki
        if self.cleaned_data.get('renew'):
456 bf0c6de5 Sofia Papagiannaki
            user.renew_token(
457 bf0c6de5 Sofia Papagiannaki
                flush_sessions=True,
458 bf0c6de5 Sofia Papagiannaki
                current_key=self.session_key
459 bf0c6de5 Sofia Papagiannaki
            )
460 890b0eaf Sofia Papagiannaki
        if commit:
461 890b0eaf Sofia Papagiannaki
            user.save()
462 890b0eaf Sofia Papagiannaki
        return user
463 64cd4730 Antony Chazapis
464 5ce3ce4f Sofia Papagiannaki
465 890b0eaf Sofia Papagiannaki
class FeedbackForm(forms.Form):
466 890b0eaf Sofia Papagiannaki
    """
467 890b0eaf Sofia Papagiannaki
    Form for writing feedback.
468 890b0eaf Sofia Papagiannaki
    """
469 0a569195 Sofia Papagiannaki
    feedback_msg = forms.CharField(widget=forms.Textarea, label=u'Message')
470 8f5a3a06 Sofia Papagiannaki
    feedback_data = forms.CharField(widget=forms.HiddenInput(), label='',
471 8f5a3a06 Sofia Papagiannaki
                                    required=False)
472 5ed6816e Sofia Papagiannaki
473 5ce3ce4f Sofia Papagiannaki
474 5ed6816e Sofia Papagiannaki
class SendInvitationForm(forms.Form):
475 5ed6816e Sofia Papagiannaki
    """
476 5ed6816e Sofia Papagiannaki
    Form for sending an invitations
477 5ed6816e Sofia Papagiannaki
    """
478 18ffbee1 Sofia Papagiannaki
479 5ce3ce4f Sofia Papagiannaki
    email = forms.EmailField(required=True, label='Email address')
480 5ce3ce4f Sofia Papagiannaki
    first_name = forms.EmailField(label='First name')
481 5ce3ce4f Sofia Papagiannaki
    last_name = forms.EmailField(label='Last name')
482 5ce3ce4f Sofia Papagiannaki
483 e2125441 Sofia Papagiannaki
484 e2125441 Sofia Papagiannaki
class ExtendedPasswordResetForm(PasswordResetForm):
485 e2125441 Sofia Papagiannaki
    """
486 dd5f8f4d Kostas Papadimitriou
    Extends PasswordResetForm by overriding
487 18ffbee1 Sofia Papagiannaki

488 dd5f8f4d Kostas Papadimitriou
    save method: to pass a custom from_email in send_mail.
489 dd5f8f4d Kostas Papadimitriou
    clean_email: to handle local auth provider checks
490 e2125441 Sofia Papagiannaki
    """
491 23c271b3 Sofia Papagiannaki
    def clean_email(self):
492 23c271b3 Sofia Papagiannaki
        email = super(ExtendedPasswordResetForm, self).clean_email()
493 23c271b3 Sofia Papagiannaki
        try:
494 dd5f8f4d Kostas Papadimitriou
            user = AstakosUser.objects.get_by_identifier(email)
495 dd5f8f4d Kostas Papadimitriou
496 dd5f8f4d Kostas Papadimitriou
            if not user.is_active:
497 dd5f8f4d Kostas Papadimitriou
                raise forms.ValidationError(_(astakos_messages.ACCOUNT_INACTIVE))
498 dd5f8f4d Kostas Papadimitriou
499 23c271b3 Sofia Papagiannaki
            if not user.has_usable_password():
500 ae497612 Olga Brani
                raise forms.ValidationError(_(astakos_messages.UNUSABLE_PASSWORD))
501 d2633501 Kostas Papadimitriou
502 d2633501 Kostas Papadimitriou
            if not user.can_change_password():
503 dd5f8f4d Kostas Papadimitriou
                raise forms.ValidationError(_(astakos_messages.AUTH_PROVIDER_CANNOT_CHANGE_PASSWORD))
504 23c271b3 Sofia Papagiannaki
        except AstakosUser.DoesNotExist, e:
505 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.EMAIL_UNKNOWN))
506 23c271b3 Sofia Papagiannaki
        return email
507 ab8bfb29 Kostas Papadimitriou
508 5ce3ce4f Sofia Papagiannaki
    def save(
509 5ce3ce4f Sofia Papagiannaki
        self, domain_override=None, email_template_name='registration/password_reset_email.html',
510 5ce3ce4f Sofia Papagiannaki
            use_https=False, token_generator=default_token_generator, request=None):
511 e2125441 Sofia Papagiannaki
        """
512 e2125441 Sofia Papagiannaki
        Generates a one-use only link for resetting password and sends to the user.
513 e2125441 Sofia Papagiannaki
        """
514 e2125441 Sofia Papagiannaki
        for user in self.users_cache:
515 d2633501 Kostas Papadimitriou
            url = user.astakosuser.get_password_reset_url(token_generator)
516 a53b19da Sofia Papagiannaki
            url = urljoin(BASEURL, url)
517 e2125441 Sofia Papagiannaki
            t = loader.get_template(email_template_name)
518 e2125441 Sofia Papagiannaki
            c = {
519 e2125441 Sofia Papagiannaki
                'email': user.email,
520 8f378756 Sofia Papagiannaki
                'url': url,
521 374611bc Sofia Papagiannaki
                'site_name': SITENAME,
522 e2125441 Sofia Papagiannaki
                'user': user,
523 a53b19da Sofia Papagiannaki
                'baseurl': BASEURL,
524 09122dd8 Sofia Papagiannaki
                'support': DEFAULT_CONTACT_EMAIL
525 e2125441 Sofia Papagiannaki
            }
526 1cbce16f Sofia Papagiannaki
            from_email = settings.SERVER_EMAIL
527 1fcf4a99 Kostas Papadimitriou
            send_mail(_(PASSWORD_RESET_EMAIL_SUBJECT),
528 5ce3ce4f Sofia Papagiannaki
                      t.render(Context(c)), from_email, [user.email])
529 5ce3ce4f Sofia Papagiannaki
530 270dd48d Sofia Papagiannaki
531 49790d9d Sofia Papagiannaki
class EmailChangeForm(forms.ModelForm):
532 34a76cdb Kostas Papadimitriou
533 49790d9d Sofia Papagiannaki
    class Meta:
534 49790d9d Sofia Papagiannaki
        model = EmailChange
535 49790d9d Sofia Papagiannaki
        fields = ('new_email_address',)
536 ab8bfb29 Kostas Papadimitriou
537 49790d9d Sofia Papagiannaki
    def clean_new_email_address(self):
538 49790d9d Sofia Papagiannaki
        addr = self.cleaned_data['new_email_address']
539 43332a76 Kostas Papadimitriou
        if reserved_verified_email(addr):
540 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.EMAIL_USED))
541 49790d9d Sofia Papagiannaki
        return addr
542 ab8bfb29 Kostas Papadimitriou
543 49790d9d Sofia Papagiannaki
    def save(self, email_template_name, request, commit=True):
544 49790d9d Sofia Papagiannaki
        ec = super(EmailChangeForm, self).save(commit=False)
545 49790d9d Sofia Papagiannaki
        ec.user = request.user
546 5ce3ce4f Sofia Papagiannaki
        activation_key = hashlib.sha1(
547 5ce3ce4f Sofia Papagiannaki
            str(random()) + smart_str(ec.new_email_address))
548 5ce3ce4f Sofia Papagiannaki
        ec.activation_key = activation_key.hexdigest()
549 49790d9d Sofia Papagiannaki
        if commit:
550 49790d9d Sofia Papagiannaki
            ec.save()
551 49790d9d Sofia Papagiannaki
        send_change_email(ec, request, email_template_name=email_template_name)
552 49790d9d Sofia Papagiannaki
553 5ce3ce4f Sofia Papagiannaki
554 270dd48d Sofia Papagiannaki
class SignApprovalTermsForm(forms.ModelForm):
555 34a76cdb Kostas Papadimitriou
556 270dd48d Sofia Papagiannaki
    class Meta:
557 270dd48d Sofia Papagiannaki
        model = AstakosUser
558 270dd48d Sofia Papagiannaki
        fields = ("has_signed_terms",)
559 18ffbee1 Sofia Papagiannaki
560 270dd48d Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
561 270dd48d Sofia Papagiannaki
        super(SignApprovalTermsForm, self).__init__(*args, **kwargs)
562 18ffbee1 Sofia Papagiannaki
563 270dd48d Sofia Papagiannaki
    def clean_has_signed_terms(self):
564 270dd48d Sofia Papagiannaki
        has_signed_terms = self.cleaned_data['has_signed_terms']
565 270dd48d Sofia Papagiannaki
        if not has_signed_terms:
566 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.SIGN_TERMS))
567 270dd48d Sofia Papagiannaki
        return has_signed_terms
568 8f5a3a06 Sofia Papagiannaki
569 5ce3ce4f Sofia Papagiannaki
570 8f5a3a06 Sofia Papagiannaki
class InvitationForm(forms.ModelForm):
571 34a76cdb Kostas Papadimitriou
572 8f5a3a06 Sofia Papagiannaki
    username = forms.EmailField(label=_("Email"))
573 ab8bfb29 Kostas Papadimitriou
574 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
575 18ffbee1 Sofia Papagiannaki
        super(InvitationForm, self).__init__(*args, **kwargs)
576 ab8bfb29 Kostas Papadimitriou
577 8f5a3a06 Sofia Papagiannaki
    class Meta:
578 8f5a3a06 Sofia Papagiannaki
        model = Invitation
579 8f5a3a06 Sofia Papagiannaki
        fields = ('username', 'realname')
580 ab8bfb29 Kostas Papadimitriou
581 8f5a3a06 Sofia Papagiannaki
    def clean_username(self):
582 8f5a3a06 Sofia Papagiannaki
        username = self.cleaned_data['username']
583 8f5a3a06 Sofia Papagiannaki
        try:
584 5ce3ce4f Sofia Papagiannaki
            Invitation.objects.get(username=username)
585 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.INVITATION_EMAIL_EXISTS))
586 8f5a3a06 Sofia Papagiannaki
        except Invitation.DoesNotExist:
587 8f5a3a06 Sofia Papagiannaki
            pass
588 18ffbee1 Sofia Papagiannaki
        return username
589 1039bab1 Sofia Papagiannaki
590 5ce3ce4f Sofia Papagiannaki
591 1039bab1 Sofia Papagiannaki
class ExtendedPasswordChangeForm(PasswordChangeForm):
592 1039bab1 Sofia Papagiannaki
    """
593 1039bab1 Sofia Papagiannaki
    Extends PasswordChangeForm by enabling user
594 1039bab1 Sofia Papagiannaki
    to optionally renew also the token.
595 1039bab1 Sofia Papagiannaki
    """
596 ee210d1d Sofia Papagiannaki
    if not NEWPASSWD_INVALIDATE_TOKEN:
597 48e9f076 Sofia Papagiannaki
        renew = forms.BooleanField(label='Renew token', required=False,
598 48e9f076 Sofia Papagiannaki
                                   initial=True,
599 48e9f076 Sofia Papagiannaki
                                   help_text='Unsetting this may result in security risk.')
600 ab8bfb29 Kostas Papadimitriou
601 1039bab1 Sofia Papagiannaki
    def __init__(self, user, *args, **kwargs):
602 bf0c6de5 Sofia Papagiannaki
        self.session_key = kwargs.pop('session_key', None)
603 1039bab1 Sofia Papagiannaki
        super(ExtendedPasswordChangeForm, self).__init__(user, *args, **kwargs)
604 ab8bfb29 Kostas Papadimitriou
605 1039bab1 Sofia Papagiannaki
    def save(self, commit=True):
606 bf0c6de5 Sofia Papagiannaki
        try:
607 bf0c6de5 Sofia Papagiannaki
            if NEWPASSWD_INVALIDATE_TOKEN or self.cleaned_data.get('renew'):
608 bf0c6de5 Sofia Papagiannaki
                self.user.renew_token()
609 bf0c6de5 Sofia Papagiannaki
            self.user.flush_sessions(current_key=self.session_key)
610 bf0c6de5 Sofia Papagiannaki
        except AttributeError:
611 bf0c6de5 Sofia Papagiannaki
            # if user model does has not such methods
612 bf0c6de5 Sofia Papagiannaki
            pass
613 53161dd8 Sofia Papagiannaki
        return super(ExtendedPasswordChangeForm, self).save(commit=commit)
614 48e9f076 Sofia Papagiannaki
615 48e9f076 Sofia Papagiannaki
class ExtendedSetPasswordForm(SetPasswordForm):
616 48e9f076 Sofia Papagiannaki
    """
617 48e9f076 Sofia Papagiannaki
    Extends SetPasswordForm by enabling user
618 48e9f076 Sofia Papagiannaki
    to optionally renew also the token.
619 48e9f076 Sofia Papagiannaki
    """
620 ee210d1d Sofia Papagiannaki
    if not NEWPASSWD_INVALIDATE_TOKEN:
621 bf0c6de5 Sofia Papagiannaki
        renew = forms.BooleanField(
622 bf0c6de5 Sofia Papagiannaki
            label='Renew token',
623 bf0c6de5 Sofia Papagiannaki
            required=False,
624 bf0c6de5 Sofia Papagiannaki
            initial=True,
625 47b77c8b Sofia Papagiannaki
            help_text='Unsetting this may result in security risk.')
626 2e90e3ec Kostas Papadimitriou
627 48e9f076 Sofia Papagiannaki
    def __init__(self, user, *args, **kwargs):
628 48e9f076 Sofia Papagiannaki
        super(ExtendedSetPasswordForm, self).__init__(user, *args, **kwargs)
629 d2633501 Kostas Papadimitriou
630 d2633501 Kostas Papadimitriou
    @transaction.commit_on_success()
631 48e9f076 Sofia Papagiannaki
    def save(self, commit=True):
632 bf0c6de5 Sofia Papagiannaki
        try:
633 bf0c6de5 Sofia Papagiannaki
            self.user = AstakosUser.objects.get(id=self.user.id)
634 bf0c6de5 Sofia Papagiannaki
            if NEWPASSWD_INVALIDATE_TOKEN or self.cleaned_data.get('renew'):
635 53161dd8 Sofia Papagiannaki
                self.user.renew_token()
636 d2633501 Kostas Papadimitriou
            #self.user.flush_sessions()
637 d2633501 Kostas Papadimitriou
            if not self.user.has_auth_provider('local'):
638 d2633501 Kostas Papadimitriou
                self.user.add_auth_provider('local', auth_backend='astakos')
639 d2633501 Kostas Papadimitriou
640 bf0c6de5 Sofia Papagiannaki
        except BaseException, e:
641 bf0c6de5 Sofia Papagiannaki
            logger.exception(e)
642 53161dd8 Sofia Papagiannaki
        return super(ExtendedSetPasswordForm, self).save(commit=commit)
643 e1a80257 Sofia Papagiannaki
644 e1a80257 Sofia Papagiannaki
645 67980f56 Georgios D. Tsoukalas
646 67980f56 Georgios D. Tsoukalas
647 67980f56 Georgios D. Tsoukalas
app_name_label       =  "Project name"
648 67980f56 Georgios D. Tsoukalas
app_name_placeholder = _("myproject.mylab.ntua.gr")
649 67980f56 Georgios D. Tsoukalas
app_name_validator   =  validators.RegexValidator(
650 67980f56 Georgios D. Tsoukalas
                            DOMAIN_VALUE_REGEX,
651 67980f56 Georgios D. Tsoukalas
                            _(astakos_messages.DOMAIN_VALUE_ERR),
652 67980f56 Georgios D. Tsoukalas
                            'invalid')
653 67980f56 Georgios D. Tsoukalas
app_name_help        =  _("""
654 67980f56 Georgios D. Tsoukalas
        The Project's name should be in a domain format.
655 67980f56 Georgios D. Tsoukalas
        The domain shouldn't neccessarily exist in the real
656 67980f56 Georgios D. Tsoukalas
        world but is helpful to imply a structure.
657 67980f56 Georgios D. Tsoukalas
        e.g.: myproject.mylab.ntua.gr or
658 67980f56 Georgios D. Tsoukalas
        myservice.myteam.myorganization""")
659 67980f56 Georgios D. Tsoukalas
app_name_widget      =  forms.TextInput(
660 67980f56 Georgios D. Tsoukalas
                            attrs={'placeholder': app_name_placeholder})
661 67980f56 Georgios D. Tsoukalas
662 67980f56 Georgios D. Tsoukalas
663 67980f56 Georgios D. Tsoukalas
app_home_label       =  "Homepage URL"
664 3ff82dca Olga Brani
app_home_placeholder =  'myinstitution.org/myproject/'
665 67980f56 Georgios D. Tsoukalas
app_home_help        =  _("""
666 67980f56 Georgios D. Tsoukalas
        URL pointing at your project's site.
667 3ff82dca Olga Brani
        e.g.: myinstitution.org/myproject/.
668 67980f56 Georgios D. Tsoukalas
        Leave blank if there is no website.""")
669 67980f56 Georgios D. Tsoukalas
app_home_widget      =  forms.TextInput(
670 67980f56 Georgios D. Tsoukalas
                            attrs={'placeholder': app_home_placeholder})
671 67980f56 Georgios D. Tsoukalas
672 67980f56 Georgios D. Tsoukalas
app_desc_label       =  _("Description")
673 67980f56 Georgios D. Tsoukalas
app_desc_help        =  _("""
674 67980f56 Georgios D. Tsoukalas
        Please provide a short but descriptive abstract of your Project,
675 67980f56 Georgios D. Tsoukalas
        so that anyone searching can quickly understand
676 67980f56 Georgios D. Tsoukalas
        what this Project is about.""")
677 67980f56 Georgios D. Tsoukalas
678 67980f56 Georgios D. Tsoukalas
app_comment_label    =  _("Comments for review (private)")
679 67980f56 Georgios D. Tsoukalas
app_comment_help     =  _("""
680 67980f56 Georgios D. Tsoukalas
        Write down any comments you may have for the reviewer
681 67980f56 Georgios D. Tsoukalas
        of this application (e.g. background and rationale to
682 67980f56 Georgios D. Tsoukalas
        support your request).
683 67980f56 Georgios D. Tsoukalas
        The comments are strictly for the review process
684 67980f56 Georgios D. Tsoukalas
        and will not be published.""")
685 67980f56 Georgios D. Tsoukalas
686 67980f56 Georgios D. Tsoukalas
app_start_date_label =  _("Start date")
687 67980f56 Georgios D. Tsoukalas
app_start_date_help  =  _("""
688 67980f56 Georgios D. Tsoukalas
        Provide a date when your need your project to be created,
689 67980f56 Georgios D. Tsoukalas
        and members to be able to join and get resources.
690 67980f56 Georgios D. Tsoukalas
        This date is only a hint to help prioritize reviews.""")
691 67980f56 Georgios D. Tsoukalas
692 67980f56 Georgios D. Tsoukalas
app_end_date_label   =  _("Termination date")
693 67980f56 Georgios D. Tsoukalas
app_end_date_help    =  _("""
694 67980f56 Georgios D. Tsoukalas
        At this date, the project will be automatically terminated
695 67980f56 Georgios D. Tsoukalas
        and its resource grants revoked from all members.
696 67980f56 Georgios D. Tsoukalas
        Unless you know otherwise,
697 67980f56 Georgios D. Tsoukalas
        it is best to start with a conservative estimation.
698 67980f56 Georgios D. Tsoukalas
        You can always re-apply for an extension, if you need.""")
699 67980f56 Georgios D. Tsoukalas
700 67980f56 Georgios D. Tsoukalas
join_policy_label    =  _("Joining policy")
701 67980f56 Georgios D. Tsoukalas
leave_policy_label   =  _("Leaving policy")
702 67980f56 Georgios D. Tsoukalas
703 67980f56 Georgios D. Tsoukalas
max_members_label    =  _("Maximum member count")
704 67980f56 Georgios D. Tsoukalas
max_members_help     =  _("""
705 67980f56 Georgios D. Tsoukalas
        Specify the maximum number of members this project may have,
706 67980f56 Georgios D. Tsoukalas
        including the owner. Beyond this number, no new members
707 67980f56 Georgios D. Tsoukalas
        may join the project and be granted the project resources.
708 67980f56 Georgios D. Tsoukalas
        Unless you certainly for otherwise,
709 67980f56 Georgios D. Tsoukalas
        it is best to start with a conservative limit.
710 67980f56 Georgios D. Tsoukalas
        You can always request a raise when you need it.""")
711 67980f56 Georgios D. Tsoukalas
712 67980f56 Georgios D. Tsoukalas
join_policies = PROJECT_MEMBER_JOIN_POLICIES.iteritems()
713 67980f56 Georgios D. Tsoukalas
leave_policies = PROJECT_MEMBER_LEAVE_POLICIES.iteritems()
714 67980f56 Georgios D. Tsoukalas
715 e1a80257 Sofia Papagiannaki
class ProjectApplicationForm(forms.ModelForm):
716 67980f56 Georgios D. Tsoukalas
717 e1a80257 Sofia Papagiannaki
    name = forms.CharField(
718 67980f56 Georgios D. Tsoukalas
        label     = app_name_label,
719 67980f56 Georgios D. Tsoukalas
        help_text = app_name_help,
720 67980f56 Georgios D. Tsoukalas
        widget    = app_name_widget,
721 67980f56 Georgios D. Tsoukalas
        validators = [app_name_validator])
722 67980f56 Georgios D. Tsoukalas
723 52784759 Olga Brani
    homepage = forms.URLField(
724 67980f56 Georgios D. Tsoukalas
        label     = app_home_label,
725 80da92f0 Kostas Papadimitriou
        help_text = app_home_help,
726 67980f56 Georgios D. Tsoukalas
        widget    = app_home_widget,
727 67980f56 Georgios D. Tsoukalas
        required  = False)
728 67980f56 Georgios D. Tsoukalas
729 67980f56 Georgios D. Tsoukalas
    description = forms.CharField(
730 67980f56 Georgios D. Tsoukalas
        label     = app_desc_label,
731 67980f56 Georgios D. Tsoukalas
        help_text = app_desc_help,
732 67980f56 Georgios D. Tsoukalas
        widget    = forms.Textarea,
733 67980f56 Georgios D. Tsoukalas
        required  = False)
734 67980f56 Georgios D. Tsoukalas
735 67980f56 Georgios D. Tsoukalas
    comments = forms.CharField(
736 67980f56 Georgios D. Tsoukalas
        label     = app_comment_label,
737 67980f56 Georgios D. Tsoukalas
        help_text = app_comment_help,
738 67980f56 Georgios D. Tsoukalas
        widget    = forms.Textarea,
739 67980f56 Georgios D. Tsoukalas
        required  = False)
740 67980f56 Georgios D. Tsoukalas
741 67980f56 Georgios D. Tsoukalas
    start_date = forms.DateTimeField(
742 67980f56 Georgios D. Tsoukalas
        label     = app_start_date_label,
743 67980f56 Georgios D. Tsoukalas
        help_text = app_start_date_help,
744 67980f56 Georgios D. Tsoukalas
        required  = False)
745 67980f56 Georgios D. Tsoukalas
746 67980f56 Georgios D. Tsoukalas
    end_date = forms.DateTimeField(
747 67980f56 Georgios D. Tsoukalas
        label     = app_end_date_label,
748 67980f56 Georgios D. Tsoukalas
        help_text = app_end_date_help)
749 67980f56 Georgios D. Tsoukalas
750 80da92f0 Kostas Papadimitriou
    member_join_policy  = forms.TypedChoiceField(
751 67980f56 Georgios D. Tsoukalas
        label     = join_policy_label,
752 80da92f0 Kostas Papadimitriou
        initial   = 2,
753 80da92f0 Kostas Papadimitriou
        coerce    = int,
754 67980f56 Georgios D. Tsoukalas
        choices   = join_policies)
755 dd5f8f4d Kostas Papadimitriou
756 80da92f0 Kostas Papadimitriou
    member_leave_policy = forms.TypedChoiceField(
757 67980f56 Georgios D. Tsoukalas
        label     = leave_policy_label,
758 80da92f0 Kostas Papadimitriou
        coerce    = int,
759 67980f56 Georgios D. Tsoukalas
        choices   = leave_policies)
760 67980f56 Georgios D. Tsoukalas
761 67980f56 Georgios D. Tsoukalas
    limit_on_members_number = forms.IntegerField(
762 67980f56 Georgios D. Tsoukalas
        label     = max_members_label,
763 67980f56 Georgios D. Tsoukalas
        help_text = max_members_help,
764 67980f56 Georgios D. Tsoukalas
        required  = False)
765 dd5f8f4d Kostas Papadimitriou
766 e1a80257 Sofia Papagiannaki
    class Meta:
767 73fbaec4 Sofia Papagiannaki
        model = ProjectApplication
768 c82bd52b Kostas Papadimitriou
        fields = ( 'name', 'homepage', 'description',
769 c82bd52b Kostas Papadimitriou
                    'start_date', 'end_date', 'comments',
770 c82bd52b Kostas Papadimitriou
                    'member_join_policy', 'member_leave_policy',
771 c82bd52b Kostas Papadimitriou
                    'limit_on_members_number')
772 c82bd52b Kostas Papadimitriou
773 40b52116 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
774 2ef98527 Sofia Papagiannaki
        instance = kwargs.get('instance')
775 2ef98527 Sofia Papagiannaki
        self.precursor_application = instance
776 40b52116 Sofia Papagiannaki
        super(ProjectApplicationForm, self).__init__(*args, **kwargs)
777 f568d0f4 Sofia Papagiannaki
        # in case of new application remove closed join policy
778 2ef98527 Sofia Papagiannaki
        if not instance:
779 2ef98527 Sofia Papagiannaki
            policies = PROJECT_MEMBER_JOIN_POLICIES.copy()
780 2ef98527 Sofia Papagiannaki
            policies.pop('3')
781 2ef98527 Sofia Papagiannaki
            self.fields['member_join_policy'].choices = policies.iteritems()
782 dd5f8f4d Kostas Papadimitriou
783 69b26576 Sofia Papagiannaki
    def clean_start_date(self):
784 69b26576 Sofia Papagiannaki
        start_date = self.cleaned_data.get('start_date')
785 f568d0f4 Sofia Papagiannaki
        if not self.precursor_application:
786 01223f04 Sofia Papagiannaki
            if start_date and (start_date - today).days < 0:
787 f568d0f4 Sofia Papagiannaki
                raise forms.ValidationError(
788 69b26576 Sofia Papagiannaki
                _(astakos_messages.INVALID_PROJECT_START_DATE))
789 69b26576 Sofia Papagiannaki
        return start_date
790 69b26576 Sofia Papagiannaki
791 69b26576 Sofia Papagiannaki
    def clean_end_date(self):
792 69b26576 Sofia Papagiannaki
        start_date = self.cleaned_data.get('start_date')
793 69b26576 Sofia Papagiannaki
        end_date = self.cleaned_data.get('end_date')
794 01223f04 Sofia Papagiannaki
        if end_date and (end_date - today).days < 0:
795 69b26576 Sofia Papagiannaki
            raise forms.ValidationError(
796 69b26576 Sofia Papagiannaki
                _(astakos_messages.INVALID_PROJECT_END_DATE))
797 f03b9ba6 Sofia Papagiannaki
        if start_date and (end_date - start_date).days <= 0:
798 69b26576 Sofia Papagiannaki
            raise forms.ValidationError(
799 69b26576 Sofia Papagiannaki
                _(astakos_messages.INCONSISTENT_PROJECT_DATES))
800 69b26576 Sofia Papagiannaki
        return end_date
801 69b26576 Sofia Papagiannaki
802 e1a80257 Sofia Papagiannaki
    def clean(self):
803 185b2190 Sofia Papagiannaki
        userid = self.data.get('user', None)
804 e1a80257 Sofia Papagiannaki
        self.user = None
805 e1a80257 Sofia Papagiannaki
        if userid:
806 e1a80257 Sofia Papagiannaki
            try:
807 e1a80257 Sofia Papagiannaki
                self.user = AstakosUser.objects.get(id=userid)
808 e1a80257 Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
809 e1a80257 Sofia Papagiannaki
                pass
810 e1a80257 Sofia Papagiannaki
        if not self.user:
811 e1a80257 Sofia Papagiannaki
            raise forms.ValidationError(_(astakos_messages.NO_APPLICANT))
812 e1a80257 Sofia Papagiannaki
        super(ProjectApplicationForm, self).clean()
813 e1a80257 Sofia Papagiannaki
        return self.cleaned_data
814 dd5f8f4d Kostas Papadimitriou
815 f3342849 Sofia Papagiannaki
    @property
816 f3342849 Sofia Papagiannaki
    def resource_policies(self):
817 e1a80257 Sofia Papagiannaki
        policies = []
818 e1a80257 Sofia Papagiannaki
        append = policies.append
819 f3342849 Sofia Papagiannaki
        for name, value in self.data.iteritems():
820 f3342849 Sofia Papagiannaki
            if not value:
821 f3342849 Sofia Papagiannaki
                continue
822 56eb807c Sofia Papagiannaki
            uplimit = value
823 56eb807c Sofia Papagiannaki
            if name.endswith('_uplimit'):
824 56eb807c Sofia Papagiannaki
                subs = name.split('_uplimit')
825 e1a80257 Sofia Papagiannaki
                prefix, suffix = subs
826 e1a80257 Sofia Papagiannaki
                s, sep, r = prefix.partition(RESOURCE_SEPARATOR)
827 e1a80257 Sofia Papagiannaki
                resource = Resource.objects.get(service__name=s, name=r)
828 e1a80257 Sofia Papagiannaki
829 e1a80257 Sofia Papagiannaki
                # keep only resource limits for selected resource groups
830 30dd9e0e Olga Brani
                if self.data.get(
831 30dd9e0e Olga Brani
                    'is_selected_%s' % resource.group, "0"
832 30dd9e0e Olga Brani
                 ) == "1":
833 6556e514 Sofia Papagiannaki
                    d = model_to_dict(resource)
834 30dd9e0e Olga Brani
                    if uplimit:
835 6556e514 Sofia Papagiannaki
                        d.update(dict(service=s, resource=r, uplimit=uplimit))
836 30dd9e0e Olga Brani
                    else:
837 6556e514 Sofia Papagiannaki
                        d.update(dict(service=s, resource=r, uplimit=None))
838 6556e514 Sofia Papagiannaki
                    append(d)
839 dd5f8f4d Kostas Papadimitriou
840 e1a80257 Sofia Papagiannaki
        return policies
841 dd5f8f4d Kostas Papadimitriou
842 e1a80257 Sofia Papagiannaki
    def save(self, commit=True):
843 15ca2bea Giorgos Korfiatis
        data = dict(self.cleaned_data)
844 69c822cc Giorgos Korfiatis
        data['precursor_application'] = self.instance.id
845 69c822cc Giorgos Korfiatis
        data['owner'] = self.user
846 69c822cc Giorgos Korfiatis
        data['resource_policies'] = self.resource_policies
847 ee4aa6eb Giorgos Korfiatis
        submit_application(data, request_user=self.user)
848 8327782d Sofia Papagiannaki
849 8327782d Sofia Papagiannaki
class ProjectSortForm(forms.Form):
850 8327782d Sofia Papagiannaki
    sorting = forms.ChoiceField(
851 8327782d Sofia Papagiannaki
        label='Sort by',
852 73fbaec4 Sofia Papagiannaki
        choices=(('name', 'Sort by Name'),
853 8327782d Sofia Papagiannaki
                 ('issue_date', 'Sort by Issue date'),
854 73fbaec4 Sofia Papagiannaki
                 ('start_date', 'Sort by Start Date'),
855 73fbaec4 Sofia Papagiannaki
                 ('end_date', 'Sort by End Date'),
856 8327782d Sofia Papagiannaki
#                  ('approved_members_num', 'Sort by Participants'),
857 d6a162d3 Sofia Papagiannaki
                 ('state', 'Sort by Status'),
858 73fbaec4 Sofia Papagiannaki
                 ('member_join_policy__description', 'Sort by Member Join Policy'),
859 7592e3e2 Sofia Papagiannaki
                 ('member_leave_policy__description', 'Sort by Member Leave Policy'),
860 7592e3e2 Sofia Papagiannaki
                 ('-name', 'Sort by Name'),
861 7592e3e2 Sofia Papagiannaki
                 ('-issue_date', 'Sort by Issue date'),
862 7592e3e2 Sofia Papagiannaki
                 ('-start_date', 'Sort by Start Date'),
863 7592e3e2 Sofia Papagiannaki
                 ('-end_date', 'Sort by End Date'),
864 7592e3e2 Sofia Papagiannaki
#                  ('-approved_members_num', 'Sort by Participants'),
865 7592e3e2 Sofia Papagiannaki
                 ('-state', 'Sort by Status'),
866 7592e3e2 Sofia Papagiannaki
                 ('-member_join_policy__description', 'Sort by Member Join Policy'),
867 7592e3e2 Sofia Papagiannaki
                 ('-member_leave_policy__description', 'Sort by Member Leave Policy')
868 8327782d Sofia Papagiannaki
        ),
869 8327782d Sofia Papagiannaki
        required=True
870 ccab6eb5 Sofia Papagiannaki
    )
871 ccab6eb5 Sofia Papagiannaki
872 ccab6eb5 Sofia Papagiannaki
class AddProjectMembersForm(forms.Form):
873 ccab6eb5 Sofia Papagiannaki
    q = forms.CharField(
874 ccab6eb5 Sofia Papagiannaki
        max_length=800, widget=forms.Textarea, label=_('Add members'),
875 e47fb17a Sofia Papagiannaki
        help_text=_(astakos_messages.ADD_PROJECT_MEMBERS_Q_HELP), required=True)
876 ccab6eb5 Sofia Papagiannaki
877 e47fb17a Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
878 e47fb17a Sofia Papagiannaki
        application_id = kwargs.pop('application_id', None)
879 e47fb17a Sofia Papagiannaki
        if application_id:
880 e47fb17a Sofia Papagiannaki
            self.project = Project.objects.get(application__id=application_id)
881 e47fb17a Sofia Papagiannaki
        self.request_user = kwargs.pop('request_user', None)
882 e47fb17a Sofia Papagiannaki
        super(AddProjectMembersForm, self).__init__(*args, **kwargs)
883 43332a76 Kostas Papadimitriou
884 ccab6eb5 Sofia Papagiannaki
    def clean(self):
885 e47fb17a Sofia Papagiannaki
        try:
886 e47fb17a Sofia Papagiannaki
            do_accept_membership_checks(self.project, self.request_user)
887 e47fb17a Sofia Papagiannaki
        except PermissionDenied, e:
888 e47fb17a Sofia Papagiannaki
            raise forms.ValidationError(e)
889 e47fb17a Sofia Papagiannaki
890 ccab6eb5 Sofia Papagiannaki
        q = self.cleaned_data.get('q') or ''
891 ccab6eb5 Sofia Papagiannaki
        users = q.split(',')
892 ccab6eb5 Sofia Papagiannaki
        users = list(u.strip() for u in users if u)
893 ccab6eb5 Sofia Papagiannaki
        db_entries = AstakosUser.objects.filter(email__in=users)
894 ccab6eb5 Sofia Papagiannaki
        unknown = list(set(users) - set(u.email for u in db_entries))
895 ccab6eb5 Sofia Papagiannaki
        if unknown:
896 e47fb17a Sofia Papagiannaki
            raise forms.ValidationError(
897 e47fb17a Sofia Papagiannaki
                _(astakos_messages.UNKNOWN_USERS) % ','.join(unknown))
898 ccab6eb5 Sofia Papagiannaki
        self.valid_users = db_entries
899 ccab6eb5 Sofia Papagiannaki
        return self.cleaned_data
900 ccab6eb5 Sofia Papagiannaki
901 ccab6eb5 Sofia Papagiannaki
    def get_valid_users(self):
902 ccab6eb5 Sofia Papagiannaki
        """Should be called after form cleaning"""
903 ccab6eb5 Sofia Papagiannaki
        try:
904 ccab6eb5 Sofia Papagiannaki
            return self.valid_users
905 ccab6eb5 Sofia Papagiannaki
        except:
906 bfe23b13 Sofia Papagiannaki
            return ()
907 bfe23b13 Sofia Papagiannaki
908 bfe23b13 Sofia Papagiannaki
class ProjectMembersSortForm(forms.Form):
909 bfe23b13 Sofia Papagiannaki
    sorting = forms.ChoiceField(
910 bfe23b13 Sofia Papagiannaki
        label='Sort by',
911 bfe23b13 Sofia Papagiannaki
        choices=(('person__email', 'User Id'),
912 bfe23b13 Sofia Papagiannaki
                 ('person__first_name', 'Name'),
913 bfe23b13 Sofia Papagiannaki
                 ('acceptance_date', 'Acceptance date')
914 bfe23b13 Sofia Papagiannaki
        ),
915 bfe23b13 Sofia Papagiannaki
        required=True
916 bfe23b13 Sofia Papagiannaki
    )
917 bfe23b13 Sofia Papagiannaki
918 6dadd24a Sofia Papagiannaki
class ProjectSearchForm(forms.Form):
919 85d444db Sofia Papagiannaki
    q = forms.CharField(max_length=200, label='Search project', required=False)