Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (39.9 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 e1a80257 Sofia Papagiannaki
    Resource, PendingThirdPartyUser, get_latest_terms, RESOURCE_SEPARATOR,
61 e47fb17a Sofia Papagiannaki
    ProjectApplication, Project)
62 c0b26605 Sofia Papagiannaki
from astakos.im.settings import (
63 c0b26605 Sofia Papagiannaki
    INVITATIONS_PER_LEVEL, BASEURL, SITENAME, RECAPTCHA_PRIVATE_KEY,
64 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 ec2e67f4 Kostas Papadimitriou
    RESOURCES_PRESENTATION_DATA)
69 45f8d9ff Sofia Papagiannaki
from astakos.im.widgets import DummyWidget, RecaptchaWidget
70 e47fb17a Sofia Papagiannaki
from astakos.im.functions import (
71 ff67242a Giorgos Korfiatis
    send_change_email, submit_application, accept_membership_checks)
72 270dd48d Sofia Papagiannaki
73 43332a76 Kostas Papadimitriou
from astakos.im.util import reserved_email, reserved_verified_email, \
74 43332a76 Kostas Papadimitriou
                            get_query, model_to_dict
75 c4b1a172 Kostas Papadimitriou
from astakos.im import auth_providers
76 64cd4730 Antony Chazapis
77 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
78 64cd4730 Antony Chazapis
79 3bf924ec Sofia Papagiannaki
import logging
80 49790d9d Sofia Papagiannaki
import hashlib
81 db7fecd9 Sofia Papagiannaki
import recaptcha.client.captcha as captcha
82 caf70869 Sofia Papagiannaki
import re
83 64cd4730 Antony Chazapis
84 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
85 e015e9e6 Sofia Papagiannaki
86 caf70869 Sofia Papagiannaki
DOMAIN_VALUE_REGEX = re.compile(
87 892410d3 Sofia Papagiannaki
    r'^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$',
88 73fbaec4 Sofia Papagiannaki
    re.IGNORECASE)
89 d2633501 Kostas Papadimitriou
90 caf70869 Sofia Papagiannaki
class StoreUserMixin(object):
91 e5966bd9 Kostas Papadimitriou
92 d2633501 Kostas Papadimitriou
    def store_user(self, user, request):
93 43332a76 Kostas Papadimitriou
        """
94 43332a76 Kostas Papadimitriou
        WARNING: this should be wrapped inside a transactional view/method.
95 43332a76 Kostas Papadimitriou
        """
96 d2633501 Kostas Papadimitriou
        user.save()
97 d2633501 Kostas Papadimitriou
        self.post_store_user(user, request)
98 d2633501 Kostas Papadimitriou
        return user
99 d2633501 Kostas Papadimitriou
100 d2633501 Kostas Papadimitriou
    def post_store_user(self, user, request):
101 d2633501 Kostas Papadimitriou
        """
102 d2633501 Kostas Papadimitriou
        Interface method for descendant backends to be able to do stuff within
103 d2633501 Kostas Papadimitriou
        the transaction enabled by store_user.
104 d2633501 Kostas Papadimitriou
        """
105 d2633501 Kostas Papadimitriou
        pass
106 d2633501 Kostas Papadimitriou
107 d2633501 Kostas Papadimitriou
108 d2633501 Kostas Papadimitriou
class LocalUserCreationForm(UserCreationForm, StoreUserMixin):
109 890b0eaf Sofia Papagiannaki
    """
110 890b0eaf Sofia Papagiannaki
    Extends the built in UserCreationForm in several ways:
111 18ffbee1 Sofia Papagiannaki

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

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

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