views.py 21.8 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
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
50
from django.contrib.auth import logout as auth_logout
51
from django.utils.http import urlencode
52
from django.http import HttpResponseRedirect, HttpResponseBadRequest
53
from django.db.utils import IntegrityError
54
from django.contrib.auth.views import password_change
55
from django.core.exceptions import ValidationError
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
from astakos.im.settings import DEFAULT_CONTACT_EMAIL, DEFAULT_FROM_EMAIL, COOKIE_NAME, COOKIE_DOMAIN, IM_MODULES, SITENAME, LOGOUT_NEXT
Antony Chazapis's avatar
Antony Chazapis committed
63
from astakos.im.functions import invite as invite_func
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)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
269
    return render_response(template_name,
270
                           reset_cookie = reset_cookie,
271
                           profile_form = form,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
272
                           context_instance = get_context(request,
273
                                                          extra_context))
274

275
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
276
277
    """
    Allows a user to create a local account.
278

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
279
280
    In case of GET request renders a form for providing the user information.
    In case of POST handles the signup.
281

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
282
    The user activation will be delegated to the backend specified by the ``backend`` keyword argument
283
284
285
    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
286
287
288
    
    Upon successful user creation if ``next`` url parameter is present the user is redirected there
    otherwise renders the same page with a success message.
289
    
290
    On unsuccessful creation, renders ``template_name`` with an error message.
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
291
292
293
    
    **Arguments**
    
294
295
    ``template_name``
        A custom template to render. This is optional;
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
296
        if not specified, this will default to ``im/signup.html``.
297
298


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

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

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
306
307
    **Template:**
    
308
    im/signup.html or ``template_name`` keyword argument.
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
309
    im/signup_complete.html or ``on_success`` keyword argument. 
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
310
    """
311
312
    if request.user.is_authenticated():
        return HttpResponseRedirect(reverse('astakos.im.views.index'))
313
    
314
    provider = get_query(request).get('provider', 'local')
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
315
    try:
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
316
317
        if not backend:
            backend = get_backend(request)
318
        form = backend.get_signup_form(provider)
319
    except Exception, e:
320
        form = SimpleBackend(request).get_signup_form(provider)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
321
        messages.add_message(request, messages.ERROR, e)
322
323
    if request.method == 'POST':
        if form.is_valid():
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
324
            user = form.save(commit=False)
325
326
327
            try:
                result = backend.handle_activation(user)
                status = messages.SUCCESS
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
328
329
                message = result.message
                user.save()
330
331
332
333
334
335
                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
336
337
338
339
340
341
342
343
            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)
344
                logger.exception(e)
345
    return render_response(template_name,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
346
                           signup_form = form,
347
                           provider = provider,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
348
                           context_instance=get_context(request, extra_context))
349

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
350
@login_required
351
@signed_terms_required
352
def feedback(request, template_name='im/feedback.html', email_template_name='im/feedback_mail.txt', extra_context={}):
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
353
354
    """
    Allows a user to send feedback.
355

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

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

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
361
    **Arguments**
362

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
363
364
    ``template_name``
        A custom template to use. This is optional; if not specified,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
365
        this will default to ``im/feedback.html``.
366

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
367
368
    ``extra_context``
        An dictionary of variables to add to the template context.
369

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
370
    **Template:**
371

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

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
374
    **Settings:**
375

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
376
    * LOGIN_URL: login uri
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
377
    * ASTAKOS_DEFAULT_CONTACT_EMAIL: List of feedback recipients
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
378
    """
379
    if request.method == 'GET':
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
380
381
382
383
        form = FeedbackForm()
    if request.method == 'POST':
        if not request.user:
            return HttpResponse('Unauthorized', status=401)
384

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
385
386
        form = FeedbackForm(request.POST)
        if form.is_valid():
387
388
            msg = form.cleaned_data['feedback_msg'],
            data = form.cleaned_data['feedback_data']
389
            try:
390
391
392
393
394
                send_feedback(msg, data, request.user, email_template_name)
            except SendMailError, e:
                message = e.message
                status = messages.ERROR
            else:
395
396
397
                message = _('Feedback successfully sent')
                status = messages.SUCCESS
            messages.add_message(request, status, message)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
398
    return render_response(template_name,
399
                           feedback_form = form,
400
                           context_instance = get_context(request, extra_context))
401

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
402
def logout(request, template='registration/logged_out.html', extra_context={}):
403
    """
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
404
    Wraps `django.contrib.auth.logout` and delete the cookie.
405
    """
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
406
407
    auth_logout(request)
    response = HttpResponse()
408
    response.delete_cookie(COOKIE_NAME, path='/', domain=COOKIE_DOMAIN)
409
410
411
412
413
    next = request.GET.get('next')
    if next:
        response['Location'] = next
        response.status_code = 302
        return response
414
415
416
417
418
    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
419
420
421
    context = get_context(request, extra_context)
    response.write(render_to_string(template, context_instance=context))
    return response
422

423
@transaction.commit_manually
424
def activate(request, email_template_name='im/welcome_email.txt', on_failure='im/signup.html'):
425
    """
426
427
    Activates the user identified by the ``auth`` request parameter, sends a welcome email
    and renews the user token.
428

429
430
    The view uses commit_manually decorator in order to ensure the user state will be updated
    only if the email will be send successfully.
431
432
433
434
435
436
    """
    token = request.GET.get('auth')
    next = request.GET.get('next')
    try:
        user = AstakosUser.objects.get(auth_token=token)
    except AstakosUser.DoesNotExist:
437
        return HttpResponseBadRequest(_('No such user'))
438
439
    
    try:
440
        local_user = AstakosUser.objects.get(email=user.email, is_active=True)
441
442
443
    except AstakosUser.DoesNotExist:
        user.is_active = True
        user.email_verified = True
444
445
446
447
        try:
            user.save()
        except ValidationError, e:
            return HttpResponseBadRequest(e)
448
    else:
449
        # switch the existing account to shibboleth one
450
451
452
        local_user.provider = 'shibboleth'
        local_user.set_unusable_password()
        local_user.third_party_identifier = user.third_party_identifier
453
454
455
456
        try:
            local_user.save()
        except ValidationError, e:
            return HttpResponseBadRequest(e)
457
458
459
        user.delete()
        user = local_user
    
460
461
462
463
464
    try:
        send_greeting(user, email_template_name)
        response = prepare_response(request, user, next, renew=True)
        transaction.commit()
        return response
465
    except SendMailError, e:
466
        message = e.message
467
468
        messages.add_message(request, messages.ERROR, message)
        transaction.rollback()
469
        return render_response(on_failure)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
470
471
472
    except BaseException, e:
        status = messages.ERROR
        message = _('Something went wrong.')
473
        messages.add_message(request, messages.ERROR, message)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
474
475
        logger.exception(e)
        transaction.rollback()
476
        return signup(request, on_failure)
477
478
479
480
481
482
483
484
485
486
487
488
489
490

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
491

492
    if not term:
493
        return HttpResponseRedirect(reverse('astakos.im.views.index'))
494
495
    f = open(term.location, 'r')
    terms = f.read()
496

497
498
499
    if request.method == 'POST':
        next = request.POST.get('next')
        if not next:
500
            next = reverse('astakos.im.views.index')
501
502
503
504
        form = SignApprovalTermsForm(request.POST, instance=request.user)
        if not form.is_valid():
            return render_response(template_name,
                           terms = terms,
505
                           approval_terms_form = form,
506
507
508
509
                           context_instance = get_context(request, extra_context))
        user = form.save()
        return HttpResponseRedirect(next)
    else:
510
        form = None
511
        if request.user.is_authenticated() and not request.user.signed_terms():
512
            form = SignApprovalTermsForm(instance=request.user)
513
514
        return render_response(template_name,
                               terms = terms,
515
                               approval_terms_form = form,
516
517
518
519
                               context_instance = get_context(request, extra_context))

@signed_terms_required
def change_password(request):
520
    return password_change(request, post_change_redirect=reverse('astakos.im.views.edit_profile'))
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
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

@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))