Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / views.py @ c063d1c3

History | View | Annotate | Download (52.5 kB)

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

    
34
import logging
35
import calendar
36
import inflect
37

    
38
engine = inflect.engine()
39

    
40
from urllib import quote
41
from functools import wraps
42
from datetime import datetime
43

    
44
from django.shortcuts import get_object_or_404
45
from django.contrib import messages
46
from django.contrib.auth.decorators import login_required
47
from django.core.urlresolvers import reverse
48
from django.db import transaction
49
from django.db.utils import IntegrityError
50
from django.http import (HttpResponse, HttpResponseBadRequest,
51
                         HttpResponseForbidden, HttpResponseRedirect,
52
                         HttpResponseBadRequest, Http404)
53
from django.shortcuts import redirect
54
from django.template import RequestContext, loader as template_loader
55
from django.utils.http import urlencode
56
from django.utils.translation import ugettext as _
57
from django.views.generic.create_update import (delete_object,
58
                                                get_model_and_form_class)
59
from django.views.generic.list_detail import object_list
60
from django.core.xheaders import populate_xheaders
61
from django.core.exceptions import ValidationError, PermissionDenied
62
from django.template.loader import render_to_string
63
from django.views.decorators.http import require_http_methods
64
from django.db.models import Q
65

    
66
import astakos.im.messages as astakos_messages
67

    
68
from astakos.im.activation_backends import get_backend, SimpleBackend
69
from astakos.im.models import (AstakosUser, ApprovalTerms, AstakosGroup,
70
                               EmailChange, GroupKind, Membership,
71
                               RESOURCE_SEPARATOR, AstakosUserAuthProvider,
72
                               PendingThirdPartyUser)
73
from astakos.im.util import get_context, prepare_response, get_query, restrict_next
74
from astakos.im.forms import (LoginForm, InvitationForm, ProfileForm,
75
                              FeedbackForm, SignApprovalTermsForm,
76
                              EmailChangeForm,
77
                              AstakosGroupCreationForm, AstakosGroupSearchForm,
78
                              AstakosGroupUpdateForm, AddGroupMembersForm,
79
                              MembersSortForm, AstakosGroupSortForm,
80
                              TimelineForm, PickResourceForm,
81
                              AstakosGroupCreationSummaryForm)
82
from astakos.im.functions import (send_feedback, SendMailError,
83
                                  logout as auth_logout,
84
                                  activate as activate_func,
85
                                  send_activation as send_activation_func,
86
                                  send_group_creation_notification,
87
                                  SendNotificationError)
88
from astakos.im.endpoints.qh import timeline_charge
89
from astakos.im.settings import (COOKIE_DOMAIN, LOGOUT_NEXT,
90
                                 LOGGING_LEVEL, PAGINATE_BY, RESOURCES_PRESENTATION_DATA, PAGINATE_BY_ALL)
91
#from astakos.im.tasks import request_billing
92
from astakos.im.api.callpoint import AstakosCallpoint
93

    
94
from astakos.im import settings
95
from astakos.im import auth_providers
96

    
97
logger = logging.getLogger(__name__)
98

    
99
callpoint = AstakosCallpoint()
100

    
101
def render_response(template, tab=None, status=200, context_instance=None, **kwargs):
102
    """
103
    Calls ``django.template.loader.render_to_string`` with an additional ``tab``
104
    keyword argument and returns an ``django.http.HttpResponse`` with the
105
    specified ``status``.
106
    """
107
    if tab is None:
108
        tab = template.partition('_')[0].partition('.html')[0]
109
    kwargs.setdefault('tab', tab)
110
    html = template_loader.render_to_string(
111
        template, kwargs, context_instance=context_instance)
112
    response = HttpResponse(html, status=status)
113
    return response
114

    
115
def requires_auth_provider(provider_id, **perms):
116
    """
117
    """
118
    def decorator(func, *args, **kwargs):
119
        @wraps(func)
120
        def wrapper(request, *args, **kwargs):
121
            provider = auth_providers.get_provider(provider_id)
122

    
123
            if not provider or not provider.is_active():
124
                raise PermissionDenied
125

    
126
            if provider:
127
                for pkey, value in perms.iteritems():
128
                    attr = 'is_available_for_%s' % pkey.lower()
129
                    if getattr(provider, attr)() != value:
130
                        msg = provider.get_message("NOT_ACTIVE_FOR_" + pkey.upper())
131
                        messages.error(request, msg)
132
                        return HttpResponseRedirect(reverse('login'))
133
            return func(request, *args)
134
        return wrapper
135
    return decorator
136

    
137

    
138
def requires_anonymous(func):
139
    """
140
    Decorator checkes whether the request.user is not Anonymous and in that case
141
    redirects to `logout`.
142
    """
143
    @wraps(func)
144
    def wrapper(request, *args):
145
        if not request.user.is_anonymous():
146
            next = urlencode({'next': request.build_absolute_uri()})
147
            logout_uri = reverse(logout) + '?' + next
148
            return HttpResponseRedirect(logout_uri)
149
        return func(request, *args)
150
    return wrapper
151

    
152

    
153
def signed_terms_required(func):
154
    """
155
    Decorator checkes whether the request.user is Anonymous and in that case
156
    redirects to `logout`.
157
    """
158
    @wraps(func)
159
    def wrapper(request, *args, **kwargs):
160
        if request.user.is_authenticated() and not request.user.signed_terms:
161
            params = urlencode({'next': request.build_absolute_uri(),
162
                                'show_form': ''})
163
            terms_uri = reverse('latest_terms') + '?' + params
164
            return HttpResponseRedirect(terms_uri)
165
        return func(request, *args, **kwargs)
166
    return wrapper
167

    
168

    
169
@require_http_methods(["GET", "POST"])
170
@signed_terms_required
171
def index(request, login_template_name='im/login.html', profile_template_name='im/profile.html', extra_context=None):
172
    """
173
    If there is logged on user renders the profile page otherwise renders login page.
174

175
    **Arguments**
176

177
    ``login_template_name``
178
        A custom login template to use. This is optional; if not specified,
179
        this will default to ``im/login.html``.
180

181
    ``profile_template_name``
182
        A custom profile template to use. This is optional; if not specified,
183
        this will default to ``im/profile.html``.
184

185
    ``extra_context``
186
        An dictionary of variables to add to the template context.
187

188
    **Template:**
189

190
    im/profile.html or im/login.html or ``template_name`` keyword argument.
191

192
    """
193
    extra_context = extra_context or {}
194
    template_name = login_template_name
195
    if request.user.is_authenticated():
196
        return HttpResponseRedirect(reverse('astakos.im.views.edit_profile'))
197

    
198
    third_party_token = request.GET.get('key', False)
199
    if third_party_token:
200
        messages.info(request, astakos_messages.AUTH_PROVIDER_LOGIN_TO_ADD)
201

    
202
    return render_response(
203
        template_name,
204
        login_form = LoginForm(request=request),
205
        context_instance = get_context(request, extra_context)
206
    )
207

    
208

    
209
@require_http_methods(["GET", "POST"])
210
@login_required
211
@signed_terms_required
212
@transaction.commit_manually
213
def invite(request, template_name='im/invitations.html', extra_context=None):
214
    """
215
    Allows a user to invite somebody else.
216

217
    In case of GET request renders a form for providing the invitee information.
218
    In case of POST checks whether the user has not run out of invitations and then
219
    sends an invitation email to singup to the service.
220

221
    The view uses commit_manually decorator in order to ensure the number of the
222
    user invitations is going to be updated only if the email has been successfully sent.
223

224
    If the user isn't logged in, redirects to settings.LOGIN_URL.
225

226
    **Arguments**
227

228
    ``template_name``
229
        A custom template to use. This is optional; if not specified,
230
        this will default to ``im/invitations.html``.
231

232
    ``extra_context``
233
        An dictionary of variables to add to the template context.
234

235
    **Template:**
236

237
    im/invitations.html or ``template_name`` keyword argument.
238

239
    **Settings:**
240

241
    The view expectes the following settings are defined:
242

243
    * LOGIN_URL: login uri
244
    """
245
    extra_context = extra_context or {}
246
    status = None
247
    message = None
248
    form = InvitationForm()
249

    
250
    inviter = request.user
251
    if request.method == 'POST':
252
        form = InvitationForm(request.POST)
253
        if inviter.invitations > 0:
254
            if form.is_valid():
255
                try:
256
                    email = form.cleaned_data.get('username')
257
                    realname = form.cleaned_data.get('realname')
258
                    inviter.invite(email, realname)
259
                    message = _(astakos_messages.INVITATION_SENT) % locals()
260
                    messages.success(request, message)
261
                except SendMailError, e:
262
                    message = e.message
263
                    messages.error(request, message)
264
                    transaction.rollback()
265
                except BaseException, e:
266
                    message = _(astakos_messages.GENERIC_ERROR)
267
                    messages.error(request, message)
268
                    logger.exception(e)
269
                    transaction.rollback()
270
                else:
271
                    transaction.commit()
272
        else:
273
            message = _(astakos_messages.MAX_INVITATION_NUMBER_REACHED)
274
            messages.error(request, message)
275

    
276
    sent = [{'email': inv.username,
277
             'realname': inv.realname,
278
             'is_consumed': inv.is_consumed}
279
            for inv in request.user.invitations_sent.all()]
280
    kwargs = {'inviter': inviter,
281
              'sent': sent}
282
    context = get_context(request, extra_context, **kwargs)
283
    return render_response(template_name,
284
                           invitation_form=form,
285
                           context_instance=context)
286

    
287

    
288
@require_http_methods(["GET", "POST"])
289
@login_required
290
@signed_terms_required
291
def edit_profile(request, template_name='im/profile.html', extra_context=None):
292
    """
293
    Allows a user to edit his/her profile.
294

295
    In case of GET request renders a form for displaying the user information.
296
    In case of POST updates the user informantion and redirects to ``next``
297
    url parameter if exists.
298

299
    If the user isn't logged in, redirects to settings.LOGIN_URL.
300

301
    **Arguments**
302

303
    ``template_name``
304
        A custom template to use. This is optional; if not specified,
305
        this will default to ``im/profile.html``.
306

307
    ``extra_context``
308
        An dictionary of variables to add to the template context.
309

310
    **Template:**
311

312
    im/profile.html or ``template_name`` keyword argument.
313

314
    **Settings:**
315

316
    The view expectes the following settings are defined:
317

318
    * LOGIN_URL: login uri
319
    """
320
    extra_context = extra_context or {}
321
    form = ProfileForm(
322
        instance=request.user,
323
        session_key=request.session.session_key
324
    )
325
    extra_context['next'] = request.GET.get('next')
326
    if request.method == 'POST':
327
        form = ProfileForm(
328
            request.POST,
329
            instance=request.user,
330
            session_key=request.session.session_key
331
        )
332
        if form.is_valid():
333
            try:
334
                prev_token = request.user.auth_token
335
                user = form.save()
336
                form = ProfileForm(
337
                    instance=user,
338
                    session_key=request.session.session_key
339
                )
340
                next = restrict_next(
341
                    request.POST.get('next'),
342
                    domain=COOKIE_DOMAIN
343
                )
344
                if next:
345
                    return redirect(next)
346
                msg = _(astakos_messages.PROFILE_UPDATED)
347
                messages.success(request, msg)
348
            except ValueError, ve:
349
                messages.success(request, ve)
350
    elif request.method == "GET":
351
        request.user.is_verified = True
352
        request.user.save()
353

    
354
    # existing providers
355
    user_providers = request.user.get_active_auth_providers()
356

    
357
    # providers that user can add
358
    user_available_providers = request.user.get_available_auth_providers()
359

    
360
    return render_response(template_name,
361
                           profile_form = form,
362
                           user_providers = user_providers,
363
                           user_available_providers = user_available_providers,
364
                           context_instance = get_context(request,
365
                                                          extra_context))
366

    
367

    
368
@transaction.commit_manually
369
@require_http_methods(["GET", "POST"])
370
def signup(request, template_name='im/signup.html',
371
           on_success='im/signup_complete.html', extra_context=None,
372
           on_success_redirect='/im/profile/',
373
           backend=None):
374
    """
375
    Allows a user to create a local account.
376

377
    In case of GET request renders a form for entering the user information.
378
    In case of POST handles the signup.
379

380
    The user activation will be delegated to the backend specified by the ``backend`` keyword argument
381
    if present, otherwise to the ``astakos.im.activation_backends.InvitationBackend``
382
    if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.activation_backends.SimpleBackend`` if not
383
    (see activation_backends);
384

385
    Upon successful user creation, if ``next`` url parameter is present the user is redirected there
386
    otherwise renders the same page with a success message.
387

388
    On unsuccessful creation, renders ``template_name`` with an error message.
389

390
    **Arguments**
391

392
    ``template_name``
393
        A custom template to render. This is optional;
394
        if not specified, this will default to ``im/signup.html``.
395

396
    ``on_success``
397
        A custom template to render in case of success. This is optional;
398
        if not specified, this will default to ``im/signup_complete.html``.
399

400
    ``extra_context``
401
        An dictionary of variables to add to the template context.
402

403
    **Template:**
404

405
    im/signup.html or ``template_name`` keyword argument.
406
    im/signup_complete.html or ``on_success`` keyword argument.
407
    """
408
    extra_context = extra_context or {}
409
    if request.user.is_authenticated():
410
        return HttpResponseRedirect(reverse('edit_profile'))
411

    
412
    provider = get_query(request).get('provider', 'local')
413
    if not auth_providers.get_provider(provider).is_available_for_create():
414
        raise PermissionDenied
415

    
416
    id = get_query(request).get('id')
417
    try:
418
        instance = AstakosUser.objects.get(id=id) if id else None
419
    except AstakosUser.DoesNotExist:
420
        instance = None
421

    
422
    third_party_token = request.REQUEST.get('third_party_token', None)
423
    if third_party_token:
424
        pending = get_object_or_404(PendingThirdPartyUser,
425
                                    token=third_party_token)
426
        provider = pending.provider
427
        instance = pending.get_user_instance()
428

    
429
    try:
430
        if not backend:
431
            backend = get_backend(request)
432
        form = backend.get_signup_form(provider, instance)
433
    except Exception, e:
434
        form = SimpleBackend(request).get_signup_form(provider)
435
        messages.error(request, e)
436

    
437
    if request.method == 'POST':
438
        if form.is_valid():
439
            user = form.save(commit=False)
440
            try:
441
                result = backend.handle_activation(user)
442
                status = messages.SUCCESS
443
                message = result.message
444

    
445
                form.store_user(user, request)
446

    
447
                if 'additional_email' in form.cleaned_data:
448
                    additional_email = form.cleaned_data['additional_email']
449
                    if additional_email != user.email:
450
                        user.additionalmail_set.create(email=additional_email)
451
                        msg = 'Additional email: %s saved for user %s.' % (
452
                            additional_email,
453
                            user.email
454
                        )
455
                        logger._log(LOGGING_LEVEL, msg, [])
456
                if user and user.is_active:
457
                    next = request.POST.get('next', '')
458
                    response = prepare_response(request, user, next=next)
459
                    transaction.commit()
460
                    return response
461
                messages.add_message(request, status, message)
462
                transaction.commit()
463
                return HttpResponseRedirect(on_success_redirect)
464

    
465
            except SendMailError, e:
466
                logger.exception(e)
467
                status = messages.ERROR
468
                message = e.message
469
                messages.error(request, message)
470
                transaction.rollback()
471
            except BaseException, e:
472
                logger.exception(e)
473
                message = _(astakos_messages.GENERIC_ERROR)
474
                messages.error(request, message)
475
                logger.exception(e)
476
                transaction.rollback()
477

    
478
    return render_response(template_name,
479
                           signup_form=form,
480
                           third_party_token=third_party_token,
481
                           provider=provider,
482
                           context_instance=get_context(request, extra_context))
483

    
484

    
485
@require_http_methods(["GET", "POST"])
486
@login_required
487
@signed_terms_required
488
def feedback(request, template_name='im/feedback.html', email_template_name='im/feedback_mail.txt', extra_context=None):
489
    """
490
    Allows a user to send feedback.
491

492
    In case of GET request renders a form for providing the feedback information.
493
    In case of POST sends an email to support team.
494

495
    If the user isn't logged in, redirects to settings.LOGIN_URL.
496

497
    **Arguments**
498

499
    ``template_name``
500
        A custom template to use. This is optional; if not specified,
501
        this will default to ``im/feedback.html``.
502

503
    ``extra_context``
504
        An dictionary of variables to add to the template context.
505

506
    **Template:**
507

508
    im/signup.html or ``template_name`` keyword argument.
509

510
    **Settings:**
511

512
    * LOGIN_URL: login uri
513
    """
514
    extra_context = extra_context or {}
515
    if request.method == 'GET':
516
        form = FeedbackForm()
517
    if request.method == 'POST':
518
        if not request.user:
519
            return HttpResponse('Unauthorized', status=401)
520

    
521
        form = FeedbackForm(request.POST)
522
        if form.is_valid():
523
            msg = form.cleaned_data['feedback_msg']
524
            data = form.cleaned_data['feedback_data']
525
            try:
526
                send_feedback(msg, data, request.user, email_template_name)
527
            except SendMailError, e:
528
                messages.error(request, message)
529
            else:
530
                message = _(astakos_messages.FEEDBACK_SENT)
531
                messages.success(request, message)
532
    return render_response(template_name,
533
                           feedback_form=form,
534
                           context_instance=get_context(request, extra_context))
535

    
536

    
537
@require_http_methods(["GET"])
538
@signed_terms_required
539
def logout(request, template='registration/logged_out.html', extra_context=None):
540
    """
541
    Wraps `django.contrib.auth.logout`.
542
    """
543
    extra_context = extra_context or {}
544
    response = HttpResponse()
545
    if request.user.is_authenticated():
546
        email = request.user.email
547
        auth_logout(request)
548
    else:
549
        response['Location'] = reverse('index')
550
        response.status_code = 301
551
        return response
552

    
553
    next = restrict_next(
554
        request.GET.get('next'),
555
        domain=COOKIE_DOMAIN
556
    )
557

    
558
    if next:
559
        response['Location'] = next
560
        response.status_code = 302
561
    elif LOGOUT_NEXT:
562
        response['Location'] = LOGOUT_NEXT
563
        response.status_code = 301
564
    else:
565
        messages.add_message(request, messages.SUCCESS, _(astakos_messages.LOGOUT_SUCCESS))
566
        response['Location'] = reverse('index')
567
        response.status_code = 301
568
    return response
569

    
570

    
571
@require_http_methods(["GET", "POST"])
572
@transaction.commit_manually
573
def activate(request, greeting_email_template_name='im/welcome_email.txt',
574
             helpdesk_email_template_name='im/helpdesk_notification.txt'):
575
    """
576
    Activates the user identified by the ``auth`` request parameter, sends a welcome email
577
    and renews the user token.
578

579
    The view uses commit_manually decorator in order to ensure the user state will be updated
580
    only if the email will be send successfully.
581
    """
582
    token = request.GET.get('auth')
583
    next = request.GET.get('next')
584
    try:
585
        user = AstakosUser.objects.get(auth_token=token)
586
    except AstakosUser.DoesNotExist:
587
        return HttpResponseBadRequest(_(astakos_messages.ACCOUNT_UNKNOWN))
588

    
589
    if user.is_active:
590
        message = _(astakos_messages.ACCOUNT_ALREADY_ACTIVE)
591
        messages.error(request, message)
592
        return index(request)
593

    
594
    try:
595
        activate_func(user, greeting_email_template_name, helpdesk_email_template_name, verify_email=True)
596
        response = prepare_response(request, user, next, renew=True)
597
        transaction.commit()
598
        return response
599
    except SendMailError, e:
600
        message = e.message
601
        messages.add_message(request, messages.ERROR, message)
602
        transaction.rollback()
603
        return index(request)
604
    except BaseException, e:
605
        status = messages.ERROR
606
        message = _(astakos_messages.GENERIC_ERROR)
607
        messages.add_message(request, messages.ERROR, message)
608
        logger.exception(e)
609
        transaction.rollback()
610
        return index(request)
611

    
612

    
613
@require_http_methods(["GET", "POST"])
614
def approval_terms(request, term_id=None, template_name='im/approval_terms.html', extra_context=None):
615
    extra_context = extra_context or {}
616
    term = None
617
    terms = None
618
    if not term_id:
619
        try:
620
            term = ApprovalTerms.objects.order_by('-id')[0]
621
        except IndexError:
622
            pass
623
    else:
624
        try:
625
            term = ApprovalTerms.objects.get(id=term_id)
626
        except ApprovalTerms.DoesNotExist, e:
627
            pass
628

    
629
    if not term:
630
        messages.error(request, _(astakos_messages.NO_APPROVAL_TERMS))
631
        return HttpResponseRedirect(reverse('index'))
632
    f = open(term.location, 'r')
633
    terms = f.read()
634

    
635
    if request.method == 'POST':
636
        next = restrict_next(
637
            request.POST.get('next'),
638
            domain=COOKIE_DOMAIN
639
        )
640
        if not next:
641
            next = reverse('index')
642
        form = SignApprovalTermsForm(request.POST, instance=request.user)
643
        if not form.is_valid():
644
            return render_response(template_name,
645
                                   terms=terms,
646
                                   approval_terms_form=form,
647
                                   context_instance=get_context(request, extra_context))
648
        user = form.save()
649
        return HttpResponseRedirect(next)
650
    else:
651
        form = None
652
        if request.user.is_authenticated() and not request.user.signed_terms:
653
            form = SignApprovalTermsForm(instance=request.user)
654
        return render_response(template_name,
655
                               terms=terms,
656
                               approval_terms_form=form,
657
                               context_instance=get_context(request, extra_context))
658

    
659

    
660
@require_http_methods(["GET", "POST"])
661
@login_required
662
@signed_terms_required
663
@transaction.commit_manually
664
def change_email(request, activation_key=None,
665
                 email_template_name='registration/email_change_email.txt',
666
                 form_template_name='registration/email_change_form.html',
667
                 confirm_template_name='registration/email_change_done.html',
668
                 extra_context=None):
669
    extra_context = extra_context or {}
670
    if activation_key:
671
        try:
672
            user = EmailChange.objects.change_email(activation_key)
673
            if request.user.is_authenticated() and request.user == user:
674
                msg = _(astakos_messages.EMAIL_CHANGED)
675
                messages.success(request, msg)
676
                auth_logout(request)
677
                response = prepare_response(request, user)
678
                transaction.commit()
679
                return response
680
        except ValueError, e:
681
            messages.error(request, e)
682
        return render_response(confirm_template_name,
683
                               modified_user=user if 'user' in locals(
684
                               ) else None,
685
                               context_instance=get_context(request,
686
                                                            extra_context))
687

    
688
    if not request.user.is_authenticated():
689
        path = quote(request.get_full_path())
690
        url = request.build_absolute_uri(reverse('index'))
691
        return HttpResponseRedirect(url + '?next=' + path)
692
    form = EmailChangeForm(request.POST or None)
693
    if request.method == 'POST' and form.is_valid():
694
        try:
695
            ec = form.save(email_template_name, request)
696
        except SendMailError, e:
697
            msg = e
698
            messages.error(request, msg)
699
            transaction.rollback()
700
        except IntegrityError, e:
701
            msg = _(astakos_messages.PENDING_EMAIL_CHANGE_REQUEST)
702
            messages.error(request, msg)
703
        else:
704
            msg = _(astakos_messages.EMAIL_CHANGE_REGISTERED)
705
            messages.success(request, msg)
706
            transaction.commit()
707
    return render_response(
708
        form_template_name,
709
        form=form,
710
        context_instance=get_context(request, extra_context)
711
    )
712

    
713

    
714
def send_activation(request, user_id, template_name='im/login.html', extra_context=None):
715

    
716
    if request.user.is_authenticated():
717
        messages.error(request, 'You are already signed in.')
718
        return HttpResponseRedirect(reverse('edit_profile'))
719

    
720
    if settings.MODERATION_ENABLED:
721
        raise PermissionDenied
722

    
723
    extra_context = extra_context or {}
724
    try:
725
        u = AstakosUser.objects.get(id=user_id)
726
    except AstakosUser.DoesNotExist:
727
        messages.error(request, _(astakos_messages.ACCOUNT_UNKNOWN))
728
    else:
729
        try:
730
            send_activation_func(u)
731
            msg = _(astakos_messages.ACTIVATION_SENT)
732
            messages.success(request, msg)
733
            return HttpResponseRedirect('/im/')
734

    
735
        except SendMailError, e:
736
            messages.error(request, e)
737
    return render_response(
738
        template_name,
739
        login_form = LoginForm(request=request),
740
        context_instance = get_context(
741
            request,
742
            extra_context
743
        )
744
    )
745

    
746
class ResourcePresentation():
747

    
748
    def __init__(self, data):
749
        self.data = data
750

    
751
    def update_from_result(self, result):
752
        if result.is_success:
753
            for r in result.data:
754
                rname = '%s%s%s' % (r.get('service'), RESOURCE_SEPARATOR, r.get('name'))
755
                if not rname in self.data['resources']:
756
                    self.data['resources'][rname] = {}
757

    
758
                self.data['resources'][rname].update(r)
759
                self.data['resources'][rname]['id'] = rname
760
                group = r.get('group')
761
                if not group in self.data['groups']:
762
                    self.data['groups'][group] = {}
763

    
764
                self.data['groups'][r.get('group')].update({'name': r.get('group')})
765

    
766
    def test(self, quota_dict):
767
        for k, v in quota_dict.iteritems():
768
            rname = k
769
            value = v
770
            if not rname in self.data['resources']:
771
                self.data['resources'][rname] = {}
772

    
773

    
774
            self.data['resources'][rname]['value'] = value
775

    
776

    
777
    def update_from_result_report(self, result):
778
        if result.is_success:
779
            for r in result.data:
780
                rname = r.get('name')
781
                if not rname in self.data['resources']:
782
                    self.data['resources'][rname] = {}
783

    
784
                self.data['resources'][rname].update(r)
785
                self.data['resources'][rname]['id'] = rname
786
                group = r.get('group')
787
                if not group in self.data['groups']:
788
                    self.data['groups'][group] = {}
789

    
790
                self.data['groups'][r.get('group')].update({'name': r.get('group')})
791

    
792
    def get_group_resources(self, group):
793
        return dict(filter(lambda t: t[1].get('group') == group, self.data['resources'].iteritems()))
794

    
795
    def get_groups_resources(self):
796
        for g in self.data['groups']:
797
            yield g, self.get_group_resources(g)
798

    
799
    def get_quota(self, group_quotas):
800
        for r, v in group_quotas.iteritems():
801
            rname = str(r)
802
            quota = self.data['resources'].get(rname)
803
            quota['value'] = v
804
            yield quota
805

    
806

    
807
    def get_policies(self, policies_data):
808
        for policy in policies_data:
809
            rname = '%s%s%s' % (policy.get('service'), RESOURCE_SEPARATOR, policy.get('resource'))
810
            policy.update(self.data['resources'].get(rname))
811
            yield policy
812

    
813
    def __repr__(self):
814
        return self.data.__repr__()
815

    
816
    def __iter__(self, *args, **kwargs):
817
        return self.data.__iter__(*args, **kwargs)
818

    
819
    def __getitem__(self, *args, **kwargs):
820
        return self.data.__getitem__(*args, **kwargs)
821

    
822
    def get(self, *args, **kwargs):
823
        return self.data.get(*args, **kwargs)
824

    
825

    
826

    
827
@require_http_methods(["GET", "POST"])
828
@signed_terms_required
829
@login_required
830
def group_add(request, kind_name='default'):
831

    
832
    result = callpoint.list_resources()
833
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
834
    resource_catalog.update_from_result(result)
835

    
836
    if not result.is_success:
837
        messages.error(
838
            request,
839
            'Unable to retrieve system resources: %s' % result.reason
840
    )
841

    
842
    try:
843
        kind = GroupKind.objects.get(name=kind_name)
844
    except:
845
        return HttpResponseBadRequest(_(astakos_messages.GROUPKIND_UNKNOWN))
846

    
847

    
848

    
849
    post_save_redirect = '/im/group/%(id)s/'
850
    context_processors = None
851
    model, form_class = get_model_and_form_class(
852
        model=None,
853
        form_class=AstakosGroupCreationForm
854
    )
855

    
856
    if request.method == 'POST':
857
        form = form_class(request.POST, request.FILES)
858
        if form.is_valid():
859
            policies = form.policies()
860
            return render_response(
861
                template='im/astakosgroup_form_summary.html',
862
                context_instance=get_context(request),
863
                form=AstakosGroupCreationSummaryForm(form.cleaned_data),
864
                policies=resource_catalog.get_policies(policies)
865
            )
866
    else:
867
        now = datetime.now()
868
        data = {
869
            'kind': kind,
870
        }
871
        for group, resources in resource_catalog.get_groups_resources():
872
            data['is_selected_%s' % group] = False
873
            for resource in resources:
874
                data['%s_uplimit' % resource] = ''
875

    
876
        form = form_class(data)
877

    
878
    # Create the template, context, response
879
    template_name = "%s/%s_form.html" % (
880
        model._meta.app_label,
881
        model._meta.object_name.lower()
882
    )
883
    t = template_loader.get_template(template_name)
884
    c = RequestContext(request, {
885
        'form': form,
886
        'kind': kind,
887
        'resource_catalog':resource_catalog,
888
    }, context_processors)
889
    return HttpResponse(t.render(c))
890

    
891

    
892
#@require_http_methods(["POST"])
893
@require_http_methods(["GET", "POST"])
894
@signed_terms_required
895
@login_required
896
def group_add_complete(request):
897
    model = AstakosGroup
898
    form = AstakosGroupCreationSummaryForm(request.POST)
899
    if form.is_valid():
900
        d = form.cleaned_data
901
        d['owners'] = [request.user]
902
        result = callpoint.create_groups((d,)).next()
903
        if result.is_success:
904
            new_object = result.data[0]
905
            msg = _(astakos_messages.OBJECT_CREATED) %\
906
                {"verbose_name": model._meta.verbose_name}
907
            messages.success(request, msg, fail_silently=True)
908

    
909
            # send notification
910
            try:
911
                send_group_creation_notification(
912
                    template_name='im/group_creation_notification.txt',
913
                    dictionary={
914
                        'group': new_object,
915
                        'owner': request.user,
916
                        'policies': d.get('policies', [])
917
                    }
918
                )
919
            except SendNotificationError, e:
920
                messages.error(request, e, fail_silently=True)
921
            post_save_redirect = '/im/group/%(id)s/'
922
            return HttpResponseRedirect(post_save_redirect % new_object)
923
        else:
924
            d = {"verbose_name": model._meta.verbose_name,
925
                 "reason":result.reason}
926
            msg = _(astakos_messages.OBJECT_CREATED_FAILED) % d
927
            messages.error(request, msg, fail_silently=True)
928
    return render_response(
929
        template='im/astakosgroup_form_summary.html',
930
        context_instance=get_context(request),
931
        form=form,
932
        policies=form.cleaned_data.get('policies')
933
    )
934

    
935

    
936
#@require_http_methods(["GET"])
937
@require_http_methods(["GET", "POST"])
938
@signed_terms_required
939
@login_required
940
def group_list(request):
941
    none = request.user.astakos_groups.none()
942
    query = """
943
        SELECT auth_group.id,
944
        auth_group.name AS groupname,
945
        im_groupkind.name AS kindname,
946
        im_astakosgroup.*,
947
        owner.email AS groupowner,
948
        (SELECT COUNT(*) FROM im_membership
949
            WHERE group_id = im_astakosgroup.group_ptr_id
950
            AND date_joined IS NOT NULL) AS approved_members_num,
951
        (SELECT CASE WHEN(
952
                    SELECT date_joined FROM im_membership
953
                    WHERE group_id = im_astakosgroup.group_ptr_id
954
                    AND person_id = %(id)s) IS NULL
955
                    THEN 0 ELSE 1 END) AS membership_status
956
        FROM im_astakosgroup
957
        INNER JOIN im_membership ON (
958
            im_astakosgroup.group_ptr_id = im_membership.group_id)
959
        INNER JOIN auth_group ON(im_astakosgroup.group_ptr_id = auth_group.id)
960
        INNER JOIN im_groupkind ON (im_astakosgroup.kind_id = im_groupkind.id)
961
        LEFT JOIN im_astakosuser_owner ON (
962
            im_astakosuser_owner.astakosgroup_id = im_astakosgroup.group_ptr_id)
963
        LEFT JOIN auth_user as owner ON (
964
            im_astakosuser_owner.astakosuser_id = owner.id)
965
        WHERE im_membership.person_id = %(id)s
966
        AND im_groupkind.name != 'default'
967
        """ % request.user.__dict__
968

    
969
    # validate sorting
970
    sorting = 'groupname'
971
    sort_form = AstakosGroupSortForm(request.GET)
972
    if sort_form.is_valid():
973
        sorting = sort_form.cleaned_data.get('sorting')
974
    query = query+" ORDER BY %s ASC" %sorting
975

    
976
    q = AstakosGroup.objects.raw(query)
977

    
978
    # Create the template, context, response
979
    template_name = "%s/%s_list.html" % (
980
        q.model._meta.app_label,
981
        q.model._meta.object_name.lower()
982
    )
983
    extra_context = dict(
984
        is_search=False,
985
        q=q,
986
        sorting=sorting,
987
        page=request.GET.get('page', 1)
988
    )
989
    return render_response(template_name,
990
                           context_instance=get_context(request, extra_context)
991
    )
992

    
993

    
994
@require_http_methods(["GET", "POST"])
995
@signed_terms_required
996
@login_required
997
def group_detail(request, group_id):
998
    q = AstakosGroup.objects.select_related().filter(pk=group_id)
999
    q = q.extra(select={
1000
        'is_member': """SELECT CASE WHEN EXISTS(
1001
                            SELECT id FROM im_membership
1002
                            WHERE group_id = im_astakosgroup.group_ptr_id
1003
                            AND person_id = %s)
1004
                        THEN 1 ELSE 0 END""" % request.user.id,
1005
        'is_owner': """SELECT CASE WHEN EXISTS(
1006
                        SELECT id FROM im_astakosuser_owner
1007
                        WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
1008
                        AND astakosuser_id = %s)
1009
                        THEN 1 ELSE 0 END""" % request.user.id,
1010
        'is_active_member': """SELECT CASE WHEN(
1011
                        SELECT date_joined FROM im_membership
1012
                        WHERE group_id = im_astakosgroup.group_ptr_id
1013
                        AND person_id = %s) IS NULL
1014
                        THEN 0 ELSE 1 END""" % request.user.id,
1015
        'kindname': """SELECT name FROM im_groupkind
1016
                       WHERE id = im_astakosgroup.kind_id"""})
1017

    
1018
    model = q.model
1019
    context_processors = None
1020
    mimetype = None
1021
    try:
1022
        obj = q.get()
1023
    except AstakosGroup.DoesNotExist:
1024
        raise Http404("No %s found matching the query" % (
1025
            model._meta.verbose_name))
1026

    
1027
    update_form = AstakosGroupUpdateForm(instance=obj)
1028
    addmembers_form = AddGroupMembersForm()
1029
    if request.method == 'POST':
1030
        update_data = {}
1031
        addmembers_data = {}
1032
        for k, v in request.POST.iteritems():
1033
            if k in update_form.fields:
1034
                update_data[k] = v
1035
            if k in addmembers_form.fields:
1036
                addmembers_data[k] = v
1037
        update_data = update_data or None
1038
        addmembers_data = addmembers_data or None
1039
        update_form = AstakosGroupUpdateForm(update_data, instance=obj)
1040
        addmembers_form = AddGroupMembersForm(addmembers_data)
1041
        if update_form.is_valid():
1042
            update_form.save()
1043
        if addmembers_form.is_valid():
1044
            try:
1045
                map(obj.approve_member, addmembers_form.valid_users)
1046
            except AssertionError:
1047
                msg = _(astakos_messages.GROUP_MAX_PARTICIPANT_NUMBER_REACHED)
1048
                messages.error(request, msg)
1049
            addmembers_form = AddGroupMembersForm()
1050

    
1051
    template_name = "%s/%s_detail.html" % (
1052
        model._meta.app_label, model._meta.object_name.lower())
1053
    t = template_loader.get_template(template_name)
1054
    c = RequestContext(request, {
1055
        'object': obj,
1056
    }, context_processors)
1057

    
1058
    # validate sorting
1059
    sorting = 'person__email'
1060
    form = MembersSortForm(request.GET)
1061
    if form.is_valid():
1062
        sorting = form.cleaned_data.get('sorting')
1063

    
1064
    result = callpoint.list_resources()
1065
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1066
    resource_catalog.update_from_result(result)
1067

    
1068

    
1069
    if not result.is_success:
1070
        messages.error(
1071
            request,
1072
            'Unable to retrieve system resources: %s' % result.reason
1073
    )
1074

    
1075
    extra_context = {'update_form': update_form,
1076
                     'addmembers_form': addmembers_form,
1077
                     'page': request.GET.get('page', 1),
1078
                     'sorting': sorting,
1079
                     'resource_catalog':resource_catalog,
1080
                     'quota':resource_catalog.get_quota(obj.quota)}
1081
    for key, value in extra_context.items():
1082
        if callable(value):
1083
            c[key] = value()
1084
        else:
1085
            c[key] = value
1086
    response = HttpResponse(t.render(c), mimetype=mimetype)
1087
    populate_xheaders(
1088
        request, response, model, getattr(obj, obj._meta.pk.name))
1089
    return response
1090

    
1091

    
1092
@require_http_methods(["GET", "POST"])
1093
@signed_terms_required
1094
@login_required
1095
def group_search(request, extra_context=None, **kwargs):
1096
    q = request.GET.get('q')
1097
    if request.method == 'GET':
1098
        form = AstakosGroupSearchForm({'q': q} if q else None)
1099
    else:
1100
        form = AstakosGroupSearchForm(get_query(request))
1101
        if form.is_valid():
1102
            q = form.cleaned_data['q'].strip()
1103

    
1104
    sorting = 'groupname'
1105
    if q:
1106
        queryset = AstakosGroup.objects.select_related()
1107
        queryset = queryset.filter(~Q(kind__name='default'))
1108
        queryset = queryset.filter(name__contains=q)
1109
        queryset = queryset.filter(approval_date__isnull=False)
1110
        queryset = queryset.extra(select={
1111
                                  'groupname': "auth_group.name",
1112
                                  'kindname': "im_groupkind.name",
1113
                                  'approved_members_num': """
1114
                    SELECT COUNT(*) FROM im_membership
1115
                    WHERE group_id = im_astakosgroup.group_ptr_id
1116
                    AND date_joined IS NOT NULL""",
1117
                                  'membership_approval_date': """
1118
                    SELECT date_joined FROM im_membership
1119
                    WHERE group_id = im_astakosgroup.group_ptr_id
1120
                    AND person_id = %s""" % request.user.id,
1121
                                  'is_member': """
1122
                    SELECT CASE WHEN EXISTS(
1123
                    SELECT date_joined FROM im_membership
1124
                    WHERE group_id = im_astakosgroup.group_ptr_id
1125
                    AND person_id = %s)
1126
                    THEN 1 ELSE 0 END""" % request.user.id,
1127
                                  'is_owner': """
1128
                    SELECT CASE WHEN EXISTS(
1129
                    SELECT id FROM im_astakosuser_owner
1130
                    WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
1131
                    AND astakosuser_id = %s)
1132
                    THEN 1 ELSE 0 END""" % request.user.id,
1133
                    'is_owner': """SELECT CASE WHEN EXISTS(
1134
                        SELECT id FROM im_astakosuser_owner
1135
                        WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
1136
                        AND astakosuser_id = %s)
1137
                        THEN 1 ELSE 0 END""" % request.user.id,
1138
                    })
1139

    
1140
        # validate sorting
1141
        sort_form = AstakosGroupSortForm(request.GET)
1142
        if sort_form.is_valid():
1143
            sorting = sort_form.cleaned_data.get('sorting')
1144
        queryset = queryset.order_by(sorting)
1145

    
1146
    else:
1147
        queryset = AstakosGroup.objects.none()
1148
    return object_list(
1149
        request,
1150
        queryset,
1151
        paginate_by=PAGINATE_BY_ALL,
1152
        page=request.GET.get('page') or 1,
1153
        template_name='im/astakosgroup_list.html',
1154
        extra_context=dict(form=form,
1155
                           is_search=True,
1156
                           q=q,
1157
                           sorting=sorting))
1158

    
1159

    
1160
@require_http_methods(["GET", "POST"])
1161
@signed_terms_required
1162
@login_required
1163
def group_all(request, extra_context=None, **kwargs):
1164
    q = AstakosGroup.objects.select_related()
1165
    q = q.filter(~Q(kind__name='default'))
1166
    q = q.filter(approval_date__isnull=False)
1167
    q = q.extra(select={
1168
                'groupname': "auth_group.name",
1169
                'kindname': "im_groupkind.name",
1170
                'approved_members_num': """
1171
                    SELECT COUNT(*) FROM im_membership
1172
                    WHERE group_id = im_astakosgroup.group_ptr_id
1173
                    AND date_joined IS NOT NULL""",
1174
                'membership_approval_date': """
1175
                    SELECT date_joined FROM im_membership
1176
                    WHERE group_id = im_astakosgroup.group_ptr_id
1177
                    AND person_id = %s""" % request.user.id,
1178
                'is_member': """
1179
                    SELECT CASE WHEN EXISTS(
1180
                    SELECT date_joined FROM im_membership
1181
                    WHERE group_id = im_astakosgroup.group_ptr_id
1182
                    AND person_id = %s)
1183
                    THEN 1 ELSE 0 END""" % request.user.id,
1184
                 'is_owner': """SELECT CASE WHEN EXISTS(
1185
                        SELECT id FROM im_astakosuser_owner
1186
                        WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
1187
                        AND astakosuser_id = %s)
1188
                        THEN 1 ELSE 0 END""" % request.user.id,   })
1189

    
1190
    # validate sorting
1191
    sorting = 'groupname'
1192
    sort_form = AstakosGroupSortForm(request.GET)
1193
    if sort_form.is_valid():
1194
        sorting = sort_form.cleaned_data.get('sorting')
1195
    q = q.order_by(sorting)
1196

    
1197
    return object_list(
1198
        request,
1199
        q,
1200
        paginate_by=PAGINATE_BY_ALL,
1201
        page=request.GET.get('page') or 1,
1202
        template_name='im/astakosgroup_list.html',
1203
        extra_context=dict(form=AstakosGroupSearchForm(),
1204
                           is_search=True,
1205
                           sorting=sorting))
1206

    
1207

    
1208
#@require_http_methods(["POST"])
1209
@require_http_methods(["POST", "GET"])
1210
@signed_terms_required
1211
@login_required
1212
def group_join(request, group_id):
1213
    m = Membership(group_id=group_id,
1214
                   person=request.user,
1215
                   date_requested=datetime.now())
1216
    try:
1217
        m.save()
1218
        post_save_redirect = reverse(
1219
            'group_detail',
1220
            kwargs=dict(group_id=group_id))
1221
        return HttpResponseRedirect(post_save_redirect)
1222
    except IntegrityError, e:
1223
        logger.exception(e)
1224
        msg = _(astakos_messages.GROUP_JOIN_FAILURE)
1225
        messages.error(request, msg)
1226
        return group_search(request)
1227

    
1228

    
1229
@require_http_methods(["POST"])
1230
@signed_terms_required
1231
@login_required
1232
def group_leave(request, group_id):
1233
    try:
1234
        m = Membership.objects.select_related().get(
1235
            group__id=group_id,
1236
            person=request.user)
1237
    except Membership.DoesNotExist:
1238
        return HttpResponseBadRequest(_(astakos_messages.NOT_MEMBER))
1239
    if request.user in m.group.owner.all():
1240
        return HttpResponseForbidden(_(astakos_messages.OWNER_CANNOT_LEAVE_GROUP))
1241
    return delete_object(
1242
        request,
1243
        model=Membership,
1244
        object_id=m.id,
1245
        template_name='im/astakosgroup_list.html',
1246
        post_delete_redirect=reverse(
1247
            'group_detail',
1248
            kwargs=dict(group_id=group_id)))
1249

    
1250

    
1251
def handle_membership(func):
1252
    @wraps(func)
1253
    def wrapper(request, group_id, user_id):
1254
        try:
1255
            m = Membership.objects.select_related().get(
1256
                group__id=group_id,
1257
                person__id=user_id)
1258
        except Membership.DoesNotExist:
1259
            return HttpResponseBadRequest(_(astakos_messages.NOT_MEMBER))
1260
        else:
1261
            if request.user not in m.group.owner.all():
1262
                return HttpResponseForbidden(_(astakos_messages.NOT_OWNER))
1263
            func(request, m)
1264
            return group_detail(request, group_id)
1265
    return wrapper
1266

    
1267

    
1268
#@require_http_methods(["POST"])
1269
@require_http_methods(["POST", "GET"])
1270
@signed_terms_required
1271
@login_required
1272
@handle_membership
1273
def approve_member(request, membership):
1274
    try:
1275
        membership.approve()
1276
        realname = membership.person.realname
1277
        msg = _(astakos_messages.MEMBER_JOINED_GROUP) % locals()
1278
        messages.success(request, msg)
1279
    except AssertionError:
1280
        msg = _(astakos_messages.GROUP_MAX_PARTICIPANT_NUMBER_REACHED)
1281
        messages.error(request, msg)
1282
    except BaseException, e:
1283
        logger.exception(e)
1284
        realname = membership.person.realname
1285
        msg = _(astakos_messages.GENERIC_ERROR)
1286
        messages.error(request, msg)
1287

    
1288

    
1289
@signed_terms_required
1290
@login_required
1291
@handle_membership
1292
def disapprove_member(request, membership):
1293
    try:
1294
        membership.disapprove()
1295
        realname = membership.person.realname
1296
        msg = astakos_messages.MEMBER_REMOVED % locals()
1297
        messages.success(request, msg)
1298
    except BaseException, e:
1299
        logger.exception(e)
1300
        msg = _(astakos_messages.GENERIC_ERROR)
1301
        messages.error(request, msg)
1302

    
1303

    
1304
#@require_http_methods(["GET"])
1305
@require_http_methods(["POST", "GET"])
1306
@signed_terms_required
1307
@login_required
1308
def resource_usage(request):
1309
    def with_class(entry):
1310
        entry['load_class'] = 'red'
1311
        max_value = float(entry['maxValue'])
1312
        curr_value = float(entry['currValue'])
1313
        entry['ratio_limited']= 0
1314
        if max_value > 0 :
1315
            entry['ratio'] = (curr_value / max_value) * 100
1316
        else:
1317
            entry['ratio'] = 0
1318
        if entry['ratio'] < 66:
1319
            entry['load_class'] = 'yellow'
1320
        if entry['ratio'] < 33:
1321
            entry['load_class'] = 'green'
1322
        if entry['ratio']<0:
1323
            entry['ratio'] = 0
1324
        if entry['ratio']>100:
1325
            entry['ratio_limited'] = 100
1326
        else:
1327
            entry['ratio_limited'] = entry['ratio']
1328

    
1329
        return entry
1330

    
1331
    def pluralize(entry):
1332
        entry['plural'] = engine.plural(entry.get('name'))
1333
        return entry
1334

    
1335
    result = callpoint.get_user_usage(request.user.id)
1336
    if result.is_success:
1337
        backenddata = map(with_class, result.data)
1338
        data = map(pluralize, result.data)
1339
    else:
1340
        data = None
1341
        messages.error(request, result.reason)
1342
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1343
    resource_catalog.update_from_result_report(result)
1344
    return render_response('im/resource_usage.html',
1345
                           data=data,
1346
                           context_instance=get_context(request),
1347
                           resource_catalog=resource_catalog,
1348
                           result=result)
1349

    
1350

    
1351
def group_create_list(request):
1352
    form = PickResourceForm()
1353
    return render_response(
1354
        template='im/astakosgroup_create_list.html',
1355
        context_instance=get_context(request),)
1356

    
1357

    
1358
##@require_http_methods(["GET"])
1359
#@require_http_methods(["POST", "GET"])
1360
#@signed_terms_required
1361
#@login_required
1362
#def billing(request):
1363
#
1364
#    today = datetime.today()
1365
#    month_last_day = calendar.monthrange(today.year, today.month)[1]
1366
#    start = request.POST.get('datefrom', None)
1367
#    if start:
1368
#        today = datetime.fromtimestamp(int(start))
1369
#        month_last_day = calendar.monthrange(today.year, today.month)[1]
1370
#
1371
#    start = datetime(today.year, today.month, 1).strftime("%s")
1372
#    end = datetime(today.year, today.month, month_last_day).strftime("%s")
1373
#    r = request_billing.apply(args=('pgerakios@grnet.gr',
1374
#                                    int(start) * 1000,
1375
#                                    int(end) * 1000))
1376
#    data = {}
1377
#
1378
#    try:
1379
#        status, data = r.result
1380
#        data = _clear_billing_data(data)
1381
#        if status != 200:
1382
#            messages.error(request, _(astakos_messages.BILLING_ERROR) % status)
1383
#    except:
1384
#        messages.error(request, r.result)
1385
#
1386
#    return render_response(
1387
#        template='im/billing.html',
1388
#        context_instance=get_context(request),
1389
#        data=data,
1390
#        zerodate=datetime(month=1, year=1970, day=1),
1391
#        today=today,
1392
#        start=int(start),
1393
#        month_last_day=month_last_day)
1394

    
1395

    
1396
#def _clear_billing_data(data):
1397
#
1398
#    # remove addcredits entries
1399
#    def isnotcredit(e):
1400
#        return e['serviceName'] != "addcredits"
1401
#
1402
#    # separate services
1403
#    def servicefilter(service_name):
1404
#        service = service_name
1405
#
1406
#        def fltr(e):
1407
#            return e['serviceName'] == service
1408
#        return fltr
1409
#
1410
#    data['bill_nocredits'] = filter(isnotcredit, data['bill'])
1411
#    data['bill_vmtime'] = filter(servicefilter('vmtime'), data['bill'])
1412
#    data['bill_diskspace'] = filter(servicefilter('diskspace'), data['bill'])
1413
#    data['bill_addcredits'] = filter(servicefilter('addcredits'), data['bill'])
1414
#
1415
#    return data
1416

    
1417

    
1418
#@require_http_methods(["GET"])
1419
@require_http_methods(["POST", "GET"])
1420
@signed_terms_required
1421
@login_required
1422
def timeline(request):
1423
#    data = {'entity':request.user.email}
1424
    timeline_body = ()
1425
    timeline_header = ()
1426
#    form = TimelineForm(data)
1427
    form = TimelineForm()
1428
    if request.method == 'POST':
1429
        data = request.POST
1430
        form = TimelineForm(data)
1431
        if form.is_valid():
1432
            data = form.cleaned_data
1433
            timeline_header = ('entity', 'resource',
1434
                               'event name', 'event date',
1435
                               'incremental cost', 'total cost')
1436
            timeline_body = timeline_charge(
1437
                data['entity'], data['resource'],
1438
                data['start_date'], data['end_date'],
1439
                data['details'], data['operation'])
1440

    
1441
    return render_response(template='im/timeline.html',
1442
                           context_instance=get_context(request),
1443
                           form=form,
1444
                           timeline_header=timeline_header,
1445
                           timeline_body=timeline_body)
1446
    return data
1447

    
1448

    
1449
# TODO: action only on POST and user should confirm the removal
1450
@require_http_methods(["GET", "POST"])
1451
@login_required
1452
@signed_terms_required
1453
def remove_auth_provider(request, pk):
1454
    try:
1455
        provider = request.user.auth_providers.get(pk=pk)
1456
    except AstakosUserAuthProvider.DoesNotExist:
1457
        raise Http404
1458

    
1459
    if provider.can_remove():
1460
        provider.delete()
1461
        return HttpResponseRedirect(reverse('edit_profile'))
1462
    else:
1463
        raise PermissionDenied
1464

    
1465

    
1466
def how_it_works(request):
1467
    return render_response(
1468
        template='im/how_it_works.html',
1469
        context_instance=get_context(request),)
1470