Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / forms.py @ 6da04174

History | View | Annotate | Download (40.5 kB)

1 aba1e498 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 18ffbee1 Sofia Papagiannaki
#
3 64cd4730 Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 64cd4730 Antony Chazapis
# without modification, are permitted provided that the following
5 64cd4730 Antony Chazapis
# conditions are met:
6 18ffbee1 Sofia Papagiannaki
#
7 64cd4730 Antony Chazapis
#   1. Redistributions of source code must retain the above
8 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
9 64cd4730 Antony Chazapis
#      disclaimer.
10 18ffbee1 Sofia Papagiannaki
#
11 64cd4730 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 64cd4730 Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 64cd4730 Antony Chazapis
#      provided with the distribution.
15 18ffbee1 Sofia Papagiannaki
#
16 64cd4730 Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 64cd4730 Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 64cd4730 Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 64cd4730 Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 64cd4730 Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 64cd4730 Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 64cd4730 Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 64cd4730 Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 64cd4730 Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 64cd4730 Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 64cd4730 Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 64cd4730 Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 18ffbee1 Sofia Papagiannaki
#
29 64cd4730 Antony Chazapis
# The views and conclusions contained in the software and
30 64cd4730 Antony Chazapis
# documentation are those of the authors and should not be
31 64cd4730 Antony Chazapis
# interpreted as representing official policies, either expressed
32 64cd4730 Antony Chazapis
# or implied, of GRNET S.A.
33 8f378756 Sofia Papagiannaki
from urlparse import urljoin
34 caf70869 Sofia Papagiannaki
from random import random
35 69b26576 Sofia Papagiannaki
from datetime import datetime, timedelta
36 64cd4730 Antony Chazapis
37 64cd4730 Antony Chazapis
from django import forms
38 64cd4730 Antony Chazapis
from django.utils.translation import ugettext as _
39 e1a80257 Sofia Papagiannaki
from django.contrib.auth.forms import (
40 e1a80257 Sofia Papagiannaki
    UserCreationForm, AuthenticationForm,
41 e1a80257 Sofia Papagiannaki
    PasswordResetForm, PasswordChangeForm,
42 73fbaec4 Sofia Papagiannaki
    SetPasswordForm)
43 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 e2125441 Sofia Papagiannaki
from django.template import Context, loader
46 e2125441 Sofia Papagiannaki
from django.utils.http import int_to_base36
47 374611bc Sofia Papagiannaki
from django.core.urlresolvers import reverse
48 18ffbee1 Sofia Papagiannaki
from django.utils.safestring import mark_safe
49 49790d9d Sofia Papagiannaki
from django.utils.encoding import smart_str
50 1cbce16f Sofia Papagiannaki
from django.conf import settings
51 678b2236 Sofia Papagiannaki
from django.forms.models import fields_for_model
52 d2633501 Kostas Papadimitriou
from django.db import transaction
53 caf70869 Sofia Papagiannaki
from django.utils.encoding import smart_unicode
54 caf70869 Sofia Papagiannaki
from django.core import validators
55 e1a80257 Sofia Papagiannaki
from django.contrib.auth.models import AnonymousUser
56 e47fb17a Sofia Papagiannaki
from django.core.exceptions import PermissionDenied
57 5ed6816e Sofia Papagiannaki
58 678b2236 Sofia Papagiannaki
from astakos.im.models import (
59 73fbaec4 Sofia Papagiannaki
    AstakosUser, EmailChange, Invitation,
60 26551b92 Kostas Papadimitriou
    Resource, PendingThirdPartyUser, get_latest_terms,
61 e47fb17a Sofia Papagiannaki
    ProjectApplication, Project)
62 c0b26605 Sofia Papagiannaki
from astakos.im.settings import (
63 c0b26605 Sofia Papagiannaki
    INVITATIONS_PER_LEVEL, BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY,
64 31bc3a62 Kostas Papadimitriou
    RECAPTCHA_ENABLED, CONTACT_EMAIL, LOGGING_LEVEL,
65 2e90e3ec Kostas Papadimitriou
    PASSWORD_RESET_EMAIL_SUBJECT, NEWPASSWD_INVALIDATE_TOKEN,
66 272cf735 Sofia Papagiannaki
    MODERATION_ENABLED, PROJECT_MEMBER_JOIN_POLICIES,
67 ec2e67f4 Kostas Papadimitriou
    PROJECT_MEMBER_LEAVE_POLICIES, EMAILCHANGE_ENABLED,
68 4161cb70 Giorgos Korfiatis
    )
69 75380308 Kostas Papadimitriou
from astakos.im import presentation
70 45f8d9ff Sofia Papagiannaki
from astakos.im.widgets import DummyWidget, RecaptchaWidget
71 e47fb17a Sofia Papagiannaki
from astakos.im.functions import (
72 ff67242a Giorgos Korfiatis
    send_change_email, submit_application, accept_membership_checks)
73 270dd48d Sofia Papagiannaki
74 43332a76 Kostas Papadimitriou
from astakos.im.util import reserved_email, reserved_verified_email, \
75 43332a76 Kostas Papadimitriou
                            get_query, model_to_dict
76 c4b1a172 Kostas Papadimitriou
from astakos.im import auth_providers
77 64cd4730 Antony Chazapis
78 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
79 64cd4730 Antony Chazapis
80 3bf924ec Sofia Papagiannaki
import logging
81 49790d9d Sofia Papagiannaki
import hashlib
82 db7fecd9 Sofia Papagiannaki
import recaptcha.client.captcha as captcha
83 caf70869 Sofia Papagiannaki
import re
84 64cd4730 Antony Chazapis
85 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
86 e015e9e6 Sofia Papagiannaki
87 caf70869 Sofia Papagiannaki
DOMAIN_VALUE_REGEX = re.compile(
88 892410d3 Sofia Papagiannaki
    r'^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$',
89 73fbaec4 Sofia Papagiannaki
    re.IGNORECASE)
90 d2633501 Kostas Papadimitriou
91 caf70869 Sofia Papagiannaki
class StoreUserMixin(object):
92 e5966bd9 Kostas Papadimitriou
93 d2633501 Kostas Papadimitriou
    def store_user(self, user, request):
94 43332a76 Kostas Papadimitriou
        """
95 43332a76 Kostas Papadimitriou
        WARNING: this should be wrapped inside a transactional view/method.
96 43332a76 Kostas Papadimitriou
        """
97 d2633501 Kostas Papadimitriou
        user.save()
98 d2633501 Kostas Papadimitriou
        self.post_store_user(user, request)
99 d2633501 Kostas Papadimitriou
        return user
100 d2633501 Kostas Papadimitriou
101 d2633501 Kostas Papadimitriou
    def post_store_user(self, user, request):
102 d2633501 Kostas Papadimitriou
        """
103 d2633501 Kostas Papadimitriou
        Interface method for descendant backends to be able to do stuff within
104 d2633501 Kostas Papadimitriou
        the transaction enabled by store_user.
105 d2633501 Kostas Papadimitriou
        """
106 d2633501 Kostas Papadimitriou
        pass
107 d2633501 Kostas Papadimitriou
108 d2633501 Kostas Papadimitriou
109 d2633501 Kostas Papadimitriou
class LocalUserCreationForm(UserCreationForm, StoreUserMixin):
110 890b0eaf Sofia Papagiannaki
    """
111 890b0eaf Sofia Papagiannaki
    Extends the built in UserCreationForm in several ways:
112 18ffbee1 Sofia Papagiannaki

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

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

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