Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / forms.py @ 95d49da0

History | View | Annotate | Download (40.6 kB)

1 aba1e498 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 18ffbee1 Sofia Papagiannaki
#
3 64cd4730 Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 64cd4730 Antony Chazapis
# without modification, are permitted provided that the following
5 64cd4730 Antony Chazapis
# conditions are met:
6 18ffbee1 Sofia Papagiannaki
#
7 64cd4730 Antony Chazapis
#   1. Redistributions of source code must retain the above
8 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
9 64cd4730 Antony Chazapis
#      disclaimer.
10 18ffbee1 Sofia Papagiannaki
#
11 64cd4730 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 64cd4730 Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 64cd4730 Antony Chazapis
#      provided with the distribution.
15 18ffbee1 Sofia Papagiannaki
#
16 64cd4730 Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 64cd4730 Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 64cd4730 Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 64cd4730 Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 64cd4730 Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 64cd4730 Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 64cd4730 Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 64cd4730 Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 64cd4730 Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 64cd4730 Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 64cd4730 Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 64cd4730 Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 18ffbee1 Sofia Papagiannaki
#
29 64cd4730 Antony Chazapis
# The views and conclusions contained in the software and
30 64cd4730 Antony Chazapis
# documentation are those of the authors and should not be
31 64cd4730 Antony Chazapis
# interpreted as representing official policies, either expressed
32 64cd4730 Antony Chazapis
# or implied, of GRNET S.A.
33 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 e47fb17a Sofia Papagiannaki
from django.core.exceptions import PermissionDenied
48 5ed6816e Sofia Papagiannaki
49 734107ef Kostas Papadimitriou
from synnefo_branding.utils import render_to_string
50 d2c9adac Christos Stavrakakis
from synnefo.lib import join_urls
51 3e0a032d Sofia Papagiannaki
from astakos.im.models import AstakosUser, EmailChange, Invitation, Resource, \
52 3e0a032d Sofia Papagiannaki
    PendingThirdPartyUser, get_latest_terms, ProjectApplication, Project
53 75380308 Kostas Papadimitriou
from astakos.im import presentation
54 45f8d9ff Sofia Papagiannaki
from astakos.im.widgets import DummyWidget, RecaptchaWidget
55 3e0a032d Sofia Papagiannaki
from astakos.im.functions import send_change_email, submit_application, \
56 3e0a032d Sofia Papagiannaki
    accept_membership_checks
57 270dd48d Sofia Papagiannaki
58 3e0a032d Sofia Papagiannaki
from astakos.im.util import reserved_verified_email, model_to_dict
59 c4b1a172 Kostas Papadimitriou
from astakos.im import auth_providers
60 8998f09a Sofia Papagiannaki
from astakos.im import settings
61 64cd4730 Antony Chazapis
62 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
63 64cd4730 Antony Chazapis
64 3bf924ec Sofia Papagiannaki
import logging
65 49790d9d Sofia Papagiannaki
import hashlib
66 db7fecd9 Sofia Papagiannaki
import recaptcha.client.captcha as captcha
67 caf70869 Sofia Papagiannaki
import re
68 64cd4730 Antony Chazapis
69 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
70 e015e9e6 Sofia Papagiannaki
71 caf70869 Sofia Papagiannaki
DOMAIN_VALUE_REGEX = re.compile(
72 892410d3 Sofia Papagiannaki
    r'^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$',
73 73fbaec4 Sofia Papagiannaki
    re.IGNORECASE)
74 d2633501 Kostas Papadimitriou
75 e7cb4085 Kostas Papadimitriou
76 caf70869 Sofia Papagiannaki
class StoreUserMixin(object):
77 e5966bd9 Kostas Papadimitriou
78 e7cb4085 Kostas Papadimitriou
    def store_user(self, user, request=None):
79 43332a76 Kostas Papadimitriou
        """
80 43332a76 Kostas Papadimitriou
        WARNING: this should be wrapped inside a transactional view/method.
81 43332a76 Kostas Papadimitriou
        """
82 d2633501 Kostas Papadimitriou
        user.save()
83 d2633501 Kostas Papadimitriou
        self.post_store_user(user, request)
84 d2633501 Kostas Papadimitriou
        return user
85 d2633501 Kostas Papadimitriou
86 d2633501 Kostas Papadimitriou
    def post_store_user(self, user, request):
87 d2633501 Kostas Papadimitriou
        """
88 d2633501 Kostas Papadimitriou
        Interface method for descendant backends to be able to do stuff within
89 d2633501 Kostas Papadimitriou
        the transaction enabled by store_user.
90 d2633501 Kostas Papadimitriou
        """
91 d2633501 Kostas Papadimitriou
        pass
92 d2633501 Kostas Papadimitriou
93 d2633501 Kostas Papadimitriou
94 d2633501 Kostas Papadimitriou
class LocalUserCreationForm(UserCreationForm, StoreUserMixin):
95 890b0eaf Sofia Papagiannaki
    """
96 890b0eaf Sofia Papagiannaki
    Extends the built in UserCreationForm in several ways:
97 18ffbee1 Sofia Papagiannaki

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

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

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