1 # Copyright 2011-2012 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
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.
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.
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.
37 from urllib import quote
38 from functools import wraps
39 from datetime import datetime, timedelta
41 from django.contrib import messages
42 from django.contrib.auth.decorators import login_required
43 from django.contrib.auth.views import password_change
44 from django.core.urlresolvers import reverse
45 from django.db import transaction
46 from django.db.models import Q
47 from django.db.utils import IntegrityError
48 from django.forms.fields import URLField
49 from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, \
50 HttpResponseRedirect, HttpResponseBadRequest
51 from django.shortcuts import redirect
52 from django.template import RequestContext, loader
53 from django.utils.http import urlencode
54 from django.utils.translation import ugettext as _
55 from django.views.generic.create_update import (create_object, delete_object,
56 get_model_and_form_class)
57 from django.views.generic.list_detail import object_list, object_detail
58 from django.http import HttpResponseBadRequest
59 from django.core.paginator import Paginator, InvalidPage
61 from astakos.im.models import (
62 AstakosUser, ApprovalTerms, AstakosGroup, Resource,
63 EmailChange, GroupKind, Membership)
64 from astakos.im.activation_backends import get_backend, SimpleBackend
65 from astakos.im.util import get_context, prepare_response, set_cookie, get_query
66 from astakos.im.forms import (LoginForm, InvitationForm, ProfileForm,
67 FeedbackForm, SignApprovalTermsForm,
68 ExtendedPasswordChangeForm, EmailChangeForm,
69 AstakosGroupCreationForm, AstakosGroupSearchForm,
70 AstakosGroupUpdateForm, AddGroupMembersForm,
72 from astakos.im.functions import (send_feedback, SendMailError,
73 invite as invite_func, logout as auth_logout,
74 activate as activate_func,
75 switch_account_to_shibboleth,
76 send_admin_notification,
77 SendNotificationError)
78 from astakos.im.settings import (
79 COOKIE_NAME, COOKIE_DOMAIN, SITENAME, LOGOUT_NEXT,
80 LOGGING_LEVEL, PAGINATE_BY)
81 from astakos.im.tasks import request_billing
83 logger = logging.getLogger(__name__)
86 def render_response(template, tab=None, status=200, reset_cookie=False,
87 context_instance=None, **kwargs):
89 Calls ``django.template.loader.render_to_string`` with an additional ``tab``
90 keyword argument and returns an ``django.http.HttpResponse`` with the
94 tab = template.partition('_')[0].partition('.html')[0]
95 kwargs.setdefault('tab', tab)
96 html = loader.render_to_string(
97 template, kwargs, context_instance=context_instance)
98 response = HttpResponse(html, status=status)
100 set_cookie(response, context_instance['request'].user)
104 def requires_anonymous(func):
106 Decorator checkes whether the request.user is not Anonymous and in that case
107 redirects to `logout`.
110 def wrapper(request, *args):
111 if not request.user.is_anonymous():
112 next = urlencode({'next': request.build_absolute_uri()})
113 logout_uri = reverse(logout) + '?' + next
114 return HttpResponseRedirect(logout_uri)
115 return func(request, *args)
119 def signed_terms_required(func):
121 Decorator checkes whether the request.user is Anonymous and in that case
122 redirects to `logout`.
125 def wrapper(request, *args, **kwargs):
126 if request.user.is_authenticated() and not request.user.signed_terms:
127 params = urlencode({'next': request.build_absolute_uri(),
129 terms_uri = reverse('latest_terms') + '?' + params
130 return HttpResponseRedirect(terms_uri)
131 return func(request, *args, **kwargs)
135 @signed_terms_required
136 def index(request, login_template_name='im/login.html', extra_context=None):
138 If there is logged on user renders the profile page otherwise renders login page.
142 ``login_template_name``
143 A custom login template to use. This is optional; if not specified,
144 this will default to ``im/login.html``.
146 ``profile_template_name``
147 A custom profile template to use. This is optional; if not specified,
148 this will default to ``im/profile.html``.
151 An dictionary of variables to add to the template context.
155 im/profile.html or im/login.html or ``template_name`` keyword argument.
158 template_name = login_template_name
159 if request.user.is_authenticated():
160 return HttpResponseRedirect(reverse('edit_profile'))
161 return render_response(template_name,
162 login_form=LoginForm(request=request),
163 context_instance=get_context(request, extra_context))
167 @signed_terms_required
168 @transaction.commit_manually
169 def invite(request, template_name='im/invitations.html', extra_context=None):
171 Allows a user to invite somebody else.
173 In case of GET request renders a form for providing the invitee information.
174 In case of POST checks whether the user has not run out of invitations and then
175 sends an invitation email to singup to the service.
177 The view uses commit_manually decorator in order to ensure the number of the
178 user invitations is going to be updated only if the email has been successfully sent.
180 If the user isn't logged in, redirects to settings.LOGIN_URL.
185 A custom template to use. This is optional; if not specified,
186 this will default to ``im/invitations.html``.
189 An dictionary of variables to add to the template context.
193 im/invitations.html or ``template_name`` keyword argument.
197 The view expectes the following settings are defined:
199 * LOGIN_URL: login uri
200 * ASTAKOS_DEFAULT_CONTACT_EMAIL: service support email
204 form = InvitationForm()
206 inviter = request.user
207 if request.method == 'POST':
208 form = InvitationForm(request.POST)
209 if inviter.invitations > 0:
212 invitation = form.save()
213 invite_func(invitation, inviter)
214 message = _('Invitation sent to %s' % invitation.username)
215 messages.success(request, message)
216 except SendMailError, e:
218 messages.error(request, message)
219 transaction.rollback()
220 except BaseException, e:
221 message = _('Something went wrong.')
222 messages.error(request, message)
224 transaction.rollback()
228 message = _('No invitations left')
229 messages.error(request, message)
231 sent = [{'email': inv.username,
232 'realname': inv.realname,
233 'is_consumed': inv.is_consumed}
234 for inv in request.user.invitations_sent.all()]
235 kwargs = {'inviter': inviter,
237 context = get_context(request, extra_context, **kwargs)
238 return render_response(template_name,
239 invitation_form=form,
240 context_instance=context)
244 @signed_terms_required
245 def edit_profile(request, template_name='im/profile.html', extra_context=None):
247 Allows a user to edit his/her profile.
249 In case of GET request renders a form for displaying the user information.
250 In case of POST updates the user informantion and redirects to ``next``
251 url parameter if exists.
253 If the user isn't logged in, redirects to settings.LOGIN_URL.
258 A custom template to use. This is optional; if not specified,
259 this will default to ``im/profile.html``.
262 An dictionary of variables to add to the template context.
266 im/profile.html or ``template_name`` keyword argument.
270 The view expectes the following settings are defined:
272 * LOGIN_URL: login uri
274 extra_context = extra_context or {}
275 form = ProfileForm(instance=request.user)
276 extra_context['next'] = request.GET.get('next')
278 if request.method == 'POST':
279 form = ProfileForm(request.POST, instance=request.user)
282 prev_token = request.user.auth_token
284 reset_cookie = user.auth_token != prev_token
285 form = ProfileForm(instance=user)
286 next = request.POST.get('next')
288 return redirect(next)
289 msg = _('Profile has been updated successfully')
290 messages.success(request, msg)
291 except ValueError, ve:
292 messages.success(request, ve)
293 elif request.method == "GET":
294 if not request.user.is_verified:
295 request.user.is_verified = True
297 return render_response(template_name,
298 reset_cookie=reset_cookie,
300 context_instance=get_context(request,
304 @transaction.commit_manually
305 def signup(request, template_name='im/signup.html', on_success='im/signup_complete.html', extra_context=None, backend=None):
307 Allows a user to create a local account.
309 In case of GET request renders a form for entering the user information.
310 In case of POST handles the signup.
312 The user activation will be delegated to the backend specified by the ``backend`` keyword argument
313 if present, otherwise to the ``astakos.im.activation_backends.InvitationBackend``
314 if settings.ASTAKOS_INVITATIONS_ENABLED is True or ``astakos.im.activation_backends.SimpleBackend`` if not
315 (see activation_backends);
317 Upon successful user creation, if ``next`` url parameter is present the user is redirected there
318 otherwise renders the same page with a success message.
320 On unsuccessful creation, renders ``template_name`` with an error message.
325 A custom template to render. This is optional;
326 if not specified, this will default to ``im/signup.html``.
329 A custom template to render in case of success. This is optional;
330 if not specified, this will default to ``im/signup_complete.html``.
333 An dictionary of variables to add to the template context.
337 im/signup.html or ``template_name`` keyword argument.
338 im/signup_complete.html or ``on_success`` keyword argument.
340 if request.user.is_authenticated():
341 return HttpResponseRedirect(reverse('edit_profile'))
343 provider = get_query(request).get('provider', 'local')
346 backend = get_backend(request)
347 form = backend.get_signup_form(provider)
349 form = SimpleBackend(request).get_signup_form(provider)
350 messages.error(request, e)
351 if request.method == 'POST':
353 user = form.save(commit=False)
355 result = backend.handle_activation(user)
356 status = messages.SUCCESS
357 message = result.message
359 if 'additional_email' in form.cleaned_data:
360 additional_email = form.cleaned_data['additional_email']
361 if additional_email != user.email:
362 user.additionalmail_set.create(email=additional_email)
363 msg = 'Additional email: %s saved for user %s.' % (
364 additional_email, user.email)
365 logger.log(LOGGING_LEVEL, msg)
366 if user and user.is_active:
367 next = request.POST.get('next', '')
369 return prepare_response(request, user, next=next)
370 messages.add_message(request, status, message)
372 return render_response(on_success,
373 context_instance=get_context(request, extra_context))
374 except SendMailError, e:
376 messages.error(request, message)
377 transaction.rollback()
378 except BaseException, e:
379 message = _('Something went wrong.')
380 messages.error(request, message)
382 transaction.rollback()
383 return render_response(template_name,
386 context_instance=get_context(request, extra_context))
390 @signed_terms_required
391 def feedback(request, template_name='im/feedback.html', email_template_name='im/feedback_mail.txt', extra_context=None):
393 Allows a user to send feedback.
395 In case of GET request renders a form for providing the feedback information.
396 In case of POST sends an email to support team.
398 If the user isn't logged in, redirects to settings.LOGIN_URL.
403 A custom template to use. This is optional; if not specified,
404 this will default to ``im/feedback.html``.
407 An dictionary of variables to add to the template context.
411 im/signup.html or ``template_name`` keyword argument.
415 * LOGIN_URL: login uri
416 * ASTAKOS_DEFAULT_CONTACT_EMAIL: List of feedback recipients
418 if request.method == 'GET':
419 form = FeedbackForm()
420 if request.method == 'POST':
422 return HttpResponse('Unauthorized', status=401)
424 form = FeedbackForm(request.POST)
426 msg = form.cleaned_data['feedback_msg']
427 data = form.cleaned_data['feedback_data']
429 send_feedback(msg, data, request.user, email_template_name)
430 except SendMailError, e:
431 messages.error(request, message)
433 message = _('Feedback successfully sent')
434 messages.success(request, message)
435 return render_response(template_name,
437 context_instance=get_context(request, extra_context))
440 @signed_terms_required
441 def logout(request, template='registration/logged_out.html', extra_context=None):
443 Wraps `django.contrib.auth.logout` and delete the cookie.
445 response = HttpResponse()
446 if request.user.is_authenticated():
447 email = request.user.email
449 response.delete_cookie(COOKIE_NAME, path='/', domain=COOKIE_DOMAIN)
450 msg = 'Cookie deleted for %s' % email
451 logger.log(LOGGING_LEVEL, msg)
452 next = request.GET.get('next')
454 response['Location'] = next
455 response.status_code = 302
458 response['Location'] = LOGOUT_NEXT
459 response.status_code = 301
461 messages.success(request, _('You have successfully logged out.'))
462 context = get_context(request, extra_context)
463 response.write(loader.render_to_string(template, context_instance=context))
467 @transaction.commit_manually
468 def activate(request, greeting_email_template_name='im/welcome_email.txt', helpdesk_email_template_name='im/helpdesk_notification.txt'):
470 Activates the user identified by the ``auth`` request parameter, sends a welcome email
471 and renews the user token.
473 The view uses commit_manually decorator in order to ensure the user state will be updated
474 only if the email will be send successfully.
476 token = request.GET.get('auth')
477 next = request.GET.get('next')
479 user = AstakosUser.objects.get(auth_token=token)
480 except AstakosUser.DoesNotExist:
481 return HttpResponseBadRequest(_('No such user'))
484 message = _('Account already active.')
485 messages.error(request, message)
486 return index(request)
489 local_user = AstakosUser.objects.get(
494 except AstakosUser.DoesNotExist:
498 greeting_email_template_name,
499 helpdesk_email_template_name,
502 response = prepare_response(request, user, next, renew=True)
505 except SendMailError, e:
507 messages.error(request, message)
508 transaction.rollback()
509 return index(request)
510 except BaseException, e:
511 message = _('Something went wrong.')
512 messages.error(request, message)
514 transaction.rollback()
515 return index(request)
518 user = switch_account_to_shibboleth(
521 greeting_email_template_name
523 response = prepare_response(request, user, next, renew=True)
526 except SendMailError, e:
528 messages.error(request, message)
529 transaction.rollback()
530 return index(request)
531 except BaseException, e:
532 message = _('Something went wrong.')
533 messages.error(request, message)
535 transaction.rollback()
536 return index(request)
539 def approval_terms(request, term_id=None, template_name='im/approval_terms.html', extra_context=None):
544 term = ApprovalTerms.objects.order_by('-id')[0]
549 term = ApprovalTerms.objects.get(id=term_id)
550 except ApprovalTerms.DoesNotExist, e:
554 return HttpResponseRedirect(reverse('index'))
555 f = open(term.location, 'r')
558 if request.method == 'POST':
559 next = request.POST.get('next')
561 next = reverse('index')
562 form = SignApprovalTermsForm(request.POST, instance=request.user)
563 if not form.is_valid():
564 return render_response(template_name,
566 approval_terms_form=form,
567 context_instance=get_context(request, extra_context))
569 return HttpResponseRedirect(next)
572 if request.user.is_authenticated() and not request.user.signed_terms:
573 form = SignApprovalTermsForm(instance=request.user)
574 return render_response(template_name,
576 approval_terms_form=form,
577 context_instance=get_context(request, extra_context))
580 @signed_terms_required
581 def change_password(request):
582 return password_change(request,
583 post_change_redirect=reverse('edit_profile'),
584 password_change_form=ExtendedPasswordChangeForm)
587 @signed_terms_required
589 @transaction.commit_manually
590 def change_email(request, activation_key=None,
591 email_template_name='registration/email_change_email.txt',
592 form_template_name='registration/email_change_form.html',
593 confirm_template_name='registration/email_change_done.html',
597 user = EmailChange.objects.change_email(activation_key)
598 if request.user.is_authenticated() and request.user == user:
599 msg = _('Email changed successfully.')
600 messages.success(request, msg)
602 response = prepare_response(request, user)
605 except ValueError, e:
606 messages.error(request, e)
607 return render_response(confirm_template_name,
608 modified_user=user if 'user' in locals(
610 context_instance=get_context(request,
613 if not request.user.is_authenticated():
614 path = quote(request.get_full_path())
615 url = request.build_absolute_uri(reverse('index'))
616 return HttpResponseRedirect(url + '?next=' + path)
617 form = EmailChangeForm(request.POST or None)
618 if request.method == 'POST' and form.is_valid():
620 ec = form.save(email_template_name, request)
621 except SendMailError, e:
623 messages.error(request, msg)
624 transaction.rollback()
625 except IntegrityError, e:
626 msg = _('There is already a pending change email request.')
627 messages.error(request, msg)
629 msg = _('Change email request has been registered succefully.\
630 You are going to receive a verification email in the new address.')
631 messages.success(request, msg)
633 return render_response(form_template_name,
635 context_instance=get_context(request,
639 @signed_terms_required
641 def group_add(request, kind_name='default'):
643 kind = GroupKind.objects.get(name=kind_name)
645 return HttpResponseBadRequest(_('No such group kind'))
647 template_loader = loader
648 post_save_redirect = '/im/group/%(id)s/'
649 context_processors = None
650 model, form_class = get_model_and_form_class(
652 form_class=AstakosGroupCreationForm
655 (str(r.id), r) for r in Resource.objects.select_related().all())
657 if request.method == 'POST':
658 form = form_class(request.POST, request.FILES, resources=resources)
660 new_object = form.save()
663 new_object.owners = [request.user]
665 # save quota policies
666 for (rid, uplimit) in form.resources():
671 # TODO Should I stay or should I go???
674 new_object.astakosgroupquota_set.create(
678 policies.append('%s %d' % (r, uplimit))
679 msg = _("The %(verbose_name)s was created successfully.") %\
680 {"verbose_name": model._meta.verbose_name}
681 messages.success(request, msg, fail_silently=True)
685 send_admin_notification(
686 template_name='im/group_creation_notification.txt',
689 'owner': request.user,
690 'policies': policies,
692 subject='%s alpha2 testing group creation notification' % SITENAME
694 except SendNotificationError, e:
695 messages.error(request, e, fail_silently=True)
696 return HttpResponseRedirect(post_save_redirect % new_object.__dict__)
702 form = form_class(data, resources=resources)
704 # Create the template, context, response
705 template_name = "%s/%s_form.html" % (
706 model._meta.app_label,
707 model._meta.object_name.lower()
709 t = template_loader.get_template(template_name)
710 c = RequestContext(request, {
713 }, context_processors)
714 return HttpResponse(t.render(c))
717 @signed_terms_required
719 def group_list(request):
720 q = request.user.astakos_groups.none()
721 list = request.user.astakos_groups.select_related().all()
723 d['own'] = [g for g in list if request.user in g.owner.all()]
724 d['other'] = list.exclude(id__in=(g.id for g in d['own']))
725 for k, queryset in d.iteritems():
726 paginator = Paginator(queryset, PAGINATE_BY)
727 page = request.GET.get('%s_page' % k, 1)
729 page_number = int(page)
732 page_number = paginator.num_pages
734 # Page is not 'last', nor can it be converted to an int.
737 page_obj = locals()['%s_page_obj' % k] = paginator.page(page_number)
740 return object_list(request, queryset=q,
741 extra_context={'is_search':False,
742 'mine': locals()['own_page_obj'],
743 'other': locals()['other_page_obj']})
746 @signed_terms_required
748 def group_detail(request, group_id):
750 group = AstakosGroup.objects.select_related().get(id=group_id)
751 except AstakosGroup.DoesNotExist:
752 return HttpResponseBadRequest(_('Invalid group.'))
753 form = AstakosGroupUpdateForm(instance=group)
754 search_form = AddGroupMembersForm()
755 return object_detail(request,
756 AstakosGroup.objects.all(),
758 extra_context={'quota': group.quota,
760 'search_form': search_form}
764 @signed_terms_required
766 def group_update(request, group_id):
767 if request.method != 'POST':
768 return HttpResponseBadRequest('Method not allowed.')
770 group = AstakosGroup.objects.select_related().get(id=group_id)
771 except AstakosGroup.DoesNotExist:
772 return HttpResponseBadRequest(_('Invalid group.'))
773 form = AstakosGroupUpdateForm(request.POST, instance=group)
776 search_form = AddGroupMembersForm()
777 return object_detail(request,
778 AstakosGroup.objects.all(),
780 extra_context={'quota': group.quota,
782 'search_form': search_form})
784 @signed_terms_required
786 def group_search(request, extra_context=None, **kwargs):
787 q = request.GET.get('q')
788 if request.method == 'GET':
789 form = AstakosGroupSearchForm({'q': q} if q else None)
791 form = AstakosGroupSearchForm(get_query(request))
793 q = form.cleaned_data['q'].strip()
795 queryset = AstakosGroup.objects.select_related(
796 ).filter(name__contains=q)
798 queryset = AstakosGroup.objects.none()
802 paginate_by=PAGINATE_BY,
803 page=request.GET.get('page') or 1,
804 template_name='im/astakosgroup_list.html',
805 extra_context=dict(form=form,
809 @signed_terms_required
811 def group_all(request, extra_context=None, **kwargs):
814 AstakosGroup.objects.select_related().all(),
815 paginate_by=PAGINATE_BY,
816 page=request.GET.get('page') or 1,
817 template_name='im/astakosgroup_list.html',
818 extra_context=dict(form=AstakosGroupSearchForm(),
822 @signed_terms_required
824 def group_join(request, group_id):
825 m = Membership(group_id=group_id,
827 date_requested=datetime.now()
831 post_save_redirect = reverse(
833 kwargs=dict(group_id=group_id)
835 return HttpResponseRedirect(post_save_redirect)
836 except IntegrityError, e:
838 msg = _('Failed to join group.')
839 messages.error(request, msg)
840 return group_search(request)
843 @signed_terms_required
845 def group_leave(request, group_id):
847 m = Membership.objects.select_related().get(
851 except Membership.DoesNotExist:
852 return HttpResponseBadRequest(_('Invalid membership.'))
853 if request.user in m.group.owner.all():
854 return HttpResponseForbidden(_('Owner can not leave the group.'))
855 return delete_object(
859 template_name='im/astakosgroup_list.html',
860 post_delete_redirect=reverse(
862 kwargs=dict(group_id=group_id)
867 def handle_membership(func):
869 def wrapper(request, group_id, user_id):
871 m = Membership.objects.select_related().get(
875 except Membership.DoesNotExist:
876 return HttpResponseBadRequest(_('Invalid membership.'))
878 if request.user not in m.group.owner.all():
879 return HttpResponseForbidden(_('User is not a group owner.'))
881 return render_response(
882 template='im/astakosgroup_detail.html',
883 context_instance=get_context(request),
890 @signed_terms_required
893 def approve_member(request, membership):
896 realname = membership.person.realname
897 msg = _('%s has been successfully joined the group.' % realname)
898 messages.success(request, msg)
899 except BaseException, e:
901 msg = _('Something went wrong during %s\'s approval.' % realname)
902 messages.error(request, msg)
905 @signed_terms_required
908 def disapprove_member(request, membership):
910 membership.disapprove()
911 realname = membership.person.realname
912 msg = _('%s has been successfully removed from the group.' % realname)
913 messages.success(request, msg)
914 except BaseException, e:
916 msg = _('Something went wrong during %s\'s disapproval.' % realname)
917 messages.error(request, msg)
922 @signed_terms_required
924 def add_members(request, group_id):
925 if request.method != 'POST':
926 return HttpResponseBadRequest(_('Bad method'))
928 group = AstakosGroup.objects.select_related().get(id=group_id)
929 except AstakosGroup.DoesNotExist:
930 return HttpResponseBadRequest(_('Invalid group.'))
931 search_form = AddGroupMembersForm(request.POST)
932 if search_form.is_valid():
933 users = search_form.get_valid_users()
934 map(group.approve_member, users)
935 search_form = AddGroupMembersForm()
936 form = AstakosGroupUpdateForm(instance=group)
937 return object_detail(request,
938 AstakosGroup.objects.all(),
940 extra_context={'quota': group.quota,
942 'search_form' : search_form}
946 @signed_terms_required
948 def resource_list(request):
949 return render_response(
950 template='im/astakosuserquota_list.html',
951 context_instance=get_context(request),
952 quota=request.user.quota
956 def group_create_list(request):
957 return render_response(
958 template='im/astakosgroup_create_list.html',
959 context_instance=get_context(request),
963 @signed_terms_required
965 def billing(request):
967 today = datetime.today()
968 month_last_day= calendar.monthrange(today.year, today.month)[1]
970 start = request.POST.get('datefrom', None)
972 today = datetime.fromtimestamp(int(start))
973 month_last_day= calendar.monthrange(today.year, today.month)[1]
975 start = datetime(today.year, today.month, 1).strftime("%s")
976 end = datetime(today.year, today.month, month_last_day).strftime("%s")
977 r = request_billing.apply(args=('pgerakios@grnet.gr',
983 status, data = r.result
984 data=clear_billing_data(data)
986 messages.error(request, _('Service response status: %d' % status))
988 messages.error(request, r.result)
992 return render_response(
993 template='im/billing.html',
994 context_instance=get_context(request),
996 zerodate=datetime(month=1,year=1970, day=1),
999 month_last_day=month_last_day)
1001 def clear_billing_data(data):
1003 # remove addcredits entries
1005 return e['serviceName'] != "addcredits"
1010 def servicefilter(service_name):
1011 service = service_name
1013 return e['serviceName'] == service
1017 data['bill_nocredits'] = filter(isnotcredit, data['bill'])
1018 data['bill_vmtime'] = filter(servicefilter('vmtime'), data['bill'])
1019 data['bill_diskspace'] = filter(servicefilter('diskspace'), data['bill'])
1020 data['bill_addcredits'] = filter(servicefilter('addcredits'), data['bill'])
1024 @signed_terms_required
1026 def timeline(request):
1027 data = {'entity':request.user.email}
1029 if request.method == 'POST':
1031 #l =[(u'test/apples', u'2012-09-20T11:39:53.1797', 192172055040L, 25569215632749L),
1032 # (u'test/pears/kate.jpg', u'2012-09-20T11:45:14.1153', 381640896L, 25626036449581L),
1033 # (u'test/pears/kate_beckinsale.jpg', u'2012-09-20T11:45:14.5830', 0L, 25626036449581L),
1034 # (u'test/pears/How To Win Friends And Influence People.pdf', u'2012-09-20T11:45:15.0694', 0L, 25626036449581L),
1035 # (u'test/pears/moms_birthday.jpg', u'2012-09-20T11:45:15.5615', 0L, 25626036449581L),
1036 # (u'test/pears/poodle_strut.mov', u'2012-09-20T11:45:16.0290', 0L, 25626036449581L),
1037 # (u'test/pears/Disturbed - Down With The Sickness.mp3', u'2012-09-20T11:45:16.4950', 0L, 25626036449581L),
1038 # (u'test/pears/army_of_darkness.avi', u'2012-09-20T11:45:16.9844', 0L, 25626036449581L),
1039 # (u'test/pears/the_mad.avi', u'2012-09-20T11:45:17.4516', 0L, 25626036449581L),
1040 # (u'test/apples/photos/animals/dogs/poodle.jpg', u'2012-09-20T11:45:17.9281', 0L, 25626036449581L),
1041 # (u'test/apples/photos/animals/dogs/terrier.jpg', u'2012-09-20T11:45:18.3918', 0L, 25626036449581L),
1042 # (u'test/apples/photos/animals/cats/persian.jpg', u'2012-09-20T11:45:18.8626', 0L, 25626036449581L),
1043 # (u'test/apples/photos/animals/cats/siamese.jpg', u'2012-09-20T11:45:19.3686', 0L, 25626036449581L),
1044 # (u'test/apples/photos/plants/fern.jpg', u'2012-09-20T11:45:19.8464', 0L, 25626036449581L),
1045 # (u'test/apples/photos/plants/rose.jpg', u'2012-09-20T11:45:20.7334', 0L, 25626036449581L)]
1046 timeline_header = (data['entity'], data['resource'],
1047 data['start_date'], data['end_date'],
1048 data['details'], data['operation'])
1049 timeline_body = [timeline_header] * 2
1050 form = TimelineForm(data)
1052 print '>>>', form.cleaned_data
1053 return render_response(template='im/timeline.html',
1054 context_instance=get_context(request),
1056 timeline_header=timeline_header,
1057 timeline_body=timeline_body)