Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / views.py @ 279d6e51

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
                        #TODO: add session message
131
                        return HttpResponseRedirect(reverse('login'))
132
            return func(request, *args)
133
        return wrapper
134
    return decorator
135

    
136

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

    
151

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

    
167

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

174
    **Arguments**
175

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

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

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

187
    **Template:**
188

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

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

    
197
    return render_response(
198
        template_name,
199
        login_form = LoginForm(request=request),
200
        context_instance = get_context(request, extra_context)
201
    )
202

    
203

    
204
@require_http_methods(["GET", "POST"])
205
@login_required
206
@signed_terms_required
207
@transaction.commit_manually
208
def invite(request, template_name='im/invitations.html', extra_context=None):
209
    """
210
    Allows a user to invite somebody else.
211

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

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

219
    If the user isn't logged in, redirects to settings.LOGIN_URL.
220

221
    **Arguments**
222

223
    ``template_name``
224
        A custom template to use. This is optional; if not specified,
225
        this will default to ``im/invitations.html``.
226

227
    ``extra_context``
228
        An dictionary of variables to add to the template context.
229

230
    **Template:**
231

232
    im/invitations.html or ``template_name`` keyword argument.
233

234
    **Settings:**
235

236
    The view expectes the following settings are defined:
237

238
    * LOGIN_URL: login uri
239
    """
240
    extra_context = extra_context or {}
241
    status = None
242
    message = None
243
    form = InvitationForm()
244

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

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

    
282

    
283
@require_http_methods(["GET", "POST"])
284
@login_required
285
@signed_terms_required
286
def edit_profile(request, template_name='im/profile.html', extra_context=None):
287
    """
288
    Allows a user to edit his/her profile.
289

290
    In case of GET request renders a form for displaying the user information.
291
    In case of POST updates the user informantion and redirects to ``next``
292
    url parameter if exists.
293

294
    If the user isn't logged in, redirects to settings.LOGIN_URL.
295

296
    **Arguments**
297

298
    ``template_name``
299
        A custom template to use. This is optional; if not specified,
300
        this will default to ``im/profile.html``.
301

302
    ``extra_context``
303
        An dictionary of variables to add to the template context.
304

305
    **Template:**
306

307
    im/profile.html or ``template_name`` keyword argument.
308

309
    **Settings:**
310

311
    The view expectes the following settings are defined:
312

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

    
349
    # existing providers
350
    user_providers = request.user.get_active_auth_providers()
351

    
352
    # providers that user can add
353
    user_available_providers = request.user.get_available_auth_providers()
354

    
355
    return render_response(template_name,
356
                           profile_form = form,
357
                           user_providers = user_providers,
358
                           user_available_providers = user_available_providers,
359
                           context_instance = get_context(request,
360
                                                          extra_context))
361

    
362

    
363
@transaction.commit_manually
364
@require_http_methods(["GET", "POST"])
365
def signup(request, template_name='im/signup.html', on_success='im/signup_complete.html', extra_context=None, backend=None):
366
    """
367
    Allows a user to create a local account.
368

369
    In case of GET request renders a form for entering the user information.
370
    In case of POST handles the signup.
371

372
    The user activation will be delegated to the backend specified by the ``backend`` keyword argument
373
    if present, otherwise to the ``astakos.im.activation_backends.InvitationBackend``
374
    if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.activation_backends.SimpleBackend`` if not
375
    (see activation_backends);
376

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

380
    On unsuccessful creation, renders ``template_name`` with an error message.
381

382
    **Arguments**
383

384
    ``template_name``
385
        A custom template to render. This is optional;
386
        if not specified, this will default to ``im/signup.html``.
387

388
    ``on_success``
389
        A custom template to render in case of success. This is optional;
390
        if not specified, this will default to ``im/signup_complete.html``.
391

392
    ``extra_context``
393
        An dictionary of variables to add to the template context.
394

395
    **Template:**
396

397
    im/signup.html or ``template_name`` keyword argument.
398
    im/signup_complete.html or ``on_success`` keyword argument.
399
    """
400
    extra_context = extra_context or {}
401
    if request.user.is_authenticated():
402
        return HttpResponseRedirect(reverse('edit_profile'))
403

    
404
    provider = get_query(request).get('provider', 'local')
405
    if not auth_providers.get_provider(provider).is_available_for_create():
406
        raise PermissionDenied
407

    
408
    id = get_query(request).get('id')
409
    try:
410
        instance = AstakosUser.objects.get(id=id) if id else None
411
    except AstakosUser.DoesNotExist:
412
        instance = None
413

    
414
    third_party_token = request.REQUEST.get('third_party_token', None)
415
    if third_party_token:
416
        pending = get_object_or_404(PendingThirdPartyUser,
417
                                    token=third_party_token)
418
        provider = pending.provider
419
        instance = pending.get_user_instance()
420

    
421
    try:
422
        if not backend:
423
            backend = get_backend(request)
424
        form = backend.get_signup_form(provider, instance)
425
    except Exception, e:
426
        form = SimpleBackend(request).get_signup_form(provider)
427
        messages.error(request, e)
428
    if request.method == 'POST':
429
        if form.is_valid():
430
            user = form.save(commit=False)
431
            try:
432
                result = backend.handle_activation(user)
433
                status = messages.SUCCESS
434
                message = result.message
435

    
436
                form.store_user(user, request)
437

    
438
                if 'additional_email' in form.cleaned_data:
439
                    additional_email = form.cleaned_data['additional_email']
440
                    if additional_email != user.email:
441
                        user.additionalmail_set.create(email=additional_email)
442
                        msg = 'Additional email: %s saved for user %s.' % (
443
                            additional_email,
444
                            user.email
445
                        )
446
                        logger._log(LOGGING_LEVEL, msg, [])
447
                if user and user.is_active:
448
                    next = request.POST.get('next', '')
449
                    response = prepare_response(request, user, next=next)
450
                    transaction.commit()
451
                    return response
452
                messages.add_message(request, status, message)
453
                transaction.commit()
454
                return render_response(
455
                    on_success,
456
                    context_instance=get_context(
457
                        request,
458
                        extra_context
459
                    )
460
                )
461
            except SendMailError, e:
462
                logger.exception(e)
463
                status = messages.ERROR
464
                message = e.message
465
                messages.error(request, message)
466
                transaction.rollback()
467
            except BaseException, e:
468
                logger.exception(e)
469
                message = _(astakos_messages.GENERIC_ERROR)
470
                messages.error(request, message)
471
                logger.exception(e)
472
                transaction.rollback()
473
    return render_response(template_name,
474
                           signup_form=form,
475
                           third_party_token=third_party_token,
476
                           provider=provider,
477
                           context_instance=get_context(request, extra_context))
478

    
479

    
480
@require_http_methods(["GET", "POST"])
481
@login_required
482
@signed_terms_required
483
def feedback(request, template_name='im/feedback.html', email_template_name='im/feedback_mail.txt', extra_context=None):
484
    """
485
    Allows a user to send feedback.
486

487
    In case of GET request renders a form for providing the feedback information.
488
    In case of POST sends an email to support team.
489

490
    If the user isn't logged in, redirects to settings.LOGIN_URL.
491

492
    **Arguments**
493

494
    ``template_name``
495
        A custom template to use. This is optional; if not specified,
496
        this will default to ``im/feedback.html``.
497

498
    ``extra_context``
499
        An dictionary of variables to add to the template context.
500

501
    **Template:**
502

503
    im/signup.html or ``template_name`` keyword argument.
504

505
    **Settings:**
506

507
    * LOGIN_URL: login uri
508
    """
509
    extra_context = extra_context or {}
510
    if request.method == 'GET':
511
        form = FeedbackForm()
512
    if request.method == 'POST':
513
        if not request.user:
514
            return HttpResponse('Unauthorized', status=401)
515

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

    
531

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

    
548
    next = restrict_next(
549
        request.GET.get('next'),
550
        domain=COOKIE_DOMAIN
551
    )
552

    
553
    if next:
554
        response['Location'] = next
555
        response.status_code = 302
556
    elif LOGOUT_NEXT:
557
        response['Location'] = LOGOUT_NEXT
558
        response.status_code = 301
559
    else:
560
        messages.add_message(request, messages.SUCCESS, _(astakos_messages.LOGOUT_SUCCESS))
561
        response['Location'] = reverse('index')
562
        response.status_code = 301
563
    return response
564

    
565

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

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

    
584
    if user.is_active:
585
        message = _(astakos_messages.ACCOUNT_ALREADY_ACTIVE)
586
        messages.error(request, message)
587
        return index(request)
588

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

    
607

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

    
624
    if not term:
625
        messages.error(request, _(astakos_messages.NO_APPROVAL_TERMS))
626
        return HttpResponseRedirect(reverse('index'))
627
    f = open(term.location, 'r')
628
    terms = f.read()
629

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

    
654

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

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

    
708

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

    
711
    if settings.MODERATION_ENABLED:
712
        raise PermissionDenied
713

    
714
    extra_context = extra_context or {}
715
    try:
716
        u = AstakosUser.objects.get(id=user_id)
717
    except AstakosUser.DoesNotExist:
718
        messages.error(request, _(astakos_messages.ACCOUNT_UNKNOWN))
719
    else:
720
        try:
721
            send_activation_func(u)
722
            msg = _(astakos_messages.ACTIVATION_SENT)
723
            messages.success(request, msg)
724
        except SendMailError, e:
725
            messages.error(request, e)
726
    return render_response(
727
        template_name,
728
        login_form = LoginForm(request=request),
729
        context_instance = get_context(
730
            request,
731
            extra_context
732
        )
733
    )
734

    
735
class ResourcePresentation():
736

    
737
    def __init__(self, data):
738
        self.data = data
739

    
740
    def update_from_result(self, result):
741
        if result.is_success:
742
            for r in result.data:
743
                rname = '%s%s%s' % (r.get('service'), RESOURCE_SEPARATOR, r.get('name'))
744
                if not rname in self.data['resources']:
745
                    self.data['resources'][rname] = {}
746

    
747
                self.data['resources'][rname].update(r)
748
                self.data['resources'][rname]['id'] = rname
749
                group = r.get('group')
750
                if not group in self.data['groups']:
751
                    self.data['groups'][group] = {}
752

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

    
755
    def test(self, quota_dict):
756
        for k, v in quota_dict.iteritems():
757
            rname = k
758
            value = v
759
            if not rname in self.data['resources']:
760
                self.data['resources'][rname] = {}
761

    
762

    
763
            self.data['resources'][rname]['value'] = value
764

    
765

    
766
    def update_from_result_report(self, result):
767
        if result.is_success:
768
            for r in result.data:
769
                rname = r.get('name')
770
                if not rname in self.data['resources']:
771
                    self.data['resources'][rname] = {}
772

    
773
                self.data['resources'][rname].update(r)
774
                self.data['resources'][rname]['id'] = rname
775
                group = r.get('group')
776
                if not group in self.data['groups']:
777
                    self.data['groups'][group] = {}
778

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

    
781
    def get_group_resources(self, group):
782
        return dict(filter(lambda t: t[1].get('group') == group, self.data['resources'].iteritems()))
783

    
784
    def get_groups_resources(self):
785
        for g in self.data['groups']:
786
            yield g, self.get_group_resources(g)
787

    
788
    def get_quota(self, group_quotas):
789
        for r, v in group_quotas.iteritems():
790
            rname = str(r)
791
            quota = self.data['resources'].get(rname)
792
            quota['value'] = v
793
            yield quota
794

    
795

    
796
    def get_policies(self, policies_data):
797
        for policy in policies_data:
798
            rname = '%s%s%s' % (policy.get('service'), RESOURCE_SEPARATOR, policy.get('resource'))
799
            policy.update(self.data['resources'].get(rname))
800
            yield policy
801

    
802
    def __repr__(self):
803
        return self.data.__repr__()
804

    
805
    def __iter__(self, *args, **kwargs):
806
        return self.data.__iter__(*args, **kwargs)
807

    
808
    def __getitem__(self, *args, **kwargs):
809
        return self.data.__getitem__(*args, **kwargs)
810

    
811
    def get(self, *args, **kwargs):
812
        return self.data.get(*args, **kwargs)
813

    
814

    
815

    
816
@require_http_methods(["GET", "POST"])
817
@signed_terms_required
818
@login_required
819
def group_add(request, kind_name='default'):
820

    
821
    result = callpoint.list_resources()
822
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
823
    resource_catalog.update_from_result(result)
824

    
825
    if not result.is_success:
826
        messages.error(
827
            request,
828
            'Unable to retrieve system resources: %s' % result.reason
829
    )
830

    
831
    try:
832
        kind = GroupKind.objects.get(name=kind_name)
833
    except:
834
        return HttpResponseBadRequest(_(astakos_messages.GROUPKIND_UNKNOWN))
835

    
836

    
837

    
838
    post_save_redirect = '/im/group/%(id)s/'
839
    context_processors = None
840
    model, form_class = get_model_and_form_class(
841
        model=None,
842
        form_class=AstakosGroupCreationForm
843
    )
844

    
845
    if request.method == 'POST':
846
        form = form_class(request.POST, request.FILES)
847
        if form.is_valid():
848
            policies = form.policies()
849
            return render_response(
850
                template='im/astakosgroup_form_summary.html',
851
                context_instance=get_context(request),
852
                form=AstakosGroupCreationSummaryForm(form.cleaned_data),
853
                policies=resource_catalog.get_policies(policies)
854
            )
855
    else:
856
        now = datetime.now()
857
        data = {
858
            'kind': kind,
859
        }
860
        for group, resources in resource_catalog.get_groups_resources():
861
            data['is_selected_%s' % group] = False
862
            for resource in resources:
863
                data['%s_uplimit' % resource] = ''
864

    
865
        form = form_class(data)
866

    
867
    # Create the template, context, response
868
    template_name = "%s/%s_form.html" % (
869
        model._meta.app_label,
870
        model._meta.object_name.lower()
871
    )
872
    t = template_loader.get_template(template_name)
873
    c = RequestContext(request, {
874
        'form': form,
875
        'kind': kind,
876
        'resource_catalog':resource_catalog,
877
    }, context_processors)
878
    return HttpResponse(t.render(c))
879

    
880

    
881
#@require_http_methods(["POST"])
882
@require_http_methods(["GET", "POST"])
883
@signed_terms_required
884
@login_required
885
def group_add_complete(request):
886
    model = AstakosGroup
887
    form = AstakosGroupCreationSummaryForm(request.POST)
888
    if form.is_valid():
889
        d = form.cleaned_data
890
        d['owners'] = [request.user]
891
        result = callpoint.create_groups((d,)).next()
892
        if result.is_success:
893
            new_object = result.data[0]
894
            msg = _(astakos_messages.OBJECT_CREATED) %\
895
                {"verbose_name": model._meta.verbose_name}
896
            messages.success(request, msg, fail_silently=True)
897

    
898
            # send notification
899
            try:
900
                send_group_creation_notification(
901
                    template_name='im/group_creation_notification.txt',
902
                    dictionary={
903
                        'group': new_object,
904
                        'owner': request.user,
905
                        'policies': d.get('policies', [])
906
                    }
907
                )
908
            except SendNotificationError, e:
909
                messages.error(request, e, fail_silently=True)
910
            post_save_redirect = '/im/group/%(id)s/'
911
            return HttpResponseRedirect(post_save_redirect % new_object)
912
        else:
913
            d = {"verbose_name": model._meta.verbose_name,
914
                 "reason":result.reason}
915
            msg = _(astakos_messages.OBJECT_CREATED_FAILED) % d
916
            messages.error(request, msg, fail_silently=True)
917
    return render_response(
918
        template='im/astakosgroup_form_summary.html',
919
        context_instance=get_context(request),
920
        form=form,
921
        policies=form.cleaned_data.get('policies')
922
    )
923

    
924

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

    
958
    # validate sorting
959
    sorting = 'groupname'
960
    ordering = ''
961
    sort_form = AstakosGroupSortForm(request.GET)
962
    if sort_form.is_valid():
963
        sorting = sort_form.cleaned_data.get('sorting')
964
        ordering = request.GET.get('ordering', 'ASC' )
965
        
966
    query = query+" ORDER BY %s %s" %(sorting, ordering)
967

    
968
    q = AstakosGroup.objects.raw(query)
969

    
970
    # Create the template, context, response
971
    template_name = "%s/%s_list.html" % (
972
        q.model._meta.app_label,
973
        q.model._meta.object_name.lower()
974
    )
975
    
976
    extra_context = dict(
977
        is_search=False,
978
        q=q,
979
        sorting=sorting,
980
        page=request.GET.get('page', 1),
981
        ordering=ordering,
982
    )
983
    return render_response(template_name,
984
                           context_instance=get_context(request, extra_context)
985
    )
986

    
987

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

    
1012
    model = q.model
1013
    context_processors = None
1014
    mimetype = None
1015
    try:
1016
        obj = q.get()
1017
    except AstakosGroup.DoesNotExist:
1018
        raise Http404("No %s found matching the query" % (
1019
            model._meta.verbose_name))
1020

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

    
1045
    template_name = "%s/%s_detail.html" % (
1046
        model._meta.app_label, model._meta.object_name.lower())
1047
    t = template_loader.get_template(template_name)
1048
    c = RequestContext(request, {
1049
        'object': obj,
1050
    }, context_processors)
1051

    
1052
    # validate sorting
1053
    sorting = 'person__email'
1054
    form = MembersSortForm(request.GET)
1055
    if form.is_valid():
1056
        sorting = form.cleaned_data.get('sorting')
1057

    
1058
    result = callpoint.list_resources()
1059
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1060
    resource_catalog.update_from_result(result)
1061

    
1062

    
1063
    if not result.is_success:
1064
        messages.error(
1065
            request,
1066
            'Unable to retrieve system resources: %s' % result.reason
1067
    )
1068

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

    
1085

    
1086
@require_http_methods(["GET", "POST"])
1087
@signed_terms_required
1088
@login_required
1089
def group_search(request, extra_context=None, **kwargs):
1090
    q = request.GET.get('q')
1091
    if request.method == 'GET':
1092
        form = AstakosGroupSearchForm({'q': q} if q else None)
1093
    else:
1094
        form = AstakosGroupSearchForm(get_query(request))
1095
        if form.is_valid():
1096
            q = form.cleaned_data['q'].strip()
1097
            
1098
    sorting = 'groupname'
1099
    order_dict = {'ASC':'', 'DESC':'-'  }
1100
    ordering = ''
1101
    sort_order = 'groupname' 
1102
    if q:
1103
        queryset = AstakosGroup.objects.select_related()
1104
        queryset = queryset.filter(~Q(kind__name='default'))
1105
        queryset = queryset.filter(name__contains=q)
1106
        queryset = queryset.filter(approval_date__isnull=False)
1107
        queryset = queryset.extra(select={
1108
                                  'groupname': "auth_group.name",
1109
                                  'kindname': "im_groupkind.name",
1110
                                  'approved_members_num': """
1111
                    SELECT COUNT(*) FROM im_membership
1112
                    WHERE group_id = im_astakosgroup.group_ptr_id
1113
                    AND date_joined IS NOT NULL""",
1114
                                  'membership_approval_date': """
1115
                    SELECT date_joined FROM im_membership
1116
                    WHERE group_id = im_astakosgroup.group_ptr_id
1117
                    AND person_id = %s""" % request.user.id,
1118
                                  'is_member': """
1119
                    SELECT CASE WHEN EXISTS(
1120
                    SELECT date_joined FROM im_membership
1121
                    WHERE group_id = im_astakosgroup.group_ptr_id
1122
                    AND person_id = %s)
1123
                    THEN 1 ELSE 0 END""" % request.user.id,
1124
                                  'is_owner': """
1125
                    SELECT CASE WHEN EXISTS(
1126
                    SELECT id FROM im_astakosuser_owner
1127
                    WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
1128
                    AND astakosuser_id = %s)
1129
                    THEN 1 ELSE 0 END""" % request.user.id,
1130
                    'is_owner': """SELECT CASE WHEN EXISTS(
1131
                        SELECT id FROM im_astakosuser_owner
1132
                        WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
1133
                        AND astakosuser_id = %s)
1134
                        THEN 1 ELSE 0 END""" % request.user.id,
1135
                    })
1136

    
1137
        # validate sorting
1138
        
1139
        
1140
        sort_form = AstakosGroupSortForm(request.GET)
1141
        if sort_form.is_valid():
1142
            sorting = sort_form.cleaned_data.get('sorting')
1143
            ordering = request.GET.get('ordering','ASC')
1144
            sort_order = order_dict[ordering]+sorting
1145
        queryset = queryset.order_by(sort_order)
1146

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

    
1161

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

    
1192
    # validate sorting
1193
    sorting = 'groupname'
1194
    order_dict = {'ASC':'', 'DESC':'-'  }
1195
    ordering = ''
1196
    sort_order = 'groupname'
1197
    sort_form = AstakosGroupSortForm(request.GET)
1198
    
1199
    if sort_form.is_valid():
1200
        sorting = sort_form.cleaned_data.get('sorting')
1201
        ordering = request.GET.get('ordering','ASC')
1202
        sort_order = order_dict[ordering]+sorting
1203
    q = q.order_by(sort_order)
1204
    
1205
    return object_list(
1206
        request,
1207
        q,
1208
        paginate_by=PAGINATE_BY_ALL,
1209
        page=request.GET.get('page') or 1,
1210
        template_name='im/astakosgroup_list.html',
1211
        extra_context=dict(form=AstakosGroupSearchForm(),
1212
                           is_search=True,
1213
                           sorting=sorting,
1214
                           ordering=ordering))
1215

    
1216

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

    
1237

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

    
1259

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

    
1276

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

    
1297

    
1298
@signed_terms_required
1299
@login_required
1300
@handle_membership
1301
def disapprove_member(request, membership):
1302
    try:
1303
        membership.disapprove()
1304
        realname = membership.person.realname
1305
        msg = astakos_messages.MEMBER_REMOVED % locals()
1306
        messages.success(request, msg)
1307
    except BaseException, e:
1308
        logger.exception(e)
1309
        msg = _(astakos_messages.GENERIC_ERROR)
1310
        messages.error(request, msg)
1311

    
1312

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

    
1338
        return entry
1339

    
1340
    def pluralize(entry):
1341
        entry['plural'] = engine.plural(entry.get('name'))
1342
        return entry
1343

    
1344
    result = callpoint.get_user_usage(request.user.id)
1345
    if result.is_success:
1346
        backenddata = map(with_class, result.data)
1347
        data = map(pluralize, result.data)
1348
    else:
1349
        data = None
1350
        messages.error(request, result.reason)
1351
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1352
    resource_catalog.update_from_result_report(result)
1353
    return render_response('im/resource_usage.html',
1354
                           data=data,
1355
                           context_instance=get_context(request),
1356
                           resource_catalog=resource_catalog,
1357
                           result=result)
1358

    
1359

    
1360
def group_create_list(request):
1361
    form = PickResourceForm()
1362
    return render_response(
1363
        template='im/astakosgroup_create_list.html',
1364
        context_instance=get_context(request),)
1365

    
1366

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

    
1404

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

    
1426

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

    
1450
    return render_response(template='im/timeline.html',
1451
                           context_instance=get_context(request),
1452
                           form=form,
1453
                           timeline_header=timeline_header,
1454
                           timeline_body=timeline_body)
1455
    return data
1456

    
1457

    
1458
# TODO: action only on POST and user should confirm the removal
1459
@require_http_methods(["GET", "POST"])
1460
@login_required
1461
@signed_terms_required
1462
def remove_auth_provider(request, pk):
1463
    try:
1464
        provider = request.user.auth_providers.get(pk=pk)
1465
    except AstakosUserAuthProvider.DoesNotExist:
1466
        raise Http404
1467

    
1468
    if provider.can_remove():
1469
        provider.delete()
1470
        return HttpResponseRedirect(reverse('edit_profile'))
1471
    else:
1472
        raise PermissionDenied
1473

    
1474

    
1475
def how_it_works(request):
1476
    return render_response(
1477
        template='im/how_it_works.html',
1478
        context_instance=get_context(request),)
1479

    
1480