views.py 22.9 KB
Newer Older
Antony Chazapis's avatar
Antony Chazapis committed
1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
4
5
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
6
#
7
8
9
#   1. Redistributions of source code must retain the above
#      copyright notice, this list of conditions and the following
#      disclaimer.
10
#
11
12
13
14
#   2. Redistributions in binary form must reproduce the above
#      copyright notice, this list of conditions and the following
#      disclaimer in the documentation and/or other materials
#      provided with the distribution.
15
#
16
17
18
19
20
21
22
23
24
25
26
27
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
30
31
32
33
34
35
36
37
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.

import logging
import socket

from smtplib import SMTPException
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
38
from urllib import quote
39
from functools import wraps
40
41

from django.core.mail import send_mail
42
from django.http import HttpResponse, HttpResponseBadRequest
43
44
45
46
from django.shortcuts import redirect
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
47
48
49
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.db import transaction
50
from django.utils.http import urlencode
51
from django.http import HttpResponseRedirect, HttpResponseBadRequest
52
from django.db.utils import IntegrityError
53
from django.contrib.auth.views import password_change
54
from django.core.exceptions import ValidationError
55
from django.db.models import Q
56

57
from astakos.im.models import AstakosUser, Invitation, ApprovalTerms
58
from astakos.im.activation_backends import get_backend, SimpleBackend
59
from astakos.im.util import get_context, prepare_response, set_cookie, get_query
60
from astakos.im.forms import *
61
from astakos.im.functions import send_greeting, send_feedback, SendMailError, \
62
    invite as invite_func, logout as auth_logout, send_helpdesk_notification
63
from astakos.im.settings import DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL, COOKIE_NAME, COOKIE_DOMAIN, IM_MODULES, SITENAME, LOGOUT_NEXT, LOGGING_LEVEL
64

65
66
logger = logging.getLogger(__name__)

67
def render_response(template, tab=None, status=200, reset_cookie=False, context_instance=None, **kwargs):
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
68
69
70
71
72
    """
    Calls ``django.template.loader.render_to_string`` with an additional ``tab``
    keyword argument and returns an ``django.http.HttpResponse`` with the
    specified ``status``.
    """
73
    if tab is None:
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
74
        tab = template.partition('_')[0].partition('.html')[0]
75
    kwargs.setdefault('tab', tab)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
76
    html = render_to_string(template, kwargs, context_instance=context_instance)
77
78
79
80
    response = HttpResponse(html, status=status)
    if reset_cookie:
        set_cookie(response, context_instance['request'].user)
    return response
81

82
83
84

def requires_anonymous(func):
    """
85
    Decorator checkes whether the request.user is not Anonymous and in that case
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
86
    redirects to `logout`.
87
88
89
90
91
    """
    @wraps(func)
    def wrapper(request, *args):
        if not request.user.is_anonymous():
            next = urlencode({'next': request.build_absolute_uri()})
92
93
            logout_uri = reverse(logout) + '?' + next
            return HttpResponseRedirect(logout_uri)
94
95
96
        return func(request, *args)
    return wrapper

97
98
99
100
101
102
103
def signed_terms_required(func):
    """
    Decorator checkes whether the request.user is Anonymous and in that case
    redirects to `logout`.
    """
    @wraps(func)
    def wrapper(request, *args, **kwargs):
104
        if request.user.is_authenticated() and not request.user.signed_terms():
105
106
107
108
109
110
111
112
            params = urlencode({'next': request.build_absolute_uri(),
                              'show_form':''})
            terms_uri = reverse('latest_terms') + '?' + params
            return HttpResponseRedirect(terms_uri)
        return func(request, *args, **kwargs)
    return wrapper

@signed_terms_required
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
113
def index(request, login_template_name='im/login.html', profile_template_name='im/profile.html', extra_context={}):
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
114
    """
115
    If there is logged on user renders the profile page otherwise renders login page.
116

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
117
    **Arguments**
118

119
120
    ``login_template_name``
        A custom login template to use. This is optional; if not specified,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
121
        this will default to ``im/login.html``.
122

123
124
    ``profile_template_name``
        A custom profile template to use. This is optional; if not specified,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
125
        this will default to ``im/profile.html``.
126

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
127
128
    ``extra_context``
        An dictionary of variables to add to the template context.
129

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
130
    **Template:**
131

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
132
    im/profile.html or im/login.html or ``template_name`` keyword argument.
133

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
134
    """
135
136
    template_name = login_template_name
    if request.user.is_authenticated():
137
        return HttpResponseRedirect(reverse('astakos.im.views.edit_profile'))
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
138
    return render_response(template_name,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
139
                           login_form = LoginForm(request=request),
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
140
                           context_instance = get_context(request, extra_context))
141

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
142
@login_required
143
@signed_terms_required
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
144
@transaction.commit_manually
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
145
def invite(request, template_name='im/invitations.html', extra_context={}):
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
146
147
    """
    Allows a user to invite somebody else.
148

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
149
150
151
    In case of GET request renders a form for providing the invitee information.
    In case of POST checks whether the user has not run out of invitations and then
    sends an invitation email to singup to the service.
152

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
153
154
    The view uses commit_manually decorator in order to ensure the number of the
    user invitations is going to be updated only if the email has been successfully sent.
155

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
156
    If the user isn't logged in, redirects to settings.LOGIN_URL.
157

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
158
    **Arguments**
159

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
160
161
    ``template_name``
        A custom template to use. This is optional; if not specified,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
162
        this will default to ``im/invitations.html``.
163

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
164
165
    ``extra_context``
        An dictionary of variables to add to the template context.
166

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
167
    **Template:**
168

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
169
    im/invitations.html or ``template_name`` keyword argument.
170

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
171
    **Settings:**
172

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
173
    The view expectes the following settings are defined:
174

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
175
    * LOGIN_URL: login uri
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
176
177
    * ASTAKOS_DEFAULT_CONTACT_EMAIL: service support email
    * ASTAKOS_DEFAULT_FROM_EMAIL: from email
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
178
    """
179
180
    status = None
    message = None
181
    form = InvitationForm()
182
    
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
183
    inviter = request.user
184
    if request.method == 'POST':
185
        form = InvitationForm(request.POST)
186
        if inviter.invitations > 0:
187
188
189
190
191
192
193
            if form.is_valid():
                try:
                    invitation = form.save()
                    invite_func(invitation, inviter)
                    status = messages.SUCCESS
                    message = _('Invitation sent to %s' % invitation.username)
                except SendMailError, e:
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
194
                    status = messages.ERROR
195
196
                    message = e.message
                    transaction.rollback()
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
197
198
199
200
201
202
203
                except BaseException, e:
                    status = messages.ERROR
                    message = _('Something went wrong.')
                    logger.exception(e)
                    transaction.rollback()
                else:
                    transaction.commit()
204
        else:
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
205
            status = messages.ERROR
206
            message = _('No invitations left')
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
207
    messages.add_message(request, status, message)
208

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
209
    sent = [{'email': inv.username,
210
211
             'realname': inv.realname,
             'is_consumed': inv.is_consumed}
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
212
             for inv in request.user.invitations_sent.all()]
213
    kwargs = {'inviter': inviter,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
214
              'sent':sent}
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
215
216
    context = get_context(request, extra_context, **kwargs)
    return render_response(template_name,
217
                           invitation_form = form,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
218
                           context_instance = context)
219

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
220
@login_required
221
@signed_terms_required
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
222
def edit_profile(request, template_name='im/profile.html', extra_context={}):
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
223
224
    """
    Allows a user to edit his/her profile.
225

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
226
    In case of GET request renders a form for displaying the user information.
227
228
    In case of POST updates the user informantion and redirects to ``next``
    url parameter if exists.
229

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
230
    If the user isn't logged in, redirects to settings.LOGIN_URL.
231

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
232
    **Arguments**
233

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
234
235
    ``template_name``
        A custom template to use. This is optional; if not specified,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
236
        this will default to ``im/profile.html``.
237

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
238
239
    ``extra_context``
        An dictionary of variables to add to the template context.
240

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
241
    **Template:**
242

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
243
    im/profile.html or ``template_name`` keyword argument.
244

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
245
    **Settings:**
246

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
247
    The view expectes the following settings are defined:
248

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
249
    * LOGIN_URL: login uri
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
250
    """
251
252
    form = ProfileForm(instance=request.user)
    extra_context['next'] = request.GET.get('next')
253
    reset_cookie = False
254
    if request.method == 'POST':
255
        form = ProfileForm(request.POST, instance=request.user)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
256
        if form.is_valid():
257
            try:
258
259
260
261
                prev_token = request.user.auth_token
                user = form.save()
                reset_cookie = user.auth_token != prev_token
                form = ProfileForm(instance=user)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
262
263
264
                next = request.POST.get('next')
                if next:
                    return redirect(next)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
265
266
267
268
                msg = _('Profile has been updated successfully')
                messages.add_message(request, messages.SUCCESS, msg)
            except ValueError, ve:
                messages.add_message(request, messages.ERROR, ve)
269
270
271
    elif request.method == "GET":
        request.user.is_verified = True
        request.user.save()
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
272
    return render_response(template_name,
273
                           reset_cookie = reset_cookie,
274
                           profile_form = form,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
275
                           context_instance = get_context(request,
276
                                                          extra_context))
277

278
def signup(request, template_name='im/signup.html', on_success='im/signup_complete.html', extra_context={}, backend=None):
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
279
280
    """
    Allows a user to create a local account.
281

282
    In case of GET request renders a form for entering the user information.
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
283
    In case of POST handles the signup.
284

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
285
    The user activation will be delegated to the backend specified by the ``backend`` keyword argument
286
287
288
    if present, otherwise to the ``astakos.im.activation_backends.InvitationBackend``
    if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.activation_backends.SimpleBackend`` if not
    (see activation_backends);
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
289
    
290
    Upon successful user creation, if ``next`` url parameter is present the user is redirected there
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
291
    otherwise renders the same page with a success message.
292
    
293
    On unsuccessful creation, renders ``template_name`` with an error message.
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
294
295
296
    
    **Arguments**
    
297
298
    ``template_name``
        A custom template to render. This is optional;
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
299
        if not specified, this will default to ``im/signup.html``.
300

301
302
    ``on_success``
        A custom template to render in case of success. This is optional;
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
303
        if not specified, this will default to ``im/signup_complete.html``.
304

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
305
306
    ``extra_context``
        An dictionary of variables to add to the template context.
307

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
308
309
    **Template:**
    
310
    im/signup.html or ``template_name`` keyword argument.
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
311
    im/signup_complete.html or ``on_success`` keyword argument. 
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
312
    """
313
    if request.user.is_authenticated():
314
        return HttpResponseRedirect(reverse('astakos.im.views.edit_profile'))
315
    
316
    provider = get_query(request).get('provider', 'local')
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
317
    try:
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
318
319
        if not backend:
            backend = get_backend(request)
320
        form = backend.get_signup_form(provider)
321
    except Exception, e:
322
        form = SimpleBackend(request).get_signup_form(provider)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
323
        messages.add_message(request, messages.ERROR, e)
324
325
    if request.method == 'POST':
        if form.is_valid():
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
326
            user = form.save(commit=False)
327
328
329
            try:
                result = backend.handle_activation(user)
                status = messages.SUCCESS
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
330
331
                message = result.message
                user.save()
332
333
334
335
                if 'additional_email' in form.cleaned_data:
                    additional_email = form.cleaned_data['additional_email']
                    if additional_email != user.email:
                        user.additionalmail_set.create(email=additional_email)
336
337
                        msg = 'Additional email: %s saved for user %s.' % (additional_email, user.email)
                        logger._log(LOGGING_LEVEL, msg, [])
338
339
340
341
342
343
                if user and user.is_active:
                    next = request.POST.get('next', '')
                    return prepare_response(request, user, next=next)
                messages.add_message(request, status, message)
                return render_response(on_success,
                                       context_instance=get_context(request, extra_context))
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
344
345
346
347
348
349
350
351
            except SendMailError, e:
                status = messages.ERROR
                message = e.message
                messages.add_message(request, status, message)
            except BaseException, e:
                status = messages.ERROR
                message = _('Something went wrong.')
                messages.add_message(request, status, message)
352
                logger.exception(e)
353
    return render_response(template_name,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
354
                           signup_form = form,
355
                           provider = provider,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
356
                           context_instance=get_context(request, extra_context))
357

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
358
@login_required
359
@signed_terms_required
360
def feedback(request, template_name='im/feedback.html', email_template_name='im/feedback_mail.txt', extra_context={}):
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
361
362
    """
    Allows a user to send feedback.
363

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
364
365
    In case of GET request renders a form for providing the feedback information.
    In case of POST sends an email to support team.
366

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
367
    If the user isn't logged in, redirects to settings.LOGIN_URL.
368

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
369
    **Arguments**
370

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
371
372
    ``template_name``
        A custom template to use. This is optional; if not specified,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
373
        this will default to ``im/feedback.html``.
374

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
375
376
    ``extra_context``
        An dictionary of variables to add to the template context.
377

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
378
    **Template:**
379

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
380
    im/signup.html or ``template_name`` keyword argument.
381

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
382
    **Settings:**
383

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
384
    * LOGIN_URL: login uri
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
385
    * ASTAKOS_DEFAULT_CONTACT_EMAIL: List of feedback recipients
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
386
    """
387
    if request.method == 'GET':
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
388
389
390
391
        form = FeedbackForm()
    if request.method == 'POST':
        if not request.user:
            return HttpResponse('Unauthorized', status=401)
392

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
393
394
        form = FeedbackForm(request.POST)
        if form.is_valid():
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
395
            msg = form.cleaned_data['feedback_msg']
396
            data = form.cleaned_data['feedback_data']
397
            try:
398
399
400
401
402
                send_feedback(msg, data, request.user, email_template_name)
            except SendMailError, e:
                message = e.message
                status = messages.ERROR
            else:
403
404
405
                message = _('Feedback successfully sent')
                status = messages.SUCCESS
            messages.add_message(request, status, message)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
406
    return render_response(template_name,
407
                           feedback_form = form,
408
                           context_instance = get_context(request, extra_context))
409

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
410
def logout(request, template='registration/logged_out.html', extra_context={}):
411
    """
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
412
    Wraps `django.contrib.auth.logout` and delete the cookie.
413
    """
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
414
    msg = 'Cookie deleted for %s' % (request.user.email)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
415
416
    auth_logout(request)
    response = HttpResponse()
417
    response.delete_cookie(COOKIE_NAME, path='/', domain=COOKIE_DOMAIN)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
418
    logger._log(LOGGING_LEVEL, msg, [])
419
420
421
422
423
    next = request.GET.get('next')
    if next:
        response['Location'] = next
        response.status_code = 302
        return response
424
425
426
427
428
    elif LOGOUT_NEXT:
        response['Location'] = LOGOUT_NEXT
        response.status_code = 301
        return response
    messages.add_message(request, messages.SUCCESS, _('You have successfully logged out.'))
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
429
430
431
    context = get_context(request, extra_context)
    response.write(render_to_string(template, context_instance=context))
    return response
432

433
@transaction.commit_manually
434
435
def activate(request, email_template_name='im/welcome_email.txt', on_failure='im/signup.html', 
                helpdesk_email_template_name='im/helpdesk_notification.txt'):
436
    """
437
438
    Activates the user identified by the ``auth`` request parameter, sends a welcome email
    and renews the user token.
439

440
441
    The view uses commit_manually decorator in order to ensure the user state will be updated
    only if the email will be send successfully.
442
443
444
445
446
447
    """
    token = request.GET.get('auth')
    next = request.GET.get('next')
    try:
        user = AstakosUser.objects.get(auth_token=token)
    except AstakosUser.DoesNotExist:
448
        return HttpResponseBadRequest(_('No such user'))
449
    
450
451
452
453
454
    if user.is_active:
        message = 'Account already active.'
        messages.add_message(request, messages.ERROR, message)
        return render_response(on_failure)
    
455
    notify_helpdesk = False
456
    try:
457
        local_user = AstakosUser.objects.get(~Q(id = user.id), email=user.email, is_active=True)
458
459
460
    except AstakosUser.DoesNotExist:
        user.is_active = True
        user.email_verified = True
461
462
463
464
        try:
            user.save()
        except ValidationError, e:
            return HttpResponseBadRequest(e)
465
        notify_helpdesk = True
466
    else:
467
        # switch the existing account to shibboleth one
468
469
470
471
472
473
474
475
476
477
478
        if user.provider == 'shibboleth':
            local_user.provider = 'shibboleth'
            local_user.set_unusable_password()
            local_user.third_party_identifier = user.third_party_identifier
            try:
                local_user.save()
            except ValidationError, e:
                return HttpResponseBadRequest(e)
            user.delete()
            user = local_user
        
479
    try:
480
481
        if notify_helpdesk:
            send_helpdesk_notification(user, helpdesk_email_template_name)
482
483
484
485
        send_greeting(user, email_template_name)
        response = prepare_response(request, user, next, renew=True)
        transaction.commit()
        return response
486
    except SendMailError, e:
487
        message = e.message
488
489
        messages.add_message(request, messages.ERROR, message)
        transaction.rollback()
490
        return render_response(on_failure)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
491
492
493
    except BaseException, e:
        status = messages.ERROR
        message = _('Something went wrong.')
494
        messages.add_message(request, messages.ERROR, message)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
495
496
        logger.exception(e)
        transaction.rollback()
497
        return signup(request, on_failure)
498
499
500
501
502
503
504
505
506
507
508
509
510
511

def approval_terms(request, term_id=None, template_name='im/approval_terms.html', extra_context={}):
    term = None
    terms = None
    if not term_id:
        try:
            term = ApprovalTerms.objects.order_by('-id')[0]
        except IndexError:
            pass
    else:
        try:
             term = ApprovalTerms.objects.get(id=term_id)
        except ApprovalTermDoesNotExist, e:
            pass
512

513
    if not term:
514
        return HttpResponseRedirect(reverse('astakos.im.views.index'))
515
516
    f = open(term.location, 'r')
    terms = f.read()
517

518
519
520
    if request.method == 'POST':
        next = request.POST.get('next')
        if not next:
521
            next = reverse('astakos.im.views.index')
522
523
524
525
        form = SignApprovalTermsForm(request.POST, instance=request.user)
        if not form.is_valid():
            return render_response(template_name,
                           terms = terms,
526
                           approval_terms_form = form,
527
528
529
530
                           context_instance = get_context(request, extra_context))
        user = form.save()
        return HttpResponseRedirect(next)
    else:
531
        form = None
532
        if request.user.is_authenticated() and not request.user.signed_terms():
533
            form = SignApprovalTermsForm(instance=request.user)
534
535
        return render_response(template_name,
                               terms = terms,
536
                               approval_terms_form = form,
537
538
539
540
                               context_instance = get_context(request, extra_context))

@signed_terms_required
def change_password(request):
541
    return password_change(request, post_change_redirect=reverse('astakos.im.views.edit_profile'))
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590

@transaction.commit_manually
def change_email(request, activation_key=None,
                 email_template_name='registration/email_change_email.txt',
                 form_template_name='registration/email_change_form.html',
                 confirm_template_name='registration/email_change_done.html',
                 extra_context={}):
    if activation_key:
        try:
            user = EmailChange.objects.change_email(activation_key)
            if request.user.is_authenticated() and request.user == user:
                msg = _('Email changed successfully.')
                messages.add_message(request, messages.SUCCESS, msg)
                auth_logout(request)
                response = prepare_response(request, user)
                transaction.commit()
                return response
        except ValueError, e:
            messages.add_message(request, messages.ERROR, e)
        return render_response(confirm_template_name,
                               modified_user = user if 'user' in locals() else None,
                               context_instance = get_context(request,
                                                              extra_context))
    
    if not request.user.is_authenticated():
        path = quote(request.get_full_path())
        url = request.build_absolute_uri(reverse('astakos.im.views.index'))
        return HttpResponseRedirect(url + '?next=' + path)
    form = EmailChangeForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        try:
            ec = form.save(email_template_name, request)
        except SendMailError, e:
            status = messages.ERROR
            msg = e
            transaction.rollback()
        except IntegrityError, e:
            status = messages.ERROR
            msg = _('There is already a pending change email request.')
        else:
            status = messages.SUCCESS
            msg = _('Change email request has been registered succefully.\
                    You are going to receive a verification email in the new address.')
            transaction.commit()
        messages.add_message(request, status, msg)
    return render_response(form_template_name,
                           form = form,
                           context_instance = get_context(request,
                                                          extra_context))