Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (38.3 kB)

1 1808f7bc Giorgos Korfiatis
# Copyright 2011, 2012, 2013 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 caf70869 Sofia Papagiannaki
from random import random
34 3e0a032d 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 3e0a032d Sofia Papagiannaki
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, \
39 3e0a032d Sofia Papagiannaki
    PasswordResetForm, PasswordChangeForm, SetPasswordForm
40 f9224cc0 Sofia Papagiannaki
from django.core.mail import send_mail, get_connection
41 e2125441 Sofia Papagiannaki
from django.contrib.auth.tokens import default_token_generator
42 374611bc Sofia Papagiannaki
from django.core.urlresolvers import reverse
43 18ffbee1 Sofia Papagiannaki
from django.utils.safestring import mark_safe
44 49790d9d Sofia Papagiannaki
from django.utils.encoding import smart_str
45 d2633501 Kostas Papadimitriou
from django.db import transaction
46 caf70869 Sofia Papagiannaki
from django.core import validators
47 5ed6816e Sofia Papagiannaki
48 3e87075a Giorgos Korfiatis
from synnefo.util import units
49 734107ef Kostas Papadimitriou
from synnefo_branding.utils import render_to_string
50 d2c9adac Christos Stavrakakis
from synnefo.lib import join_urls
51 bd2c6bc5 Kostas Papadimitriou
from astakos.im.fields import EmailField
52 3e0a032d Sofia Papagiannaki
from astakos.im.models import AstakosUser, EmailChange, Invitation, Resource, \
53 3e0a032d Sofia Papagiannaki
    PendingThirdPartyUser, get_latest_terms, ProjectApplication, Project
54 75380308 Kostas Papadimitriou
from astakos.im import presentation
55 45f8d9ff Sofia Papagiannaki
from astakos.im.widgets import DummyWidget, RecaptchaWidget
56 3e0a032d Sofia Papagiannaki
from astakos.im.functions import send_change_email, submit_application, \
57 f12bcb3d Giorgos Korfiatis
    accept_membership_project_checks, ProjectError
58 270dd48d Sofia Papagiannaki
59 3e0a032d Sofia Papagiannaki
from astakos.im.util import reserved_verified_email, model_to_dict
60 c4b1a172 Kostas Papadimitriou
from astakos.im import auth_providers
61 8998f09a Sofia Papagiannaki
from astakos.im import settings
62 1808f7bc Giorgos Korfiatis
from astakos.im import auth
63 64cd4730 Antony Chazapis
64 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
65 64cd4730 Antony Chazapis
66 3bf924ec Sofia Papagiannaki
import logging
67 49790d9d Sofia Papagiannaki
import hashlib
68 db7fecd9 Sofia Papagiannaki
import recaptcha.client.captcha as captcha
69 caf70869 Sofia Papagiannaki
import re
70 64cd4730 Antony Chazapis
71 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
72 e015e9e6 Sofia Papagiannaki
73 caf70869 Sofia Papagiannaki
DOMAIN_VALUE_REGEX = re.compile(
74 892410d3 Sofia Papagiannaki
    r'^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$',
75 73fbaec4 Sofia Papagiannaki
    re.IGNORECASE)
76 d2633501 Kostas Papadimitriou
77 e7cb4085 Kostas Papadimitriou
78 1808f7bc Giorgos Korfiatis
class LocalUserCreationForm(UserCreationForm):
79 890b0eaf Sofia Papagiannaki
    """
80 890b0eaf Sofia Papagiannaki
    Extends the built in UserCreationForm in several ways:
81 18ffbee1 Sofia Papagiannaki

82 e47fb17a Sofia Papagiannaki
    * Adds email, first_name, last_name, recaptcha_challenge_field,
83 e47fb17a Sofia Papagiannaki
    * recaptcha_response_field field.
84 5ed6816e Sofia Papagiannaki
    * The username field isn't visible and it is assigned a generated id.
85 18ffbee1 Sofia Papagiannaki
    * User created is not active.
86 890b0eaf Sofia Papagiannaki
    """
87 db7fecd9 Sofia Papagiannaki
    recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
88 5ce3ce4f Sofia Papagiannaki
    recaptcha_response_field = forms.CharField(
89 5ce3ce4f Sofia Papagiannaki
        widget=RecaptchaWidget, label='')
90 bd2c6bc5 Kostas Papadimitriou
    email = EmailField()
91 18ffbee1 Sofia Papagiannaki
92 794852f2 Sofia Papagiannaki
    class Meta:
93 794852f2 Sofia Papagiannaki
        model = AstakosUser
94 5ce3ce4f Sofia Papagiannaki
        fields = ("email", "first_name", "last_name",
95 5ce3ce4f Sofia Papagiannaki
                  "has_signed_terms", "has_signed_terms")
96 18ffbee1 Sofia Papagiannaki
97 64cd4730 Antony Chazapis
    def __init__(self, *args, **kwargs):
98 64cd4730 Antony Chazapis
        """
99 890b0eaf Sofia Papagiannaki
        Changes the order of fields, and removes the username field.
100 64cd4730 Antony Chazapis
        """
101 bf0c6de5 Sofia Papagiannaki
        request = kwargs.pop('request', None)
102 e7cb4085 Kostas Papadimitriou
        provider = kwargs.pop('provider', 'local')
103 e7cb4085 Kostas Papadimitriou
104 e7cb4085 Kostas Papadimitriou
        # we only use LocalUserCreationForm for local provider
105 e7cb4085 Kostas Papadimitriou
        if not provider == 'local':
106 e7cb4085 Kostas Papadimitriou
            raise Exception('Invalid provider')
107 e7cb4085 Kostas Papadimitriou
108 e7726e14 Kostas Papadimitriou
        self.ip = None
109 672d445a Sofia Papagiannaki
        if request:
110 672d445a Sofia Papagiannaki
            self.ip = request.META.get('REMOTE_ADDR',
111 e7cb4085 Kostas Papadimitriou
                                       request.META.get('HTTP_X_REAL_IP',
112 e7cb4085 Kostas Papadimitriou
                                                        None))
113 ab8bfb29 Kostas Papadimitriou
114 15efc749 Sofia Papagiannaki
        super(LocalUserCreationForm, self).__init__(*args, **kwargs)
115 890b0eaf Sofia Papagiannaki
        self.fields.keyOrder = ['email', 'first_name', 'last_name',
116 d8f63346 Sofia Papagiannaki
                                'password1', 'password2']
117 1b3398a0 Olga Brani
118 8998f09a Sofia Papagiannaki
        if settings.RECAPTCHA_ENABLED:
119 53bf2659 Sofia Papagiannaki
            self.fields.keyOrder.extend(['recaptcha_challenge_field',
120 5ce3ce4f Sofia Papagiannaki
                                         'recaptcha_response_field', ])
121 1b3398a0 Olga Brani
        if get_latest_terms():
122 1b3398a0 Olga Brani
            self.fields.keyOrder.append('has_signed_terms')
123 18ffbee1 Sofia Papagiannaki
124 18ffbee1 Sofia Papagiannaki
        if 'has_signed_terms' in self.fields:
125 18ffbee1 Sofia Papagiannaki
            # Overriding field label since we need to apply a link
126 18ffbee1 Sofia Papagiannaki
            # to the terms within the label
127 18ffbee1 Sofia Papagiannaki
            terms_link_html = '<a href="%s" target="_blank">%s</a>' \
128 5ce3ce4f Sofia Papagiannaki
                % (reverse('latest_terms'), _("the terms"))
129 18ffbee1 Sofia Papagiannaki
            self.fields['has_signed_terms'].label = \
130 5ce3ce4f Sofia Papagiannaki
                mark_safe("I agree with %s" % terms_link_html)
131 18ffbee1 Sofia Papagiannaki
132 af4eb974 Sofia Papagiannaki
    def clean_email(self):
133 e5966bd9 Kostas Papadimitriou
        email = self.cleaned_data['email']
134 881c856c Sofia Papagiannaki
        if not email:
135 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.REQUIRED_FIELD))
136 43332a76 Kostas Papadimitriou
        if reserved_verified_email(email):
137 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.EMAIL_USED))
138 0a569195 Sofia Papagiannaki
        return email
139 18ffbee1 Sofia Papagiannaki
140 270dd48d Sofia Papagiannaki
    def clean_has_signed_terms(self):
141 270dd48d Sofia Papagiannaki
        has_signed_terms = self.cleaned_data['has_signed_terms']
142 270dd48d Sofia Papagiannaki
        if not has_signed_terms:
143 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.SIGN_TERMS))
144 270dd48d Sofia Papagiannaki
        return has_signed_terms
145 18ffbee1 Sofia Papagiannaki
146 db7fecd9 Sofia Papagiannaki
    def clean_recaptcha_response_field(self):
147 db7fecd9 Sofia Papagiannaki
        if 'recaptcha_challenge_field' in self.cleaned_data:
148 db7fecd9 Sofia Papagiannaki
            self.validate_captcha()
149 db7fecd9 Sofia Papagiannaki
        return self.cleaned_data['recaptcha_response_field']
150 db7fecd9 Sofia Papagiannaki
151 db7fecd9 Sofia Papagiannaki
    def clean_recaptcha_challenge_field(self):
152 db7fecd9 Sofia Papagiannaki
        if 'recaptcha_response_field' in self.cleaned_data:
153 db7fecd9 Sofia Papagiannaki
            self.validate_captcha()
154 db7fecd9 Sofia Papagiannaki
        return self.cleaned_data['recaptcha_challenge_field']
155 db7fecd9 Sofia Papagiannaki
156 db7fecd9 Sofia Papagiannaki
    def validate_captcha(self):
157 db7fecd9 Sofia Papagiannaki
        rcf = self.cleaned_data['recaptcha_challenge_field']
158 db7fecd9 Sofia Papagiannaki
        rrf = self.cleaned_data['recaptcha_response_field']
159 8998f09a Sofia Papagiannaki
        check = captcha.submit(
160 8998f09a Sofia Papagiannaki
            rcf, rrf, settings.RECAPTCHA_PRIVATE_KEY, self.ip)
161 db7fecd9 Sofia Papagiannaki
        if not check.is_valid:
162 8998f09a Sofia Papagiannaki
            raise forms.ValidationError(_(
163 8998f09a Sofia Papagiannaki
                astakos_messages.CAPTCHA_VALIDATION_ERR))
164 18ffbee1 Sofia Papagiannaki
165 1808f7bc Giorgos Korfiatis
    def create_user(self):
166 1808f7bc Giorgos Korfiatis
        try:
167 1808f7bc Giorgos Korfiatis
            data = self.cleaned_data
168 1808f7bc Giorgos Korfiatis
        except AttributeError:
169 1808f7bc Giorgos Korfiatis
            self.is_valid()
170 1808f7bc Giorgos Korfiatis
            data = self.cleaned_data
171 1808f7bc Giorgos Korfiatis
172 1808f7bc Giorgos Korfiatis
        user = auth.make_local_user(
173 1808f7bc Giorgos Korfiatis
            email=data['email'], password=data['password1'],
174 1808f7bc Giorgos Korfiatis
            first_name=data['first_name'], last_name=data['last_name'],
175 1808f7bc Giorgos Korfiatis
            has_signed_terms=True)
176 890b0eaf Sofia Papagiannaki
        return user
177 64cd4730 Antony Chazapis
178 5ce3ce4f Sofia Papagiannaki
179 1808f7bc Giorgos Korfiatis
class ThirdPartyUserCreationForm(forms.ModelForm):
180 bd2c6bc5 Kostas Papadimitriou
    email = EmailField(
181 1177e91b Olga Brani
        label='Contact email',
182 e7cb4085 Kostas Papadimitriou
        help_text='This is needed for contact purposes. '
183 e7cb4085 Kostas Papadimitriou
        'It doesn&#39;t need to be the same with the one you '
184 d6ea9b3d Olga Brani
        'provided to login previously. '
185 1177e91b Olga Brani
    )
186 43332a76 Kostas Papadimitriou
187 8f5a3a06 Sofia Papagiannaki
    class Meta:
188 8f5a3a06 Sofia Papagiannaki
        model = AstakosUser
189 e7cb4085 Kostas Papadimitriou
        fields = ['email', 'first_name', 'last_name', 'has_signed_terms']
190 ab8bfb29 Kostas Papadimitriou
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 e7cb4085 Kostas Papadimitriou
196 e7cb4085 Kostas Papadimitriou
        self.provider = kwargs.pop('provider', None)
197 139eae46 Kostas Papadimitriou
        self.request = kwargs.pop('request', None)
198 e7cb4085 Kostas Papadimitriou
        if not self.provider or self.provider == 'local':
199 e7cb4085 Kostas Papadimitriou
            raise Exception('Invalid provider, %r' % self.provider)
200 e7cb4085 Kostas Papadimitriou
201 e7cb4085 Kostas Papadimitriou
        # ThirdPartyUserCreationForm should always get instantiated with
202 e7cb4085 Kostas Papadimitriou
        # a third_party_token value
203 e7cb4085 Kostas Papadimitriou
        self.third_party_token = kwargs.pop('third_party_token', None)
204 e7cb4085 Kostas Papadimitriou
        if not self.third_party_token:
205 e7cb4085 Kostas Papadimitriou
            raise Exception('ThirdPartyUserCreationForm'
206 e7cb4085 Kostas Papadimitriou
                            ' requires third_party_token')
207 2e90e3ec Kostas Papadimitriou
208 8f5a3a06 Sofia Papagiannaki
        super(ThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
209 2e90e3ec Kostas Papadimitriou
210 4a13d054 Kostas Papadimitriou
        if not get_latest_terms():
211 4a13d054 Kostas Papadimitriou
            del self.fields['has_signed_terms']
212 4a13d054 Kostas Papadimitriou
213 18ffbee1 Sofia Papagiannaki
        if 'has_signed_terms' in self.fields:
214 18ffbee1 Sofia Papagiannaki
            # Overriding field label since we need to apply a link
215 18ffbee1 Sofia Papagiannaki
            # to the terms within the label
216 18ffbee1 Sofia Papagiannaki
            terms_link_html = '<a href="%s" target="_blank">%s</a>' \
217 5ce3ce4f Sofia Papagiannaki
                % (reverse('latest_terms'), _("the terms"))
218 18ffbee1 Sofia Papagiannaki
            self.fields['has_signed_terms'].label = \
219 9d20fe23 Kostas Papadimitriou
                mark_safe("I agree with %s" % terms_link_html)
220 2e90e3ec Kostas Papadimitriou
221 18ffbee1 Sofia Papagiannaki
    def clean_email(self):
222 e5966bd9 Kostas Papadimitriou
        email = self.cleaned_data['email']
223 18ffbee1 Sofia Papagiannaki
        if not email:
224 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.REQUIRED_FIELD))
225 43332a76 Kostas Papadimitriou
        if reserved_verified_email(email):
226 e7cb4085 Kostas Papadimitriou
            provider_id = self.provider
227 9d20fe23 Kostas Papadimitriou
            provider = auth_providers.get_provider(provider_id)
228 9d20fe23 Kostas Papadimitriou
            extra_message = provider.get_add_to_existing_account_msg
229 564a2292 Kostas Papadimitriou
230 e7cb4085 Kostas Papadimitriou
            raise forms.ValidationError(mark_safe(
231 e7cb4085 Kostas Papadimitriou
                _(astakos_messages.EMAIL_USED) + ' ' + extra_message))
232 0a569195 Sofia Papagiannaki
        return email
233 ab8bfb29 Kostas Papadimitriou
234 18ffbee1 Sofia Papagiannaki
    def clean_has_signed_terms(self):
235 18ffbee1 Sofia Papagiannaki
        has_signed_terms = self.cleaned_data['has_signed_terms']
236 18ffbee1 Sofia Papagiannaki
        if not has_signed_terms:
237 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.SIGN_TERMS))
238 18ffbee1 Sofia Papagiannaki
        return has_signed_terms
239 ab8bfb29 Kostas Papadimitriou
240 e7cb4085 Kostas Papadimitriou
    def _get_pending_user(self):
241 e7cb4085 Kostas Papadimitriou
        return PendingThirdPartyUser.objects.get(token=self.third_party_token)
242 e7cb4085 Kostas Papadimitriou
243 1808f7bc Giorgos Korfiatis
    def create_user(self):
244 1808f7bc Giorgos Korfiatis
        try:
245 1808f7bc Giorgos Korfiatis
            data = self.cleaned_data
246 1808f7bc Giorgos Korfiatis
        except AttributeError:
247 1808f7bc Giorgos Korfiatis
            self.is_valid()
248 1808f7bc Giorgos Korfiatis
            data = self.cleaned_data
249 1808f7bc Giorgos Korfiatis
250 1808f7bc Giorgos Korfiatis
        user = auth.make_user(
251 1808f7bc Giorgos Korfiatis
            email=data["email"],
252 1808f7bc Giorgos Korfiatis
            first_name=data["first_name"], last_name=data["last_name"],
253 1808f7bc Giorgos Korfiatis
            has_signed_terms=True)
254 e7cb4085 Kostas Papadimitriou
        pending = self._get_pending_user()
255 9d20fe23 Kostas Papadimitriou
        provider = pending.get_provider(user)
256 9d20fe23 Kostas Papadimitriou
        provider.add_to_user()
257 9d20fe23 Kostas Papadimitriou
        pending.delete()
258 8f5a3a06 Sofia Papagiannaki
        return user
259 8f5a3a06 Sofia Papagiannaki
260 5ce3ce4f Sofia Papagiannaki
261 5ed6816e Sofia Papagiannaki
class LoginForm(AuthenticationForm):
262 bd2c6bc5 Kostas Papadimitriou
    username = EmailField(label=_("Email"))
263 672d445a Sofia Papagiannaki
    recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
264 5ce3ce4f Sofia Papagiannaki
    recaptcha_response_field = forms.CharField(
265 5ce3ce4f Sofia Papagiannaki
        widget=RecaptchaWidget, label='')
266 ab8bfb29 Kostas Papadimitriou
267 672d445a Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
268 672d445a Sofia Papagiannaki
        was_limited = kwargs.get('was_limited', False)
269 672d445a Sofia Papagiannaki
        request = kwargs.get('request', None)
270 672d445a Sofia Papagiannaki
        if request:
271 8fb8d0cf Giorgos Korfiatis
            self.ip = request.META.get(
272 8fb8d0cf Giorgos Korfiatis
                'REMOTE_ADDR',
273 8fb8d0cf Giorgos Korfiatis
                request.META.get('HTTP_X_REAL_IP', None))
274 ab8bfb29 Kostas Papadimitriou
275 672d445a Sofia Papagiannaki
        t = ('request', 'was_limited')
276 672d445a Sofia Papagiannaki
        for elem in t:
277 672d445a Sofia Papagiannaki
            if elem in kwargs.keys():
278 672d445a Sofia Papagiannaki
                kwargs.pop(elem)
279 672d445a Sofia Papagiannaki
        super(LoginForm, self).__init__(*args, **kwargs)
280 ab8bfb29 Kostas Papadimitriou
281 672d445a Sofia Papagiannaki
        self.fields.keyOrder = ['username', 'password']
282 8998f09a Sofia Papagiannaki
        if was_limited and settings.RECAPTCHA_ENABLED:
283 672d445a Sofia Papagiannaki
            self.fields.keyOrder.extend(['recaptcha_challenge_field',
284 5ce3ce4f Sofia Papagiannaki
                                         'recaptcha_response_field', ])
285 9a06d96f Olga Brani
286 9a06d96f Olga Brani
    def clean_username(self):
287 4bdd7e3d Kostas Papadimitriou
        return self.cleaned_data['username'].lower()
288 ab8bfb29 Kostas Papadimitriou
289 672d445a Sofia Papagiannaki
    def clean_recaptcha_response_field(self):
290 672d445a Sofia Papagiannaki
        if 'recaptcha_challenge_field' in self.cleaned_data:
291 672d445a Sofia Papagiannaki
            self.validate_captcha()
292 672d445a Sofia Papagiannaki
        return self.cleaned_data['recaptcha_response_field']
293 672d445a Sofia Papagiannaki
294 672d445a Sofia Papagiannaki
    def clean_recaptcha_challenge_field(self):
295 672d445a Sofia Papagiannaki
        if 'recaptcha_response_field' in self.cleaned_data:
296 672d445a Sofia Papagiannaki
            self.validate_captcha()
297 672d445a Sofia Papagiannaki
        return self.cleaned_data['recaptcha_challenge_field']
298 672d445a Sofia Papagiannaki
299 672d445a Sofia Papagiannaki
    def validate_captcha(self):
300 672d445a Sofia Papagiannaki
        rcf = self.cleaned_data['recaptcha_challenge_field']
301 672d445a Sofia Papagiannaki
        rrf = self.cleaned_data['recaptcha_response_field']
302 8998f09a Sofia Papagiannaki
        check = captcha.submit(
303 8998f09a Sofia Papagiannaki
            rcf, rrf, settings.RECAPTCHA_PRIVATE_KEY, self.ip)
304 672d445a Sofia Papagiannaki
        if not check.is_valid:
305 8998f09a Sofia Papagiannaki
            raise forms.ValidationError(_(
306 8998f09a Sofia Papagiannaki
                astakos_messages.CAPTCHA_VALIDATION_ERR))
307 2e90e3ec Kostas Papadimitriou
308 eedb3923 Sofia Papagiannaki
    def clean(self):
309 1f3b4b39 Sofia Papagiannaki
        """
310 1f3b4b39 Sofia Papagiannaki
        Override default behavior in order to check user's activation later
311 1f3b4b39 Sofia Papagiannaki
        """
312 c4b1a172 Kostas Papadimitriou
        username = self.cleaned_data.get('username')
313 c4b1a172 Kostas Papadimitriou
314 bea8a810 Kostas Papadimitriou
        if username:
315 bea8a810 Kostas Papadimitriou
            try:
316 bea8a810 Kostas Papadimitriou
                user = AstakosUser.objects.get_by_identifier(username)
317 bea8a810 Kostas Papadimitriou
                if not user.has_auth_provider('local'):
318 9d20fe23 Kostas Papadimitriou
                    provider = auth_providers.get_provider('local', user)
319 e7cb4085 Kostas Papadimitriou
                    msg = provider.get_login_disabled_msg
320 e7cb4085 Kostas Papadimitriou
                    raise forms.ValidationError(mark_safe(msg))
321 bea8a810 Kostas Papadimitriou
            except AstakosUser.DoesNotExist:
322 bea8a810 Kostas Papadimitriou
                pass
323 c4b1a172 Kostas Papadimitriou
324 1f3b4b39 Sofia Papagiannaki
        try:
325 1f3b4b39 Sofia Papagiannaki
            super(LoginForm, self).clean()
326 51db2da2 Giorgos Korfiatis
        except forms.ValidationError:
327 c4b1a172 Kostas Papadimitriou
            if self.user_cache is None:
328 c4b1a172 Kostas Papadimitriou
                raise
329 c4b1a172 Kostas Papadimitriou
            if not self.user_cache.is_active:
330 9d20fe23 Kostas Papadimitriou
                msg = self.user_cache.get_inactive_message('local')
331 9d20fe23 Kostas Papadimitriou
                raise forms.ValidationError(msg)
332 1f3b4b39 Sofia Papagiannaki
            if self.request:
333 1f3b4b39 Sofia Papagiannaki
                if not self.request.session.test_cookie_worked():
334 1f3b4b39 Sofia Papagiannaki
                    raise
335 eedb3923 Sofia Papagiannaki
        return self.cleaned_data
336 64cd4730 Antony Chazapis
337 5ce3ce4f Sofia Papagiannaki
338 890b0eaf Sofia Papagiannaki
class ProfileForm(forms.ModelForm):
339 890b0eaf Sofia Papagiannaki
    """
340 890b0eaf Sofia Papagiannaki
    Subclass of ``ModelForm`` for permiting user to edit his/her profile.
341 9a06d96f Olga Brani
    Most of the fields are readonly since the user is not allowed to change
342 9a06d96f Olga Brani
    them.
343 18ffbee1 Sofia Papagiannaki

344 8fb8d0cf Giorgos Korfiatis
    The class defines a save method which sets ``is_verified`` to True so as
345 8fb8d0cf Giorgos Korfiatis
    the user during the next login will not to be redirected to profile page.
346 890b0eaf Sofia Papagiannaki
    """
347 bd2c6bc5 Kostas Papadimitriou
    email = EmailField(label='E-mail address',
348 bd2c6bc5 Kostas Papadimitriou
                       help_text='E-mail address')
349 c301698f Sofia Papagiannaki
    renew = forms.BooleanField(label='Renew token', required=False)
350 18ffbee1 Sofia Papagiannaki
351 890b0eaf Sofia Papagiannaki
    class Meta:
352 890b0eaf Sofia Papagiannaki
        model = AstakosUser
353 bebd2649 Kostas Papadimitriou
        fields = ('email', 'first_name', 'last_name')
354 18ffbee1 Sofia Papagiannaki
355 64cd4730 Antony Chazapis
    def __init__(self, *args, **kwargs):
356 bf0c6de5 Sofia Papagiannaki
        self.session_key = kwargs.pop('session_key', None)
357 890b0eaf Sofia Papagiannaki
        super(ProfileForm, self).__init__(*args, **kwargs)
358 890b0eaf Sofia Papagiannaki
        instance = getattr(self, 'instance', None)
359 bebd2649 Kostas Papadimitriou
        ro_fields = ('email',)
360 890b0eaf Sofia Papagiannaki
        if instance and instance.id:
361 890b0eaf Sofia Papagiannaki
            for field in ro_fields:
362 890b0eaf Sofia Papagiannaki
                self.fields[field].widget.attrs['readonly'] = True
363 18ffbee1 Sofia Papagiannaki
364 98b84806 Kostas Papadimitriou
    def clean_email(self):
365 98b84806 Kostas Papadimitriou
        return self.instance.email
366 98b84806 Kostas Papadimitriou
367 0eb1f53a Ilias Tsitsimpis
    def save(self, commit=True, **kwargs):
368 0eb1f53a Ilias Tsitsimpis
        user = super(ProfileForm, self).save(commit=False, **kwargs)
369 890b0eaf Sofia Papagiannaki
        user.is_verified = True
370 c301698f Sofia Papagiannaki
        if self.cleaned_data.get('renew'):
371 bf0c6de5 Sofia Papagiannaki
            user.renew_token(
372 bf0c6de5 Sofia Papagiannaki
                flush_sessions=True,
373 bf0c6de5 Sofia Papagiannaki
                current_key=self.session_key
374 bf0c6de5 Sofia Papagiannaki
            )
375 890b0eaf Sofia Papagiannaki
        if commit:
376 0eb1f53a Ilias Tsitsimpis
            user.save(**kwargs)
377 890b0eaf Sofia Papagiannaki
        return user
378 64cd4730 Antony Chazapis
379 5ce3ce4f Sofia Papagiannaki
380 890b0eaf Sofia Papagiannaki
class FeedbackForm(forms.Form):
381 890b0eaf Sofia Papagiannaki
    """
382 890b0eaf Sofia Papagiannaki
    Form for writing feedback.
383 890b0eaf Sofia Papagiannaki
    """
384 0a569195 Sofia Papagiannaki
    feedback_msg = forms.CharField(widget=forms.Textarea, label=u'Message')
385 8f5a3a06 Sofia Papagiannaki
    feedback_data = forms.CharField(widget=forms.HiddenInput(), label='',
386 8f5a3a06 Sofia Papagiannaki
                                    required=False)
387 5ed6816e Sofia Papagiannaki
388 5ce3ce4f Sofia Papagiannaki
389 5ed6816e Sofia Papagiannaki
class SendInvitationForm(forms.Form):
390 5ed6816e Sofia Papagiannaki
    """
391 5ed6816e Sofia Papagiannaki
    Form for sending an invitations
392 5ed6816e Sofia Papagiannaki
    """
393 18ffbee1 Sofia Papagiannaki
394 bd2c6bc5 Kostas Papadimitriou
    email = EmailField(required=True, label='Email address')
395 bd2c6bc5 Kostas Papadimitriou
    first_name = EmailField(label='First name')
396 bd2c6bc5 Kostas Papadimitriou
    last_name = EmailField(label='Last name')
397 5ce3ce4f Sofia Papagiannaki
398 e2125441 Sofia Papagiannaki
399 e2125441 Sofia Papagiannaki
class ExtendedPasswordResetForm(PasswordResetForm):
400 e2125441 Sofia Papagiannaki
    """
401 dd5f8f4d Kostas Papadimitriou
    Extends PasswordResetForm by overriding
402 18ffbee1 Sofia Papagiannaki

403 dd5f8f4d Kostas Papadimitriou
    save method: to pass a custom from_email in send_mail.
404 dd5f8f4d Kostas Papadimitriou
    clean_email: to handle local auth provider checks
405 e2125441 Sofia Papagiannaki
    """
406 23c271b3 Sofia Papagiannaki
    def clean_email(self):
407 9d20fe23 Kostas Papadimitriou
        # we override the default django auth clean_email to provide more
408 9d20fe23 Kostas Papadimitriou
        # detailed messages in case of inactive users
409 9d20fe23 Kostas Papadimitriou
        email = self.cleaned_data['email']
410 23c271b3 Sofia Papagiannaki
        try:
411 dd5f8f4d Kostas Papadimitriou
            user = AstakosUser.objects.get_by_identifier(email)
412 9d20fe23 Kostas Papadimitriou
            self.users_cache = [user]
413 dd5f8f4d Kostas Papadimitriou
            if not user.is_active:
414 2c6bc262 Kostas Papadimitriou
                if not user.has_auth_provider('local', auth_backend='astakos'):
415 2c6bc262 Kostas Papadimitriou
                    provider = auth_providers.get_provider('local', user)
416 2c6bc262 Kostas Papadimitriou
                    msg = mark_safe(provider.get_unusable_password_msg)
417 2c6bc262 Kostas Papadimitriou
                    raise forms.ValidationError(msg)
418 2c6bc262 Kostas Papadimitriou
419 e7cb4085 Kostas Papadimitriou
                msg = mark_safe(user.get_inactive_message('local'))
420 e7cb4085 Kostas Papadimitriou
                raise forms.ValidationError(msg)
421 dd5f8f4d Kostas Papadimitriou
422 9d20fe23 Kostas Papadimitriou
            provider = auth_providers.get_provider('local', user)
423 23c271b3 Sofia Papagiannaki
            if not user.has_usable_password():
424 9d20fe23 Kostas Papadimitriou
                msg = provider.get_unusable_password_msg
425 e7cb4085 Kostas Papadimitriou
                raise forms.ValidationError(mark_safe(msg))
426 d2633501 Kostas Papadimitriou
427 d2633501 Kostas Papadimitriou
            if not user.can_change_password():
428 9d20fe23 Kostas Papadimitriou
                msg = provider.get_cannot_change_password_msg
429 e7cb4085 Kostas Papadimitriou
                raise forms.ValidationError(mark_safe(msg))
430 9d20fe23 Kostas Papadimitriou
431 9d20fe23 Kostas Papadimitriou
        except AstakosUser.DoesNotExist:
432 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.EMAIL_UNKNOWN))
433 23c271b3 Sofia Papagiannaki
        return email
434 ab8bfb29 Kostas Papadimitriou
435 8fb8d0cf Giorgos Korfiatis
    def save(self, domain_override=None,
436 8fb8d0cf Giorgos Korfiatis
             email_template_name='registration/password_reset_email.html',
437 8fb8d0cf Giorgos Korfiatis
             use_https=False, token_generator=default_token_generator,
438 0eb1f53a Ilias Tsitsimpis
             request=None, **kwargs):
439 e2125441 Sofia Papagiannaki
        """
440 8fb8d0cf Giorgos Korfiatis
        Generates a one-use only link for resetting password and sends to the
441 8fb8d0cf Giorgos Korfiatis
        user.
442 0a70d2c5 Christos Stavrakakis

443 e2125441 Sofia Papagiannaki
        """
444 e2125441 Sofia Papagiannaki
        for user in self.users_cache:
445 d2633501 Kostas Papadimitriou
            url = user.astakosuser.get_password_reset_url(token_generator)
446 95d49da0 Sofia Papagiannaki
            url = join_urls(settings.BASE_HOST, url)
447 e2125441 Sofia Papagiannaki
            c = {
448 e2125441 Sofia Papagiannaki
                'email': user.email,
449 8f378756 Sofia Papagiannaki
                'url': url,
450 8998f09a Sofia Papagiannaki
                'site_name': settings.SITENAME,
451 e2125441 Sofia Papagiannaki
                'user': user,
452 6c7af65c Georgios D. Tsoukalas
                'baseurl': settings.BASE_URL,
453 8998f09a Sofia Papagiannaki
                'support': settings.CONTACT_EMAIL
454 e2125441 Sofia Papagiannaki
            }
455 734107ef Kostas Papadimitriou
            message = render_to_string(email_template_name, c)
456 1cbce16f Sofia Papagiannaki
            from_email = settings.SERVER_EMAIL
457 cfb7dd4f Giorgos Korfiatis
            send_mail(_(astakos_messages.PASSWORD_RESET_EMAIL_SUBJECT),
458 734107ef Kostas Papadimitriou
                      message,
459 f9224cc0 Sofia Papagiannaki
                      from_email,
460 f9224cc0 Sofia Papagiannaki
                      [user.email],
461 28f4439f Sofia Papagiannaki
                      connection=get_connection())
462 5ce3ce4f Sofia Papagiannaki
463 270dd48d Sofia Papagiannaki
464 49790d9d Sofia Papagiannaki
class EmailChangeForm(forms.ModelForm):
465 34a76cdb Kostas Papadimitriou
466 bd2c6bc5 Kostas Papadimitriou
    new_email_address = EmailField()
467 bd2c6bc5 Kostas Papadimitriou
468 49790d9d Sofia Papagiannaki
    class Meta:
469 49790d9d Sofia Papagiannaki
        model = EmailChange
470 49790d9d Sofia Papagiannaki
        fields = ('new_email_address',)
471 ab8bfb29 Kostas Papadimitriou
472 49790d9d Sofia Papagiannaki
    def clean_new_email_address(self):
473 49790d9d Sofia Papagiannaki
        addr = self.cleaned_data['new_email_address']
474 43332a76 Kostas Papadimitriou
        if reserved_verified_email(addr):
475 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.EMAIL_USED))
476 49790d9d Sofia Papagiannaki
        return addr
477 ab8bfb29 Kostas Papadimitriou
478 8fb8d0cf Giorgos Korfiatis
    def save(self, request,
479 8fb8d0cf Giorgos Korfiatis
             email_template_name='registration/email_change_email.txt',
480 0eb1f53a Ilias Tsitsimpis
             commit=True, **kwargs):
481 0eb1f53a Ilias Tsitsimpis
        ec = super(EmailChangeForm, self).save(commit=False, **kwargs)
482 49790d9d Sofia Papagiannaki
        ec.user = request.user
483 3fbf7863 Kostas Papadimitriou
        # delete pending email changes
484 3fbf7863 Kostas Papadimitriou
        request.user.emailchanges.all().delete()
485 3fbf7863 Kostas Papadimitriou
486 5ce3ce4f Sofia Papagiannaki
        activation_key = hashlib.sha1(
487 5ce3ce4f Sofia Papagiannaki
            str(random()) + smart_str(ec.new_email_address))
488 5ce3ce4f Sofia Papagiannaki
        ec.activation_key = activation_key.hexdigest()
489 49790d9d Sofia Papagiannaki
        if commit:
490 0eb1f53a Ilias Tsitsimpis
            ec.save(**kwargs)
491 49790d9d Sofia Papagiannaki
        send_change_email(ec, request, email_template_name=email_template_name)
492 49790d9d Sofia Papagiannaki
493 5ce3ce4f Sofia Papagiannaki
494 270dd48d Sofia Papagiannaki
class SignApprovalTermsForm(forms.ModelForm):
495 34a76cdb Kostas Papadimitriou
496 270dd48d Sofia Papagiannaki
    class Meta:
497 270dd48d Sofia Papagiannaki
        model = AstakosUser
498 270dd48d Sofia Papagiannaki
        fields = ("has_signed_terms",)
499 18ffbee1 Sofia Papagiannaki
500 270dd48d Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
501 270dd48d Sofia Papagiannaki
        super(SignApprovalTermsForm, self).__init__(*args, **kwargs)
502 18ffbee1 Sofia Papagiannaki
503 270dd48d Sofia Papagiannaki
    def clean_has_signed_terms(self):
504 270dd48d Sofia Papagiannaki
        has_signed_terms = self.cleaned_data['has_signed_terms']
505 270dd48d Sofia Papagiannaki
        if not has_signed_terms:
506 ae497612 Olga Brani
            raise forms.ValidationError(_(astakos_messages.SIGN_TERMS))
507 270dd48d Sofia Papagiannaki
        return has_signed_terms
508 8f5a3a06 Sofia Papagiannaki
509 0eb1f53a Ilias Tsitsimpis
    def save(self, commit=True, **kwargs):
510 0eb1f53a Ilias Tsitsimpis
        user = super(SignApprovalTermsForm, self).save(commit=commit, **kwargs)
511 e7cb4085 Kostas Papadimitriou
        user.date_signed_terms = datetime.now()
512 e7cb4085 Kostas Papadimitriou
        if commit:
513 0eb1f53a Ilias Tsitsimpis
            user.save(**kwargs)
514 e7cb4085 Kostas Papadimitriou
        return user
515 e7cb4085 Kostas Papadimitriou
516 5ce3ce4f Sofia Papagiannaki
517 8f5a3a06 Sofia Papagiannaki
class InvitationForm(forms.ModelForm):
518 34a76cdb Kostas Papadimitriou
519 bd2c6bc5 Kostas Papadimitriou
    username = EmailField(label=_("Email"))
520 ab8bfb29 Kostas Papadimitriou
521 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
522 18ffbee1 Sofia Papagiannaki
        super(InvitationForm, self).__init__(*args, **kwargs)
523 ab8bfb29 Kostas Papadimitriou
524 8f5a3a06 Sofia Papagiannaki
    class Meta:
525 8f5a3a06 Sofia Papagiannaki
        model = Invitation
526 8f5a3a06 Sofia Papagiannaki
        fields = ('username', 'realname')
527 ab8bfb29 Kostas Papadimitriou
528 8f5a3a06 Sofia Papagiannaki
    def clean_username(self):
529 8f5a3a06 Sofia Papagiannaki
        username = self.cleaned_data['username']
530 8f5a3a06 Sofia Papagiannaki
        try:
531 5ce3ce4f Sofia Papagiannaki
            Invitation.objects.get(username=username)
532 8fb8d0cf Giorgos Korfiatis
            raise forms.ValidationError(
533 8fb8d0cf Giorgos Korfiatis
                _(astakos_messages.INVITATION_EMAIL_EXISTS))
534 8f5a3a06 Sofia Papagiannaki
        except Invitation.DoesNotExist:
535 8f5a3a06 Sofia Papagiannaki
            pass
536 18ffbee1 Sofia Papagiannaki
        return username
537 1039bab1 Sofia Papagiannaki
538 5ce3ce4f Sofia Papagiannaki
539 1039bab1 Sofia Papagiannaki
class ExtendedPasswordChangeForm(PasswordChangeForm):
540 1039bab1 Sofia Papagiannaki
    """
541 1039bab1 Sofia Papagiannaki
    Extends PasswordChangeForm by enabling user
542 1039bab1 Sofia Papagiannaki
    to optionally renew also the token.
543 1039bab1 Sofia Papagiannaki
    """
544 8998f09a Sofia Papagiannaki
    if not settings.NEWPASSWD_INVALIDATE_TOKEN:
545 8fb8d0cf Giorgos Korfiatis
        renew = forms.BooleanField(
546 8fb8d0cf Giorgos Korfiatis
            label='Renew token', required=False,
547 8fb8d0cf Giorgos Korfiatis
            initial=True,
548 8fb8d0cf Giorgos Korfiatis
            help_text='Unsetting this may result in security risk.')
549 ab8bfb29 Kostas Papadimitriou
550 1039bab1 Sofia Papagiannaki
    def __init__(self, user, *args, **kwargs):
551 bf0c6de5 Sofia Papagiannaki
        self.session_key = kwargs.pop('session_key', None)
552 1039bab1 Sofia Papagiannaki
        super(ExtendedPasswordChangeForm, self).__init__(user, *args, **kwargs)
553 ab8bfb29 Kostas Papadimitriou
554 0eb1f53a Ilias Tsitsimpis
    def save(self, commit=True, **kwargs):
555 bf0c6de5 Sofia Papagiannaki
        try:
556 8998f09a Sofia Papagiannaki
            if settings.NEWPASSWD_INVALIDATE_TOKEN or \
557 8998f09a Sofia Papagiannaki
                    self.cleaned_data.get('renew'):
558 bf0c6de5 Sofia Papagiannaki
                self.user.renew_token()
559 bf0c6de5 Sofia Papagiannaki
            self.user.flush_sessions(current_key=self.session_key)
560 bf0c6de5 Sofia Papagiannaki
        except AttributeError:
561 bf0c6de5 Sofia Papagiannaki
            # if user model does has not such methods
562 bf0c6de5 Sofia Papagiannaki
            pass
563 60b59fed Ilias Tsitsimpis
        return super(ExtendedPasswordChangeForm, self).save(commit=commit,
564 0eb1f53a Ilias Tsitsimpis
                                                            **kwargs)
565 48e9f076 Sofia Papagiannaki
566 8fb8d0cf Giorgos Korfiatis
567 48e9f076 Sofia Papagiannaki
class ExtendedSetPasswordForm(SetPasswordForm):
568 48e9f076 Sofia Papagiannaki
    """
569 48e9f076 Sofia Papagiannaki
    Extends SetPasswordForm by enabling user
570 48e9f076 Sofia Papagiannaki
    to optionally renew also the token.
571 48e9f076 Sofia Papagiannaki
    """
572 8998f09a Sofia Papagiannaki
    if not settings.NEWPASSWD_INVALIDATE_TOKEN:
573 bf0c6de5 Sofia Papagiannaki
        renew = forms.BooleanField(
574 bf0c6de5 Sofia Papagiannaki
            label='Renew token',
575 bf0c6de5 Sofia Papagiannaki
            required=False,
576 bf0c6de5 Sofia Papagiannaki
            initial=True,
577 47b77c8b Sofia Papagiannaki
            help_text='Unsetting this may result in security risk.')
578 2e90e3ec Kostas Papadimitriou
579 48e9f076 Sofia Papagiannaki
    def __init__(self, user, *args, **kwargs):
580 48e9f076 Sofia Papagiannaki
        super(ExtendedSetPasswordForm, self).__init__(user, *args, **kwargs)
581 d2633501 Kostas Papadimitriou
582 d2633501 Kostas Papadimitriou
    @transaction.commit_on_success()
583 0eb1f53a Ilias Tsitsimpis
    def save(self, commit=True, **kwargs):
584 bf0c6de5 Sofia Papagiannaki
        try:
585 bf0c6de5 Sofia Papagiannaki
            self.user = AstakosUser.objects.get(id=self.user.id)
586 8998f09a Sofia Papagiannaki
            if settings.NEWPASSWD_INVALIDATE_TOKEN or \
587 8998f09a Sofia Papagiannaki
                    self.cleaned_data.get('renew'):
588 53161dd8 Sofia Papagiannaki
                self.user.renew_token()
589 9d20fe23 Kostas Papadimitriou
590 9d20fe23 Kostas Papadimitriou
            provider = auth_providers.get_provider('local', self.user)
591 9d20fe23 Kostas Papadimitriou
            if provider.get_add_policy:
592 9d20fe23 Kostas Papadimitriou
                provider.add_to_user()
593 d2633501 Kostas Papadimitriou
594 bf0c6de5 Sofia Papagiannaki
        except BaseException, e:
595 bf0c6de5 Sofia Papagiannaki
            logger.exception(e)
596 60b59fed Ilias Tsitsimpis
        return super(ExtendedSetPasswordForm, self).save(commit=commit,
597 0eb1f53a Ilias Tsitsimpis
                                                         **kwargs)
598 e1a80257 Sofia Papagiannaki
599 e1a80257 Sofia Papagiannaki
600 8fb8d0cf Giorgos Korfiatis
app_name_label = "Project name"
601 67980f56 Georgios D. Tsoukalas
app_name_placeholder = _("myproject.mylab.ntua.gr")
602 8fb8d0cf Giorgos Korfiatis
app_name_validator = validators.RegexValidator(
603 8fb8d0cf Giorgos Korfiatis
    DOMAIN_VALUE_REGEX,
604 8fb8d0cf Giorgos Korfiatis
    _(astakos_messages.DOMAIN_VALUE_ERR),
605 8fb8d0cf Giorgos Korfiatis
    'invalid')
606 8fb8d0cf Giorgos Korfiatis
app_name_help = _("""
607 c9a6e558 Constantinos Venetsanopoulos
        The project's name should be in a domain format.
608 67980f56 Georgios D. Tsoukalas
        The domain shouldn't neccessarily exist in the real
609 67980f56 Georgios D. Tsoukalas
        world but is helpful to imply a structure.
610 67980f56 Georgios D. Tsoukalas
        e.g.: myproject.mylab.ntua.gr or
611 67980f56 Georgios D. Tsoukalas
        myservice.myteam.myorganization""")
612 8fb8d0cf Giorgos Korfiatis
app_name_widget = forms.TextInput(
613 8fb8d0cf Giorgos Korfiatis
    attrs={'placeholder': app_name_placeholder})
614 67980f56 Georgios D. Tsoukalas
615 67980f56 Georgios D. Tsoukalas
616 8fb8d0cf Giorgos Korfiatis
app_home_label = "Homepage URL"
617 8fb8d0cf Giorgos Korfiatis
app_home_placeholder = 'myinstitution.org/myproject/'
618 8fb8d0cf Giorgos Korfiatis
app_home_help = _("""
619 67980f56 Georgios D. Tsoukalas
        URL pointing at your project's site.
620 3ff82dca Olga Brani
        e.g.: myinstitution.org/myproject/.
621 67980f56 Georgios D. Tsoukalas
        Leave blank if there is no website.""")
622 8fb8d0cf Giorgos Korfiatis
app_home_widget = forms.TextInput(
623 8fb8d0cf Giorgos Korfiatis
    attrs={'placeholder': app_home_placeholder})
624 67980f56 Georgios D. Tsoukalas
625 8fb8d0cf Giorgos Korfiatis
app_desc_label = _("Description")
626 8fb8d0cf Giorgos Korfiatis
app_desc_help = _("""
627 c53afd76 Georgios D. Tsoukalas
        Please provide a short but descriptive abstract of your
628 c9a6e558 Constantinos Venetsanopoulos
        project, so that anyone searching can quickly understand
629 c9a6e558 Constantinos Venetsanopoulos
        what this project is about.""")
630 67980f56 Georgios D. Tsoukalas
631 8fb8d0cf Giorgos Korfiatis
app_comment_label = _("Comments for review (private)")
632 8fb8d0cf Giorgos Korfiatis
app_comment_help = _("""
633 67980f56 Georgios D. Tsoukalas
        Write down any comments you may have for the reviewer
634 67980f56 Georgios D. Tsoukalas
        of this application (e.g. background and rationale to
635 67980f56 Georgios D. Tsoukalas
        support your request).
636 67980f56 Georgios D. Tsoukalas
        The comments are strictly for the review process
637 c53afd76 Georgios D. Tsoukalas
        and will not be made public.""")
638 67980f56 Georgios D. Tsoukalas
639 8fb8d0cf Giorgos Korfiatis
app_start_date_label = _("Start date")
640 8fb8d0cf Giorgos Korfiatis
app_start_date_help = _("""
641 67980f56 Georgios D. Tsoukalas
        Provide a date when your need your project to be created,
642 67980f56 Georgios D. Tsoukalas
        and members to be able to join and get resources.
643 67980f56 Georgios D. Tsoukalas
        This date is only a hint to help prioritize reviews.""")
644 67980f56 Georgios D. Tsoukalas
645 8fb8d0cf Giorgos Korfiatis
app_end_date_label = _("Termination date")
646 8fb8d0cf Giorgos Korfiatis
app_end_date_help = _("""
647 67980f56 Georgios D. Tsoukalas
        At this date, the project will be automatically terminated
648 c53afd76 Georgios D. Tsoukalas
        and its resource grants revoked from all members. If you are
649 c53afd76 Georgios D. Tsoukalas
        not certain, it is best to start with a conservative estimation.
650 67980f56 Georgios D. Tsoukalas
        You can always re-apply for an extension, if you need.""")
651 67980f56 Georgios D. Tsoukalas
652 8fb8d0cf Giorgos Korfiatis
join_policy_label = _("Joining policy")
653 8fb8d0cf Giorgos Korfiatis
app_member_join_policy_help = _("""
654 c53afd76 Georgios D. Tsoukalas
        Select how new members are accepted into the project.""")
655 8fb8d0cf Giorgos Korfiatis
leave_policy_label = _("Leaving policy")
656 8fb8d0cf Giorgos Korfiatis
app_member_leave_policy_help = _("""
657 c53afd76 Georgios D. Tsoukalas
        Select how new members can leave the project.""")
658 67980f56 Georgios D. Tsoukalas
659 8fb8d0cf Giorgos Korfiatis
max_members_label = _("Maximum member count")
660 8fb8d0cf Giorgos Korfiatis
max_members_help = _("""
661 67980f56 Georgios D. Tsoukalas
        Specify the maximum number of members this project may have,
662 67980f56 Georgios D. Tsoukalas
        including the owner. Beyond this number, no new members
663 67980f56 Georgios D. Tsoukalas
        may join the project and be granted the project resources.
664 c53afd76 Georgios D. Tsoukalas
        If you are not certain, it is best to start with a conservative
665 c53afd76 Georgios D. Tsoukalas
        limit. You can always request a raise when you need it.""")
666 67980f56 Georgios D. Tsoukalas
667 251b83be Giorgos Korfiatis
join_policies = presentation.PROJECT_MEMBER_JOIN_POLICIES.items()
668 251b83be Giorgos Korfiatis
leave_policies = presentation.PROJECT_MEMBER_LEAVE_POLICIES.items()
669 67980f56 Georgios D. Tsoukalas
670 8fb8d0cf Giorgos Korfiatis
671 e1a80257 Sofia Papagiannaki
class ProjectApplicationForm(forms.ModelForm):
672 67980f56 Georgios D. Tsoukalas
673 e1a80257 Sofia Papagiannaki
    name = forms.CharField(
674 8fb8d0cf Giorgos Korfiatis
        label=app_name_label,
675 8fb8d0cf Giorgos Korfiatis
        help_text=app_name_help,
676 8fb8d0cf Giorgos Korfiatis
        widget=app_name_widget,
677 8fb8d0cf Giorgos Korfiatis
        validators=[app_name_validator])
678 67980f56 Georgios D. Tsoukalas
679 52784759 Olga Brani
    homepage = forms.URLField(
680 8fb8d0cf Giorgos Korfiatis
        label=app_home_label,
681 8fb8d0cf Giorgos Korfiatis
        help_text=app_home_help,
682 8fb8d0cf Giorgos Korfiatis
        widget=app_home_widget,
683 8fb8d0cf Giorgos Korfiatis
        required=False)
684 67980f56 Georgios D. Tsoukalas
685 67980f56 Georgios D. Tsoukalas
    description = forms.CharField(
686 8fb8d0cf Giorgos Korfiatis
        label=app_desc_label,
687 8fb8d0cf Giorgos Korfiatis
        help_text=app_desc_help,
688 8fb8d0cf Giorgos Korfiatis
        widget=forms.Textarea,
689 8fb8d0cf Giorgos Korfiatis
        required=False)
690 67980f56 Georgios D. Tsoukalas
691 67980f56 Georgios D. Tsoukalas
    comments = forms.CharField(
692 8fb8d0cf Giorgos Korfiatis
        label=app_comment_label,
693 8fb8d0cf Giorgos Korfiatis
        help_text=app_comment_help,
694 8fb8d0cf Giorgos Korfiatis
        widget=forms.Textarea,
695 8fb8d0cf Giorgos Korfiatis
        required=False)
696 67980f56 Georgios D. Tsoukalas
697 67980f56 Georgios D. Tsoukalas
    start_date = forms.DateTimeField(
698 8fb8d0cf Giorgos Korfiatis
        label=app_start_date_label,
699 8fb8d0cf Giorgos Korfiatis
        help_text=app_start_date_help,
700 8fb8d0cf Giorgos Korfiatis
        required=False)
701 67980f56 Georgios D. Tsoukalas
702 67980f56 Georgios D. Tsoukalas
    end_date = forms.DateTimeField(
703 8fb8d0cf Giorgos Korfiatis
        label=app_end_date_label,
704 8fb8d0cf Giorgos Korfiatis
        help_text=app_end_date_help)
705 67980f56 Georgios D. Tsoukalas
706 8fb8d0cf Giorgos Korfiatis
    member_join_policy = forms.TypedChoiceField(
707 8fb8d0cf Giorgos Korfiatis
        label=join_policy_label,
708 8fb8d0cf Giorgos Korfiatis
        help_text=app_member_join_policy_help,
709 8fb8d0cf Giorgos Korfiatis
        initial=2,
710 8fb8d0cf Giorgos Korfiatis
        coerce=int,
711 8fb8d0cf Giorgos Korfiatis
        choices=join_policies)
712 dd5f8f4d Kostas Papadimitriou
713 80da92f0 Kostas Papadimitriou
    member_leave_policy = forms.TypedChoiceField(
714 8fb8d0cf Giorgos Korfiatis
        label=leave_policy_label,
715 8fb8d0cf Giorgos Korfiatis
        help_text=app_member_leave_policy_help,
716 8fb8d0cf Giorgos Korfiatis
        coerce=int,
717 8fb8d0cf Giorgos Korfiatis
        choices=leave_policies)
718 67980f56 Georgios D. Tsoukalas
719 67980f56 Georgios D. Tsoukalas
    limit_on_members_number = forms.IntegerField(
720 8fb8d0cf Giorgos Korfiatis
        label=max_members_label,
721 8fb8d0cf Giorgos Korfiatis
        help_text=max_members_help,
722 8fb8d0cf Giorgos Korfiatis
        min_value=0,
723 e98239db Giorgos Korfiatis
        required=True)
724 dd5f8f4d Kostas Papadimitriou
725 e1a80257 Sofia Papagiannaki
    class Meta:
726 73fbaec4 Sofia Papagiannaki
        model = ProjectApplication
727 8fb8d0cf Giorgos Korfiatis
        fields = ('name', 'homepage', 'description',
728 8fb8d0cf Giorgos Korfiatis
                  'start_date', 'end_date', 'comments',
729 8fb8d0cf Giorgos Korfiatis
                  'member_join_policy', 'member_leave_policy',
730 8fb8d0cf Giorgos Korfiatis
                  'limit_on_members_number')
731 c82bd52b Kostas Papadimitriou
732 40b52116 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
733 2ef98527 Sofia Papagiannaki
        instance = kwargs.get('instance')
734 2ef98527 Sofia Papagiannaki
        self.precursor_application = instance
735 40b52116 Sofia Papagiannaki
        super(ProjectApplicationForm, self).__init__(*args, **kwargs)
736 f568d0f4 Sofia Papagiannaki
        # in case of new application remove closed join policy
737 2ef98527 Sofia Papagiannaki
        if not instance:
738 251b83be Giorgos Korfiatis
            policies = presentation.PROJECT_MEMBER_JOIN_POLICIES.copy()
739 251b83be Giorgos Korfiatis
            policies.pop(3)
740 2ef98527 Sofia Papagiannaki
            self.fields['member_join_policy'].choices = policies.iteritems()
741 dd5f8f4d Kostas Papadimitriou
742 69b26576 Sofia Papagiannaki
    def clean_start_date(self):
743 69b26576 Sofia Papagiannaki
        start_date = self.cleaned_data.get('start_date')
744 f568d0f4 Sofia Papagiannaki
        if not self.precursor_application:
745 8172d87d Sofia Papagiannaki
            today = datetime.now()
746 8172d87d Sofia Papagiannaki
            today = datetime(today.year, today.month, today.day)
747 01223f04 Sofia Papagiannaki
            if start_date and (start_date - today).days < 0:
748 f568d0f4 Sofia Papagiannaki
                raise forms.ValidationError(
749 8fb8d0cf Giorgos Korfiatis
                    _(astakos_messages.INVALID_PROJECT_START_DATE))
750 69b26576 Sofia Papagiannaki
        return start_date
751 69b26576 Sofia Papagiannaki
752 69b26576 Sofia Papagiannaki
    def clean_end_date(self):
753 69b26576 Sofia Papagiannaki
        start_date = self.cleaned_data.get('start_date')
754 69b26576 Sofia Papagiannaki
        end_date = self.cleaned_data.get('end_date')
755 8172d87d Sofia Papagiannaki
        today = datetime.now()
756 8172d87d Sofia Papagiannaki
        today = datetime(today.year, today.month, today.day)
757 01223f04 Sofia Papagiannaki
        if end_date and (end_date - today).days < 0:
758 69b26576 Sofia Papagiannaki
            raise forms.ValidationError(
759 69b26576 Sofia Papagiannaki
                _(astakos_messages.INVALID_PROJECT_END_DATE))
760 f03b9ba6 Sofia Papagiannaki
        if start_date and (end_date - start_date).days <= 0:
761 69b26576 Sofia Papagiannaki
            raise forms.ValidationError(
762 69b26576 Sofia Papagiannaki
                _(astakos_messages.INCONSISTENT_PROJECT_DATES))
763 69b26576 Sofia Papagiannaki
        return end_date
764 69b26576 Sofia Papagiannaki
765 e1a80257 Sofia Papagiannaki
    def clean(self):
766 185b2190 Sofia Papagiannaki
        userid = self.data.get('user', None)
767 51db2da2 Giorgos Korfiatis
        self.resource_policies
768 e1a80257 Sofia Papagiannaki
        self.user = None
769 e1a80257 Sofia Papagiannaki
        if userid:
770 e1a80257 Sofia Papagiannaki
            try:
771 e1a80257 Sofia Papagiannaki
                self.user = AstakosUser.objects.get(id=userid)
772 e1a80257 Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
773 e1a80257 Sofia Papagiannaki
                pass
774 e1a80257 Sofia Papagiannaki
        if not self.user:
775 e1a80257 Sofia Papagiannaki
            raise forms.ValidationError(_(astakos_messages.NO_APPLICANT))
776 e1a80257 Sofia Papagiannaki
        super(ProjectApplicationForm, self).clean()
777 e1a80257 Sofia Papagiannaki
        return self.cleaned_data
778 dd5f8f4d Kostas Papadimitriou
779 f3342849 Sofia Papagiannaki
    @property
780 f3342849 Sofia Papagiannaki
    def resource_policies(self):
781 e1a80257 Sofia Papagiannaki
        policies = []
782 e1a80257 Sofia Papagiannaki
        append = policies.append
783 f3342849 Sofia Papagiannaki
        for name, value in self.data.iteritems():
784 f3342849 Sofia Papagiannaki
            if not value:
785 f3342849 Sofia Papagiannaki
                continue
786 56eb807c Sofia Papagiannaki
            uplimit = value
787 56eb807c Sofia Papagiannaki
            if name.endswith('_uplimit'):
788 56eb807c Sofia Papagiannaki
                subs = name.split('_uplimit')
789 e1a80257 Sofia Papagiannaki
                prefix, suffix = subs
790 37d59b27 Kostas Papadimitriou
                try:
791 37d59b27 Kostas Papadimitriou
                    resource = Resource.objects.get(name=prefix)
792 37d59b27 Kostas Papadimitriou
                except Resource.DoesNotExist:
793 37d59b27 Kostas Papadimitriou
                    raise forms.ValidationError("Resource %s does not exist" %
794 37d59b27 Kostas Papadimitriou
                                                resource.name)
795 e1a80257 Sofia Papagiannaki
                # keep only resource limits for selected resource groups
796 8fb8d0cf Giorgos Korfiatis
                if self.data.get('is_selected_%s' %
797 8fb8d0cf Giorgos Korfiatis
                                 resource.group, "0") == "1":
798 2dc27ac1 Giorgos Korfiatis
                    if not resource.ui_visible:
799 37d59b27 Kostas Papadimitriou
                        raise forms.ValidationError("Invalid resource %s" %
800 37d59b27 Kostas Papadimitriou
                                                    resource.name)
801 6556e514 Sofia Papagiannaki
                    d = model_to_dict(resource)
802 3e87075a Giorgos Korfiatis
                    try:
803 3e87075a Giorgos Korfiatis
                        uplimit = long(uplimit)
804 3e87075a Giorgos Korfiatis
                    except ValueError:
805 3e87075a Giorgos Korfiatis
                        m = "Limit should be an integer"
806 3e87075a Giorgos Korfiatis
                        raise forms.ValidationError(m)
807 3e87075a Giorgos Korfiatis
                    display = units.show(uplimit, resource.unit)
808 3e87075a Giorgos Korfiatis
                    d.update(dict(resource=prefix, uplimit=uplimit,
809 3e87075a Giorgos Korfiatis
                                  display_uplimit=display))
810 6556e514 Sofia Papagiannaki
                    append(d)
811 dd5f8f4d Kostas Papadimitriou
812 75380308 Kostas Papadimitriou
        ordered_keys = presentation.RESOURCES['resources_order']
813 8fb8d0cf Giorgos Korfiatis
814 37d59b27 Kostas Papadimitriou
        def resource_order(r):
815 37d59b27 Kostas Papadimitriou
            if r['str_repr'] in ordered_keys:
816 37d59b27 Kostas Papadimitriou
                return ordered_keys.index(r['str_repr'])
817 37d59b27 Kostas Papadimitriou
            else:
818 37d59b27 Kostas Papadimitriou
                return -1
819 37d59b27 Kostas Papadimitriou
820 37d59b27 Kostas Papadimitriou
        policies = sorted(policies, key=resource_order)
821 e1a80257 Sofia Papagiannaki
        return policies
822 dd5f8f4d Kostas Papadimitriou
823 eee9ec4d Giorgos Korfiatis
    def cleaned_resource_policies(self):
824 b2369828 Giorgos Korfiatis
        policies = {}
825 b2369828 Giorgos Korfiatis
        for d in self.resource_policies:
826 b2369828 Giorgos Korfiatis
            policies[d["name"]] = {
827 b2369828 Giorgos Korfiatis
                "project_capacity": None,
828 b2369828 Giorgos Korfiatis
                "member_capacity": d["uplimit"]
829 b2369828 Giorgos Korfiatis
            }
830 b2369828 Giorgos Korfiatis
831 b2369828 Giorgos Korfiatis
        return policies
832 eee9ec4d Giorgos Korfiatis
833 0eb1f53a Ilias Tsitsimpis
    def save(self, commit=True, **kwargs):
834 15ca2bea Giorgos Korfiatis
        data = dict(self.cleaned_data)
835 42732c26 Giorgos Korfiatis
        is_new = self.instance.id is None
836 c4028837 Giorgos Korfiatis
        data['project_id'] = self.instance.chain.id if not is_new else None
837 42732c26 Giorgos Korfiatis
        data['owner'] = self.user if is_new else self.instance.owner
838 b2369828 Giorgos Korfiatis
        data['resources'] = self.cleaned_resource_policies()
839 6da04174 Giorgos Korfiatis
        data['request_user'] = self.user
840 6da04174 Giorgos Korfiatis
        submit_application(**data)
841 6da04174 Giorgos Korfiatis
842 8327782d Sofia Papagiannaki
843 8327782d Sofia Papagiannaki
class ProjectSortForm(forms.Form):
844 8327782d Sofia Papagiannaki
    sorting = forms.ChoiceField(
845 8327782d Sofia Papagiannaki
        label='Sort by',
846 73fbaec4 Sofia Papagiannaki
        choices=(('name', 'Sort by Name'),
847 8327782d Sofia Papagiannaki
                 ('issue_date', 'Sort by Issue date'),
848 73fbaec4 Sofia Papagiannaki
                 ('start_date', 'Sort by Start Date'),
849 73fbaec4 Sofia Papagiannaki
                 ('end_date', 'Sort by End Date'),
850 8fb8d0cf Giorgos Korfiatis
                 # ('approved_members_num', 'Sort by Participants'),
851 d6a162d3 Sofia Papagiannaki
                 ('state', 'Sort by Status'),
852 8fb8d0cf Giorgos Korfiatis
                 ('member_join_policy__description',
853 8fb8d0cf Giorgos Korfiatis
                  'Sort by Member Join Policy'),
854 8fb8d0cf Giorgos Korfiatis
                 ('member_leave_policy__description',
855 8fb8d0cf Giorgos Korfiatis
                  'Sort by Member Leave Policy'),
856 7592e3e2 Sofia Papagiannaki
                 ('-name', 'Sort by Name'),
857 7592e3e2 Sofia Papagiannaki
                 ('-issue_date', 'Sort by Issue date'),
858 7592e3e2 Sofia Papagiannaki
                 ('-start_date', 'Sort by Start Date'),
859 7592e3e2 Sofia Papagiannaki
                 ('-end_date', 'Sort by End Date'),
860 8fb8d0cf Giorgos Korfiatis
                 # ('-approved_members_num', 'Sort by Participants'),
861 7592e3e2 Sofia Papagiannaki
                 ('-state', 'Sort by Status'),
862 8fb8d0cf Giorgos Korfiatis
                 ('-member_join_policy__description',
863 8fb8d0cf Giorgos Korfiatis
                  'Sort by Member Join Policy'),
864 8fb8d0cf Giorgos Korfiatis
                 ('-member_leave_policy__description',
865 8fb8d0cf Giorgos Korfiatis
                  'Sort by Member Leave Policy')
866 8fb8d0cf Giorgos Korfiatis
                 ),
867 8327782d Sofia Papagiannaki
        required=True
868 ccab6eb5 Sofia Papagiannaki
    )
869 ccab6eb5 Sofia Papagiannaki
870 8fb8d0cf Giorgos Korfiatis
871 ccab6eb5 Sofia Papagiannaki
class AddProjectMembersForm(forms.Form):
872 ccab6eb5 Sofia Papagiannaki
    q = forms.CharField(
873 8fb8d0cf Giorgos Korfiatis
        widget=forms.Textarea(
874 8fb8d0cf Giorgos Korfiatis
            attrs={
875 8fb8d0cf Giorgos Korfiatis
                'placeholder':
876 8fb8d0cf Giorgos Korfiatis
                astakos_messages.ADD_PROJECT_MEMBERS_Q_PLACEHOLDER}),
877 3f3dc4b7 Olga Brani
        label=_('Add members'),
878 b6e2252e Kostas Papadimitriou
        help_text=_(astakos_messages.ADD_PROJECT_MEMBERS_Q_HELP),
879 3f3dc4b7 Olga Brani
        required=True,)
880 ccab6eb5 Sofia Papagiannaki
881 e47fb17a Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
882 ff67242a Giorgos Korfiatis
        chain_id = kwargs.pop('chain_id', None)
883 ff67242a Giorgos Korfiatis
        if chain_id:
884 ff67242a Giorgos Korfiatis
            self.project = Project.objects.get(id=chain_id)
885 e47fb17a Sofia Papagiannaki
        self.request_user = kwargs.pop('request_user', None)
886 e47fb17a Sofia Papagiannaki
        super(AddProjectMembersForm, self).__init__(*args, **kwargs)
887 43332a76 Kostas Papadimitriou
888 ccab6eb5 Sofia Papagiannaki
    def clean(self):
889 e47fb17a Sofia Papagiannaki
        try:
890 866e5768 Giorgos Korfiatis
            accept_membership_project_checks(self.project, self.request_user)
891 f12bcb3d Giorgos Korfiatis
        except ProjectError as e:
892 e47fb17a Sofia Papagiannaki
            raise forms.ValidationError(e)
893 e47fb17a Sofia Papagiannaki
894 ccab6eb5 Sofia Papagiannaki
        q = self.cleaned_data.get('q') or ''
895 ccab6eb5 Sofia Papagiannaki
        users = q.split(',')
896 ccab6eb5 Sofia Papagiannaki
        users = list(u.strip() for u in users if u)
897 733f013d Giorgos Korfiatis
        db_entries = AstakosUser.objects.accepted().filter(email__in=users)
898 ccab6eb5 Sofia Papagiannaki
        unknown = list(set(users) - set(u.email for u in db_entries))
899 ccab6eb5 Sofia Papagiannaki
        if unknown:
900 e47fb17a Sofia Papagiannaki
            raise forms.ValidationError(
901 e47fb17a Sofia Papagiannaki
                _(astakos_messages.UNKNOWN_USERS) % ','.join(unknown))
902 ccab6eb5 Sofia Papagiannaki
        self.valid_users = db_entries
903 ccab6eb5 Sofia Papagiannaki
        return self.cleaned_data
904 ccab6eb5 Sofia Papagiannaki
905 ccab6eb5 Sofia Papagiannaki
    def get_valid_users(self):
906 ccab6eb5 Sofia Papagiannaki
        """Should be called after form cleaning"""
907 ccab6eb5 Sofia Papagiannaki
        try:
908 ccab6eb5 Sofia Papagiannaki
            return self.valid_users
909 ccab6eb5 Sofia Papagiannaki
        except:
910 bfe23b13 Sofia Papagiannaki
            return ()
911 bfe23b13 Sofia Papagiannaki
912 8fb8d0cf Giorgos Korfiatis
913 bfe23b13 Sofia Papagiannaki
class ProjectMembersSortForm(forms.Form):
914 bfe23b13 Sofia Papagiannaki
    sorting = forms.ChoiceField(
915 bfe23b13 Sofia Papagiannaki
        label='Sort by',
916 bfe23b13 Sofia Papagiannaki
        choices=(('person__email', 'User Id'),
917 bfe23b13 Sofia Papagiannaki
                 ('person__first_name', 'Name'),
918 bfe23b13 Sofia Papagiannaki
                 ('acceptance_date', 'Acceptance date')
919 8fb8d0cf Giorgos Korfiatis
                 ),
920 bfe23b13 Sofia Papagiannaki
        required=True
921 bfe23b13 Sofia Papagiannaki
    )
922 bfe23b13 Sofia Papagiannaki
923 f7400729 Kostas Papadimitriou
924 6dadd24a Sofia Papagiannaki
class ProjectSearchForm(forms.Form):
925 85d444db Sofia Papagiannaki
    q = forms.CharField(max_length=200, label='Search project', required=False)
926 5550bcfb Kostas Papadimitriou
927 f7400729 Kostas Papadimitriou
928 f7400729 Kostas Papadimitriou
class ExtendedProfileForm(ProfileForm):
929 f7400729 Kostas Papadimitriou
    """
930 f7400729 Kostas Papadimitriou
    Profile form that combines `email change` and `password change` user
931 f7400729 Kostas Papadimitriou
    actions by propagating submited data to internal EmailChangeForm
932 f7400729 Kostas Papadimitriou
    and ExtendedPasswordChangeForm objects.
933 f7400729 Kostas Papadimitriou
    """
934 f7400729 Kostas Papadimitriou
935 f7400729 Kostas Papadimitriou
    password_change_form = None
936 f7400729 Kostas Papadimitriou
    email_change_form = None
937 17ad5d37 Kostas Papadimitriou
938 3fbf7863 Kostas Papadimitriou
    password_change = False
939 17ad5d37 Kostas Papadimitriou
    email_change = False
940 17ad5d37 Kostas Papadimitriou
941 f7400729 Kostas Papadimitriou
    extra_forms_fields = {
942 f7400729 Kostas Papadimitriou
        'email': ['new_email_address'],
943 f7400729 Kostas Papadimitriou
        'password': ['old_password', 'new_password1', 'new_password2']
944 f7400729 Kostas Papadimitriou
    }
945 f7400729 Kostas Papadimitriou
946 d0632ab1 Olga Brani
    fields = ('email')
947 f7400729 Kostas Papadimitriou
    change_password = forms.BooleanField(initial=False, required=False)
948 f7400729 Kostas Papadimitriou
    change_email = forms.BooleanField(initial=False, required=False)
949 c55e39e7 Kostas Papadimitriou
950 2da6f56b Kostas Papadimitriou
    email_changed = False
951 2da6f56b Kostas Papadimitriou
    password_changed = False
952 f7400729 Kostas Papadimitriou
953 f7400729 Kostas Papadimitriou
    def __init__(self, *args, **kwargs):
954 95cb469b Kostas Papadimitriou
        session_key = kwargs.get('session_key', None)
955 c55e39e7 Kostas Papadimitriou
        self.fields_list = [
956 8fb8d0cf Giorgos Korfiatis
            'email',
957 8fb8d0cf Giorgos Korfiatis
            'new_email_address',
958 8fb8d0cf Giorgos Korfiatis
            'first_name',
959 8fb8d0cf Giorgos Korfiatis
            'last_name',
960 8fb8d0cf Giorgos Korfiatis
            'old_password',
961 8fb8d0cf Giorgos Korfiatis
            'new_password1',
962 8fb8d0cf Giorgos Korfiatis
            'new_password2',
963 8fb8d0cf Giorgos Korfiatis
            'change_email',
964 8fb8d0cf Giorgos Korfiatis
            'change_password',
965 c55e39e7 Kostas Papadimitriou
        ]
966 c55e39e7 Kostas Papadimitriou
967 f7400729 Kostas Papadimitriou
        super(ExtendedProfileForm, self).__init__(*args, **kwargs)
968 95cb469b Kostas Papadimitriou
        self.session_key = session_key
969 3fbf7863 Kostas Papadimitriou
        if self.instance.can_change_password():
970 3fbf7863 Kostas Papadimitriou
            self.password_change = True
971 3fbf7863 Kostas Papadimitriou
        else:
972 2b7a87d7 Olga Brani
            self.fields_list.remove('old_password')
973 2b7a87d7 Olga Brani
            self.fields_list.remove('new_password1')
974 2b7a87d7 Olga Brani
            self.fields_list.remove('new_password2')
975 2b7a87d7 Olga Brani
            self.fields_list.remove('change_password')
976 3fbf7863 Kostas Papadimitriou
            del self.fields['change_password']
977 3fbf7863 Kostas Papadimitriou
978 8998f09a Sofia Papagiannaki
        if settings.EMAILCHANGE_ENABLED and self.instance.can_change_email():
979 17ad5d37 Kostas Papadimitriou
            self.email_change = True
980 17ad5d37 Kostas Papadimitriou
        else:
981 d0632ab1 Olga Brani
            self.fields_list.remove('new_email_address')
982 2b7a87d7 Olga Brani
            self.fields_list.remove('change_email')
983 17ad5d37 Kostas Papadimitriou
            del self.fields['change_email']
984 17ad5d37 Kostas Papadimitriou
985 f7400729 Kostas Papadimitriou
        self._init_extra_forms()
986 f7400729 Kostas Papadimitriou
        self.save_extra_forms = []
987 f7400729 Kostas Papadimitriou
        self.success_messages = []
988 d0632ab1 Olga Brani
        self.fields.keyOrder = self.fields_list
989 c55e39e7 Kostas Papadimitriou
990 f7400729 Kostas Papadimitriou
    def _init_extra_form_fields(self):
991 17ad5d37 Kostas Papadimitriou
        if self.email_change:
992 17ad5d37 Kostas Papadimitriou
            self.fields.update(self.email_change_form.fields)
993 17ad5d37 Kostas Papadimitriou
            self.fields['new_email_address'].required = False
994 8fb8d0cf Giorgos Korfiatis
            self.fields['email'].help_text = _(
995 8fb8d0cf Giorgos Korfiatis
                'Change the email associated with '
996 8fb8d0cf Giorgos Korfiatis
                'your account. This email will '
997 8fb8d0cf Giorgos Korfiatis
                'remain active until you verify '
998 8fb8d0cf Giorgos Korfiatis
                'your new one.')
999 3fbf7863 Kostas Papadimitriou
1000 3fbf7863 Kostas Papadimitriou
        if self.password_change:
1001 3fbf7863 Kostas Papadimitriou
            self.fields.update(self.password_change_form.fields)
1002 3fbf7863 Kostas Papadimitriou
            self.fields['old_password'].required = False
1003 003d8fcf Olga Brani
            self.fields['old_password'].label = _('Password')
1004 c9a6e558 Constantinos Venetsanopoulos
            self.fields['old_password'].help_text = _('Change your password.')
1005 861458fd Kostas Papadimitriou
            self.fields['old_password'].initial = 'password'
1006 69a6ca1a Kostas Papadimitriou
            self.fields['old_password'].widget.render_value = True
1007 3fbf7863 Kostas Papadimitriou
            self.fields['new_password1'].required = False
1008 3fbf7863 Kostas Papadimitriou
            self.fields['new_password2'].required = False
1009 f7400729 Kostas Papadimitriou
1010 f7400729 Kostas Papadimitriou
    def _update_extra_form_errors(self):
1011 f7400729 Kostas Papadimitriou
        if self.cleaned_data.get('change_password'):
1012 f7400729 Kostas Papadimitriou
            self.errors.update(self.password_change_form.errors)
1013 f7400729 Kostas Papadimitriou
        if self.cleaned_data.get('change_email'):
1014 f7400729 Kostas Papadimitriou
            self.errors.update(self.email_change_form.errors)
1015 f7400729 Kostas Papadimitriou
1016 f7400729 Kostas Papadimitriou
    def _init_extra_forms(self):
1017 f7400729 Kostas Papadimitriou
        self.email_change_form = EmailChangeForm(self.data)
1018 8fb8d0cf Giorgos Korfiatis
        self.password_change_form = ExtendedPasswordChangeForm(
1019 8fb8d0cf Giorgos Korfiatis
            user=self.instance,
1020 8fb8d0cf Giorgos Korfiatis
            data=self.data, session_key=self.session_key)
1021 f7400729 Kostas Papadimitriou
        self._init_extra_form_fields()
1022 f7400729 Kostas Papadimitriou
1023 f7400729 Kostas Papadimitriou
    def is_valid(self):
1024 f7400729 Kostas Papadimitriou
        password, email = True, True
1025 f7400729 Kostas Papadimitriou
        profile = super(ExtendedProfileForm, self).is_valid()
1026 3fbf7863 Kostas Papadimitriou
        if profile and self.cleaned_data.get('change_password', None):
1027 36f1eabb Kostas Papadimitriou
            self.password_change_form.fields['new_password1'].required = True
1028 36f1eabb Kostas Papadimitriou
            self.password_change_form.fields['new_password2'].required = True
1029 f7400729 Kostas Papadimitriou
            password = self.password_change_form.is_valid()
1030 3fbf7863 Kostas Papadimitriou
            self.save_extra_forms.append('password')
1031 f7400729 Kostas Papadimitriou
        if profile and self.cleaned_data.get('change_email'):
1032 7b4ca7fe Olga Brani
            self.fields['new_email_address'].required = True
1033 f7400729 Kostas Papadimitriou
            email = self.email_change_form.is_valid()
1034 3fbf7863 Kostas Papadimitriou
            self.save_extra_forms.append('email')
1035 f7400729 Kostas Papadimitriou
1036 f7400729 Kostas Papadimitriou
        if not password or not email:
1037 f7400729 Kostas Papadimitriou
            self._update_extra_form_errors()
1038 f7400729 Kostas Papadimitriou
1039 f7400729 Kostas Papadimitriou
        return all([profile, password, email])
1040 f7400729 Kostas Papadimitriou
1041 3fbf7863 Kostas Papadimitriou
    def save(self, request, *args, **kwargs):
1042 f7400729 Kostas Papadimitriou
        if 'email' in self.save_extra_forms:
1043 3fbf7863 Kostas Papadimitriou
            self.email_change_form.save(request, *args, **kwargs)
1044 2da6f56b Kostas Papadimitriou
            self.email_changed = True
1045 f7400729 Kostas Papadimitriou
        if 'password' in self.save_extra_forms:
1046 f7400729 Kostas Papadimitriou
            self.password_change_form.save(*args, **kwargs)
1047 2da6f56b Kostas Papadimitriou
            self.password_changed = True
1048 f7400729 Kostas Papadimitriou
        return super(ExtendedProfileForm, self).save(*args, **kwargs)