Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (53.6 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.contrib import messages
45
from django.contrib.auth.decorators import login_required
46
from django.core.urlresolvers import reverse
47
from django.db import transaction
48
from django.db.utils import IntegrityError
49
from django.http import (HttpResponse, HttpResponseBadRequest,
50
                         HttpResponseForbidden, HttpResponseRedirect,
51
                         HttpResponseBadRequest, Http404)
52
from django.shortcuts import redirect
53
from django.template import RequestContext, loader as template_loader
54
from django.utils.http import urlencode
55
from django.utils.translation import ugettext as _
56
from django.views.generic.create_update import (
57
    create_object, delete_object, get_model_and_form_class
58
)
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
from astakos.im.activation_backends import get_backend, SimpleBackend
67
from astakos.im.models import (
68
    AstakosUser, ApprovalTerms, AstakosGroup,
69
    EmailChange, GroupKind, Membership,
70
    RESOURCE_SEPARATOR, AstakosUserAuthProvider,
71
    ProjectApplication
72
)
73
from astakos.im.util import get_context, prepare_response, get_query, restrict_next
74
from astakos.im.forms import (
75
    LoginForm, InvitationForm, ProfileForm,
76
    FeedbackForm, SignApprovalTermsForm,
77
    EmailChangeForm,
78
    AstakosGroupCreationForm, AstakosGroupSearchForm,
79
    AstakosGroupUpdateForm, AddGroupMembersForm,
80
    MembersSortForm, AstakosGroupSortForm,
81
    TimelineForm, PickResourceForm,
82
    AstakosGroupCreationSummaryForm,
83
    ProjectApplicationForm
84
)
85
from astakos.im.functions import (send_feedback, SendMailError,
86
                                  logout as auth_logout,
87
                                  activate as activate_func,
88
                                  send_activation as send_activation_func,
89
#                                   send_group_creation_notification,
90
                                  SendNotificationError)
91
from astakos.im.endpoints.qh import timeline_charge
92
from astakos.im.settings import (COOKIE_DOMAIN, LOGOUT_NEXT,
93
                                 LOGGING_LEVEL, PAGINATE_BY, RESOURCES_PRESENTATION_DATA, PAGINATE_BY_ALL)
94
#from astakos.im.tasks import request_billing
95
from astakos.im.api.callpoint import AstakosCallpoint
96
# from .generic_views import create_object
97

    
98
import astakos.im.messages as astakos_messages
99
from astakos.im import settings
100
from astakos.im import auth_providers
101

    
102
logger = logging.getLogger(__name__)
103

    
104
callpoint = AstakosCallpoint()
105

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

    
120
def requires_auth_provider(provider_id, **perms):
121
    """
122
    """
123
    def decorator(func, *args, **kwargs):
124
        @wraps(func)
125
        def wrapper(request, *args, **kwargs):
126
            provider = auth_providers.get_provider(provider_id)
127

    
128
            if not provider or not provider.is_active():
129
                raise PermissionDenied
130

    
131
            if provider:
132
                for pkey, value in perms.iteritems():
133
                    attr = 'is_available_for_%s' % pkey.lower()
134
                    if getattr(provider, attr)() != value:
135
                        raise PermissionDenied
136
            return func(request, *args)
137
        return wrapper
138
    return decorator
139

    
140

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

    
155

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

    
171

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

178
    **Arguments**
179

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

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

188
    ``extra_context``
189
        An dictionary of variables to add to the template context.
190

191
    **Template:**
192

193
    im/profile.html or im/login.html or ``template_name`` keyword argument.
194

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

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

    
207

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

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

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

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

225
    **Arguments**
226

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

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

234
    **Template:**
235

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

238
    **Settings:**
239

240
    The view expectes the following settings are defined:
241

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

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

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

    
286

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

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

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

300
    **Arguments**
301

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

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

309
    **Template:**
310

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

313
    **Settings:**
314

315
    The view expectes the following settings are defined:
316

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

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

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

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

    
366

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

373
    In case of GET request renders a form for entering the user information.
374
    In case of POST handles the signup.
375

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

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

384
    On unsuccessful creation, renders ``template_name`` with an error message.
385

386
    **Arguments**
387

388
    ``template_name``
389
        A custom template to render. This is optional;
390
        if not specified, this will default to ``im/signup.html``.
391

392
    ``on_success``
393
        A custom template to render in case of success. This is optional;
394
        if not specified, this will default to ``im/signup_complete.html``.
395

396
    ``extra_context``
397
        An dictionary of variables to add to the template context.
398

399
    **Template:**
400

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

    
408
    provider = get_query(request).get('provider', 'local')
409
    if not auth_providers.get_provider(provider).is_available_for_create():
410
        raise PermissionDenied
411

    
412
    id = get_query(request).get('id')
413
    try:
414
        instance = AstakosUser.objects.get(id=id) if id else None
415
    except AstakosUser.DoesNotExist:
416
        instance = None
417

    
418
    third_party_token = request.REQUEST.get('third_party_token', None)
419

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

    
435
                form.store_user(user, request)
436

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

    
478

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

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

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

491
    **Arguments**
492

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

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

500
    **Template:**
501

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

504
    **Settings:**
505

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

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

    
530

    
531
@require_http_methods(["GET"])
532
@signed_terms_required
533
def logout(request, template='registration/logged_out.html', extra_context=None):
534
    """
535
    Wraps `django.contrib.auth.logout`.
536
    """
537
    extra_context = extra_context or {}
538
    response = HttpResponse()
539
    if request.user.is_authenticated():
540
        email = request.user.email
541
        auth_logout(request)
542
    next = restrict_next(
543
        request.GET.get('next'),
544
        domain=COOKIE_DOMAIN
545
    )
546
    if next:
547
        response['Location'] = next
548
        response.status_code = 302
549
    elif LOGOUT_NEXT:
550
        response['Location'] = LOGOUT_NEXT
551
        response.status_code = 301
552
    else:
553
        messages.add_message(request, messages.SUCCESS, _(astakos_messages.LOGOUT_SUCCESS))
554
        context = get_context(request, extra_context)
555
        response.write(render_to_string(template, context_instance=context))
556
    return response
557

    
558

    
559
@require_http_methods(["GET", "POST"])
560
@transaction.commit_manually
561
def activate(request, greeting_email_template_name='im/welcome_email.txt',
562
             helpdesk_email_template_name='im/helpdesk_notification.txt'):
563
    """
564
    Activates the user identified by the ``auth`` request parameter, sends a welcome email
565
    and renews the user token.
566

567
    The view uses commit_manually decorator in order to ensure the user state will be updated
568
    only if the email will be send successfully.
569
    """
570
    token = request.GET.get('auth')
571
    next = request.GET.get('next')
572
    try:
573
        user = AstakosUser.objects.get(auth_token=token)
574
    except AstakosUser.DoesNotExist:
575
        return HttpResponseBadRequest(_(astakos_messages.ACCOUNT_UNKNOWN))
576

    
577
    if user.is_active:
578
        message = _(astakos_messages.ACCOUNT_ALREADY_ACTIVE)
579
        messages.error(request, message)
580
        return index(request)
581

    
582
    try:
583
        activate_func(user, greeting_email_template_name, helpdesk_email_template_name, verify_email=True)
584
        response = prepare_response(request, user, next, renew=True)
585
        transaction.commit()
586
        return response
587
    except SendMailError, e:
588
        message = e.message
589
        messages.add_message(request, messages.ERROR, message)
590
        transaction.rollback()
591
        return index(request)
592
    except BaseException, e:
593
        status = messages.ERROR
594
        message = _(astakos_messages.GENERIC_ERROR)
595
        messages.add_message(request, messages.ERROR, message)
596
        logger.exception(e)
597
        transaction.rollback()
598
        return index(request)
599

    
600

    
601
@require_http_methods(["GET", "POST"])
602
def approval_terms(request, term_id=None, template_name='im/approval_terms.html', extra_context=None):
603
    extra_context = extra_context or {}
604
    term = None
605
    terms = None
606
    if not term_id:
607
        try:
608
            term = ApprovalTerms.objects.order_by('-id')[0]
609
        except IndexError:
610
            pass
611
    else:
612
        try:
613
            term = ApprovalTerms.objects.get(id=term_id)
614
        except ApprovalTerms.DoesNotExist, e:
615
            pass
616

    
617
    if not term:
618
        messages.error(request, _(astakos_messages.NO_APPROVAL_TERMS))
619
        return HttpResponseRedirect(reverse('index'))
620
    f = open(term.location, 'r')
621
    terms = f.read()
622

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

    
647

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

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

    
701

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

    
704
    if settings.MODERATION_ENABLED:
705
        raise PermissionDenied
706

    
707
    extra_context = extra_context or {}
708
    try:
709
        u = AstakosUser.objects.get(id=user_id)
710
    except AstakosUser.DoesNotExist:
711
        messages.error(request, _(astakos_messages.ACCOUNT_UNKNOWN))
712
    else:
713
        try:
714
            send_activation_func(u)
715
            msg = _(astakos_messages.ACTIVATION_SENT)
716
            messages.success(request, msg)
717
        except SendMailError, e:
718
            messages.error(request, e)
719
    return render_response(
720
        template_name,
721
        login_form = LoginForm(request=request),
722
        context_instance = get_context(
723
            request,
724
            extra_context
725
        )
726
    )
727

    
728
class ResourcePresentation():
729

    
730
    def __init__(self, data):
731
        self.data = data
732

    
733
    def update_from_result(self, result):
734
        if result.is_success:
735
            for r in result.data:
736
                rname = '%s%s%s' % (r.get('service'), RESOURCE_SEPARATOR, r.get('name'))
737
                if not rname in self.data['resources']:
738
                    self.data['resources'][rname] = {}
739

    
740
                self.data['resources'][rname].update(r)
741
                self.data['resources'][rname]['id'] = rname
742
                group = r.get('group')
743
                if not group in self.data['groups']:
744
                    self.data['groups'][group] = {}
745

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

    
748
    def test(self, quota_dict):
749
        for k, v in quota_dict.iteritems():
750
            rname = k
751
            value = v
752
            if not rname in self.data['resources']:
753
                self.data['resources'][rname] = {}
754

    
755

    
756
            self.data['resources'][rname]['value'] = value
757

    
758

    
759
    def update_from_result_report(self, result):
760
        if result.is_success:
761
            for r in result.data:
762
                rname = r.get('name')
763
                if not rname in self.data['resources']:
764
                    self.data['resources'][rname] = {}
765

    
766
                self.data['resources'][rname].update(r)
767
                self.data['resources'][rname]['id'] = rname
768
                group = r.get('group')
769
                if not group in self.data['groups']:
770
                    self.data['groups'][group] = {}
771

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

    
774
    def get_group_resources(self, group):
775
        return dict(filter(lambda t: t[1].get('group') == group, self.data['resources'].iteritems()))
776

    
777
    def get_groups_resources(self):
778
        for g in self.data['groups']:
779
            yield g, self.get_group_resources(g)
780

    
781
    def get_quota(self, group_quotas):
782
        for r, v in group_quotas.iteritems():
783
            rname = str(r)
784
            quota = self.data['resources'].get(rname)
785
            quota['value'] = v
786
            yield quota
787

    
788

    
789
    def get_policies(self, policies_data):
790
        for policy in policies_data:
791
            rname = '%s%s%s' % (policy.get('service'), RESOURCE_SEPARATOR, policy.get('resource'))
792
            policy.update(self.data['resources'].get(rname))
793
            yield policy
794

    
795
    def __repr__(self):
796
        return self.data.__repr__()
797

    
798
    def __iter__(self, *args, **kwargs):
799
        return self.data.__iter__(*args, **kwargs)
800

    
801
    def __getitem__(self, *args, **kwargs):
802
        return self.data.__getitem__(*args, **kwargs)
803

    
804
    def get(self, *args, **kwargs):
805
        return self.data.get(*args, **kwargs)
806

    
807

    
808

    
809
@require_http_methods(["GET", "POST"])
810
@signed_terms_required
811
@login_required
812
def group_add(request, kind_name='default'):
813
    result = callpoint.list_resources()
814
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
815
    resource_catalog.update_from_result(result)
816

    
817
    if not result.is_success:
818
        messages.error(
819
            request,
820
            'Unable to retrieve system resources: %s' % result.reason
821
    )
822

    
823
    try:
824
        kind = GroupKind.objects.get(name=kind_name)
825
    except:
826
        return HttpResponseBadRequest(_(astakos_messages.GROUPKIND_UNKNOWN))
827

    
828

    
829

    
830
    post_save_redirect = '/im/group/%(id)s/'
831
    context_processors = None
832
    model, form_class = get_model_and_form_class(
833
        model=None,
834
        form_class=AstakosGroupCreationForm
835
    )
836

    
837
    if request.method == 'POST':
838
        form = form_class(request.POST, request.FILES)
839
        if form.is_valid():
840
            policies = form.policies()
841
            return render_response(
842
                template='im/astakosgroup_form_summary.html',
843
                context_instance=get_context(request),
844
                form=AstakosGroupCreationSummaryForm(form.cleaned_data),
845
                policies=resource_catalog.get_policies(policies)
846
            )
847
    else:
848
        now = datetime.now()
849
        data = {
850
            'kind': kind,
851
        }
852
        for group, resources in resource_catalog.get_groups_resources():
853
            data['is_selected_%s' % group] = False
854
            for resource in resources:
855
                data['%s_uplimit' % resource] = ''
856

    
857
        form = form_class(data)
858

    
859
    # Create the template, context, response
860
    template_name = "%s/%s_form.html" % (
861
        model._meta.app_label,
862
        model._meta.object_name.lower()
863
    )
864
    t = template_loader.get_template(template_name)
865
    c = RequestContext(request, {
866
        'form': form,
867
        'kind': kind,
868
        'resource_catalog':resource_catalog,
869
    }, context_processors)
870
    return HttpResponse(t.render(c))
871

    
872

    
873
#@require_hsttp_methods(["POST"])
874
@require_http_methods(["GET", "POST"])
875
@signed_terms_required
876
@login_required
877
@transaction.commit_manually
878
def group_add_complete(request):
879
    model = AstakosGroup
880
    form = AstakosGroupCreationSummaryForm(request.POST)
881
    if form.is_valid():
882
        d = form.cleaned_data
883
        d['owners'] = [request.user]
884
        result = callpoint.create_groups((d,)).next()
885
        if result.is_success:
886
            new_object = result.data[0]
887
            # send notification
888
            try:
889
                send_group_creation_notification(
890
                    template_name='im/group_creation_notification.txt',
891
                    dictionary={
892
                        'group': new_object,
893
                        'owner': request.user,
894
                        'policies': d.get('policies', [])
895
                    }
896
                )
897
            except SendNotificationError, e:
898
                messages.error(request, e, fail_silently=True)
899
                transaction.rollback()
900
            else:
901
                msg = _(astakos_messages.OBJECT_CREATED) %\
902
                    {"verbose_name": model._meta.verbose_name}
903
                message = _(astakos_messages.NOTIFICATION_SENT) % 'a project'
904
                messages.success(request, msg, fail_silently=True)
905
                transaction.commit()
906
        else:
907
            d = {"verbose_name": model._meta.verbose_name,
908
                 "reason":result.reason}
909
            msg = _(astakos_messages.OBJECT_CREATED_FAILED) % d
910
            messages.error(request, msg, fail_silently=True)
911
    return render_response(
912
        template='im/astakosgroup_form_summary.html',
913
        context_instance=get_context(request),
914
        form=form,
915
        policies=form.cleaned_data.get('policies')
916
    )
917

    
918

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

    
952
    # validate sorting
953
    sorting = 'groupname'
954
    sort_form = AstakosGroupSortForm(request.GET)
955
    if sort_form.is_valid():
956
        sorting = sort_form.cleaned_data.get('sorting')
957
    query = query+" ORDER BY %s ASC" %sorting
958
    
959
    q = AstakosGroup.objects.raw(query)
960
    
961
    # Create the template, context, response
962
    template_name = "%s/%s_list.html" % (
963
        q.model._meta.app_label,
964
        q.model._meta.object_name.lower()
965
    )
966
    extra_context = dict(
967
        is_search=False,
968
        q=q,
969
        sorting=sorting,
970
        page=request.GET.get('page', 1)
971
    )
972
    return render_response(template_name,
973
                           context_instance=get_context(request, extra_context)
974
    )
975

    
976

    
977
@require_http_methods(["GET", "POST"])
978
@signed_terms_required
979
@login_required
980
def group_detail(request, group_id):
981
    q = AstakosGroup.objects.select_related().filter(pk=group_id)
982
    q = q.extra(select={
983
        'is_member': """SELECT CASE WHEN EXISTS(
984
                            SELECT id FROM im_membership
985
                            WHERE group_id = im_astakosgroup.group_ptr_id
986
                            AND person_id = %s)
987
                        THEN 1 ELSE 0 END""" % request.user.id,
988
        'is_owner': """SELECT CASE WHEN EXISTS(
989
                        SELECT id FROM im_astakosuser_owner
990
                        WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
991
                        AND astakosuser_id = %s)
992
                        THEN 1 ELSE 0 END""" % request.user.id,
993
        'is_active_member': """SELECT CASE WHEN(
994
                        SELECT date_joined FROM im_membership
995
                        WHERE group_id = im_astakosgroup.group_ptr_id
996
                        AND person_id = %s) IS NULL
997
                        THEN 0 ELSE 1 END""" % request.user.id,
998
        'kindname': """SELECT name FROM im_groupkind
999
                       WHERE id = im_astakosgroup.kind_id"""})
1000

    
1001
    model = q.model
1002
    context_processors = None
1003
    mimetype = None
1004
    try:
1005
        obj = q.get()
1006
    except AstakosGroup.DoesNotExist:
1007
        raise Http404("No %s found matching the query" % (
1008
            model._meta.verbose_name))
1009

    
1010
    update_form = AstakosGroupUpdateForm(instance=obj)
1011
    addmembers_form = AddGroupMembersForm()
1012
    if request.method == 'POST':
1013
        update_data = {}
1014
        addmembers_data = {}
1015
        for k, v in request.POST.iteritems():
1016
            if k in update_form.fields:
1017
                update_data[k] = v
1018
            if k in addmembers_form.fields:
1019
                addmembers_data[k] = v
1020
        update_data = update_data or None
1021
        addmembers_data = addmembers_data or None
1022
        update_form = AstakosGroupUpdateForm(update_data, instance=obj)
1023
        addmembers_form = AddGroupMembersForm(addmembers_data)
1024
        if update_form.is_valid():
1025
            update_form.save()
1026
        if addmembers_form.is_valid():
1027
            try:
1028
                map(obj.approve_member, addmembers_form.valid_users)
1029
            except AssertionError:
1030
                msg = _(astakos_messages.GROUP_MAX_PARTICIPANT_NUMBER_REACHED)
1031
                messages.error(request, msg)
1032
            addmembers_form = AddGroupMembersForm()
1033

    
1034
    template_name = "%s/%s_detail.html" % (
1035
        model._meta.app_label, model._meta.object_name.lower())
1036
    t = template_loader.get_template(template_name)
1037
    c = RequestContext(request, {
1038
        'object': obj,
1039
    }, context_processors)
1040

    
1041
    # validate sorting
1042
    sorting = 'person__email'
1043
    form = MembersSortForm(request.GET)
1044
    if form.is_valid():
1045
        sorting = form.cleaned_data.get('sorting')
1046
    
1047
    result = callpoint.list_resources()
1048
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1049
    resource_catalog.update_from_result(result)
1050

    
1051

    
1052
    if not result.is_success:
1053
        messages.error(
1054
            request,
1055
            'Unable to retrieve system resources: %s' % result.reason
1056
    )
1057

    
1058
    extra_context = {'update_form': update_form,
1059
                     'addmembers_form': addmembers_form,
1060
                     'page': request.GET.get('page', 1),
1061
                     'sorting': sorting,
1062
                     'resource_catalog':resource_catalog,
1063
                     'quota':resource_catalog.get_quota(obj.quota)}
1064
    for key, value in extra_context.items():
1065
        if callable(value):
1066
            c[key] = value()
1067
        else:
1068
            c[key] = value
1069
    response = HttpResponse(t.render(c), mimetype=mimetype)
1070
    populate_xheaders(
1071
        request, response, model, getattr(obj, obj._meta.pk.name))
1072
    return response
1073

    
1074

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

    
1129
    else:
1130
        queryset = AstakosGroup.objects.none()
1131
    return object_list(
1132
        request,
1133
        queryset,
1134
        paginate_by=PAGINATE_BY_ALL,
1135
        page=request.GET.get('page') or 1,
1136
        template_name='im/astakosgroup_list.html',
1137
        extra_context=dict(form=form,
1138
                           is_search=True,
1139
                           q=q,
1140
                           sorting=sorting))
1141

    
1142

    
1143
@require_http_methods(["GET", "POST"])
1144
@signed_terms_required
1145
@login_required
1146
def group_all(request, extra_context=None, **kwargs):
1147
    q = AstakosGroup.objects.select_related()
1148
    q = q.filter(~Q(kind__name='default'))
1149
    q = q.filter(approval_date__isnull=False)
1150
    q = q.extra(select={
1151
                'groupname': "auth_group.name",
1152
                'kindname': "im_groupkind.name",
1153
                'approved_members_num': """
1154
                    SELECT COUNT(*) FROM im_membership
1155
                    WHERE group_id = im_astakosgroup.group_ptr_id
1156
                    AND date_joined IS NOT NULL""",
1157
                'membership_approval_date': """
1158
                    SELECT date_joined FROM im_membership
1159
                    WHERE group_id = im_astakosgroup.group_ptr_id
1160
                    AND person_id = %s""" % request.user.id,
1161
                'is_member': """
1162
                    SELECT CASE WHEN EXISTS(
1163
                    SELECT date_joined FROM im_membership
1164
                    WHERE group_id = im_astakosgroup.group_ptr_id
1165
                    AND person_id = %s)
1166
                    THEN 1 ELSE 0 END""" % request.user.id,
1167
                 'is_owner': """SELECT CASE WHEN EXISTS(
1168
                        SELECT id FROM im_astakosuser_owner
1169
                        WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
1170
                        AND astakosuser_id = %s)
1171
                        THEN 1 ELSE 0 END""" % request.user.id,   })
1172
    
1173
    # validate sorting
1174
    sorting = 'groupname'
1175
    sort_form = AstakosGroupSortForm(request.GET)
1176
    if sort_form.is_valid():
1177
        sorting = sort_form.cleaned_data.get('sorting')
1178
    q = q.order_by(sorting)
1179
    
1180
    return object_list(
1181
        request,
1182
        q,
1183
        paginate_by=PAGINATE_BY_ALL,
1184
        page=request.GET.get('page') or 1,
1185
        template_name='im/astakosgroup_list.html',
1186
        extra_context=dict(form=AstakosGroupSearchForm(),
1187
                           is_search=True,
1188
                           sorting=sorting))
1189

    
1190

    
1191
#@require_http_methods(["POST"])
1192
@require_http_methods(["POST", "GET"])
1193
@signed_terms_required
1194
@login_required
1195
def group_join(request, group_id):
1196
    m = Membership(group_id=group_id,
1197
                   person=request.user,
1198
                   date_requested=datetime.now())
1199
    try:
1200
        m.save()
1201
        post_save_redirect = reverse(
1202
            'group_detail',
1203
            kwargs=dict(group_id=group_id))
1204
        return HttpResponseRedirect(post_save_redirect)
1205
    except IntegrityError, e:
1206
        logger.exception(e)
1207
        msg = _(astakos_messages.GROUP_JOIN_FAILURE)
1208
        messages.error(request, msg)
1209
        return group_search(request)
1210

    
1211

    
1212
@require_http_methods(["POST"])
1213
@signed_terms_required
1214
@login_required
1215
def group_leave(request, group_id):
1216
    try:
1217
        m = Membership.objects.select_related().get(
1218
            group__id=group_id,
1219
            person=request.user)
1220
    except Membership.DoesNotExist:
1221
        return HttpResponseBadRequest(_(astakos_messages.NOT_MEMBER))
1222
    if request.user in m.group.owner.all():
1223
        return HttpResponseForbidden(_(astakos_messages.OWNER_CANNOT_LEAVE_GROUP))
1224
    return delete_object(
1225
        request,
1226
        model=Membership,
1227
        object_id=m.id,
1228
        template_name='im/astakosgroup_list.html',
1229
        post_delete_redirect=reverse(
1230
            'group_detail',
1231
            kwargs=dict(group_id=group_id)))
1232

    
1233

    
1234
def handle_membership(func):
1235
    @wraps(func)
1236
    def wrapper(request, group_id, user_id):
1237
        try:
1238
            m = Membership.objects.select_related().get(
1239
                group__id=group_id,
1240
                person__id=user_id)
1241
        except Membership.DoesNotExist:
1242
            return HttpResponseBadRequest(_(astakos_messages.NOT_MEMBER))
1243
        else:
1244
            if request.user not in m.group.owner.all():
1245
                return HttpResponseForbidden(_(astakos_messages.NOT_OWNER))
1246
            func(request, m)
1247
            return group_detail(request, group_id)
1248
    return wrapper
1249

    
1250

    
1251
#@require_http_methods(["POST"])
1252
@require_http_methods(["POST", "GET"])
1253
@signed_terms_required
1254
@login_required
1255
@handle_membership
1256
def approve_member(request, membership):
1257
    try:
1258
        membership.approve()
1259
        realname = membership.person.realname
1260
        msg = _(astakos_messages.MEMBER_JOINED_GROUP) % locals()
1261
        messages.success(request, msg)
1262
    except AssertionError:
1263
        msg = _(astakos_messages.GROUP_MAX_PARTICIPANT_NUMBER_REACHED)
1264
        messages.error(request, msg)
1265
    except BaseException, e:
1266
        logger.exception(e)
1267
        realname = membership.person.realname
1268
        msg = _(astakos_messages.GENERIC_ERROR)
1269
        messages.error(request, msg)
1270

    
1271

    
1272
@signed_terms_required
1273
@login_required
1274
@handle_membership
1275
def disapprove_member(request, membership):
1276
    try:
1277
        membership.disapprove()
1278
        realname = membership.person.realname
1279
        msg = astakos_messages.MEMBER_REMOVED % locals()
1280
        messages.success(request, msg)
1281
    except BaseException, e:
1282
        logger.exception(e)
1283
        msg = _(astakos_messages.GENERIC_ERROR)
1284
        messages.error(request, msg)
1285

    
1286

    
1287
#@require_http_methods(["GET"])
1288
@require_http_methods(["POST", "GET"])
1289
@signed_terms_required
1290
@login_required
1291
def resource_usage(request):
1292
    def with_class(entry):
1293
        entry['load_class'] = 'red'
1294
        max_value = float(entry['maxValue'])
1295
        curr_value = float(entry['currValue'])
1296
        entry['ratio_limited']= 0
1297
        if max_value > 0 :
1298
            entry['ratio'] = (curr_value / max_value) * 100
1299
        else:
1300
            entry['ratio'] = 0
1301
        if entry['ratio'] < 66:
1302
            entry['load_class'] = 'yellow'
1303
        if entry['ratio'] < 33:
1304
            entry['load_class'] = 'green'
1305
        if entry['ratio']<0:
1306
            entry['ratio'] = 0
1307
        if entry['ratio']>100:
1308
            entry['ratio_limited'] = 100
1309
        else:
1310
            entry['ratio_limited'] = entry['ratio']
1311
        
1312
        return entry
1313

    
1314
    def pluralize(entry):
1315
        entry['plural'] = engine.plural(entry.get('name'))
1316
        return entry
1317

    
1318
    result = callpoint.get_user_usage(request.user.id)
1319
    if result.is_success:
1320
        backenddata = map(with_class, result.data)
1321
        data = map(pluralize, result.data)
1322
    else:
1323
        data = None
1324
        messages.error(request, result.reason)
1325
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1326
    resource_catalog.update_from_result_report(result)
1327
    return render_response('im/resource_usage.html',
1328
                           data=data,
1329
                           context_instance=get_context(request),
1330
                           resource_catalog=resource_catalog,
1331
                           result=result)
1332

    
1333

    
1334
def group_create_list(request):
1335
    form = PickResourceForm()
1336
    return render_response(
1337
        template='im/astakosgroup_create_list.html',
1338
        context_instance=get_context(request),)
1339

    
1340

    
1341
##@require_http_methods(["GET"])
1342
#@require_http_methods(["POST", "GET"])
1343
#@signed_terms_required
1344
#@login_required
1345
#def billing(request):
1346
#
1347
#    today = datetime.today()
1348
#    month_last_day = calendar.monthrange(today.year, today.month)[1]
1349
#    start = request.POST.get('datefrom', None)
1350
#    if start:
1351
#        today = datetime.fromtimestamp(int(start))
1352
#        month_last_day = calendar.monthrange(today.year, today.month)[1]
1353
#
1354
#    start = datetime(today.year, today.month, 1).strftime("%s")
1355
#    end = datetime(today.year, today.month, month_last_day).strftime("%s")
1356
#    r = request_billing.apply(args=('pgerakios@grnet.gr',
1357
#                                    int(start) * 1000,
1358
#                                    int(end) * 1000))
1359
#    data = {}
1360
#
1361
#    try:
1362
#        status, data = r.result
1363
#        data = _clear_billing_data(data)
1364
#        if status != 200:
1365
#            messages.error(request, _(astakos_messages.BILLING_ERROR) % status)
1366
#    except:
1367
#        messages.error(request, r.result)
1368
#
1369
#    return render_response(
1370
#        template='im/billing.html',
1371
#        context_instance=get_context(request),
1372
#        data=data,
1373
#        zerodate=datetime(month=1, year=1970, day=1),
1374
#        today=today,
1375
#        start=int(start),
1376
#        month_last_day=month_last_day)
1377

    
1378

    
1379
#def _clear_billing_data(data):
1380
#
1381
#    # remove addcredits entries
1382
#    def isnotcredit(e):
1383
#        return e['serviceName'] != "addcredits"
1384
#
1385
#    # separate services
1386
#    def servicefilter(service_name):
1387
#        service = service_name
1388
#
1389
#        def fltr(e):
1390
#            return e['serviceName'] == service
1391
#        return fltr
1392
#
1393
#    data['bill_nocredits'] = filter(isnotcredit, data['bill'])
1394
#    data['bill_vmtime'] = filter(servicefilter('vmtime'), data['bill'])
1395
#    data['bill_diskspace'] = filter(servicefilter('diskspace'), data['bill'])
1396
#    data['bill_addcredits'] = filter(servicefilter('addcredits'), data['bill'])
1397
#
1398
#    return data
1399

    
1400

    
1401
# #@require_http_methods(["GET"])
1402
# @require_http_methods(["POST", "GET"])
1403
# @signed_terms_required
1404
# @login_required
1405
# def timeline(request):
1406
# #    data = {'entity':request.user.email}
1407
#     timeline_body = ()
1408
#     timeline_header = ()
1409
# #    form = TimelineForm(data)
1410
#     form = TimelineForm()
1411
#     if request.method == 'POST':
1412
#         data = request.POST
1413
#         form = TimelineForm(data)
1414
#         if form.is_valid():
1415
#             data = form.cleaned_data
1416
#             timeline_header = ('entity', 'resource',
1417
#                                'event name', 'event date',
1418
#                                'incremental cost', 'total cost')
1419
#             timeline_body = timeline_charge(
1420
#                 data['entity'], data['resource'],
1421
#                 data['start_date'], data['end_date'],
1422
#                 data['details'], data['operation'])
1423
# 
1424
#     return render_response(template='im/timeline.html',
1425
#                            context_instance=get_context(request),
1426
#                            form=form,
1427
#                            timeline_header=timeline_header,
1428
#                            timeline_body=timeline_body)
1429
#     return data
1430

    
1431

    
1432
# TODO: action only on POST and user should confirm the removal
1433
@require_http_methods(["GET", "POST"])
1434
@login_required
1435
@signed_terms_required
1436
def remove_auth_provider(request, pk):
1437
    try:
1438
        provider = request.user.auth_providers.get(pk=pk)
1439
    except AstakosUserAuthProvider.DoesNotExist:
1440
        raise Http404
1441

    
1442
    if provider.can_remove():
1443
        provider.delete()
1444
        return HttpResponseRedirect(reverse('edit_profile'))
1445
    else:
1446
        raise PermissionDenied
1447

    
1448

    
1449
def how_it_works(request):
1450
    return render_response(
1451
        template='im/how_it_works.html',
1452
        context_instance=get_context(request),)
1453

    
1454
@require_http_methods(["GET", "POST"])
1455
@signed_terms_required
1456
@login_required
1457
def project_add(request):
1458
    result = callpoint.list_resources()
1459
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1460
    resource_catalog.update_from_result(result)
1461

    
1462
    if not result.is_success:
1463
        messages.error(
1464
            request,
1465
            'Unable to retrieve system resources: %s' % result.reason
1466
    )
1467
    extra_context = {'resource_catalog':resource_catalog}
1468
    return create_object(request, template_name='im/projects/projectapplication_form.html',
1469
        extra_context=extra_context, post_save_redirect=request.path,
1470
        form_class=ProjectApplicationForm)
1471

    
1472
@require_http_methods(["GET"])
1473
@signed_terms_required
1474
@login_required
1475
def project_application_list(request):
1476
    queryset = ProjectApplication.objects.all()
1477
    return object_list(
1478
        request,
1479
        queryset,
1480
        paginate_by=PAGINATE_BY_ALL,
1481
        page=request.GET.get('page') or 1,
1482
        template_name='im/projects/projectapplication_list.html')
1483

    
1484

    
1485
@require_http_methods(["GET"])
1486
@signed_terms_required
1487
@login_required
1488
def project_list(request):
1489
    pass
1490

    
1491
@require_http_methods(["GET", "POST"])
1492
@signed_terms_required
1493
@login_required
1494
def project_detail(request, serial):
1495
    pass
1496

    
1497
@require_http_methods(["GET", "POST"])
1498
@signed_terms_required
1499
@login_required
1500
def project_search(request):
1501
    pass
1502

    
1503
@require_http_methods(["GET"])
1504
@signed_terms_required
1505
@login_required
1506
def project_all(request):
1507
    pass
1508

    
1509
@require_http_methods(["GET", "POST"])
1510
@signed_terms_required
1511
@login_required
1512
def project_join(request, serial):
1513
    pass
1514

    
1515
@require_http_methods(["GET", "POST"])
1516
@signed_terms_required
1517
@login_required
1518
def project_leave(request, serial):
1519
    pass
1520

    
1521
@require_http_methods(["POST"])
1522
@signed_terms_required
1523
@login_required
1524
def project_approve_member(request, serial, user_id):
1525
    pass
1526

    
1527
@require_http_methods(["POST"])
1528
@signed_terms_required
1529
@login_required
1530
def project_remove_member(request, serial, user_id):
1531
    pass
1532