Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (63.9 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 (
51
    HttpResponse, HttpResponseBadRequest,
52
    HttpResponseForbidden, HttpResponseRedirect,
53
    HttpResponseBadRequest, Http404)
54
from django.shortcuts import redirect
55
from django.template import RequestContext, loader as template_loader
56
from django.utils.http import urlencode
57
from django.utils.translation import ugettext as _
58
from django.views.generic.create_update import (
59
    create_object, update_object, delete_object, get_model_and_form_class)
60
from django.views.generic.list_detail import object_list, object_detail
61
from django.core.xheaders import populate_xheaders
62
from django.core.exceptions import ValidationError, PermissionDenied
63
from django.template.loader import render_to_string
64
from django.views.decorators.http import require_http_methods
65
from django.db.models import Q
66
from django.core.exceptions import PermissionDenied
67

    
68
import astakos.im.messages as astakos_messages
69

    
70
from astakos.im.activation_backends import get_backend, SimpleBackend
71
from astakos.im.models import (
72
    AstakosUser, ApprovalTerms,
73
#     AstakosGroup, Membership
74
    EmailChange, GroupKind,
75
    RESOURCE_SEPARATOR, AstakosUserAuthProvider,
76
    ProjectApplication, ProjectMembership, Project)
77
from astakos.im.util import (
78
    get_context, prepare_response, get_query, restrict_next)
79
from astakos.im.forms import (
80
    LoginForm, InvitationForm, ProfileForm,
81
    FeedbackForm, SignApprovalTermsForm,
82
    EmailChangeForm,
83
#     AstakosGroupCreationForm, AstakosGroupSearchForm,
84
#     AstakosGroupUpdateForm, AddGroupMembersForm,
85
#     MembersSortForm, AstakosGroupSortForm,
86
#     TimelineForm, PickResourceForm,
87
#     AstakosGroupCreationSummaryForm,
88
    ProjectApplicationForm, ProjectSortForm,
89
    AddProjectMembersForm, ProjectSearchForm,
90
    ProjectMembersSortForm)
91
from astakos.im.functions import (
92
    send_feedback, SendMailError,
93
    logout as auth_logout,
94
    activate as activate_func,
95
    invite,
96
    send_activation as send_activation_func,
97
#     send_group_creation_notification,
98
    SendNotificationError,
99
    accept_membership, reject_membership, remove_membership,
100
    leave_project, join_project)
101
# from astakos.im.endpoints.qh import timeline_charge
102
from astakos.im.settings import (
103
    COOKIE_DOMAIN, LOGOUT_NEXT,
104
    LOGGING_LEVEL, PAGINATE_BY,
105
    RESOURCES_PRESENTATION_DATA, PAGINATE_BY_ALL)
106
#from astakos.im.tasks import request_billing
107
from astakos.im.api.callpoint import AstakosCallpoint
108

    
109
from astakos.im import settings
110
from astakos.im import auth_providers
111

    
112
logger = logging.getLogger(__name__)
113

    
114
callpoint = AstakosCallpoint()
115

    
116
def render_response(template, tab=None, status=200, context_instance=None, **kwargs):
117
    """
118
    Calls ``django.template.loader.render_to_string`` with an additional ``tab``
119
    keyword argument and returns an ``django.http.HttpResponse`` with the
120
    specified ``status``.
121
    """
122
    if tab is None:
123
        tab = template.partition('_')[0].partition('.html')[0]
124
    kwargs.setdefault('tab', tab)
125
    html = template_loader.render_to_string(
126
        template, kwargs, context_instance=context_instance)
127
    response = HttpResponse(html, status=status)
128
    return response
129

    
130
def requires_auth_provider(provider_id, **perms):
131
    """
132
    """
133
    def decorator(func, *args, **kwargs):
134
        @wraps(func)
135
        def wrapper(request, *args, **kwargs):
136
            provider = auth_providers.get_provider(provider_id)
137

    
138
            if not provider or not provider.is_active():
139
                raise PermissionDenied
140

    
141
            if provider:
142
                for pkey, value in perms.iteritems():
143
                    attr = 'is_available_for_%s' % pkey.lower()
144
                    if getattr(provider, attr)() != value:
145
                        msg = provider.get_message("NOT_ACTIVE_FOR_" + pkey.upper())
146
                        messages.error(request, msg)
147
                        return HttpResponseRedirect(reverse('login'))
148
            return func(request, *args)
149
        return wrapper
150
    return decorator
151

    
152

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

    
167

    
168
def signed_terms_required(func):
169
    """
170
    Decorator checkes whether the request.user is Anonymous and in that case
171
    redirects to `logout`.
172
    """
173
    @wraps(func)
174
    def wrapper(request, *args, **kwargs):
175
        if request.user.is_authenticated() and not request.user.signed_terms:
176
            params = urlencode({'next': request.build_absolute_uri(),
177
                                'show_form': ''})
178
            terms_uri = reverse('latest_terms') + '?' + params
179
            return HttpResponseRedirect(terms_uri)
180
        return func(request, *args, **kwargs)
181
    return wrapper
182

    
183

    
184
@require_http_methods(["GET", "POST"])
185
@signed_terms_required
186
def index(request, login_template_name='im/login.html', profile_template_name='im/profile.html', extra_context=None):
187
    """
188
    If there is logged on user renders the profile page otherwise renders login page.
189

190
    **Arguments**
191

192
    ``login_template_name``
193
        A custom login template to use. This is optional; if not specified,
194
        this will default to ``im/login.html``.
195

196
    ``profile_template_name``
197
        A custom profile template to use. This is optional; if not specified,
198
        this will default to ``im/profile.html``.
199

200
    ``extra_context``
201
        An dictionary of variables to add to the template context.
202

203
    **Template:**
204

205
    im/profile.html or im/login.html or ``template_name`` keyword argument.
206

207
    """
208
    extra_context = extra_context or {}
209
    template_name = login_template_name
210
    if request.user.is_authenticated():
211
        return HttpResponseRedirect(reverse('astakos.im.views.edit_profile'))
212

    
213
    third_party_token = request.GET.get('key', False)
214
    if third_party_token:
215
        messages.info(request, astakos_messages.AUTH_PROVIDER_LOGIN_TO_ADD)
216

    
217
    return render_response(
218
        template_name,
219
        login_form = LoginForm(request=request),
220
        context_instance = get_context(request, extra_context)
221
    )
222

    
223

    
224
@require_http_methods(["GET", "POST"])
225
@login_required
226
@signed_terms_required
227
@transaction.commit_manually
228
def invite(request, template_name='im/invitations.html', extra_context=None):
229
    """
230
    Allows a user to invite somebody else.
231

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

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

239
    If the user isn't logged in, redirects to settings.LOGIN_URL.
240

241
    **Arguments**
242

243
    ``template_name``
244
        A custom template to use. This is optional; if not specified,
245
        this will default to ``im/invitations.html``.
246

247
    ``extra_context``
248
        An dictionary of variables to add to the template context.
249

250
    **Template:**
251

252
    im/invitations.html or ``template_name`` keyword argument.
253

254
    **Settings:**
255

256
    The view expectes the following settings are defined:
257

258
    * LOGIN_URL: login uri
259
    """
260
    extra_context = extra_context or {}
261
    status = None
262
    message = None
263
    form = InvitationForm()
264

    
265
    inviter = request.user
266
    if request.method == 'POST':
267
        form = InvitationForm(request.POST)
268
        if inviter.invitations > 0:
269
            if form.is_valid():
270
                try:
271
                    email = form.cleaned_data.get('username')
272
                    realname = form.cleaned_data.get('realname')
273
                    invite(inviter, email, realname)
274
                    message = _(astakos_messages.INVITATION_SENT) % locals()
275
                    messages.success(request, message)
276
                except SendMailError, e:
277
                    message = e.message
278
                    messages.error(request, message)
279
                    transaction.rollback()
280
                except BaseException, e:
281
                    message = _(astakos_messages.GENERIC_ERROR)
282
                    messages.error(request, message)
283
                    logger.exception(e)
284
                    transaction.rollback()
285
                else:
286
                    transaction.commit()
287
        else:
288
            message = _(astakos_messages.MAX_INVITATION_NUMBER_REACHED)
289
            messages.error(request, message)
290

    
291
    sent = [{'email': inv.username,
292
             'realname': inv.realname,
293
             'is_consumed': inv.is_consumed}
294
            for inv in request.user.invitations_sent.all()]
295
    kwargs = {'inviter': inviter,
296
              'sent': sent}
297
    context = get_context(request, extra_context, **kwargs)
298
    return render_response(template_name,
299
                           invitation_form=form,
300
                           context_instance=context)
301

    
302

    
303
@require_http_methods(["GET", "POST"])
304
@login_required
305
@signed_terms_required
306
def edit_profile(request, template_name='im/profile.html', extra_context=None):
307
    """
308
    Allows a user to edit his/her profile.
309

310
    In case of GET request renders a form for displaying the user information.
311
    In case of POST updates the user informantion and redirects to ``next``
312
    url parameter if exists.
313

314
    If the user isn't logged in, redirects to settings.LOGIN_URL.
315

316
    **Arguments**
317

318
    ``template_name``
319
        A custom template to use. This is optional; if not specified,
320
        this will default to ``im/profile.html``.
321

322
    ``extra_context``
323
        An dictionary of variables to add to the template context.
324

325
    **Template:**
326

327
    im/profile.html or ``template_name`` keyword argument.
328

329
    **Settings:**
330

331
    The view expectes the following settings are defined:
332

333
    * LOGIN_URL: login uri
334
    """
335
    extra_context = extra_context or {}
336
    form = ProfileForm(
337
        instance=request.user,
338
        session_key=request.session.session_key
339
    )
340
    extra_context['next'] = request.GET.get('next')
341
    if request.method == 'POST':
342
        form = ProfileForm(
343
            request.POST,
344
            instance=request.user,
345
            session_key=request.session.session_key
346
        )
347
        if form.is_valid():
348
            try:
349
                prev_token = request.user.auth_token
350
                user = form.save()
351
                form = ProfileForm(
352
                    instance=user,
353
                    session_key=request.session.session_key
354
                )
355
                next = restrict_next(
356
                    request.POST.get('next'),
357
                    domain=COOKIE_DOMAIN
358
                )
359
                if next:
360
                    return redirect(next)
361
                msg = _(astakos_messages.PROFILE_UPDATED)
362
                messages.success(request, msg)
363
            except ValueError, ve:
364
                messages.success(request, ve)
365
    elif request.method == "GET":
366
        request.user.is_verified = True
367
        request.user.save()
368

    
369
    # existing providers
370
    user_providers = request.user.get_active_auth_providers()
371

    
372
    # providers that user can add
373
    user_available_providers = request.user.get_available_auth_providers()
374

    
375
    return render_response(template_name,
376
                           profile_form = form,
377
                           user_providers = user_providers,
378
                           user_available_providers = user_available_providers,
379
                           context_instance = get_context(request,
380
                                                          extra_context))
381

    
382

    
383
@transaction.commit_manually
384
@require_http_methods(["GET", "POST"])
385
def signup(request, template_name='im/signup.html',
386
           on_success='im/signup_complete.html', extra_context=None,
387
           on_success_redirect='/im/profile/',
388
           backend=None):
389
    """
390
    Allows a user to create a local account.
391

392
    In case of GET request renders a form for entering the user information.
393
    In case of POST handles the signup.
394

395
    The user activation will be delegated to the backend specified by the ``backend`` keyword argument
396
    if present, otherwise to the ``astakos.im.activation_backends.InvitationBackend``
397
    if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.activation_backends.SimpleBackend`` if not
398
    (see activation_backends);
399

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

403
    On unsuccessful creation, renders ``template_name`` with an error message.
404

405
    **Arguments**
406

407
    ``template_name``
408
        A custom template to render. This is optional;
409
        if not specified, this will default to ``im/signup.html``.
410

411
    ``on_success``
412
        A custom template to render in case of success. This is optional;
413
        if not specified, this will default to ``im/signup_complete.html``.
414

415
    ``extra_context``
416
        An dictionary of variables to add to the template context.
417

418
    **Template:**
419

420
    im/signup.html or ``template_name`` keyword argument.
421
    im/signup_complete.html or ``on_success`` keyword argument.
422
    """
423
    extra_context = extra_context or {}
424
    if request.user.is_authenticated():
425
        return HttpResponseRedirect(reverse('edit_profile'))
426

    
427
    provider = get_query(request).get('provider', 'local')
428
    if not auth_providers.get_provider(provider).is_available_for_create():
429
        raise PermissionDenied
430

    
431
    id = get_query(request).get('id')
432
    try:
433
        instance = AstakosUser.objects.get(id=id) if id else None
434
    except AstakosUser.DoesNotExist:
435
        instance = None
436

    
437
    third_party_token = request.REQUEST.get('third_party_token', None)
438
    if third_party_token:
439
        pending = get_object_or_404(PendingThirdPartyUser,
440
                                    token=third_party_token)
441
        provider = pending.provider
442
        instance = pending.get_user_instance()
443

    
444
    try:
445
        if not backend:
446
            backend = get_backend(request)
447
        form = backend.get_signup_form(provider, instance)
448
    except Exception, e:
449
        form = SimpleBackend(request).get_signup_form(provider)
450
        messages.error(request, e)
451

    
452
    if request.method == 'POST':
453
        if form.is_valid():
454
            user = form.save(commit=False)
455
            try:
456
                result = backend.handle_activation(user)
457
                status = messages.SUCCESS
458
                message = result.message
459

    
460
                form.store_user(user, request)
461

    
462
                if 'additional_email' in form.cleaned_data:
463
                    additional_email = form.cleaned_data['additional_email']
464
                    if additional_email != user.email:
465
                        user.additionalmail_set.create(email=additional_email)
466
                        msg = 'Additional email: %s saved for user %s.' % (
467
                            additional_email,
468
                            user.email
469
                        )
470
                        logger._log(LOGGING_LEVEL, msg, [])
471
                if user and user.is_active:
472
                    next = request.POST.get('next', '')
473
                    response = prepare_response(request, user, next=next)
474
                    transaction.commit()
475
                    return response
476
                messages.add_message(request, status, message)
477
                transaction.commit()
478
                return HttpResponseRedirect(on_success_redirect)
479

    
480
            except SendMailError, e:
481
                logger.exception(e)
482
                status = messages.ERROR
483
                message = e.message
484
                messages.error(request, message)
485
                transaction.rollback()
486
            except BaseException, e:
487
                logger.exception(e)
488
                message = _(astakos_messages.GENERIC_ERROR)
489
                messages.error(request, message)
490
                logger.exception(e)
491
                transaction.rollback()
492

    
493
    return render_response(template_name,
494
                           signup_form=form,
495
                           third_party_token=third_party_token,
496
                           provider=provider,
497
                           context_instance=get_context(request, extra_context))
498

    
499

    
500
@require_http_methods(["GET", "POST"])
501
@login_required
502
@signed_terms_required
503
def feedback(request, template_name='im/feedback.html', email_template_name='im/feedback_mail.txt', extra_context=None):
504
    """
505
    Allows a user to send feedback.
506

507
    In case of GET request renders a form for providing the feedback information.
508
    In case of POST sends an email to support team.
509

510
    If the user isn't logged in, redirects to settings.LOGIN_URL.
511

512
    **Arguments**
513

514
    ``template_name``
515
        A custom template to use. This is optional; if not specified,
516
        this will default to ``im/feedback.html``.
517

518
    ``extra_context``
519
        An dictionary of variables to add to the template context.
520

521
    **Template:**
522

523
    im/signup.html or ``template_name`` keyword argument.
524

525
    **Settings:**
526

527
    * LOGIN_URL: login uri
528
    """
529
    extra_context = extra_context or {}
530
    if request.method == 'GET':
531
        form = FeedbackForm()
532
    if request.method == 'POST':
533
        if not request.user:
534
            return HttpResponse('Unauthorized', status=401)
535

    
536
        form = FeedbackForm(request.POST)
537
        if form.is_valid():
538
            msg = form.cleaned_data['feedback_msg']
539
            data = form.cleaned_data['feedback_data']
540
            try:
541
                send_feedback(msg, data, request.user, email_template_name)
542
            except SendMailError, e:
543
                messages.error(request, message)
544
            else:
545
                message = _(astakos_messages.FEEDBACK_SENT)
546
                messages.success(request, message)
547
    return render_response(template_name,
548
                           feedback_form=form,
549
                           context_instance=get_context(request, extra_context))
550

    
551

    
552
@require_http_methods(["GET"])
553
@signed_terms_required
554
def logout(request, template='registration/logged_out.html', extra_context=None):
555
    """
556
    Wraps `django.contrib.auth.logout`.
557
    """
558
    extra_context = extra_context or {}
559
    response = HttpResponse()
560
    if request.user.is_authenticated():
561
        email = request.user.email
562
        auth_logout(request)
563
    else:
564
        response['Location'] = reverse('index')
565
        response.status_code = 301
566
        return response
567

    
568
    next = restrict_next(
569
        request.GET.get('next'),
570
        domain=COOKIE_DOMAIN
571
    )
572

    
573
    if next:
574
        response['Location'] = next
575
        response.status_code = 302
576
    elif LOGOUT_NEXT:
577
        response['Location'] = LOGOUT_NEXT
578
        response.status_code = 301
579
    else:
580
        messages.add_message(request, messages.SUCCESS, _(astakos_messages.LOGOUT_SUCCESS))
581
        response['Location'] = reverse('index')
582
        response.status_code = 301
583
    return response
584

    
585

    
586
@require_http_methods(["GET", "POST"])
587
@transaction.commit_manually
588
def activate(request, greeting_email_template_name='im/welcome_email.txt',
589
             helpdesk_email_template_name='im/helpdesk_notification.txt'):
590
    """
591
    Activates the user identified by the ``auth`` request parameter, sends a welcome email
592
    and renews the user token.
593

594
    The view uses commit_manually decorator in order to ensure the user state will be updated
595
    only if the email will be send successfully.
596
    """
597
    token = request.GET.get('auth')
598
    next = request.GET.get('next')
599
    try:
600
        user = AstakosUser.objects.get(auth_token=token)
601
    except AstakosUser.DoesNotExist:
602
        messages.error(request, _(astakos_messages.ACCOUNT_UNKNOWN))
603
        return HttpResponseRedirect(reverse('index'))
604

    
605
    if user.is_active:
606
        message = _(astakos_messages.ACCOUNT_ALREADY_ACTIVE)
607
        messages.error(request, message)
608
        return HttpResponseRedirect(reverse('index'))
609

    
610
    try:
611
        activate_func(user, greeting_email_template_name,
612
                      helpdesk_email_template_name, verify_email=True)
613
        response = prepare_response(request, user, next, renew=True)
614
        transaction.commit()
615
        messages.success(request, astakos_messages.ACCOUNT_ACTIVATED)
616
        return HttpResponseRedirect(reverse('edit_profile'))
617
    except SendMailError, e:
618
        message = e.message
619
        messages.add_message(request, messages.ERROR, message)
620
        transaction.rollback()
621
        return HttpResponseRedirect(reverse('index'))
622
    except BaseException, e:
623
        status = messages.ERROR
624
        message = _(astakos_messages.GENERIC_ERROR)
625
        messages.add_message(request, messages.ERROR, message)
626
        logger.exception(e)
627
        transaction.rollback()
628
        return HttpResponseRedirect(reverse('index'))
629

    
630

    
631
@require_http_methods(["GET", "POST"])
632
def approval_terms(request, term_id=None, template_name='im/approval_terms.html', extra_context=None):
633
    extra_context = extra_context or {}
634
    term = None
635
    terms = None
636
    if not term_id:
637
        try:
638
            term = ApprovalTerms.objects.order_by('-id')[0]
639
        except IndexError:
640
            pass
641
    else:
642
        try:
643
            term = ApprovalTerms.objects.get(id=term_id)
644
        except ApprovalTerms.DoesNotExist, e:
645
            pass
646

    
647
    if not term:
648
        messages.error(request, _(astakos_messages.NO_APPROVAL_TERMS))
649
        return HttpResponseRedirect(reverse('index'))
650
    f = open(term.location, 'r')
651
    terms = f.read()
652

    
653
    if request.method == 'POST':
654
        next = restrict_next(
655
            request.POST.get('next'),
656
            domain=COOKIE_DOMAIN
657
        )
658
        if not next:
659
            next = reverse('index')
660
        form = SignApprovalTermsForm(request.POST, instance=request.user)
661
        if not form.is_valid():
662
            return render_response(template_name,
663
                                   terms=terms,
664
                                   approval_terms_form=form,
665
                                   context_instance=get_context(request, extra_context))
666
        user = form.save()
667
        return HttpResponseRedirect(next)
668
    else:
669
        form = None
670
        if request.user.is_authenticated() and not request.user.signed_terms:
671
            form = SignApprovalTermsForm(instance=request.user)
672
        return render_response(template_name,
673
                               terms=terms,
674
                               approval_terms_form=form,
675
                               context_instance=get_context(request, extra_context))
676

    
677

    
678
@require_http_methods(["GET", "POST"])
679
@login_required
680
@signed_terms_required
681
@transaction.commit_manually
682
def change_email(request, activation_key=None,
683
                 email_template_name='registration/email_change_email.txt',
684
                 form_template_name='registration/email_change_form.html',
685
                 confirm_template_name='registration/email_change_done.html',
686
                 extra_context=None):
687
    extra_context = extra_context or {}
688
    if activation_key:
689
        try:
690
            user = EmailChange.objects.change_email(activation_key)
691
            if request.user.is_authenticated() and request.user == user:
692
                msg = _(astakos_messages.EMAIL_CHANGED)
693
                messages.success(request, msg)
694
                auth_logout(request)
695
                response = prepare_response(request, user)
696
                transaction.commit()
697
                return response
698
        except ValueError, e:
699
            messages.error(request, e)
700
        return render_response(confirm_template_name,
701
                               modified_user=user if 'user' in locals(
702
                               ) else None,
703
                               context_instance=get_context(request,
704
                                                            extra_context))
705

    
706
    if not request.user.is_authenticated():
707
        path = quote(request.get_full_path())
708
        url = request.build_absolute_uri(reverse('index'))
709
        return HttpResponseRedirect(url + '?next=' + path)
710
    form = EmailChangeForm(request.POST or None)
711
    if request.method == 'POST' and form.is_valid():
712
        try:
713
            ec = form.save(email_template_name, request)
714
        except SendMailError, e:
715
            msg = e
716
            messages.error(request, msg)
717
            transaction.rollback()
718
        except IntegrityError, e:
719
            msg = _(astakos_messages.PENDING_EMAIL_CHANGE_REQUEST)
720
            messages.error(request, msg)
721
        else:
722
            msg = _(astakos_messages.EMAIL_CHANGE_REGISTERED)
723
            messages.success(request, msg)
724
            transaction.commit()
725
    return render_response(
726
        form_template_name,
727
        form=form,
728
        context_instance=get_context(request, extra_context)
729
    )
730

    
731

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

    
734
    if request.user.is_authenticated():
735
        messages.error(request, 'You are already signed in.')
736
        return HttpResponseRedirect(reverse('edit_profile'))
737

    
738
    if settings.MODERATION_ENABLED:
739
        raise PermissionDenied
740

    
741
    extra_context = extra_context or {}
742
    try:
743
        u = AstakosUser.objects.get(id=user_id)
744
    except AstakosUser.DoesNotExist:
745
        messages.error(request, _(astakos_messages.ACCOUNT_UNKNOWN))
746
    else:
747
        try:
748
            send_activation_func(u)
749
            msg = _(astakos_messages.ACTIVATION_SENT)
750
            messages.success(request, msg)
751
            return HttpResponseRedirect('/im/')
752

    
753
        except SendMailError, e:
754
            messages.error(request, e)
755
    return render_response(
756
        template_name,
757
        login_form = LoginForm(request=request),
758
        context_instance = get_context(
759
            request,
760
            extra_context
761
        )
762
    )
763

    
764
class ResourcePresentation():
765

    
766
    def __init__(self, data):
767
        self.data = data
768

    
769
    def update_from_result(self, result):
770
        if result.is_success:
771
            for r in result.data:
772
                rname = '%s%s%s' % (r.get('service'), RESOURCE_SEPARATOR, r.get('name'))
773
                if not rname in self.data['resources']:
774
                    self.data['resources'][rname] = {}
775

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

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

    
784
    def test(self, quota_dict):
785
        for k, v in quota_dict.iteritems():
786
            rname = k
787
            value = v
788
            if not rname in self.data['resources']:
789
                self.data['resources'][rname] = {}
790

    
791

    
792
            self.data['resources'][rname]['value'] = value
793

    
794

    
795
    def update_from_result_report(self, result):
796
        if result.is_success:
797
            for r in result.data:
798
                rname = r.get('name')
799
                if not rname in self.data['resources']:
800
                    self.data['resources'][rname] = {}
801

    
802
                self.data['resources'][rname].update(r)
803
                self.data['resources'][rname]['id'] = rname
804
                group = r.get('group')
805
                if not group in self.data['groups']:
806
                    self.data['groups'][group] = {}
807

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

    
810
    def get_group_resources(self, group):
811
        return dict(filter(lambda t: t[1].get('group') == group, self.data['resources'].iteritems()))
812

    
813
    def get_groups_resources(self):
814
        for g in self.data['groups']:
815
            yield g, self.get_group_resources(g)
816

    
817
    def get_quota(self, group_quotas):
818
        for r, v in group_quotas:
819
            rname = str(r)
820
            quota = self.data['resources'].get(rname)
821
            quota['value'] = v
822
            yield quota
823

    
824

    
825
    def get_policies(self, policies_data):
826
        for policy in policies_data:
827
            rname = '%s%s%s' % (policy.get('service'), RESOURCE_SEPARATOR, policy.get('resource'))
828
            policy.update(self.data['resources'].get(rname))
829
            yield policy
830

    
831
    def __repr__(self):
832
        return self.data.__repr__()
833

    
834
    def __iter__(self, *args, **kwargs):
835
        return self.data.__iter__(*args, **kwargs)
836

    
837
    def __getitem__(self, *args, **kwargs):
838
        return self.data.__getitem__(*args, **kwargs)
839

    
840
    def get(self, *args, **kwargs):
841
        return self.data.get(*args, **kwargs)
842

    
843

    
844

    
845
# @require_http_methods(["GET", "POST"])
846
# @signed_terms_required
847
# @login_required
848
# def group_add(request, kind_name='default'):
849
#     result = callpoint.list_resources()
850
#     resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
851
#     resource_catalog.update_from_result(result)
852
# 
853
#     if not result.is_success:
854
#         messages.error(
855
#             request,
856
#             'Unable to retrieve system resources: %s' % result.reason
857
#     )
858
# 
859
#     try:
860
#         kind = GroupKind.objects.get(name=kind_name)
861
#     except:
862
#         return HttpResponseBadRequest(_(astakos_messages.GROUPKIND_UNKNOWN))
863
# 
864
# 
865
# 
866
#     post_save_redirect = '/im/group/%(id)s/'
867
#     context_processors = None
868
#     model, form_class = get_model_and_form_class(
869
#         model=None,
870
#         form_class=AstakosGroupCreationForm
871
#     )
872
# 
873
#     if request.method == 'POST':
874
#         form = form_class(request.POST, request.FILES)
875
#         if form.is_valid():
876
#             policies = form.policies()
877
#             return render_response(
878
#                 template='im/astakosgroup_form_summary.html',
879
#                 context_instance=get_context(request),
880
#                 form=AstakosGroupCreationSummaryForm(form.cleaned_data),
881
#                 policies=resource_catalog.get_policies(policies)
882
#             )
883
#     else:
884
#         now = datetime.now()
885
#         data = {
886
#             'kind': kind,
887
#         }
888
#         for group, resources in resource_catalog.get_groups_resources():
889
#             data['is_selected_%s' % group] = False
890
#             for resource in resources:
891
#                 data['%s_uplimit' % resource] = ''
892
# 
893
#         form = form_class(data)
894
# 
895
#     # Create the template, context, response
896
#     template_name = "%s/%s_form.html" % (
897
#         model._meta.app_label,
898
#         model._meta.object_name.lower()
899
#     )
900
#     t = template_loader.get_template(template_name)
901
#     c = RequestContext(request, {
902
#         'form': form,
903
#         'kind': kind,
904
#         'resource_catalog':resource_catalog,
905
#     }, context_processors)
906
#     return HttpResponse(t.render(c))
907

    
908

    
909
##@require_hsttp_methods(["POST"])
910
# @require_http_methods(["GET", "POST"])
911
# @signed_terms_required
912
# @login_required
913
# @transaction.commit_manually
914
# def group_add_complete(request):
915
#     model = AstakosGroup
916
#     form = AstakosGroupCreationSummaryForm(request.POST)
917
#     if form.is_valid():
918
#         d = form.cleaned_data
919
#         d['owners'] = [request.user]
920
#         result = callpoint.create_groups((d,)).next()
921
#         if result.is_success:
922
#             new_object = result.data[0]
923
#             # send notification
924
#             try:
925
#                 send_group_creation_notification(
926
#                     template_name='im/group_creation_notification.txt',
927
#                     dictionary={
928
#                         'group': new_object,
929
#                         'owner': request.user,
930
#                         'policies': d.get('policies', [])
931
#                     }
932
#                 )
933
#             except SendNotificationError, e:
934
#                 messages.error(request, e, fail_silently=True)
935
#                 transaction.rollback()
936
#             else:
937
#                 msg = _(astakos_messages.OBJECT_CREATED) %\
938
#                     {"verbose_name": model._meta.verbose_name}
939
#                 message = _(astakos_messages.NOTIFICATION_SENT) % 'a project'
940
#                 messages.success(request, msg, fail_silently=True)
941
#                 transaction.commit()
942
#         else:
943
#             d = {"verbose_name": model._meta.verbose_name,
944
#                  "reason":result.reason}
945
#             msg = _(astakos_messages.OBJECT_CREATED_FAILED) % d
946
#             messages.error(request, msg, fail_silently=True)
947
#     return render_response(
948
#         template='im/astakosgroup_form_summary.html',
949
#         context_instance=get_context(request),
950
#         form=form,
951
#         policies=form.cleaned_data.get('policies')
952
#     )
953

    
954
##@require_http_methods(["GET"])
955
# @require_http_methods(["GET", "POST"])
956
# @signed_terms_required
957
# @login_required
958
# def group_list(request):
959
#     none = request.user.astakos_groups.none()
960
#     query = """
961
#         SELECT auth_group.id,
962
#         auth_group.name AS groupname,
963
#         im_groupkind.name AS kindname,
964
#         im_astakosgroup.*,
965
#         owner.email AS groupowner,
966
#         (SELECT COUNT(*) FROM im_membership
967
#             WHERE group_id = im_astakosgroup.group_ptr_id
968
#             AND date_joined IS NOT NULL) AS approved_members_num,
969
#         (SELECT CASE WHEN(
970
#                     SELECT date_joined FROM im_membership
971
#                     WHERE group_id = im_astakosgroup.group_ptr_id
972
#                     AND person_id = %(id)s) IS NULL
973
#                     THEN 0 ELSE 1 END) AS membership_status
974
#         FROM im_astakosgroup
975
#         INNER JOIN im_membership ON (
976
#             im_astakosgroup.group_ptr_id = im_membership.group_id)
977
#         INNER JOIN auth_group ON(im_astakosgroup.group_ptr_id = auth_group.id)
978
#         INNER JOIN im_groupkind ON (im_astakosgroup.kind_id = im_groupkind.id)
979
#         LEFT JOIN im_astakosuser_owner ON (
980
#             im_astakosuser_owner.astakosgroup_id = im_astakosgroup.group_ptr_id)
981
#         LEFT JOIN auth_user as owner ON (
982
#             im_astakosuser_owner.astakosuser_id = owner.id)
983
#         WHERE im_membership.person_id = %(id)s
984
#         AND im_groupkind.name != 'default'
985
#         """ % request.user.__dict__
986
# 
987
#     # validate sorting
988
#     sorting = 'groupname'
989
#     sort_form = AstakosGroupSortForm(request.GET)
990
#     if sort_form.is_valid():
991
#         sorting = sort_form.cleaned_data.get('sorting')
992
#     query = query+" ORDER BY %s ASC" %sorting
993
#     
994
#     q = AstakosGroup.objects.raw(query)
995
#     
996
#     # Create the template, context, response
997
#     template_name = "%s/%s_list.html" % (
998
#         q.model._meta.app_label,
999
#         q.model._meta.object_name.lower()
1000
#     )
1001
#     extra_context = dict(
1002
#         is_search=False,
1003
#         q=q,
1004
#         sorting=sorting,
1005
#         page=request.GET.get('page', 1)
1006
#     )
1007
#     return render_response(template_name,
1008
#                            context_instance=get_context(request, extra_context)
1009
#     )
1010

    
1011

    
1012
# @require_http_methods(["GET", "POST"])
1013
# @signed_terms_required
1014
# @login_required
1015
# def group_detail(request, group_id):
1016
#     q = AstakosGroup.objects.select_related().filter(pk=group_id)
1017
#     q = q.extra(select={
1018
#         'is_member': """SELECT CASE WHEN EXISTS(
1019
#                             SELECT id FROM im_membership
1020
#                             WHERE group_id = im_astakosgroup.group_ptr_id
1021
#                             AND person_id = %s)
1022
#                         THEN 1 ELSE 0 END""" % request.user.id,
1023
#         'is_owner': """SELECT CASE WHEN EXISTS(
1024
#                         SELECT id FROM im_astakosuser_owner
1025
#                         WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
1026
#                         AND astakosuser_id = %s)
1027
#                         THEN 1 ELSE 0 END""" % request.user.id,
1028
#         'is_active_member': """SELECT CASE WHEN(
1029
#                         SELECT date_joined FROM im_membership
1030
#                         WHERE group_id = im_astakosgroup.group_ptr_id
1031
#                         AND person_id = %s) IS NULL
1032
#                         THEN 0 ELSE 1 END""" % request.user.id,
1033
#         'kindname': """SELECT name FROM im_groupkind
1034
#                        WHERE id = im_astakosgroup.kind_id"""})
1035
# 
1036
#     model = q.model
1037
#     context_processors = None
1038
#     mimetype = None
1039
#     try:
1040
#         obj = q.get()
1041
#     except AstakosGroup.DoesNotExist:
1042
#         raise Http404("No %s found matching the query" % (
1043
#             model._meta.verbose_name))
1044
# 
1045
#     update_form = AstakosGroupUpdateForm(instance=obj)
1046
#     addmembers_form = AddGroupMembersForm()
1047
#     if request.method == 'POST':
1048
#         update_data = {}
1049
#         addmembers_data = {}
1050
#         for k, v in request.POST.iteritems():
1051
#             if k in update_form.fields:
1052
#                 update_data[k] = v
1053
#             if k in addmembers_form.fields:
1054
#                 addmembers_data[k] = v
1055
#         update_data = update_data or None
1056
#         addmembers_data = addmembers_data or None
1057
#         update_form = AstakosGroupUpdateForm(update_data, instance=obj)
1058
#         addmembers_form = AddGroupMembersForm(addmembers_data)
1059
#         if update_form.is_valid():
1060
#             update_form.save()
1061
#         if addmembers_form.is_valid():
1062
#             try:
1063
#                 map(obj.approve_member, addmembers_form.valid_users)
1064
#             except AssertionError:
1065
#                 msg = _(astakos_messages.GROUP_MAX_PARTICIPANT_NUMBER_REACHED)
1066
#                 messages.error(request, msg)
1067
#             addmembers_form = AddGroupMembersForm()
1068
# 
1069
#     template_name = "%s/%s_detail.html" % (
1070
#         model._meta.app_label, model._meta.object_name.lower())
1071
#     t = template_loader.get_template(template_name)
1072
#     c = RequestContext(request, {
1073
#         'object': obj,
1074
#     }, context_processors)
1075
# 
1076
#     # validate sorting
1077
#     sorting = 'person__email'
1078
#     form = MembersSortForm(request.GET)
1079
#     if form.is_valid():
1080
#         sorting = form.cleaned_data.get('sorting')
1081
#     
1082
#     result = callpoint.list_resources()
1083
#     resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1084
#     resource_catalog.update_from_result(result)
1085
# 
1086
# 
1087
#     if not result.is_success:
1088
#         messages.error(
1089
#             request,
1090
#             'Unable to retrieve system resources: %s' % result.reason
1091
#     )
1092
# 
1093
#     extra_context = {'update_form': update_form,
1094
#                      'addmembers_form': addmembers_form,
1095
#                      'page': request.GET.get('page', 1),
1096
#                      'sorting': sorting,
1097
#                      'resource_catalog':resource_catalog,
1098
#                      'quota':resource_catalog.get_quota(obj.quota)}
1099
#     for key, value in extra_context.items():
1100
#         if callable(value):
1101
#             c[key] = value()
1102
#         else:
1103
#             c[key] = value
1104
#     response = HttpResponse(t.render(c), mimetype=mimetype)
1105
#     populate_xheaders(
1106
#         request, response, model, getattr(obj, obj._meta.pk.name))
1107
#     return response
1108

    
1109

    
1110
# @require_http_methods(["GET", "POST"])
1111
# @signed_terms_required
1112
# @login_required
1113
# def group_search(request, extra_context=None, **kwargs):
1114
#     q = request.GET.get('q')
1115
#     if request.method == 'GET':
1116
#         form = AstakosGroupSearchForm({'q': q} if q else None)
1117
#     else:
1118
#         form = AstakosGroupSearchForm(get_query(request))
1119
#         if form.is_valid():
1120
#             q = form.cleaned_data['q'].strip()
1121
#     
1122
#     sorting = 'groupname'
1123
#     if q:
1124
#         queryset = AstakosGroup.objects.select_related()
1125
#         queryset = queryset.filter(~Q(kind__name='default'))
1126
#         queryset = queryset.filter(name__contains=q)
1127
#         queryset = queryset.filter(approval_date__isnull=False)
1128
#         queryset = queryset.extra(select={
1129
#                                   'groupname': "auth_group.name",
1130
#                                   'kindname': "im_groupkind.name",
1131
#                                   'approved_members_num': """
1132
#                     SELECT COUNT(*) FROM im_membership
1133
#                     WHERE group_id = im_astakosgroup.group_ptr_id
1134
#                     AND date_joined IS NOT NULL""",
1135
#                                   'membership_approval_date': """
1136
#                     SELECT date_joined FROM im_membership
1137
#                     WHERE group_id = im_astakosgroup.group_ptr_id
1138
#                     AND person_id = %s""" % request.user.id,
1139
#                                   'is_member': """
1140
#                     SELECT CASE WHEN EXISTS(
1141
#                     SELECT date_joined FROM im_membership
1142
#                     WHERE group_id = im_astakosgroup.group_ptr_id
1143
#                     AND person_id = %s)
1144
#                     THEN 1 ELSE 0 END""" % request.user.id,
1145
#                                   'is_owner': """
1146
#                     SELECT CASE WHEN EXISTS(
1147
#                     SELECT id FROM im_astakosuser_owner
1148
#                     WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
1149
#                     AND astakosuser_id = %s)
1150
#                     THEN 1 ELSE 0 END""" % request.user.id,
1151
#                     'is_owner': """SELECT CASE WHEN EXISTS(
1152
#                         SELECT id FROM im_astakosuser_owner
1153
#                         WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
1154
#                         AND astakosuser_id = %s)
1155
#                         THEN 1 ELSE 0 END""" % request.user.id,
1156
#                     })
1157
#         
1158
#         # validate sorting
1159
#         sort_form = AstakosGroupSortForm(request.GET)
1160
#         if sort_form.is_valid():
1161
#             sorting = sort_form.cleaned_data.get('sorting')
1162
#         queryset = queryset.order_by(sorting)
1163
# 
1164
#     else:
1165
#         queryset = AstakosGroup.objects.none()
1166
#     return object_list(
1167
#         request,
1168
#         queryset,
1169
#         paginate_by=PAGINATE_BY_ALL,
1170
#         page=request.GET.get('page') or 1,
1171
#         template_name='im/astakosgroup_list.html',
1172
#         extra_context=dict(form=form,
1173
#                            is_search=True,
1174
#                            q=q,
1175
#                            sorting=sorting))
1176

    
1177

    
1178
# @require_http_methods(["GET", "POST"])
1179
# @signed_terms_required
1180
# @login_required
1181
# def group_all(request, extra_context=None, **kwargs):
1182
#     q = AstakosGroup.objects.select_related()
1183
#     q = q.filter(~Q(kind__name='default'))
1184
#     q = q.filter(approval_date__isnull=False)
1185
#     q = q.extra(select={
1186
#                 'groupname': "auth_group.name",
1187
#                 'kindname': "im_groupkind.name",
1188
#                 'approved_members_num': """
1189
#                     SELECT COUNT(*) FROM im_membership
1190
#                     WHERE group_id = im_astakosgroup.group_ptr_id
1191
#                     AND date_joined IS NOT NULL""",
1192
#                 'membership_approval_date': """
1193
#                     SELECT date_joined FROM im_membership
1194
#                     WHERE group_id = im_astakosgroup.group_ptr_id
1195
#                     AND person_id = %s""" % request.user.id,
1196
#                 'is_member': """
1197
#                     SELECT CASE WHEN EXISTS(
1198
#                     SELECT date_joined FROM im_membership
1199
#                     WHERE group_id = im_astakosgroup.group_ptr_id
1200
#                     AND person_id = %s)
1201
#                     THEN 1 ELSE 0 END""" % request.user.id,
1202
#                  'is_owner': """SELECT CASE WHEN EXISTS(
1203
#                         SELECT id FROM im_astakosuser_owner
1204
#                         WHERE astakosgroup_id = im_astakosgroup.group_ptr_id
1205
#                         AND astakosuser_id = %s)
1206
#                         THEN 1 ELSE 0 END""" % request.user.id,   })
1207
#     
1208
#     # validate sorting
1209
#     sorting = 'groupname'
1210
#     sort_form = AstakosGroupSortForm(request.GET)
1211
#     if sort_form.is_valid():
1212
#         sorting = sort_form.cleaned_data.get('sorting')
1213
#     q = q.order_by(sorting)
1214
#     
1215
#     return object_list(
1216
#         request,
1217
#         q,
1218
#         paginate_by=PAGINATE_BY_ALL,
1219
#         page=request.GET.get('page') or 1,
1220
#         template_name='im/astakosgroup_list.html',
1221
#         extra_context=dict(form=AstakosGroupSearchForm(),
1222
#                            is_search=True,
1223
#                            sorting=sorting))
1224

    
1225

    
1226
##@require_http_methods(["POST"])
1227
# @require_http_methods(["POST", "GET"])
1228
# @signed_terms_required
1229
# @login_required
1230
# def group_join(request, group_id):
1231
#     m = Membership(group_id=group_id,
1232
#                    person=request.user,
1233
#                    date_requested=datetime.now())
1234
#     try:
1235
#         m.save()
1236
#         post_save_redirect = reverse(
1237
#             'group_detail',
1238
#             kwargs=dict(group_id=group_id))
1239
#         return HttpResponseRedirect(post_save_redirect)
1240
#     except IntegrityError, e:
1241
#         logger.exception(e)
1242
#         msg = _(astakos_messages.GROUP_JOIN_FAILURE)
1243
#         messages.error(request, msg)
1244
#         return group_search(request)
1245
# 
1246
# 
1247
# @require_http_methods(["POST"])
1248
# @signed_terms_required
1249
# @login_required
1250
# def group_leave(request, group_id):
1251
#     try:
1252
#         m = Membership.objects.select_related().get(
1253
#             group__id=group_id,
1254
#             person=request.user)
1255
#     except Membership.DoesNotExist:
1256
#         return HttpResponseBadRequest(_(astakos_messages.NOT_MEMBER))
1257
#     if request.user in m.group.owner.all():
1258
#         return HttpResponseForbidden(_(astakos_messages.OWNER_CANNOT_LEAVE_GROUP))
1259
#     return delete_object(
1260
#         request,
1261
#         model=Membership,
1262
#         object_id=m.id,
1263
#         template_name='im/astakosgroup_list.html',
1264
#         post_delete_redirect=reverse(
1265
#             'group_detail',
1266
#             kwargs=dict(group_id=group_id)))
1267

    
1268

    
1269
# def handle_membership(func):
1270
#     @wraps(func)
1271
#     def wrapper(request, group_id, user_id):
1272
#         try:
1273
#             m = Membership.objects.select_related().get(
1274
#                 group__id=group_id,
1275
#                 person__id=user_id)
1276
#         except Membership.DoesNotExist:
1277
#             return HttpResponseBadRequest(_(astakos_messages.NOT_MEMBER))
1278
#         else:
1279
#             if request.user not in m.group.owner.all():
1280
#                 return HttpResponseForbidden(_(astakos_messages.NOT_OWNER))
1281
#             func(request, m)
1282
#             return group_detail(request, group_id)
1283
#     return wrapper
1284

    
1285

    
1286
#@require_http_methods(["POST"])
1287
# @require_http_methods(["POST", "GET"])
1288
# @signed_terms_required
1289
# @login_required
1290
# @handle_membership
1291
# def approve_member(request, membership):
1292
#     try:
1293
#         membership.approve()
1294
#         realname = membership.person.realname
1295
#         msg = _(astakos_messages.MEMBER_JOINED_GROUP) % locals()
1296
#         messages.success(request, msg)
1297
#     except AssertionError:
1298
#         msg = _(astakos_messages.GROUP_MAX_PARTICIPANT_NUMBER_REACHED)
1299
#         messages.error(request, msg)
1300
#     except BaseException, e:
1301
#         logger.exception(e)
1302
#         realname = membership.person.realname
1303
#         msg = _(astakos_messages.GENERIC_ERROR)
1304
#         messages.error(request, msg)
1305

    
1306

    
1307
# @signed_terms_required
1308
# @login_required
1309
# @handle_membership
1310
# def disapprove_member(request, membership):
1311
#     try:
1312
#         membership.disapprove()
1313
#         realname = membership.person.realname
1314
#         msg = astakos_messages.MEMBER_REMOVED % locals()
1315
#         messages.success(request, msg)
1316
#     except BaseException, e:
1317
#         logger.exception(e)
1318
#         msg = _(astakos_messages.GENERIC_ERROR)
1319
#         messages.error(request, msg)
1320

    
1321

    
1322
@require_http_methods(["GET"])
1323
@require_http_methods(["POST", "GET"])
1324
@signed_terms_required
1325
@login_required
1326
def resource_usage(request):
1327
    def with_class(entry):
1328
        entry['load_class'] = 'red'
1329
        max_value = float(entry['maxValue'])
1330
        curr_value = float(entry['currValue'])
1331
        entry['ratio_limited']= 0
1332
        if max_value > 0 :
1333
            entry['ratio'] = (curr_value / max_value) * 100
1334
        else:
1335
            entry['ratio'] = 0
1336
        if entry['ratio'] < 66:
1337
            entry['load_class'] = 'yellow'
1338
        if entry['ratio'] < 33:
1339
            entry['load_class'] = 'green'
1340
        if entry['ratio']<0:
1341
            entry['ratio'] = 0
1342
        if entry['ratio']>100:
1343
            entry['ratio_limited'] = 100
1344
        else:
1345
            entry['ratio_limited'] = entry['ratio']
1346

    
1347
        return entry
1348

    
1349
    def pluralize(entry):
1350
        entry['plural'] = engine.plural(entry.get('name'))
1351
        return entry
1352

    
1353
    result = callpoint.get_user_usage(request.user.id)
1354
    if result.is_success:
1355
        backenddata = map(with_class, result.data)
1356
        data = map(pluralize, result.data)
1357
    else:
1358
        data = None
1359
        messages.error(request, result.reason)
1360
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1361
    resource_catalog.update_from_result_report(result)
1362
    return render_response('im/resource_usage.html',
1363
                           data=data,
1364
                           context_instance=get_context(request),
1365
                           resource_catalog=resource_catalog,
1366
                           result=result)
1367

    
1368

    
1369
# def group_create_list(request):
1370
#     form = PickResourceForm()
1371
#     return render_response(
1372
#         template='im/astakosgroup_create_list.html',
1373
#         context_instance=get_context(request),)
1374

    
1375

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

    
1413

    
1414
#def _clear_billing_data(data):
1415
#
1416
#    # remove addcredits entries
1417
#    def isnotcredit(e):
1418
#        return e['serviceName'] != "addcredits"
1419
#
1420
#    # separate services
1421
#    def servicefilter(service_name):
1422
#        service = service_name
1423
#
1424
#        def fltr(e):
1425
#            return e['serviceName'] == service
1426
#        return fltr
1427
#
1428
#    data['bill_nocredits'] = filter(isnotcredit, data['bill'])
1429
#    data['bill_vmtime'] = filter(servicefilter('vmtime'), data['bill'])
1430
#    data['bill_diskspace'] = filter(servicefilter('diskspace'), data['bill'])
1431
#    data['bill_addcredits'] = filter(servicefilter('addcredits'), data['bill'])
1432
#
1433
#    return data
1434

    
1435

    
1436
# #@require_http_methods(["GET"])
1437
# @require_http_methods(["POST", "GET"])
1438
# @signed_terms_required
1439
# @login_required
1440
# def timeline(request):
1441
# #    data = {'entity':request.user.email}
1442
#     timeline_body = ()
1443
#     timeline_header = ()
1444
# #    form = TimelineForm(data)
1445
#     form = TimelineForm()
1446
#     if request.method == 'POST':
1447
#         data = request.POST
1448
#         form = TimelineForm(data)
1449
#         if form.is_valid():
1450
#             data = form.cleaned_data
1451
#             timeline_header = ('entity', 'resource',
1452
#                                'event name', 'event date',
1453
#                                'incremental cost', 'total cost')
1454
#             timeline_body = timeline_charge(
1455
#                 data['entity'], data['resource'],
1456
#                 data['start_date'], data['end_date'],
1457
#                 data['details'], data['operation'])
1458
# 
1459
#     return render_response(template='im/timeline.html',
1460
#                            context_instance=get_context(request),
1461
#                            form=form,
1462
#                            timeline_header=timeline_header,
1463
#                            timeline_body=timeline_body)
1464
#     return data
1465

    
1466

    
1467
# TODO: action only on POST and user should confirm the removal
1468
@require_http_methods(["GET", "POST"])
1469
@login_required
1470
@signed_terms_required
1471
def remove_auth_provider(request, pk):
1472
    try:
1473
        provider = request.user.auth_providers.get(pk=pk)
1474
    except AstakosUserAuthProvider.DoesNotExist:
1475
        raise Http404
1476

    
1477
    if provider.can_remove():
1478
        provider.delete()
1479
        return HttpResponseRedirect(reverse('edit_profile'))
1480
    else:
1481
        raise PermissionDenied
1482

    
1483

    
1484
def how_it_works(request):
1485
    return render_response(
1486
        'im/how_it_works.html',
1487
        context_instance=get_context(request))
1488

    
1489
@require_http_methods(["GET", "POST"])
1490
@signed_terms_required
1491
@login_required
1492
@transaction.commit_manually
1493
def project_add(request):
1494
    rollback = False
1495
    result = callpoint.list_resources()
1496
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1497
    resource_catalog.update_from_result(result)
1498

    
1499
    if not result.is_success:
1500
        messages.error(
1501
            request,
1502
            'Unable to retrieve system resources: %s' % result.reason
1503
    )
1504
    extra_context = {'resource_catalog':resource_catalog}
1505

    
1506
    try:
1507
        r = create_object(request, template_name='im/projects/projectapplication_form.html',
1508
            extra_context=extra_context, post_save_redirect='/im/project/list/',
1509
            form_class=ProjectApplicationForm)
1510
        return r
1511
    except BaseException, e:
1512
        logger.exception(e)
1513
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
1514
        rollback = True
1515
        return render_response(
1516
            'im/projects/projectapplication_form.html',
1517
            sorting = 'name',
1518
            form = ProjectApplicationForm(),
1519
            context_instance=get_context(request, extra_context)
1520
        )
1521
    finally:
1522
        if rollback:
1523
            transaction.rollback()
1524
        else:
1525
            transaction.commit()
1526

    
1527

    
1528
@require_http_methods(["GET"])
1529
@signed_terms_required
1530
@login_required
1531
def project_list(request):
1532
    q = ProjectApplication.objects.filter(owner=request.user)
1533
    q |= ProjectApplication.objects.filter(
1534
        project__in=request.user.projectmembership_set.values_list('project', flat=True)
1535
    )
1536
    q = q.select_related()
1537
    sorting = 'name'
1538
    sort_form = ProjectSortForm(request.GET)
1539
    if sort_form.is_valid():
1540
        sorting = sort_form.cleaned_data.get('sorting')
1541
    q = q.order_by(sorting)
1542
    
1543
    return object_list(
1544
        request,
1545
        q,
1546
        paginate_by=PAGINATE_BY_ALL,
1547
        page=request.GET.get('page') or 1,
1548
        template_name='im/projects/project_list.html',
1549
        extra_context={
1550
            'is_search':False,
1551
            'sorting':sorting
1552
        }
1553
    )
1554

    
1555
@require_http_methods(["GET", "POST"])
1556
@signed_terms_required
1557
@login_required
1558
def project_update(request, application_id):
1559
    result = callpoint.list_resources()
1560
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1561
    resource_catalog.update_from_result(result)
1562

    
1563
    if not result.is_success:
1564
        messages.error(
1565
            request,
1566
            'Unable to retrieve system resources: %s' % result.reason
1567
    )
1568
    extra_context = {'resource_catalog':resource_catalog}
1569
    return update_object(
1570
        request,
1571
        object_id=application_id,
1572
#         slug_field='projectapplication__id',
1573
        template_name='im/projects/projectapplication_form.html',
1574
        extra_context=extra_context, post_save_redirect='/im/project/list/',
1575
        form_class=ProjectApplicationForm)
1576

    
1577

    
1578
@require_http_methods(["GET", "POST"])
1579
@signed_terms_required
1580
@login_required
1581
@transaction.commit_manually
1582
def project_detail(request, application_id):
1583
    result = callpoint.list_resources()
1584
    resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1585
    resource_catalog.update_from_result(result)
1586

    
1587
    addmembers_form = AddProjectMembersForm()
1588
    if request.method == 'POST':
1589
        addmembers_form = AddProjectMembersForm(request.POST)
1590
        if addmembers_form.is_valid():
1591
            try:
1592
                rollback = False
1593
                map(lambda u: accept_membership(
1594
                        application_id,
1595
                        u,
1596
                        request_user=request.user),
1597
                    addmembers_form.valid_users)
1598
            except (IOError, PermissionDenied), e:
1599
                messages.error(request, e)
1600
            except BaseException, e:
1601
                rollback = True
1602
                messages.error(request, e)
1603
            finally:
1604
                if rollback == True:
1605
                    transaction.rollback()
1606
                else:
1607
                    transaction.commit()
1608
            addmembers_form = AddProjectMembersForm()
1609
    
1610
    # validate sorting
1611
    sorting = 'person__email'
1612
    form = ProjectMembersSortForm(request.GET or request.POST)
1613
    if form.is_valid():
1614
        sorting = form.cleaned_data.get('sorting')
1615

    
1616
    return object_detail(
1617
        request,
1618
        queryset=ProjectApplication.objects.select_related(),
1619
        object_id=application_id,
1620
        template_name='im/projects/project_detail.html',
1621
        extra_context={
1622
            'resource_catalog':resource_catalog,
1623
            'sorting':sorting,
1624
            'addmembers_form':addmembers_form
1625
        }
1626
    )
1627

    
1628
@require_http_methods(["GET", "POST"])
1629
@signed_terms_required
1630
@login_required
1631
def project_search(request):
1632
    queryset = ProjectApplication.objects
1633
    if request.method == 'GET':
1634
        form = ProjectSearchForm()
1635
        queryset = queryset.none()
1636
    else:
1637
        form = ProjectSearchForm(request.POST)
1638
        if form.is_valid():
1639
            q = form.cleaned_data['q'].strip()
1640
            queryset = queryset.filter(~Q(project__last_approval_date__isnull=True))
1641
            queryset = queryset.filter(name__contains=q)
1642
    sorting = 'name'        
1643
    # validate sorting
1644
    sort_form = ProjectSortForm(request.GET)
1645
    if sort_form.is_valid():
1646
        sorting = sort_form.cleaned_data.get('sorting')
1647
    queryset = queryset.order_by(sorting)
1648
    return object_list(
1649
        request,
1650
        queryset,
1651
        paginate_by=PAGINATE_BY_ALL,
1652
        page=request.GET.get('page') or 1,
1653
        template_name='im/projects/project_list.html',
1654
        extra_context=dict(
1655
            form=form,
1656
            is_search=True,
1657
            sorting=sorting
1658
        )
1659
    )
1660

    
1661

    
1662
@require_http_methods(["GET"])
1663
@signed_terms_required
1664
@login_required
1665
def project_all(request):
1666
    q = ProjectApplication.objects.filter(
1667
        ~Q(project__last_approval_date__isnull=True))
1668
    q = q.select_related()
1669
    sorting = 'name'
1670
    sort_form = ProjectSortForm(request.GET)
1671
    if sort_form.is_valid():
1672
        sorting = sort_form.cleaned_data.get('sorting')
1673
    q = q.order_by(sorting)
1674
    
1675
    return object_list(
1676
        request,
1677
        q,
1678
        paginate_by=PAGINATE_BY_ALL,
1679
        page=request.GET.get('page') or 1,
1680
        template_name='im/projects/project_list.html',
1681
        extra_context={
1682
            'form':ProjectSearchForm(),
1683
            'is_search':True,
1684
            'sorting':sorting
1685
        }
1686
    )
1687

    
1688
@require_http_methods(["POST"])
1689
@signed_terms_required
1690
@login_required
1691
@transaction.commit_manually
1692
def project_join(request, application_id):
1693
    next = request.POST.get('next')
1694
    if not next:
1695
        return HttpResponseBadRequest(
1696
            _(astakos_messages.MISSING_NEXT_PARAMETER))
1697

    
1698
    rollback = False
1699
    try:
1700
        join_project(application_id, request.user)
1701
    except (IOError, PermissionDenied), e:
1702
        messages.error(request, e)
1703
    except BaseException, e:
1704
        logger.exception(e)
1705
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
1706
        rollback = True
1707
    else:
1708
        return project_detail(request, id)
1709
    finally:
1710
        if rollback:
1711
            transaction.rollback()
1712
        else:
1713
            transaction.commit()
1714

    
1715
    next = restrict_next(
1716
        request.POST.get('next'),
1717
        domain=COOKIE_DOMAIN)
1718
    return redirect(next)
1719

    
1720
@require_http_methods(["POST"])
1721
@signed_terms_required
1722
@login_required
1723
@transaction.commit_manually
1724
def project_leave(request, application_id):
1725
    next = request.POST.get('next')
1726
    if not next:
1727
        return HttpResponseBadRequest(
1728
            _(astakos_messages.MISSING_NEXT_PARAMETER))
1729

    
1730
    rollback = False
1731
    try:
1732
        leave_project(application_id, request.user)
1733
    except (IOError, PermissionDenied), e:
1734
        messages.error(request, e)
1735
    except BaseException, e:
1736
        logger.exception(e)
1737
        messages.error(_(astakos_messages.GENERIC_ERRO))
1738
        rollback = True
1739
    finally:
1740
        if rollback:
1741
            transaction.rollback()
1742
        else:
1743
            transaction.commit()
1744
    
1745
    next = restrict_next(
1746
        request.POST.get('next'),
1747
        domain=COOKIE_DOMAIN)
1748
    return redirect(next)
1749

    
1750
@require_http_methods(["GET"])
1751
@signed_terms_required
1752
@login_required
1753
@transaction.commit_manually
1754
def project_approve_member(request, application_id, user_id):
1755
    rollback = False
1756
    try:
1757
        m = accept_membership(application_id, user_id, request.user)
1758
    except (IOError, PermissionDenied), e:
1759
        messages.error(request, e)
1760
    except BaseException, e:
1761
        logger.exception(e)
1762
        messages.error(_(astakos_messages.GENERIC_ERRO))
1763
        rollback = True
1764
    else:
1765
        realname = m.person.realname
1766
        msg = _(astakos_messages.USER_JOINED_PROJECT) % locals()
1767
        messages.success(request, msg)
1768
    finally:
1769
        if rollback:
1770
            transaction.rollback()
1771
        else:
1772
            transaction.commit()
1773
    return project_detail(request, application_id)
1774

    
1775
@require_http_methods(["GET"])
1776
@signed_terms_required
1777
@login_required
1778
@transaction.commit_manually
1779
def project_remove_member(request, application_id, user_id):
1780
    rollback = False
1781
    try:
1782
        m = remove_membership(application_id, user_id, request.user)
1783
    except (IOError, PermissionDenied), e:
1784
        messages.error(request, e)
1785
    except BaseException, e:
1786
        logger.exception(e)
1787
        messages.error(_(astakos_messages.GENERIC_ERRO))
1788
        rollback = True
1789
    else:
1790
        realname = m.person.realname
1791
        msg = _(astakos_messages.USER_LEFT_PROJECT) % locals()
1792
        messages.success(request, msg)
1793
    finally:
1794
        if rollback:
1795
            transaction.rollback()
1796
        else:
1797
            transaction.commit()
1798
    return project_detail(request, application_id)
1799

    
1800
@require_http_methods(["GET"])
1801
@signed_terms_required
1802
@login_required
1803
@transaction.commit_manually
1804
def project_reject_member(request, application_id, user_id):
1805
    rollback = False
1806
    try:
1807
        m = reject_membership(application_id, user_id, request.user)
1808
    except (IOError, PermissionDenied), e:
1809
        messages.error(request, e)
1810
    except BaseException, e:
1811
        logger.exception(e)
1812
        messages.error(_(astakos_messages.GENERIC_ERRO))
1813
        rollback = True
1814
    else:
1815
        realname = m.person.realname
1816
        msg = _(astakos_messages.USER_LEFT_PROJECT) % locals()
1817
        messages.success(request, msg)
1818
    finally:
1819
        if rollback:
1820
            transaction.rollback()
1821
        else:
1822
            transaction.commit()
1823
    return project_detail(request, application_id)