Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (52.7 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
        messages.error(request, _(astakos_messages.ACCOUNT_UNKNOWN))
588
        return HttpResponseRedirect(reverse('index'))
589

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

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

    
615

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

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

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

    
662

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

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

    
716

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

    
719
    if request.user.is_authenticated():
720
        messages.error(request, 'You are already signed in.')
721
        return HttpResponseRedirect(reverse('edit_profile'))
722

    
723
    if settings.MODERATION_ENABLED:
724
        raise PermissionDenied
725

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

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

    
749
class ResourcePresentation():
750

    
751
    def __init__(self, data):
752
        self.data = data
753

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

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

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

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

    
776

    
777
            self.data['resources'][rname]['value'] = value
778

    
779

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

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

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

    
795
    def get_group_resources(self, group):
796
        return dict(filter(lambda t: t[1].get('group') == group, self.data['resources'].iteritems()))
797

    
798
    def get_groups_resources(self):
799
        for g in self.data['groups']:
800
            yield g, self.get_group_resources(g)
801

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

    
809

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

    
816
    def __repr__(self):
817
        return self.data.__repr__()
818

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

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

    
825
    def get(self, *args, **kwargs):
826
        return self.data.get(*args, **kwargs)
827

    
828

    
829

    
830
@require_http_methods(["GET", "POST"])
831
@signed_terms_required
832
@login_required
833
def group_add(request, kind_name='default'):
834

    
835
    result = callpoint.list_resources()
836
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
837
    resource_catalog.update_from_result(result)
838

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

    
845
    try:
846
        kind = GroupKind.objects.get(name=kind_name)
847
    except:
848
        return HttpResponseBadRequest(_(astakos_messages.GROUPKIND_UNKNOWN))
849

    
850

    
851

    
852
    post_save_redirect = '/im/group/%(id)s/'
853
    context_processors = None
854
    model, form_class = get_model_and_form_class(
855
        model=None,
856
        form_class=AstakosGroupCreationForm
857
    )
858

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

    
879
        form = form_class(data)
880

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

    
894

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

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

    
938

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

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

    
979
    q = AstakosGroup.objects.raw(query)
980

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

    
996

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

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

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

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

    
1061
    # validate sorting
1062
    sorting = 'person__email'
1063
    form = MembersSortForm(request.GET)
1064
    if form.is_valid():
1065
        sorting = form.cleaned_data.get('sorting')
1066

    
1067
    result = callpoint.list_resources()
1068
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1069
    resource_catalog.update_from_result(result)
1070

    
1071

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

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

    
1094

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

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

    
1143
        # validate sorting
1144
        sort_form = AstakosGroupSortForm(request.GET)
1145
        if sort_form.is_valid():
1146
            sorting = sort_form.cleaned_data.get('sorting')
1147
        queryset = queryset.order_by(sorting)
1148

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

    
1162

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

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

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

    
1210

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

    
1231

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

    
1253

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

    
1270

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

    
1291

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

    
1306

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

    
1332
        return entry
1333

    
1334
    def pluralize(entry):
1335
        entry['plural'] = engine.plural(entry.get('name'))
1336
        return entry
1337

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

    
1353

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

    
1360

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

    
1398

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

    
1420

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

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

    
1451

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

    
1462
    if provider.can_remove():
1463
        provider.delete()
1464
        return HttpResponseRedirect(reverse('edit_profile'))
1465
    else:
1466
        raise PermissionDenied
1467

    
1468

    
1469
def how_it_works(request):
1470
    return render_response(
1471
        template='im/how_it_works.html',
1472
        context_instance=get_context(request),)
1473