Revision 5ce3ce4f

b/snf-astakos-app/astakos/__init__.py
1 1
# Copyright (c) Django Software Foundation and individual contributors.
2 2
# All rights reserved.
3
# 
3
#
4 4
# Redistribution and use in source and binary forms, with or without modification,
5 5
# are permitted provided that the following conditions are met:
6
# 
7
#     1. Redistributions of source code must retain the above copyright notice, 
6
#
7
#     1. Redistributions of source code must retain the above copyright notice,
8 8
#        this list of conditions and the following disclaimer.
9
#     
10
#     2. Redistributions in binary form must reproduce the above copyright 
9
#
10
#     2. Redistributions in binary form must reproduce the above copyright
11 11
#        notice, this list of conditions and the following disclaimer in the
12 12
#        documentation and/or other materials provided with the distribution.
13
# 
13
#
14 14
#     3. Neither the name of Django nor the names of its contributors may be used
15 15
#        to endorse or promote products derived from this software without
16 16
#        specific prior written permission.
17
# 
17
#
18 18
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 19
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 20
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
......
28 28

  
29 29
VERSION = (0, 3, 0, 'alpha', 0)
30 30

  
31

  
31 32
def get_version():
32 33
    version = '%s.%s' % (VERSION[0], VERSION[1])
33 34
    if VERSION[2]:
b/snf-astakos-app/astakos/im/activation_backends.py
47 47

  
48 48
logger = logging.getLogger(__name__)
49 49

  
50

  
50 51
def get_backend(request):
51 52
    """
52 53
    Returns an instance of an activation backend,
......
59 60
    """
60 61
    module = 'astakos.im.activation_backends'
61 62
    prefix = 'Invitations' if INVITATIONS_ENABLED else 'Simple'
62
    backend_class_name = '%sBackend' %prefix
63
    backend_class_name = '%sBackend' % prefix
63 64
    try:
64 65
        mod = import_module(module)
65 66
    except ImportError, e:
66
        raise ImproperlyConfigured('Error loading activation backend %s: "%s"' % (module, e))
67
        raise ImproperlyConfigured(
68
            'Error loading activation backend %s: "%s"' % (module, e))
67 69
    try:
68 70
        backend_class = getattr(mod, backend_class_name)
69 71
    except AttributeError:
70 72
        raise ImproperlyConfigured('Module "%s" does not define a activation backend named "%s"' % (module, backend_class_name))
71 73
    return backend_class(request)
72 74

  
75

  
73 76
class ActivationBackend(object):
74 77
    def __init__(self, request):
75 78
        self.request = request
76
    
79

  
77 80
    def _is_preaccepted(self, user):
78 81
        # return True if user email matches specific patterns
79 82
        for pattern in RE_USER_EMAIL_PATTERNS:
80 83
            if re.match(pattern, user.email):
81 84
                return True
82 85
        return False
83
    
86

  
84 87
    def get_signup_form(self, provider='local', instance=None):
85 88
        """
86 89
        Returns a form instance of the relevant class
87 90
        """
88 91
        main = provider.capitalize() if provider == 'local' else 'ThirdParty'
89
        suffix  = 'UserCreationForm'
92
        suffix = 'UserCreationForm'
90 93
        formclass = '%s%s' % (main, suffix)
91 94
        request = self.request
92 95
        initial_data = None
......
94 97
            if provider == request.POST.get('provider', ''):
95 98
                initial_data = request.POST
96 99
        return globals()[formclass](initial_data, instance=instance, request=request)
97
    
98
    def handle_activation(self, user, \
99
                          activation_template_name='im/activation_email.txt', \
100
                          greeting_template_name='im/welcome_email.txt', \
101
                          admin_email_template_name='im/account_notification.txt', \
100

  
101
    def handle_activation(self, user,
102
                          activation_template_name='im/activation_email.txt',
103
                          greeting_template_name='im/welcome_email.txt',
104
                          admin_email_template_name='im/account_notification.txt',
102 105
                          switch_accounts_email_template_name='im/switch_accounts_email.txt'):
103 106
        """
104 107
        If the user is already active returns immediately.
......
117 120
            if user.conflicting_email():
118 121
                send_verification(user, switch_accounts_email_template_name)
119 122
                return SwitchAccountsVerificationSent(user.email)
120
            
123

  
121 124
            if self._is_preaccepted(user):
122 125
                if user.email_verified:
123 126
                    activate(user, greeting_template_name)
......
128 131
            else:
129 132
                send_admin_notification(
130 133
                    template_name=admin_email_template_name,
131
                    dictionary={'user':user, 'group_creation':True},
134
                    dictionary={'user': user, 'group_creation': True},
132 135
                    subject='%s alpha2 testing account notification' % SITENAME
133 136
                )
134 137
                return NotificationSent()
......
136 139
            logger.exception(e)
137 140
            raise e
138 141

  
142

  
139 143
class InvitationsBackend(ActivationBackend):
140 144
    """
141 145
    A activation backend which implements the following workflow: a user
......
148 152
    def get_signup_form(self, provider='local', instance=None):
149 153
        """
150 154
        Returns a form instance of the relevant class
151
        
155

  
152 156
        raises Invitation.DoesNotExist and ValueError if invitation is consumed
153 157
        or invitation username is reserved.
154 158
        """
......
157 161
        initial_data = self.get_signup_initial_data(provider)
158 162
        prefix = 'Invited' if invitation else ''
159 163
        main = provider.capitalize()
160
        suffix  = 'UserCreationForm'
164
        suffix = 'UserCreationForm'
161 165
        formclass = '%s%s%s' % (prefix, main, suffix)
162 166
        return globals()[formclass](initial_data, instance=instance, request=self.request)
163 167

  
......
174 178
            if invitation:
175 179
                # create a tmp user with the invitation realname
176 180
                # to extract first and last name
177
                u = AstakosUser(realname = invitation.realname)
178
                initial_data = {'email':invitation.username,
179
                                'inviter':invitation.inviter.realname,
180
                                'first_name':u.first_name,
181
                                'last_name':u.last_name,
182
                                'provider':provider}
181
                u = AstakosUser(realname=invitation.realname)
182
                initial_data = {'email': invitation.username,
183
                                'inviter': invitation.inviter.realname,
184
                                'first_name': u.first_name,
185
                                'last_name': u.last_name,
186
                                'provider': provider}
183 187
        else:
184 188
            if provider == request.POST.get('provider', ''):
185 189
                initial_data = request.POST
......
200 204
            return True
201 205
        return False
202 206

  
207

  
203 208
class SimpleBackend(ActivationBackend):
204 209
    """
205 210
    A activation backend which implements the following workflow: a user
......
213 218
            return False
214 219
        return True
215 220

  
221

  
216 222
class ActivationResult(object):
217 223
    def __init__(self, message):
218 224
        self.message = message
219 225

  
226

  
220 227
class VerificationSent(ActivationResult):
221 228
    def __init__(self):
222 229
        message = _('Verification sent.')
223 230
        super(VerificationSent, self).__init__(message)
224 231

  
232

  
225 233
class SwitchAccountsVerificationSent(ActivationResult):
226 234
    def __init__(self, email):
227 235
        message = _('This email is already associated with another \
......
230 238
                    to %s. Otherwise just ignore it.' % email)
231 239
        super(SwitchAccountsVerificationSent, self).__init__(message)
232 240

  
241

  
233 242
class NotificationSent(ActivationResult):
234 243
    def __init__(self):
235 244
        message = _('Your request for an account was successfully received and is now pending \
......
237 246
                    your interest in ~okeanos! The GRNET team.')
238 247
        super(NotificationSent, self).__init__(message)
239 248

  
249

  
240 250
class RegistationCompleted(ActivationResult):
241 251
    def __init__(self):
242 252
        message = _('Registration completed. You can now login.')
243
        super(RegistationCompleted, self).__init__(message)
253
        super(RegistationCompleted, self).__init__(message)
b/snf-astakos-app/astakos/im/api/__init__.py
51 51

  
52 52
absolute = lambda request, url: request.build_absolute_uri(url)
53 53

  
54

  
54 55
def render_fault(request, fault):
55 56
    if isinstance(fault, InternalServerError) and settings.DEBUG:
56 57
        fault.details = format_exc(fault)
......
63 64
    response['Content-Length'] = len(response.content)
64 65
    return response
65 66

  
67

  
66 68
def api_method(http_method=None):
67 69
    """Decorator function for views that implement an API method."""
68 70
    def decorator(func):
......
82 84
        return wrapper
83 85
    return decorator
84 86

  
87

  
85 88
def _get_user_by_username(user_id):
86 89
    try:
87
        user = AstakosUser.objects.get(username = user_id)
90
        user = AstakosUser.objects.get(username=user_id)
88 91
    except AstakosUser.DoesNotExist:
89 92
        raise ItemNotFound('Invalid userid')
90 93
    else:
91 94
        response = HttpResponse()
92
        response.status=200
93
        user_info = {'id':user.id,
94
                     'username':user.username,
95
                     'email':[user.email],
96
                     'name':user.realname,
97
                     'auth_token_created':user.auth_token_created.strftime(format),
98
                     'auth_token_expires':user.auth_token_expires.strftime(format),
99
                     'has_credits':user.has_credits,
100
                     'enabled':user.is_active,
101
                     'groups':[g.name for g in user.groups.all()]}
95
        response.status = 200
96
        user_info = {'id': user.id,
97
                     'username': user.username,
98
                     'email': [user.email],
99
                     'name': user.realname,
100
                     'auth_token_created': user.auth_token_created.strftime(format),
101
                     'auth_token_expires': user.auth_token_expires.strftime(format),
102
                     'has_credits': user.has_credits,
103
                     'enabled': user.is_active,
104
                     'groups': [g.name for g in user.groups.all()]}
102 105
        response.content = json.dumps(user_info)
103 106
        response['Content-Type'] = 'application/json; charset=UTF-8'
104 107
        response['Content-Length'] = len(response.content)
105 108
        return response
106 109

  
110

  
107 111
def _get_user_by_email(email):
108 112
    if not email:
109 113
        raise BadRequest('Email missing')
110 114
    try:
111
        user = AstakosUser.objects.get(email = email)
115
        user = AstakosUser.objects.get(email=email)
112 116
    except AstakosUser.DoesNotExist:
113 117
        raise ItemNotFound('Invalid email')
114
    
118

  
115 119
    if not user.is_active:
116 120
        raise ItemNotFound('Inactive user')
117 121
    else:
118 122
        response = HttpResponse()
119
        response.status=200
120
        user_info = {'id':user.id,
121
                     'username':user.username,
122
                     'email':[user.email],
123
                     'enabled':user.is_active,
124
                     'name':user.realname,
125
                     'auth_token_created':user.auth_token_created.strftime(format),
126
                     'auth_token_expires':user.auth_token_expires.strftime(format),
127
                     'has_credits':user.has_credits,
128
                     'groups':[g.name for g in user.groups.all()],
129
                     'user_permissions':[p.codename for p in user.user_permissions.all()]}
123
        response.status = 200
124
        user_info = {'id': user.id,
125
                     'username': user.username,
126
                     'email': [user.email],
127
                     'enabled': user.is_active,
128
                     'name': user.realname,
129
                     'auth_token_created': user.auth_token_created.strftime(format),
130
                     'auth_token_expires': user.auth_token_expires.strftime(format),
131
                     'has_credits': user.has_credits,
132
                     'groups': [g.name for g in user.groups.all()],
133
                     'user_permissions': [p.codename for p in user.user_permissions.all()]}
130 134
        response.content = json.dumps(user_info)
131 135
        response['Content-Type'] = 'application/json; charset=UTF-8'
132 136
        response['Content-Length'] = len(response.content)
133 137
        return response
134 138

  
139

  
135 140
@api_method(http_method='GET')
136 141
def get_services(request):
137 142
    callback = request.GET.get('callback', None)
138 143
    services = Service.objects.all()
139
    data = tuple({'id':s.pk, 'name':s.name, 'url':s.url, 'icon':s.icon} for s in services)
144
    data = tuple({'id': s.pk, 'name': s.name, 'url': s.url, 'icon':
145
                 s.icon} for s in services)
140 146
    data = json.dumps(data)
141 147
    mimetype = 'application/json'
142 148

  
......
146 152

  
147 153
    return HttpResponse(content=data, mimetype=mimetype)
148 154

  
155

  
149 156
@api_method()
150 157
def get_menu(request, with_extra_links=False, with_signout=True):
151 158
    user = request.user
......
159 166
            pass
160 167
    if not isinstance(user, AstakosUser):
161 168
        index_url = reverse('index')
162
        l = [{ 'url': absolute(request, index_url), 'name': "Sign in"}]
169
        l = [{'url': absolute(request, index_url), 'name': "Sign in"}]
163 170
    else:
164 171
        l = []
165 172
        append = l.append
......
216 223
                        ),
217 224
                        item(
218 225
                            url=absolute(request,
219
                                reverse('group_create_list')
220
                            ),
226
                                         reverse('group_create_list')
227
                                         ),
221 228
                            name="Create"
222 229
                        ),
223 230
                        item(
......
246 253
                    name="Sign out"
247 254
                )
248 255
            )
249
    
256

  
250 257
    callback = request.GET.get('callback', None)
251 258
    data = json.dumps(tuple(l))
252 259
    mimetype = 'application/json'
......
257 264

  
258 265
    return HttpResponse(content=data, mimetype=mimetype)
259 266

  
267

  
260 268
class MenuItem(dict):
261 269
    current_path = ''
262
    
270

  
263 271
    def __init__(self, *args, **kwargs):
264 272
        super(MenuItem, self).__init__(*args, **kwargs)
265 273
        if kwargs.get('url') or kwargs.get('submenu'):
266 274
            self.__set_is_active__()
267
    
275

  
268 276
    def __setitem__(self, key, value):
269 277
        super(MenuItem, self).__setitem__(key, value)
270 278
        if key in ('url', 'submenu'):
271 279
            self.__set_is_active__()
272
    
280

  
273 281
    def __set_is_active__(self):
274 282
        if self.get('is_active'):
275 283
            return
......
285 293
                self.__setitem__('is_active', True)
286 294
            except StopIteration:
287 295
                return
288
        
296

  
289 297
    def __setattribute__(self, name, value):
290 298
        super(MenuItem, self).__setattribute__(name, value)
291 299
        if name == 'current_path':
292
            self.__set_is_active__()
300
            self.__set_is_active__()
b/snf-astakos-app/astakos/im/api/admin.py
39 39
from django.http import HttpResponse
40 40
from django.utils import simplejson as json
41 41

  
42
from astakos.im.api.faults import (Fault, Unauthorized, InternalServerError, BadRequest,
42
from astakos.im.api.faults import (
43
    Fault, Unauthorized, InternalServerError, BadRequest,
43 44
    Forbidden)
44 45
from astakos.im.api import render_fault, _get_user_by_email, _get_user_by_username
45 46
from astakos.im.models import AstakosUser
......
48 49
logger = logging.getLogger(__name__)
49 50
format = ('%a, %d %b %Y %H:%M:%S GMT')
50 51

  
52

  
51 53
def api_method(http_method=None, token_required=False, perms=None):
52 54
    """Decorator function for views that implement an API method."""
53 55
    if not perms:
......
81 83
        return wrapper
82 84
    return decorator
83 85

  
86

  
84 87
@api_method(http_method='GET', token_required=True)
85 88
def authenticate_old(request, user=None):
86 89
    # Normal Response Codes: 204
......
102 105
        raise Unauthorized('Pending approval terms')
103 106

  
104 107
    response = HttpResponse()
105
    response.status=204
106
    user_info = {'username':user.username,
107
                 'uniq':user.email,
108
                 'auth_token':user.auth_token,
109
                 'auth_token_created':user.auth_token_created.isoformat(),
110
                 'auth_token_expires':user.auth_token_expires.isoformat(),
111
                 'has_credits':user.has_credits,
112
                 'has_signed_terms':user.signed_terms,
113
                 'groups':[g.name for g in user.groups.all()]}
108
    response.status = 204
109
    user_info = {'username': user.username,
110
                 'uniq': user.email,
111
                 'auth_token': user.auth_token,
112
                 'auth_token_created': user.auth_token_created.isoformat(),
113
                 'auth_token_expires': user.auth_token_expires.isoformat(),
114
                 'has_credits': user.has_credits,
115
                 'has_signed_terms': user.signed_terms,
116
                 'groups': [g.name for g in user.groups.all()]}
114 117
    response.content = json.dumps(user_info)
115 118
    response['Content-Type'] = 'application/json; charset=UTF-8'
116 119
    response['Content-Length'] = len(response.content)
117 120
    return response
118 121

  
122

  
119 123
@api_method(http_method='GET', token_required=True)
120 124
def authenticate(request, user=None):
121 125
    # Normal Response Codes: 204
......
137 141
        raise Unauthorized('Pending approval terms')
138 142

  
139 143
    response = HttpResponse()
140
    response.status=204
141
    user_info = {'userid':user.username,
142
                 'email':[user.email],
143
                 'name':user.realname,
144
                 'auth_token':user.auth_token,
145
                 'auth_token_created':epoch(user.auth_token_created),
146
                 'auth_token_expires':epoch(user.auth_token_expires),
147
                 'has_credits':user.has_credits,
148
                 'is_active':user.is_active,
149
                 'groups':[g.name for g in user.groups.all()]}
144
    response.status = 204
145
    user_info = {'userid': user.username,
146
                 'email': [user.email],
147
                 'name': user.realname,
148
                 'auth_token': user.auth_token,
149
                 'auth_token_created': epoch(user.auth_token_created),
150
                 'auth_token_expires': epoch(user.auth_token_expires),
151
                 'has_credits': user.has_credits,
152
                 'is_active': user.is_active,
153
                 'groups': [g.name for g in user.groups.all()]}
150 154
    response.content = json.dumps(user_info)
151 155
    response['Content-Type'] = 'application/json; charset=UTF-8'
152 156
    response['Content-Length'] = len(response.content)
153 157
    return response
154 158

  
159

  
155 160
@api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
156 161
def get_user_by_email(request, user=None):
157 162
    # Normal Response Codes: 200
......
163 168
    email = request.GET.get('name')
164 169
    return _get_user_by_email(email)
165 170

  
171

  
166 172
@api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
167 173
def get_user_by_username(request, user_id, user=None):
168 174
    # Normal Response Codes: 200
b/snf-astakos-app/astakos/im/api/faults.py
1 1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
# 
2
#
3 3
# Redistribution and use in source and binary forms, with or
4 4
# without modification, are permitted provided that the following
5 5
# conditions are met:
6
# 
6
#
7 7
#   1. Redistributions of source code must retain the above
8 8
#      copyright notice, this list of conditions and the following
9 9
#      disclaimer.
10
# 
10
#
11 11
#   2. Redistributions in binary form must reproduce the above
12 12
#      copyright notice, this list of conditions and the following
13 13
#      disclaimer in the documentation and/or other materials
14 14
#      provided with the distribution.
15
# 
15
#
16 16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
......
25 25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 27
# POSSIBILITY OF SUCH DAMAGE.
28
# 
28
#
29 29
# The views and conclusions contained in the software and
30 30
# documentation are those of the authors and should not be
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34

  
34 35
def camelCase(s):
35 36
    return s[0].lower() + s[1:]
36 37

  
38

  
37 39
class Fault(Exception):
38 40
    def __init__(self, message='', details='', name=''):
39 41
        Exception.__init__(self, message, details, name)
......
41 43
        self.details = details
42 44
        self.name = name or camelCase(self.__class__.__name__)
43 45

  
46

  
44 47
class BadRequest(Fault):
45 48
    code = 400
46 49

  
50

  
47 51
class Unauthorized(Fault):
48 52
    code = 401
49 53

  
54

  
50 55
class InternalServerError(Fault):
51 56
    code = 500
52 57

  
58

  
53 59
class Forbidden(Fault):
54 60
    code = 403
55 61

  
62

  
56 63
class ItemNotFound(Fault):
57
    code = 404
64
    code = 404
b/snf-astakos-app/astakos/im/api/service.py
47 47

  
48 48
logger = logging.getLogger(__name__)
49 49

  
50

  
50 51
def api_method(http_method=None, token_required=False):
51 52
    """Decorator function for views that implement an API method."""
52 53
    def decorator(func):
......
61 62
                        raise Unauthorized('Access denied')
62 63
                    try:
63 64
                        service = Service.objects.get(auth_token=x_auth_token)
64
                        
65

  
65 66
                        # Check if the token has expired.
66 67
                        if (time() - mktime(service.auth_token_expires.timetuple())) > 0:
67 68
                            raise Unauthorized('Authentication expired')
......
78 79
        return wrapper
79 80
    return decorator
80 81

  
82

  
81 83
@api_method(http_method='GET', token_required=True)
82 84
def get_user_by_email(request, user=None):
83 85
    # Normal Response Codes: 200
......
89 91
    email = request.GET.get('name')
90 92
    return _get_user_by_email(email)
91 93

  
94

  
92 95
@api_method(http_method='GET', token_required=True)
93 96
def get_user_by_username(request, user_id, user=None):
94 97
    # Normal Response Codes: 200
......
99 102
    #                       itemNotFound (404)
100 103
    return _get_user_by_username(user_id)
101 104

  
105

  
102 106
@csrf_exempt
103 107
@api_method(http_method='POST', token_required=True)
104 108
def send_feedback(request, email_template_name='im/feedback_mail.txt'):
......
109 113
    auth_token = request.POST.get('auth', '')
110 114
    if not auth_token:
111 115
        raise BadRequest('Missing user authentication')
112
    
113
    user  = None
116

  
117
    user = None
114 118
    try:
115 119
        user = AstakosUser.objects.get(auth_token=auth_token)
116 120
    except:
117 121
        pass
118
    
122

  
119 123
    if not user:
120 124
        raise BadRequest('Invalid user authentication')
121
    
125

  
122 126
    form = FeedbackForm(request.POST)
123 127
    if not form.is_valid():
124 128
        raise BadRequest('Invalid data')
125
    
129

  
126 130
    msg = form.cleaned_data['feedback_msg']
127 131
    data = form.cleaned_data['feedback_data']
128 132
    send_feedback_func(msg, data, user, email_template_name)
129 133
    response = HttpResponse(status=200)
130 134
    response['Content-Length'] = len(response.content)
131
    return response
135
    return response
b/snf-astakos-app/astakos/im/auth_backends.py
36 36

  
37 37
from astakos.im.models import AstakosUser
38 38

  
39

  
39 40
class TokenBackend(ModelBackend):
40 41
    """
41 42
    AuthenticationBackend used to authenticate using token instead
......
54 55
        except AstakosUser.DoesNotExist:
55 56
            return None
56 57

  
58

  
57 59
class EmailBackend(ModelBackend):
58 60
    """
59 61
    If the ``username`` parameter is actually an email uses email to authenticate
60 62
    the user else tries the username.
61
    
63

  
62 64
    Used from ``astakos.im.forms.LoginForm`` to authenticate.
63 65
    """
64 66
    def authenticate(self, username=None, password=None):
......
77 79
                return None
78 80
        if user.check_password(password):
79 81
            return user
80
    
82

  
81 83
    def get_user(self, user_id):
82 84
        try:
83 85
            return AstakosUser.objects.get(pk=user_id)
84 86
        except AstakosUser.DoesNotExist:
85
            return None
87
            return None
b/snf-astakos-app/astakos/im/context_processors.py
32 32
# or implied, of GRNET S.A.
33 33

  
34 34
from astakos.im.settings import IM_MODULES, INVITATIONS_ENABLED, IM_STATIC_URL, \
35
        LOGIN_MESSAGES, SIGNUP_MESSAGES, PROFILE_MESSAGES, \
36
        GLOBAL_MESSAGES, PROFILE_EXTRA_LINKS
35
    LOGIN_MESSAGES, SIGNUP_MESSAGES, PROFILE_MESSAGES, \
36
    GLOBAL_MESSAGES, PROFILE_EXTRA_LINKS
37 37
from astakos.im.api import get_menu
38 38
from astakos.im.util import get_query
39 39
from astakos.im.models import GroupKind
40 40

  
41 41
from django.utils import simplejson as json
42 42

  
43

  
43 44
def im_modules(request):
44 45
    return {'im_modules': IM_MODULES}
45 46

  
47

  
46 48
def next(request):
47
    return {'next' : get_query(request).get('next', '')}
49
    return {'next': get_query(request).get('next', '')}
50

  
48 51

  
49 52
def code(request):
50
    return {'code' : request.GET.get('code', '')}
53
    return {'code': request.GET.get('code', '')}
54

  
51 55

  
52 56
def invitations(request):
53
    return {'invitations_enabled' :INVITATIONS_ENABLED}
57
    return {'invitations_enabled': INVITATIONS_ENABLED}
58

  
54 59

  
55 60
def media(request):
56
    return {'IM_STATIC_URL' : IM_STATIC_URL}
61
    return {'IM_STATIC_URL': IM_STATIC_URL}
62

  
57 63

  
58 64
def custom_messages(request):
59
    EXTRA_MESSAGES_SET = bool(GLOBAL_MESSAGES or SIGNUP_MESSAGES or \
60
            LOGIN_MESSAGES or PROFILE_MESSAGES)
65
    EXTRA_MESSAGES_SET = bool(GLOBAL_MESSAGES or SIGNUP_MESSAGES or
66
                              LOGIN_MESSAGES or PROFILE_MESSAGES)
61 67
    return {
62
            'GLOBAL_MESSAGES' : GLOBAL_MESSAGES,
63
            'SIGNUP_MESSAGES' : SIGNUP_MESSAGES,
64
            'LOGIN_MESSAGES' : LOGIN_MESSAGES,
65
            'PROFILE_MESSAGES' : PROFILE_MESSAGES,
66
            'PROFILE_EXTRA_LINKS' : PROFILE_EXTRA_LINKS,
67
            'EXTRA_MESSAGES_SET' : EXTRA_MESSAGES_SET
68
           }
68
        'GLOBAL_MESSAGES': GLOBAL_MESSAGES,
69
        'SIGNUP_MESSAGES': SIGNUP_MESSAGES,
70
        'LOGIN_MESSAGES': LOGIN_MESSAGES,
71
        'PROFILE_MESSAGES': PROFILE_MESSAGES,
72
        'PROFILE_EXTRA_LINKS': PROFILE_EXTRA_LINKS,
73
        'EXTRA_MESSAGES_SET': EXTRA_MESSAGES_SET
74
    }
75

  
69 76

  
70 77
def menu(request):
71 78
    try:
......
74 81
    except Exception, e:
75 82
        return {}
76 83
    else:
77
        return {'menu':menu_items}
84
        return {'menu': menu_items}
85

  
78 86

  
79 87
def group_kinds(request):
80
    return {'group_kinds':GroupKind.objects.exclude(
88
    return {'group_kinds': GroupKind.objects.exclude(
81 89
            name='default'
82
        ).values_list('name', flat=True)
83
    }
90
            ).values_list('name', flat=True)
91
            }
b/snf-astakos-app/astakos/im/endpoints/aquarium/client.py
37 37

  
38 38
from astakos.im.settings import AQUARIUM_URL
39 39

  
40

  
40 41
class AquariumClient():
41 42
    def get_billing(self, user, start, end):
42 43
        if not AQUARIUM_URL:
43 44
            return
44
        
45

  
45 46
        url = AQUARIUM_URL.rstrip('/')
46 47
        url = '%s/%s/bill/%d/%d' % (url, user, start, end)
47 48
        r = requests.get(url)
......
49 50
            return r.status_code, json.loads(r.text)
50 51
        except ValueError:
51 52
            pass
52
        return r.status_code, None
53
        return r.status_code, None
b/snf-astakos-app/astakos/im/endpoints/aquarium/consumer.py
40 40

  
41 41
from astakos.im.models import AstakosUser
42 42

  
43

  
43 44
def on_creditevent(msg):
44 45
    """
45 46
    Queue handler for updating AstakosUser has_credits
......
51 52
        user.has_credits = has_credits
52 53
        user.save()
53 54
    except BaseException, e:
54
        logger.exception(e)
55
        logger.exception(e)
b/snf-astakos-app/astakos/im/endpoints/aquarium/producer.py
40 40

  
41 41
if QUEUE_CONNECTION:
42 42
    from synnefo.lib.queue import (exchange_connect, exchange_send,
43
            exchange_close, UserEvent, Receipt
44
    )
43
                                   exchange_close, UserEvent, Receipt
44
                                   )
45 45

  
46
QUEUE_CLIENT_ID = '3' # Astakos.
46
QUEUE_CLIENT_ID = '3'  # Astakos.
47 47
INSTANCE_ID = '1'
48 48
RESOURCE = 'addcredits'
49 49
DEFAULT_CREDITS = 1000
50 50

  
51 51
logging.basicConfig(format='%(asctime)s [%(levelname)s] %(name)s %(message)s',
52
        datefmt='%Y-%m-%d %H:%M:%S'
53
)
52
                    datefmt='%Y-%m-%d %H:%M:%S'
53
                    )
54 54
logger = logging.getLogger('endpoint.aquarium')
55 55

  
56

  
56 57
def wrapper(func):
57 58
    @wraps(func)
58 59
    def wrapper(*args, **kwargs):
59 60
        if not QUEUE_CONNECTION:
60 61
            return
61
        
62

  
62 63
        try:
63 64
            body, key = func(*args, **kwargs)
64 65
            conn = exchange_connect(QUEUE_CONNECTION)
......
71 72
            logger.exception(e)
72 73
    return wrapper
73 74

  
75

  
74 76
@wrapper
75 77
def report_user_event(user, create=False):
76 78
    eventType = 'create' if not create else 'modify'
77 79
    body = UserEvent(QUEUE_CLIENT_ID, user.email, user.is_active, eventType, {}
78
    ).format()
80
                     ).format()
79 81
    routing_key = '%s.user'
80 82
    return body, routing_key
81 83

  
84

  
82 85
@wrapper
83 86
def report_user_credits_event(user):
84 87
    body = Receipt(QUEUE_CLIENT_ID, user.email, INSTANCE_ID, RESOURCE,
85
        DEFAULT_CREDITS, details={}
86
    ).format()
88
                   DEFAULT_CREDITS, details={}
89
                   ).format()
87 90
    routing_key = '%s.resource'
88 91
    return body, routing_key
89 92

  
93

  
90 94
def report_credits_event():
91 95
    # better approach?
92 96
    from astakos.im.models import AstakosUser
b/snf-astakos-app/astakos/im/endpoints/quotaholder.py
1 1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
# 
2
#
3 3
# Redistribution and use in source and binary forms, with or
4 4
# without modification, are permitted provided that the following
5 5
# conditions are met:
6
# 
6
#
7 7
#   1. Redistributions of source code must retain the above
8 8
#      copyright notice, this list of conditions and the following
9 9
#      disclaimer.
10
# 
10
#
11 11
#   2. Redistributions in binary form must reproduce the above
12 12
#      copyright notice, this list of conditions and the following
13 13
#      disclaimer in the documentation and/or other materials
14 14
#      provided with the distribution.
15
# 
15
#
16 16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
......
25 25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 27
# POSSIBILITY OF SUCH DAMAGE.
28
# 
28
#
29 29
# The views and conclusions contained in the software and
30 30
# documentation are those of the authors and should not be
31 31
# interpreted as representing official policies, either expressed
......
45 45

  
46 46
logger = logging.getLogger(__name__)
47 47

  
48

  
48 49
def register_users(users, client=None):
49 50
    if not users:
50 51
        return
51
    
52

  
52 53
    if not QUOTA_HOLDER_URL:
53 54
        return
54
    
55

  
55 56
    c = client or QuotaholderHTTP(QUOTA_HOLDER_URL)
56 57
    data = []
57 58
    append = data.append
......
60 61
            entity = user.email
61 62
        except AttributeError:
62 63
            continue
63
        else:    
64
        else:
64 65
            args = entity, owner, key, ownerkey = (
65 66
                entity, 'system', ENTITY_KEY, ''
66 67
            )
67 68
            append(args)
68
    
69

  
69 70
    if not data:
70 71
        return
71
    
72

  
72 73
    rejected = c.create_entity(
73 74
        context={},
74 75
        create_entity=data,
75 76
    )
76 77
    msg = _('Create entities: %s - Rejected: %s' % (data, rejected,))
77 78
    logger.log(LOGGING_LEVEL, msg)
78
    
79

  
79 80
    created = filter(lambda u: unicode(u.email) not in rejected, users)
80 81
    send_quota(created, c)
81 82
    return rejected
82 83

  
84

  
83 85
def send_quota(users, client=None):
84 86
    if not users:
85 87
        return
86
    
88

  
87 89
    if not QUOTA_HOLDER_URL:
88 90
        return
89
    
91

  
90 92
    c = client or QuotaholderHTTP(QUOTA_HOLDER_URL)
91 93
    data = []
92 94
    append = data.append
......
97 99
            continue
98 100
        else:
99 101
            for resource, limit in user.quota.iteritems():
100
                args = entity, resource, key, quantity, capacity, import_limit , \
101
                    export_limit, flags =(
102
                args = entity, resource, key, quantity, capacity, import_limit, \
103
                    export_limit, flags = (
102 104
                        entity, resource, ENTITY_KEY, '0', str(limit), 0, 0, 0
103
                )
105
                    )
104 106
                append(args)
105
    
107

  
106 108
    if not data:
107 109
        return
108
    
110

  
109 111
    rejected = c.set_quota(context={}, set_quota=data)
110 112
    msg = _('Set quota: %s - Rejected: %s' % (data, rejected,))
111 113
    logger.log(LOGGING_LEVEL, msg)
112 114
    return rejected
113 115

  
116

  
114 117
def get_quota(users, client=None):
115 118
    if not users:
116 119
        return
117
    
120

  
118 121
    if not QUOTA_HOLDER_URL:
119 122
        return
120
    
123

  
121 124
    c = client or QuotaholderHTTP(QUOTA_HOLDER_URL)
122 125
    data = []
123 126
    append = data.append
......
130 133
            for r in user.quota.keys():
131 134
                args = entity, resource, key = entity, r, ENTITY_KEY
132 135
                append(args)
133
    
136

  
134 137
    if not data:
135 138
        return
136
    
139

  
137 140
    r = c.get_quota(context={}, get_quota=data)
138 141
    msg = _('Get quota: %s' % data)
139 142
    logger.log(LOGGING_LEVEL, msg)
140
    return r
143
    return r
b/snf-astakos-app/astakos/im/forms.py
35 35
from django import forms
36 36
from django.utils.translation import ugettext as _
37 37
from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm,
38
    PasswordResetForm, PasswordChangeForm
39
)
38
                                       PasswordResetForm, PasswordChangeForm
39
                                       )
40 40
from django.core.mail import send_mail
41 41
from django.contrib.auth.tokens import default_token_generator
42 42
from django.template import Context, loader
......
47 47
from django.forms.extras.widgets import SelectDateWidget
48 48
from django.conf import settings
49 49

  
50
from astakos.im.models import (AstakosUser, EmailChange, AstakosGroup, Invitation,
50
from astakos.im.models import (
51
    AstakosUser, EmailChange, AstakosGroup, Invitation,
51 52
    Membership, GroupKind, get_latest_terms
52 53
)
53 54
from astakos.im.settings import (INVITATIONS_PER_LEVEL, BASEURL, SITENAME,
54
    RECAPTCHA_PRIVATE_KEY, RECAPTCHA_ENABLED, DEFAULT_CONTACT_EMAIL,
55
    LOGGING_LEVEL
56
)
55
                                 RECAPTCHA_PRIVATE_KEY, RECAPTCHA_ENABLED, DEFAULT_CONTACT_EMAIL,
56
                                 LOGGING_LEVEL
57
                                 )
57 58
from astakos.im.widgets import DummyWidget, RecaptchaWidget
58 59
from astakos.im.functions import send_change_email
59 60

  
......
66 67

  
67 68
logger = logging.getLogger(__name__)
68 69

  
70

  
69 71
class LocalUserCreationForm(UserCreationForm):
70 72
    """
71 73
    Extends the built in UserCreationForm in several ways:
......
75 77
    * User created is not active.
76 78
    """
77 79
    recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
78
    recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='')
80
    recaptcha_response_field = forms.CharField(
81
        widget=RecaptchaWidget, label='')
79 82

  
80 83
    class Meta:
81 84
        model = AstakosUser
82
        fields = ("email", "first_name", "last_name", "has_signed_terms", "has_signed_terms")
85
        fields = ("email", "first_name", "last_name",
86
                  "has_signed_terms", "has_signed_terms")
83 87

  
84 88
    def __init__(self, *args, **kwargs):
85 89
        """
......
90 94
            kwargs.pop('request')
91 95
            self.ip = request.META.get('REMOTE_ADDR',
92 96
                                       request.META.get('HTTP_X_REAL_IP', None))
93
        
97

  
94 98
        super(LocalUserCreationForm, self).__init__(*args, **kwargs)
95 99
        self.fields.keyOrder = ['email', 'first_name', 'last_name',
96 100
                                'password1', 'password2']
97 101

  
98 102
        if RECAPTCHA_ENABLED:
99 103
            self.fields.keyOrder.extend(['recaptcha_challenge_field',
100
                                         'recaptcha_response_field',])
104
                                         'recaptcha_response_field', ])
101 105
        if get_latest_terms():
102 106
            self.fields.keyOrder.append('has_signed_terms')
103
            
107

  
104 108
        if 'has_signed_terms' in self.fields:
105 109
            # Overriding field label since we need to apply a link
106 110
            # to the terms within the label
107 111
            terms_link_html = '<a href="%s" target="_blank">%s</a>' \
108
                    % (reverse('latest_terms'), _("the terms"))
112
                % (reverse('latest_terms'), _("the terms"))
109 113
            self.fields['has_signed_terms'].label = \
110
                    mark_safe("I agree with %s" % terms_link_html)
114
                mark_safe("I agree with %s" % terms_link_html)
111 115

  
112 116
    def clean_email(self):
113 117
        email = self.cleaned_data['email']
......
138 142
        rrf = self.cleaned_data['recaptcha_response_field']
139 143
        check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip)
140 144
        if not check.is_valid:
141
            raise forms.ValidationError(_('You have not entered the correct words'))
145
            raise forms.ValidationError(
146
                _('You have not entered the correct words'))
142 147

  
143 148
    def save(self, commit=True):
144 149
        """
......
152 157
            logger.log(LOGGING_LEVEL, 'Created user %s' % user.email)
153 158
        return user
154 159

  
160

  
155 161
class InvitedLocalUserCreationForm(LocalUserCreationForm):
156 162
    """
157 163
    Extends the LocalUserCreationForm: email is readonly.
......
170 176
        ro = ('email', 'username',)
171 177
        for f in ro:
172 178
            self.fields[f].widget.attrs['readonly'] = True
173
        
174 179

  
175 180
    def save(self, commit=True):
176 181
        user = super(InvitedLocalUserCreationForm, self).save(commit=False)
......
182 187
            user.save()
183 188
        return user
184 189

  
190

  
185 191
class ThirdPartyUserCreationForm(forms.ModelForm):
186 192
    class Meta:
187 193
        model = AstakosUser
188
        fields = ("email", "first_name", "last_name", "third_party_identifier", "has_signed_terms")
189
    
194
        fields = ("email", "first_name", "last_name",
195
                  "third_party_identifier", "has_signed_terms")
196

  
190 197
    def __init__(self, *args, **kwargs):
191 198
        """
192 199
        Changes the order of fields, and removes the username field.
......
195 202
        if self.request:
196 203
            kwargs.pop('request')
197 204
        super(ThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
198
        self.fields.keyOrder = ['email', 'first_name', 'last_name', 'third_party_identifier']
205
        self.fields.keyOrder = ['email', 'first_name', 'last_name',
206
                                'third_party_identifier']
199 207
        if get_latest_terms():
200 208
            self.fields.keyOrder.append('has_signed_terms')
201 209
        #set readonly form fields
202 210
        ro = ["third_party_identifier"]
203 211
        for f in ro:
204 212
            self.fields[f].widget.attrs['readonly'] = True
205
        
213

  
206 214
        if 'has_signed_terms' in self.fields:
207 215
            # Overriding field label since we need to apply a link
208 216
            # to the terms within the label
209 217
            terms_link_html = '<a href="%s" target="_blank">%s</a>' \
210
                    % (reverse('latest_terms'), _("the terms"))
218
                % (reverse('latest_terms'), _("the terms"))
211 219
            self.fields['has_signed_terms'].label = \
212
                    mark_safe("I agree with %s" % terms_link_html)
213
    
220
                mark_safe("I agree with %s" % terms_link_html)
221

  
214 222
    def clean_email(self):
215 223
        email = self.cleaned_data['email']
216 224
        if not email:
217 225
            raise forms.ValidationError(_("This field is required"))
218 226
        return email
219
    
227

  
220 228
    def clean_has_signed_terms(self):
221 229
        has_signed_terms = self.cleaned_data['has_signed_terms']
222 230
        if not has_signed_terms:
223 231
            raise forms.ValidationError(_('You have to agree with the terms'))
224 232
        return has_signed_terms
225
    
233

  
226 234
    def save(self, commit=True):
227 235
        user = super(ThirdPartyUserCreationForm, self).save(commit=False)
228 236
        user.set_unusable_password()
......
233 241
            logger.log(LOGGING_LEVEL, 'Created user %s' % user.email)
234 242
        return user
235 243

  
244

  
236 245
class InvitedThirdPartyUserCreationForm(ThirdPartyUserCreationForm):
237 246
    """
238 247
    Extends the ThirdPartyUserCreationForm: email is readonly.
......
241 250
        """
242 251
        Changes the order of fields, and removes the username field.
243 252
        """
244
        super(InvitedThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
253
        super(
254
            InvitedThirdPartyUserCreationForm, self).__init__(*args, **kwargs)
245 255

  
246 256
        #set readonly form fields
247 257
        ro = ('email',)
248 258
        for f in ro:
249 259
            self.fields[f].widget.attrs['readonly'] = True
250
    
260

  
251 261
    def save(self, commit=True):
252
        user = super(InvitedThirdPartyUserCreationForm, self).save(commit=False)
262
        user = super(
263
            InvitedThirdPartyUserCreationForm, self).save(commit=False)
253 264
        level = user.invitation.inviter.level + 1
254 265
        user.level = level
255 266
        user.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
......
258 269
            user.save()
259 270
        return user
260 271

  
272

  
261 273
class ShibbolethUserCreationForm(ThirdPartyUserCreationForm):
262
    additional_email = forms.CharField(widget=forms.HiddenInput(), label='', required = False)
263
    
274
    additional_email = forms.CharField(
275
        widget=forms.HiddenInput(), label='', required=False)
276

  
264 277
    def __init__(self, *args, **kwargs):
265 278
        super(ShibbolethUserCreationForm, self).__init__(*args, **kwargs)
266 279
        self.fields.keyOrder.append('additional_email')
267 280
        # copy email value to additional_mail in case user will change it
268 281
        name = 'email'
269 282
        field = self.fields[name]
270
        self.initial['additional_email'] = self.initial.get(name, field.initial)
271
    
283
        self.initial['additional_email'] = self.initial.get(name,
284
                                                            field.initial)
285

  
272 286
    def clean_email(self):
273 287
        email = self.cleaned_data['email']
274
        for user in AstakosUser.objects.filter(email = email):
288
        for user in AstakosUser.objects.filter(email=email):
275 289
            if user.provider == 'shibboleth':
276 290
                raise forms.ValidationError(_("This email is already associated with another shibboleth account."))
277 291
            elif not user.is_active:
......
280 294
        super(ShibbolethUserCreationForm, self).clean_email()
281 295
        return email
282 296

  
297

  
283 298
class InvitedShibbolethUserCreationForm(ShibbolethUserCreationForm, InvitedThirdPartyUserCreationForm):
284 299
    pass
285
    
300

  
301

  
286 302
class LoginForm(AuthenticationForm):
287 303
    username = forms.EmailField(label=_("Email"))
288 304
    recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
289
    recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='')
290
    
305
    recaptcha_response_field = forms.CharField(
306
        widget=RecaptchaWidget, label='')
307

  
291 308
    def __init__(self, *args, **kwargs):
292 309
        was_limited = kwargs.get('was_limited', False)
293 310
        request = kwargs.get('request', None)
294 311
        if request:
295 312
            self.ip = request.META.get('REMOTE_ADDR',
296 313
                                       request.META.get('HTTP_X_REAL_IP', None))
297
        
314

  
298 315
        t = ('request', 'was_limited')
299 316
        for elem in t:
300 317
            if elem in kwargs.keys():
301 318
                kwargs.pop(elem)
302 319
        super(LoginForm, self).__init__(*args, **kwargs)
303
        
320

  
304 321
        self.fields.keyOrder = ['username', 'password']
305 322
        if was_limited and RECAPTCHA_ENABLED:
306 323
            self.fields.keyOrder.extend(['recaptcha_challenge_field',
307
                                         'recaptcha_response_field',])
308
    
324
                                         'recaptcha_response_field', ])
325

  
309 326
    def clean_recaptcha_response_field(self):
310 327
        if 'recaptcha_challenge_field' in self.cleaned_data:
311 328
            self.validate_captcha()
......
321 338
        rrf = self.cleaned_data['recaptcha_response_field']
322 339
        check = captcha.submit(rcf, rrf, RECAPTCHA_PRIVATE_KEY, self.ip)
323 340
        if not check.is_valid:
324
            raise forms.ValidationError(_('You have not entered the correct words'))
325
    
341
            raise forms.ValidationError(
342
                _('You have not entered the correct words'))
343

  
326 344
    def clean(self):
327 345
        super(LoginForm, self).clean()
328 346
        if self.user_cache and self.user_cache.provider not in ('local', ''):
329 347
            raise forms.ValidationError(_('Local login is not the current authentication method for this account.'))
330 348
        return self.cleaned_data
331 349

  
350

  
332 351
class ProfileForm(forms.ModelForm):
333 352
    """
334 353
    Subclass of ``ModelForm`` for permiting user to edit his/her profile.
......
341 360

  
342 361
    class Meta:
343 362
        model = AstakosUser
344
        fields = ('email', 'first_name', 'last_name', 'auth_token', 'auth_token_expires')
363
        fields = ('email', 'first_name', 'last_name', 'auth_token',
364
                  'auth_token_expires')
345 365

  
346 366
    def __init__(self, *args, **kwargs):
347 367
        super(ProfileForm, self).__init__(*args, **kwargs)
......
360 380
            user.save()
361 381
        return user
362 382

  
383

  
363 384
class FeedbackForm(forms.Form):
364 385
    """
365 386
    Form for writing feedback.
......
368 389
    feedback_data = forms.CharField(widget=forms.HiddenInput(), label='',
369 390
                                    required=False)
370 391

  
392

  
371 393
class SendInvitationForm(forms.Form):
372 394
    """
373 395
    Form for sending an invitations
374 396
    """
375 397

  
376
    email = forms.EmailField(required = True, label = 'Email address')
377
    first_name = forms.EmailField(label = 'First name')
378
    last_name = forms.EmailField(label = 'Last name')
398
    email = forms.EmailField(required=True, label='Email address')
399
    first_name = forms.EmailField(label='First name')
400
    last_name = forms.EmailField(label='Last name')
401

  
379 402

  
380 403
class ExtendedPasswordResetForm(PasswordResetForm):
381 404
    """
......
390 413
        try:
391 414
            user = AstakosUser.objects.get(email=email, is_active=True)
392 415
            if not user.has_usable_password():
393
                raise forms.ValidationError(_("This account has not a usable password."))
416
                raise forms.ValidationError(
417
                    _("This account has not a usable password."))
394 418
        except AstakosUser.DoesNotExist:
395 419
            raise forms.ValidationError(_('That e-mail address doesn\'t have an associated user account. Are you sure you\'ve registered?'))
396 420
        return email
397
    
398
    def save(self, domain_override=None, email_template_name='registration/password_reset_email.html',
399
             use_https=False, token_generator=default_token_generator, request=None):
421

  
422
    def save(
423
        self, domain_override=None, email_template_name='registration/password_reset_email.html',
424
            use_https=False, token_generator=default_token_generator, request=None):
400 425
        """
401 426
        Generates a one-use only link for resetting password and sends to the user.
402 427
        """
403 428
        for user in self.users_cache:
404 429
            url = reverse('django.contrib.auth.views.password_reset_confirm',
405
                kwargs={'uidb36':int_to_base36(user.id),
406
                    'token':token_generator.make_token(user)
407
                }
408
            )
430
                          kwargs={'uidb36': int_to_base36(user.id),
431
                                  'token': token_generator.make_token(user)
432
                                  }
433
                          )
409 434
            url = urljoin(BASEURL, url)
410 435
            t = loader.get_template(email_template_name)
411 436
            c = {
......
418 443
            }
419 444
            from_email = settings.SERVER_EMAIL
420 445
            send_mail(_("Password reset on %s alpha2 testing") % SITENAME,
421
                t.render(Context(c)), from_email, [user.email])
446
                      t.render(Context(c)), from_email, [user.email])
447

  
422 448

  
423 449
class EmailChangeForm(forms.ModelForm):
424 450
    class Meta:
425 451
        model = EmailChange
426 452
        fields = ('new_email_address',)
427
            
453

  
428 454
    def clean_new_email_address(self):
429 455
        addr = self.cleaned_data['new_email_address']
430 456
        if AstakosUser.objects.filter(email__iexact=addr):
431 457
            raise forms.ValidationError(_(u'This email address is already in use. Please supply a different email address.'))
432 458
        return addr
433
    
459

  
434 460
    def save(self, email_template_name, request, commit=True):
435 461
        ec = super(EmailChangeForm, self).save(commit=False)
436 462
        ec.user = request.user
437
        activation_key = hashlib.sha1(str(random()) + smart_str(ec.new_email_address))
438
        ec.activation_key=activation_key.hexdigest()
463
        activation_key = hashlib.sha1(
464
            str(random()) + smart_str(ec.new_email_address))
465
        ec.activation_key = activation_key.hexdigest()
439 466
        if commit:
440 467
            ec.save()
441 468
        send_change_email(ec, request, email_template_name=email_template_name)
442 469

  
470

  
443 471
class SignApprovalTermsForm(forms.ModelForm):
444 472
    class Meta:
445 473
        model = AstakosUser
......
454 482
            raise forms.ValidationError(_('You have to agree with the terms'))
455 483
        return has_signed_terms
456 484

  
485

  
457 486
class InvitationForm(forms.ModelForm):
458 487
    username = forms.EmailField(label=_("Email"))
459
    
488

  
460 489
    def __init__(self, *args, **kwargs):
461 490
        super(InvitationForm, self).__init__(*args, **kwargs)
462
    
491

  
463 492
    class Meta:
464 493
        model = Invitation
465 494
        fields = ('username', 'realname')
466
    
495

  
467 496
    def clean_username(self):
468 497
        username = self.cleaned_data['username']
469 498
        try:
470
            Invitation.objects.get(username = username)
471
            raise forms.ValidationError(_('There is already invitation for this email.'))
499
            Invitation.objects.get(username=username)
500
            raise forms.ValidationError(
501
                _('There is already invitation for this email.'))
472 502
        except Invitation.DoesNotExist:
473 503
            pass
474 504
        return username
475 505

  
506

  
476 507
class ExtendedPasswordChangeForm(PasswordChangeForm):
477 508
    """
478 509
    Extends PasswordChangeForm by enabling user
479 510
    to optionally renew also the token.
480 511
    """
481 512
    renew = forms.BooleanField(label='Renew token', required=False)
482
    
513

  
483 514
    def __init__(self, user, *args, **kwargs):
484 515
        super(ExtendedPasswordChangeForm, self).__init__(user, *args, **kwargs)
485
    
516

  
486 517
    def save(self, commit=True):
487 518
        user = super(ExtendedPasswordChangeForm, self).save(commit=False)
488 519
        if self.cleaned_data.get('renew'):
......
491 522
            user.save()
492 523
        return user
493 524

  
525

  
494 526
class AstakosGroupCreationForm(forms.ModelForm):
495 527
#     issue_date = forms.DateField(widget=SelectDateWidget())
496 528
#     expiration_date = forms.DateField(widget=SelectDateWidget())
......
508 540
    
509 541
    class Meta:
510 542
        model = AstakosGroup
511
    
543

  
512 544
    def __init__(self, *args, **kwargs):
513 545
        try:
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff