Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / views.py @ 5200e864

History | View | Annotate | Download (65.5 kB)

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

    
34
import logging
35
import calendar
36
import inflect
37

    
38
engine = inflect.engine()
39

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

    
44
from django.shortcuts import get_object_or_404
45
from django.contrib import messages
46
from django.contrib.auth.decorators import login_required
47
from django.core.urlresolvers import reverse
48
from django.db import transaction
49
from django.db.utils import IntegrityError
50
from django.http import (
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
    apply_extra_context, 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
                        #TODO: add session message
146
                        return HttpResponseRedirect(reverse('login'))
147
            return func(request, *args)
148
        return wrapper
149
    return decorator
150

    
151

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

    
166

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

    
182

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

189
    **Arguments**
190

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

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

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

202
    **Template:**
203

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

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

    
212
    return render_response(
213
        template_name,
214
        login_form = LoginForm(request=request),
215
        context_instance = get_context(request, extra_context)
216
    )
217

    
218

    
219
@require_http_methods(["GET", "POST"])
220
@login_required
221
@signed_terms_required
222
@transaction.commit_manually
223
def invite(request, template_name='im/invitations.html', extra_context=None):
224
    """
225
    Allows a user to invite somebody else.
226

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

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

234
    If the user isn't logged in, redirects to settings.LOGIN_URL.
235

236
    **Arguments**
237

238
    ``template_name``
239
        A custom template to use. This is optional; if not specified,
240
        this will default to ``im/invitations.html``.
241

242
    ``extra_context``
243
        An dictionary of variables to add to the template context.
244

245
    **Template:**
246

247
    im/invitations.html or ``template_name`` keyword argument.
248

249
    **Settings:**
250

251
    The view expectes the following settings are defined:
252

253
    * LOGIN_URL: login uri
254
    """
255
    extra_context = extra_context or {}
256
    status = None
257
    message = None
258
    form = InvitationForm()
259

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

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

    
297

    
298
@require_http_methods(["GET", "POST"])
299
@login_required
300
@signed_terms_required
301
def edit_profile(request, template_name='im/profile.html', extra_context=None):
302
    """
303
    Allows a user to edit his/her profile.
304

305
    In case of GET request renders a form for displaying the user information.
306
    In case of POST updates the user informantion and redirects to ``next``
307
    url parameter if exists.
308

309
    If the user isn't logged in, redirects to settings.LOGIN_URL.
310

311
    **Arguments**
312

313
    ``template_name``
314
        A custom template to use. This is optional; if not specified,
315
        this will default to ``im/profile.html``.
316

317
    ``extra_context``
318
        An dictionary of variables to add to the template context.
319

320
    **Template:**
321

322
    im/profile.html or ``template_name`` keyword argument.
323

324
    **Settings:**
325

326
    The view expectes the following settings are defined:
327

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

    
364
    # existing providers
365
    user_providers = request.user.get_active_auth_providers()
366

    
367
    # providers that user can add
368
    user_available_providers = request.user.get_available_auth_providers()
369

    
370
    return render_response(template_name,
371
                           profile_form = form,
372
                           user_providers = user_providers,
373
                           user_available_providers = user_available_providers,
374
                           context_instance = get_context(request,
375
                                                          extra_context))
376

    
377

    
378
@transaction.commit_manually
379
@require_http_methods(["GET", "POST"])
380
def signup(request, template_name='im/signup.html', on_success='im/signup_complete.html', extra_context=None, backend=None):
381
    """
382
    Allows a user to create a local account.
383

384
    In case of GET request renders a form for entering the user information.
385
    In case of POST handles the signup.
386

387
    The user activation will be delegated to the backend specified by the ``backend`` keyword argument
388
    if present, otherwise to the ``astakos.im.activation_backends.InvitationBackend``
389
    if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.activation_backends.SimpleBackend`` if not
390
    (see activation_backends);
391

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

395
    On unsuccessful creation, renders ``template_name`` with an error message.
396

397
    **Arguments**
398

399
    ``template_name``
400
        A custom template to render. This is optional;
401
        if not specified, this will default to ``im/signup.html``.
402

403
    ``on_success``
404
        A custom template to render in case of success. This is optional;
405
        if not specified, this will default to ``im/signup_complete.html``.
406

407
    ``extra_context``
408
        An dictionary of variables to add to the template context.
409

410
    **Template:**
411

412
    im/signup.html or ``template_name`` keyword argument.
413
    im/signup_complete.html or ``on_success`` keyword argument.
414
    """
415
    extra_context = extra_context or {}
416
    if request.user.is_authenticated():
417
        return HttpResponseRedirect(reverse('edit_profile'))
418

    
419
    provider = get_query(request).get('provider', 'local')
420
    if not auth_providers.get_provider(provider).is_available_for_create():
421
        raise PermissionDenied
422

    
423
    id = get_query(request).get('id')
424
    try:
425
        instance = AstakosUser.objects.get(id=id) if id else None
426
    except AstakosUser.DoesNotExist:
427
        instance = None
428

    
429
    third_party_token = request.REQUEST.get('third_party_token', None)
430
    if third_party_token:
431
        pending = get_object_or_404(PendingThirdPartyUser,
432
                                    token=third_party_token)
433
        provider = pending.provider
434
        instance = pending.get_user_instance()
435

    
436
    try:
437
        if not backend:
438
            backend = get_backend(request)
439
        form = backend.get_signup_form(provider, instance)
440
    except Exception, e:
441
        form = SimpleBackend(request).get_signup_form(provider)
442
        messages.error(request, e)
443
    if request.method == 'POST':
444
        if form.is_valid():
445
            user = form.save(commit=False)
446
            try:
447
                result = backend.handle_activation(user)
448
                status = messages.SUCCESS
449
                message = result.message
450

    
451
                form.store_user(user, request)
452

    
453
                if 'additional_email' in form.cleaned_data:
454
                    additional_email = form.cleaned_data['additional_email']
455
                    if additional_email != user.email:
456
                        user.additionalmail_set.create(email=additional_email)
457
                        msg = 'Additional email: %s saved for user %s.' % (
458
                            additional_email,
459
                            user.email
460
                        )
461
                        logger._log(LOGGING_LEVEL, msg, [])
462
                if user and user.is_active:
463
                    next = request.POST.get('next', '')
464
                    response = prepare_response(request, user, next=next)
465
                    transaction.commit()
466
                    return response
467
                messages.add_message(request, status, message)
468
                transaction.commit()
469
                return render_response(
470
                    on_success,
471
                    context_instance=get_context(
472
                        request,
473
                        extra_context
474
                    )
475
                )
476
            except SendMailError, e:
477
                logger.exception(e)
478
                status = messages.ERROR
479
                message = e.message
480
                messages.error(request, message)
481
                transaction.rollback()
482
            except BaseException, e:
483
                logger.exception(e)
484
                message = _(astakos_messages.GENERIC_ERROR)
485
                messages.error(request, message)
486
                logger.exception(e)
487
                transaction.rollback()
488
    return render_response(template_name,
489
                           signup_form=form,
490
                           third_party_token=third_party_token,
491
                           provider=provider,
492
                           context_instance=get_context(request, extra_context))
493

    
494

    
495
@require_http_methods(["GET", "POST"])
496
@login_required
497
@signed_terms_required
498
def feedback(request, template_name='im/feedback.html', email_template_name='im/feedback_mail.txt', extra_context=None):
499
    """
500
    Allows a user to send feedback.
501

502
    In case of GET request renders a form for providing the feedback information.
503
    In case of POST sends an email to support team.
504

505
    If the user isn't logged in, redirects to settings.LOGIN_URL.
506

507
    **Arguments**
508

509
    ``template_name``
510
        A custom template to use. This is optional; if not specified,
511
        this will default to ``im/feedback.html``.
512

513
    ``extra_context``
514
        An dictionary of variables to add to the template context.
515

516
    **Template:**
517

518
    im/signup.html or ``template_name`` keyword argument.
519

520
    **Settings:**
521

522
    * LOGIN_URL: login uri
523
    """
524
    extra_context = extra_context or {}
525
    if request.method == 'GET':
526
        form = FeedbackForm()
527
    if request.method == 'POST':
528
        if not request.user:
529
            return HttpResponse('Unauthorized', status=401)
530

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

    
546

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

    
563
    next = restrict_next(
564
        request.GET.get('next'),
565
        domain=COOKIE_DOMAIN
566
    )
567

    
568
    if next:
569
        response['Location'] = next
570
        response.status_code = 302
571
    elif LOGOUT_NEXT:
572
        response['Location'] = LOGOUT_NEXT
573
        response.status_code = 301
574
    else:
575
        messages.add_message(request, messages.SUCCESS, _(astakos_messages.LOGOUT_SUCCESS))
576
        response['Location'] = reverse('index')
577
        response.status_code = 301
578
    return response
579

    
580

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

589
    The view uses commit_manually decorator in order to ensure the user state will be updated
590
    only if the email will be send successfully.
591
    """
592
    token = request.GET.get('auth')
593
    next = request.GET.get('next')
594
    try:
595
        user = AstakosUser.objects.get(auth_token=token)
596
    except AstakosUser.DoesNotExist:
597
        return HttpResponseBadRequest(_(astakos_messages.ACCOUNT_UNKNOWN))
598

    
599
    if user.is_active:
600
        message = _(astakos_messages.ACCOUNT_ALREADY_ACTIVE)
601
        messages.error(request, message)
602
        return index(request)
603

    
604
    try:
605
        activate_func(user, greeting_email_template_name, helpdesk_email_template_name, verify_email=True)
606
        response = prepare_response(request, user, next, renew=True)
607
        transaction.commit()
608
        return response
609
    except SendMailError, e:
610
        message = e.message
611
        messages.add_message(request, messages.ERROR, message)
612
        transaction.rollback()
613
        return index(request)
614
    except BaseException, e:
615
        status = messages.ERROR
616
        message = _(astakos_messages.GENERIC_ERROR)
617
        messages.add_message(request, messages.ERROR, message)
618
        logger.exception(e)
619
        transaction.rollback()
620
        return index(request)
621

    
622

    
623
@require_http_methods(["GET", "POST"])
624
def approval_terms(request, term_id=None, template_name='im/approval_terms.html', extra_context=None):
625
    extra_context = extra_context or {}
626
    term = None
627
    terms = None
628
    if not term_id:
629
        try:
630
            term = ApprovalTerms.objects.order_by('-id')[0]
631
        except IndexError:
632
            pass
633
    else:
634
        try:
635
            term = ApprovalTerms.objects.get(id=term_id)
636
        except ApprovalTerms.DoesNotExist, e:
637
            pass
638

    
639
    if not term:
640
        messages.error(request, _(astakos_messages.NO_APPROVAL_TERMS))
641
        return HttpResponseRedirect(reverse('index'))
642
    f = open(term.location, 'r')
643
    terms = f.read()
644

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

    
669

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

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

    
723

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

    
726
    if settings.MODERATION_ENABLED:
727
        raise PermissionDenied
728

    
729
    extra_context = extra_context or {}
730
    try:
731
        u = AstakosUser.objects.get(id=user_id)
732
    except AstakosUser.DoesNotExist:
733
        messages.error(request, _(astakos_messages.ACCOUNT_UNKNOWN))
734
    else:
735
        try:
736
            send_activation_func(u)
737
            msg = _(astakos_messages.ACTIVATION_SENT)
738
            messages.success(request, msg)
739
        except SendMailError, e:
740
            messages.error(request, e)
741
    return render_response(
742
        template_name,
743
        login_form = LoginForm(request=request),
744
        context_instance = get_context(
745
            request,
746
            extra_context
747
        )
748
    )
749

    
750
# class ResourcePresentation():
751
# 
752
#     def __init__(self, data):
753
#         self.data = data
754
# 
755
#     def update_from_result(self, result):
756
#         if result.is_success:
757
#             for r in result.data:
758
#                 rname = '%s%s%s' % (r.get('service'), RESOURCE_SEPARATOR, r.get('name'))
759
#                 if not rname in self.data['resources']:
760
#                     self.data['resources'][rname] = {}
761
# 
762
#                 self.data['resources'][rname].update(r)
763
#                 self.data['resources'][rname]['id'] = rname
764
#                 group = r.get('group')
765
#                 if not group in self.data['groups']:
766
#                     self.data['groups'][group] = {}
767
# 
768
#                 self.data['groups'][r.get('group')].update({'name': r.get('group')})
769
# 
770
#     def test(self, quota_dict):
771
#         for k, v in quota_dict.iteritems():
772
#             rname = k
773
#             value = v
774
#             if not rname in self.data['resources']:
775
#                 self.data['resources'][rname] = {}
776
# 
777
# 
778
#             self.data['resources'][rname]['value'] = value
779
# 
780
# 
781
#     def update_from_result_report(self, result):
782
#         if result.is_success:
783
#             for r in result.data:
784
#                 rname = r.get('name')
785
#                 if not rname in self.data['resources']:
786
#                     self.data['resources'][rname] = {}
787
# 
788
#                 self.data['resources'][rname].update(r)
789
#                 self.data['resources'][rname]['id'] = rname
790
#                 group = r.get('group')
791
#                 if not group in self.data['groups']:
792
#                     self.data['groups'][group] = {}
793
# 
794
#                 self.data['groups'][r.get('group')].update({'name': r.get('group')})
795
# 
796
#     def get_group_resources(self, group):
797
#         return dict(filter(lambda t: t[1].get('group') == group, self.data['resources'].iteritems()))
798
# 
799
#     def get_groups_resources(self):
800
#         for g in self.data['groups']:
801
#             yield g, self.get_group_resources(g)
802
# 
803
#     def get_quota(self, group_quotas):
804
#         for r, v in group_quotas:
805
#             rname = str(r)
806
#             quota = self.data['resources'].get(rname)
807
#             quota['value'] = v
808
#             yield quota
809
# 
810
# 
811
#     def get_policies(self, policies_data):
812
#         for policy in policies_data:
813
#             rname = '%s%s%s' % (policy.get('service'), RESOURCE_SEPARATOR, policy.get('resource'))
814
#             policy.update(self.data['resources'].get(rname))
815
#             yield policy
816
# 
817
#     def __repr__(self):
818
#         return self.data.__repr__()
819
# 
820
#     def __iter__(self, *args, **kwargs):
821
#         return self.data.__iter__(*args, **kwargs)
822
# 
823
#     def __getitem__(self, *args, **kwargs):
824
#         return self.data.__getitem__(*args, **kwargs)
825
# 
826
#     def get(self, *args, **kwargs):
827
#         return self.data.get(*args, **kwargs)
828

    
829

    
830

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

    
894

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

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

    
997

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

    
1095

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

    
1163

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

    
1211

    
1212
##@require_http_methods(["POST"])
1213
# @require_http_methods(["POST", "GET"])
1214
# @signed_terms_required
1215
# @login_required
1216
# def group_join(request, group_id):
1217
#     m = Membership(group_id=group_id,
1218
#                    person=request.user,
1219
#                    date_requested=datetime.now())
1220
#     try:
1221
#         m.save()
1222
#         post_save_redirect = reverse(
1223
#             'group_detail',
1224
#             kwargs=dict(group_id=group_id))
1225
#         return HttpResponseRedirect(post_save_redirect)
1226
#     except IntegrityError, e:
1227
#         logger.exception(e)
1228
#         msg = _(astakos_messages.GROUP_JOIN_FAILURE)
1229
#         messages.error(request, msg)
1230
#         return group_search(request)
1231
# 
1232
# 
1233
# @require_http_methods(["POST"])
1234
# @signed_terms_required
1235
# @login_required
1236
# def group_leave(request, group_id):
1237
#     try:
1238
#         m = Membership.objects.select_related().get(
1239
#             group__id=group_id,
1240
#             person=request.user)
1241
#     except Membership.DoesNotExist:
1242
#         return HttpResponseBadRequest(_(astakos_messages.NOT_MEMBER))
1243
#     if request.user in m.group.owner.all():
1244
#         return HttpResponseForbidden(_(astakos_messages.OWNER_CANNOT_LEAVE_GROUP))
1245
#     return delete_object(
1246
#         request,
1247
#         model=Membership,
1248
#         object_id=m.id,
1249
#         template_name='im/astakosgroup_list.html',
1250
#         post_delete_redirect=reverse(
1251
#             'group_detail',
1252
#             kwargs=dict(group_id=group_id)))
1253

    
1254

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

    
1271

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

    
1292

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

    
1307

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

    
1333
        return entry
1334

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

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

    
1355

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

    
1362

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

    
1400

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

    
1422

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

    
1453

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

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

    
1470

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

    
1476
@transaction.commit_manually
1477
def _create_object(request, model=None, template_name=None,
1478
        template_loader=template_loader, extra_context=None, post_save_redirect=None,
1479
        login_required=False, context_processors=None, form_class=None):
1480
    """
1481
    Based of django.views.generic.create_update.create_object which shows a
1482
    summary page before creating the object.
1483
    """
1484
    rollback = False
1485
    response = None
1486
    try:
1487
        if extra_context is None: extra_context = {}
1488
        if login_required and not request.user.is_authenticated():
1489
            return redirect_to_login(request.path)
1490
    
1491
        model, form_class = get_model_and_form_class(model, form_class)
1492
        extra_context['edit'] = 0
1493
        if request.method == 'POST':
1494
            form = form_class(request.POST, request.FILES)
1495
            if form.is_valid():
1496
                verify = request.GET.get('verify')
1497
                edit = request.GET.get('edit')
1498
                if verify == '1':
1499
                    extra_context['show_form'] = False
1500
                    extra_context['form_data'] = form.cleaned_data
1501
                elif edit == '1':
1502
                    extra_context['show_form'] = True
1503
                else:
1504
                    new_object = form.save()
1505
                    
1506
                    msg = _("The %(verbose_name)s was created successfully.") %\
1507
                                {"verbose_name": model._meta.verbose_name}
1508
                    messages.success(request, msg, fail_silently=True)
1509
                    response = redirect(post_save_redirect, new_object)
1510
        else:
1511
            form = form_class()
1512
    except BaseException, e:
1513
        logger.exception(e)
1514
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
1515
        rollback = True
1516
    finally:
1517
        if rollback:
1518
            transaction.rollback()
1519
        else:
1520
            transaction.commit()
1521
        if response == None:
1522
            # Create the template, context, response
1523
            if not template_name:
1524
                template_name = "%s/%s_form.html" %\
1525
                     (model._meta.app_label, model._meta.object_name.lower())
1526
            t = template_loader.get_template(template_name)
1527
            c = RequestContext(request, {
1528
                'form': form
1529
            }, context_processors)
1530
            apply_extra_context(extra_context, c)
1531
            response = HttpResponse(t.render(c))
1532
        return response
1533

    
1534
@require_http_methods(["GET", "POST"])
1535
@signed_terms_required
1536
@login_required
1537
def project_add(request):
1538
    result = callpoint.list_resources()
1539
    if not result.is_success:
1540
        messages.error(
1541
            request,
1542
            'Unable to retrieve system resources: %s' % result.reason
1543
    )
1544
    else:
1545
        resource_catalog = result.data
1546
    extra_context = {'resource_catalog':resource_catalog, 'show_form':True}
1547
    return _create_object(request, template_name='im/projects/projectapplication_form.html',
1548
        extra_context=extra_context, post_save_redirect='/im/project/list/',
1549
        form_class=ProjectApplicationForm)
1550

    
1551

    
1552
@require_http_methods(["GET"])
1553
@signed_terms_required
1554
@login_required
1555
def project_list(request):
1556
    q = ProjectApplication.objects.filter(owner=request.user)
1557
    q |= ProjectApplication.objects.filter(
1558
        project__in=request.user.projectmembership_set.values_list('project', flat=True)
1559
    )
1560
    q = q.select_related()
1561
    sorting = 'name'
1562
    sort_form = ProjectSortForm(request.GET)
1563
    if sort_form.is_valid():
1564
        sorting = sort_form.cleaned_data.get('sorting')
1565
    q = q.order_by(sorting)
1566
    
1567
    return object_list(
1568
        request,
1569
        q,
1570
        paginate_by=PAGINATE_BY_ALL,
1571
        page=request.GET.get('page') or 1,
1572
        template_name='im/projects/project_list.html',
1573
        extra_context={
1574
            'is_search':False,
1575
            'sorting':sorting
1576
        }
1577
    )
1578

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

    
1587
    if not result.is_success:
1588
        messages.error(
1589
            request,
1590
            'Unable to retrieve system resources: %s' % result.reason
1591
    )
1592
    else:
1593
        resource_catalog = result.data
1594
    extra_context = {'resource_catalog':resource_catalog, 'show_form':True}
1595
    return update_object(
1596
        request,
1597
        object_id=application_id,
1598
#         slug_field='projectapplication__id',
1599
        template_name='im/projects/projectapplication_form.html',
1600
        extra_context=extra_context, post_save_redirect='/im/project/list/',
1601
        form_class=ProjectApplicationForm)
1602

    
1603

    
1604
@require_http_methods(["GET", "POST"])
1605
@signed_terms_required
1606
@login_required
1607
@transaction.commit_manually
1608
def project_detail(request, application_id):
1609
    result = callpoint.list_resources()
1610
    if not result.is_success:
1611
        messages.error(
1612
            request,
1613
            'Unable to retrieve system resources: %s' % result.reason
1614
    )
1615
    else:
1616
        resource_catalog = result.data
1617
#     resource_catalog = ResourcePresentation(RESOURCES_PRESENTATION_DATA)
1618
#     resource_catalog.update_from_result(result)
1619

    
1620
    addmembers_form = AddProjectMembersForm()
1621
    if request.method == 'POST':
1622
        addmembers_form = AddProjectMembersForm(request.POST)
1623
        if addmembers_form.is_valid():
1624
            try:
1625
                rollback = False
1626
                map(lambda u: accept_membership(
1627
                        application_id,
1628
                        u,
1629
                        request_user=request.user),
1630
                    addmembers_form.valid_users)
1631
            except (IOError, PermissionDenied), e:
1632
                messages.error(request, e)
1633
            except BaseException, e:
1634
                rollback = True
1635
                messages.error(request, e)
1636
            finally:
1637
                if rollback == True:
1638
                    transaction.rollback()
1639
                else:
1640
                    transaction.commit()
1641
            addmembers_form = AddProjectMembersForm()
1642
    
1643
    # validate sorting
1644
    sorting = 'person__email'
1645
    form = ProjectMembersSortForm(request.GET or request.POST)
1646
    if form.is_valid():
1647
        sorting = form.cleaned_data.get('sorting')
1648

    
1649
    return object_detail(
1650
        request,
1651
        queryset=ProjectApplication.objects.select_related(),
1652
        object_id=application_id,
1653
        template_name='im/projects/project_detail.html',
1654
        extra_context={
1655
            'resource_catalog':resource_catalog,
1656
            'sorting':sorting,
1657
            'addmembers_form':addmembers_form
1658
        }
1659
    )
1660

    
1661
@require_http_methods(["GET", "POST"])
1662
@signed_terms_required
1663
@login_required
1664
def project_search(request):
1665
    queryset = ProjectApplication.objects
1666
    if request.method == 'GET':
1667
        form = ProjectSearchForm()
1668
        queryset = queryset.none()
1669
    else:
1670
        form = ProjectSearchForm(request.POST)
1671
        if form.is_valid():
1672
            q = form.cleaned_data['q'].strip()
1673
            queryset = queryset.filter(~Q(project__last_approval_date__isnull=True))
1674
            queryset = queryset.filter(name__contains=q)
1675
    sorting = 'name'        
1676
    # validate sorting
1677
    sort_form = ProjectSortForm(request.GET)
1678
    if sort_form.is_valid():
1679
        sorting = sort_form.cleaned_data.get('sorting')
1680
    queryset = queryset.order_by(sorting)
1681
    return object_list(
1682
        request,
1683
        queryset,
1684
        paginate_by=PAGINATE_BY_ALL,
1685
        page=request.GET.get('page') or 1,
1686
        template_name='im/projects/project_list.html',
1687
        extra_context=dict(
1688
            form=form,
1689
            is_search=True,
1690
            sorting=sorting
1691
        )
1692
    )
1693

    
1694

    
1695
@require_http_methods(["GET"])
1696
@signed_terms_required
1697
@login_required
1698
def project_all(request):
1699
    q = ProjectApplication.objects.filter(
1700
        ~Q(project__last_approval_date__isnull=True))
1701
    q = q.select_related()
1702
    sorting = 'name'
1703
    sort_form = ProjectSortForm(request.GET)
1704
    if sort_form.is_valid():
1705
        sorting = sort_form.cleaned_data.get('sorting')
1706
    q = q.order_by(sorting)
1707
    
1708
    return object_list(
1709
        request,
1710
        q,
1711
        paginate_by=PAGINATE_BY_ALL,
1712
        page=request.GET.get('page') or 1,
1713
        template_name='im/projects/project_list.html',
1714
        extra_context={
1715
            'form':ProjectSearchForm(),
1716
            'is_search':True,
1717
            'sorting':sorting
1718
        }
1719
    )
1720

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

    
1731
    rollback = False
1732
    try:
1733
        join_project(application_id, request.user)
1734
    except (IOError, PermissionDenied), e:
1735
        messages.error(request, e)
1736
    except BaseException, e:
1737
        logger.exception(e)
1738
        messages.error(request, _(astakos_messages.GENERIC_ERROR))
1739
        rollback = True
1740
    else:
1741
        return project_detail(request, id)
1742
    finally:
1743
        if rollback:
1744
            transaction.rollback()
1745
        else:
1746
            transaction.commit()
1747

    
1748
    next = restrict_next(
1749
        request.POST.get('next'),
1750
        domain=COOKIE_DOMAIN)
1751
    return redirect(next)
1752

    
1753
@require_http_methods(["POST"])
1754
@signed_terms_required
1755
@login_required
1756
@transaction.commit_manually
1757
def project_leave(request, application_id):
1758
    next = request.POST.get('next')
1759
    if not next:
1760
        return HttpResponseBadRequest(
1761
            _(astakos_messages.MISSING_NEXT_PARAMETER))
1762

    
1763
    rollback = False
1764
    try:
1765
        leave_project(application_id, request.user)
1766
    except (IOError, PermissionDenied), e:
1767
        messages.error(request, e)
1768
    except BaseException, e:
1769
        logger.exception(e)
1770
        messages.error(_(astakos_messages.GENERIC_ERRO))
1771
        rollback = True
1772
    finally:
1773
        if rollback:
1774
            transaction.rollback()
1775
        else:
1776
            transaction.commit()
1777
    
1778
    next = restrict_next(
1779
        request.POST.get('next'),
1780
        domain=COOKIE_DOMAIN)
1781
    return redirect(next)
1782

    
1783
@require_http_methods(["GET"])
1784
@signed_terms_required
1785
@login_required
1786
@transaction.commit_manually
1787
def project_approve_member(request, application_id, user_id):
1788
    rollback = False
1789
    try:
1790
        m = accept_membership(application_id, user_id, request.user)
1791
    except (IOError, PermissionDenied), e:
1792
        messages.error(request, e)
1793
    except BaseException, e:
1794
        logger.exception(e)
1795
        messages.error(_(astakos_messages.GENERIC_ERRO))
1796
        rollback = True
1797
    else:
1798
        realname = m.person.realname
1799
        msg = _(astakos_messages.USER_JOINED_PROJECT) % locals()
1800
        messages.success(request, msg)
1801
    finally:
1802
        if rollback:
1803
            transaction.rollback()
1804
        else:
1805
            transaction.commit()
1806
    return project_detail(request, application_id)
1807

    
1808
@require_http_methods(["GET"])
1809
@signed_terms_required
1810
@login_required
1811
@transaction.commit_manually
1812
def project_remove_member(request, application_id, user_id):
1813
    rollback = False
1814
    try:
1815
        m = remove_membership(application_id, user_id, request.user)
1816
    except (IOError, PermissionDenied), e:
1817
        messages.error(request, e)
1818
    except BaseException, e:
1819
        logger.exception(e)
1820
        messages.error(_(astakos_messages.GENERIC_ERRO))
1821
        rollback = True
1822
    else:
1823
        realname = m.person.realname
1824
        msg = _(astakos_messages.USER_LEFT_PROJECT) % locals()
1825
        messages.success(request, msg)
1826
    finally:
1827
        if rollback:
1828
            transaction.rollback()
1829
        else:
1830
            transaction.commit()
1831
    return project_detail(request, application_id)
1832

    
1833
@require_http_methods(["GET"])
1834
@signed_terms_required
1835
@login_required
1836
@transaction.commit_manually
1837
def project_reject_member(request, application_id, user_id):
1838
    rollback = False
1839
    try:
1840
        m = reject_membership(application_id, user_id, request.user)
1841
    except (IOError, PermissionDenied), e:
1842
        messages.error(request, e)
1843
    except BaseException, e:
1844
        logger.exception(e)
1845
        messages.error(_(astakos_messages.GENERIC_ERRO))
1846
        rollback = True
1847
    else:
1848
        realname = m.person.realname
1849
        msg = _(astakos_messages.USER_LEFT_PROJECT) % locals()
1850
        messages.success(request, msg)
1851
    finally:
1852
        if rollback:
1853
            transaction.rollback()
1854
        else:
1855
            transaction.commit()
1856
    return project_detail(request, application_id)
1857

    
1858