views.py 22.1 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

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

64
65
logger = logging.getLogger(__name__)

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

81
82
83

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

96
97
98
99
100
101
102
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):
103
        if request.user.is_authenticated() and not request.user.signed_terms():
104
105
106
107
108
109
110
111
            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
112
def index(request, login_template_name='im/login.html', profile_template_name='im/profile.html', extra_context={}):
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
113
    """
114
    If there is logged on user renders the profile page otherwise renders login page.
115

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

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

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

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

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

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

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

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

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
148
149
150
    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.
151

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
152
153
    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.
154

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
284
    The user activation will be delegated to the backend specified by the ``backend`` keyword argument
285
286
287
    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
288
289
290
    
    Upon successful user creation if ``next`` url parameter is present the user is redirected there
    otherwise renders the same page with a success message.
291
    
292
    On unsuccessful creation, renders ``template_name`` with an error message.
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
293
294
295
    
    **Arguments**
    
296
297
    ``template_name``
        A custom template to render. This is optional;
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
298
        if not specified, this will default to ``im/signup.html``.
299
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
314
    if request.user.is_authenticated():
        return HttpResponseRedirect(reverse('astakos.im.views.index'))
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
338
339
340
341
                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
342
343
344
345
346
347
348
349
            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)
350
                logger.exception(e)
351
    return render_response(template_name,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
352
                           signup_form = form,
353
                           provider = provider,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
354
                           context_instance=get_context(request, extra_context))
355

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

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

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

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
367
    **Arguments**
368

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

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

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
376
    **Template:**
377

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

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
380
    **Settings:**
381

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

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

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

429
@transaction.commit_manually
430
def activate(request, email_template_name='im/welcome_email.txt', on_failure='im/signup.html'):
431
    """
432
433
    Activates the user identified by the ``auth`` request parameter, sends a welcome email
    and renews the user token.
434

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

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
497

498
    if not term:
499
        return HttpResponseRedirect(reverse('astakos.im.views.index'))
500
501
    f = open(term.location, 'r')
    terms = f.read()
502

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

@signed_terms_required
def change_password(request):
526
    return password_change(request, post_change_redirect=reverse('astakos.im.views.edit_profile'))
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
570
571
572
573
574
575

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