Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / forms.py @ 9a94c0f1

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

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

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

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

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