l.append(dict(url=absolute(reverse('invite')), name="Invitations"))
l.append(dict(url=absolute(reverse('feedback')), name="Feedback"))
l.append(dict(url=absolute(reverse('group_list')), name="Groups"))
+ l.append(dict(url=absolute(reverse('resource_list')), name="Resources"))
if with_signout:
l.append(dict(url=absolute(reverse('logout')), name="Sign out"))
from os.path import abspath
from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group
+
+from astakos.im.models import AstakosGroup
from ._common import add_group_permission
name = args[0].decode('utf8')
try:
- Group.objects.get(name=name)
+ AstakosGroup.objects.get(name=name)
raise CommandError("A group with this name already exists")
- except Group.DoesNotExist, e:
- group = Group(name=name)
+ except AstakosGroup.DoesNotExist, e:
+ group = AstakosGroup(name=name)
group.save()
msg = "Created group id %d" % (group.id,)
self.stdout.write(msg + '\n')
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group
-from astakos.im.models import AstakosUser
+from astakos.im.models import AstakosUser, AstakosGroup
from ._common import format_bool
class Command(BaseCommand):
- help = "List g"
+ help = "List groups"
option_list = BaseCommand.option_list + (
make_option('-c',
dest='csv',
default=False,
help="Use pipes to separate values"),
+ make_option('-p',
+ action='store_true',
+ dest='pending',
+ default=False,
+ help="List only groups pending enable"),
)
def handle(self, *args, **options):
if args:
raise CommandError("Command doesn't accept any arguments")
- groups = Group.objects.all()
+ groups = AstakosGroup.objects.all()
+
+ if options.get('pending'):
+ groups = filter(lambda g: g.is_disabled, groups)
- labels = ('id', 'name', 'permissions')
- columns = (3, 12, 50)
+ labels = ('id', 'name', 'enabled', 'permissions')
+ columns = (3, 12, 12, 50)
- if not options['csv']:
+ if not options.get('csv'):
line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
self.stdout.write(line + '\n')
sep = '-' * len(line)
self.stdout.write(sep + '\n')
for group in groups:
- fields = (str(group.id), group.name,
- ','.join(p.codename for p in group.permissions.all()))
+ fields = ( str(group.id),
+ group.name,
+ format_bool(group.is_enabled),
+ ','.join(p.codename for p in group.permissions.all()))
- if options['csv']:
+ if options.get('csv'):
line = '|'.join(fields)
else:
line = ' '.join(f.rjust(w) for f, w in zip(fields, columns))
+++ /dev/null
-# Copyright 2012 GRNET S.A. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the following
-# conditions are met:
-#
-# 1. Redistributions of source code must retain the above
-# copyright notice, this list of conditions and the following
-# disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following
-# disclaimer in the documentation and/or other materials
-# provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
-# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# The views and conclusions contained in the software and
-# documentation are those of the authors and should not be
-# interpreted as representing official policies, either expressed
-# or implied, of GRNET S.A.
-
-from optparse import make_option
-
-from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group
-from django.core.exceptions import ValidationError
-
-from astakos.im.models import AstakosUser
-from ._common import remove_group_permission
-
-class Command(BaseCommand):
- args = "<groupname> <permission> [<permissions> ...]"
- help = "Remove group permissions"
-
- def handle(self, *args, **options):
- if len(args) < 2:
- raise CommandError("Please provide a group name and at least one permission")
-
- group = None
- try:
- if args[0].isdigit():
- group = Group.objects.get(id=args[0])
- else:
- group = Group.objects.get(name=args[0])
- except Group.DoesNotExist, e:
- raise CommandError("Invalid group")
-
- try:
- for pname in args[1:]:
- r = remove_group_permission(group, pname)
- if r < 0:
- self.stdout.write('Invalid permission codename: %s\n' % pname)
- elif r == 0:
- self.stdout.write('Group has not permission: %s\n' % pname)
- elif r > 0:
- self.stdout.write('Permission: %s removed successfully\n' % pname)
- except Exception, e:
- raise CommandError(e)
\ No newline at end of file
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group, Permission
+from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
-from astakos.im.models import AstakosUser
+from astakos.im.models import AstakosUser, AstakosGroup
from ._common import add_group_permission
class Command(BaseCommand):
- args = "<groupname> <permission> [<permissions> ...]"
- help = "Add group permissions"
+ args = "<groupname>"
+ help = "Update group"
+
+ option_list = BaseCommand.option_list + (
+ make_option('--add-permission',
+ dest='add-permission',
+ help="Add user permission"),
+ make_option('--delete-permission',
+ dest='delete-permission',
+ help="Delete user permission"),
+ make_option('--enable',
+ action='store_true',
+ dest='enable',
+ default=False,
+ help="Enable group"),
+ )
def handle(self, *args, **options):
- if len(args) < 2:
- raise CommandError("Please provide a group name and at least one permission")
+ if len(args) < 1:
+ raise CommandError("Please provide a group identifier")
group = None
try:
if args[0].isdigit():
- group = Group.objects.get(id=args[0])
+ group = AstakosGroup.objects.get(id=args[0])
else:
- group = Group.objects.get(name=args[0])
- except Group.DoesNotExist, e:
+ group = AstakosGroup.objects.get(name=args[0])
+ except AstakosGroup.DoesNotExist, e:
raise CommandError("Invalid group")
try:
content_type = ContentType.objects.get(app_label='im',
model='astakosuser')
- for pname in args[1:]:
+
+ pname = options.get('add-permission')
+ if pname:
r, created = add_group_permission(group, pname)
if created:
self.stdout.write('Permission: %s created successfully\n' % pname)
self.stdout.write('Group has already permission: %s\n' % pname)
else:
self.stdout.write('Permission: %s added successfully\n' % pname)
+
+ pname = options.get('delete-permission')
+ if pname:
+ r = remove_group_permission(group, pname)
+ if r < 0:
+ self.stdout.write('Invalid permission codename: %s\n' % pname)
+ elif r == 0:
+ self.stdout.write('Group has not permission: %s\n' % pname)
+ elif r > 0:
+ self.stdout.write('Permission: %s removed successfully\n' % pname)
+
+ if options.get('enable'):
+ group.enable()
except Exception, e:
raise CommandError(e)
\ No newline at end of file
from base64 import b64encode
from urlparse import urlparse, urlunparse
from random import randint
+from collections import defaultdict
from django.db import models, IntegrityError
from django.contrib.auth.models import User, UserManager, Group
@property
def is_disabled(self):
- if not approval_date:
- return False
- return True
+ if not self.approval_date:
+ return True
+ return False
@property
- def is_active(self):
+ def is_enabled(self):
if self.is_disabled:
return False
if not self.issue_date:
def participants(self):
return len(self.approved_members)
- def approve(self):
+ def enable(self):
self.approval_date = datetime.now()
self.save()
- def disapprove(self):
+ def disable(self):
self.approval_date = None
self.save()
return map(lambda m:m.person, f)
@property
- def policies(self):
- return self.astakosgroupquota_set.all()
+ def quota(self):
+ d = {}
+ for q in self.astakosgroupquota_set.all():
+ d[q.resource.name] = q.limit
+ return d
@property
def has_undefined_policies(self):
return Invitation.objects.get(username=self.email)
except Invitation.DoesNotExist:
return None
-
+
+ @property
+ def quota(self):
+ d = defaultdict(int)
+ for q in self.astakosuserquota_set.all():
+ d[q.resource.name] += q.limit
+ for g in self.astakos_groups.all():
+ if not g.is_enabled:
+ continue
+ for r, limit in g.quota.iteritems():
+ d[r] += limit
+ # TODO set default for remaining
+ return d
+
def save(self, update_timestamps=True, **kwargs):
if update_timestamps:
if not self.id:
self.save()
return False
return True
-
- def enroll_group(self, group):
- self.membership_set.add(group)
-
- def get_astakos_groups(self, approved=True):
- if approved:
- return self.membership_set().filter(is_approved=True)
- return self.membership_set().all()
class Membership(models.Model):
person = models.ForeignKey(AstakosUser)
class Meta:
unique_together = ("person", "group")
- def save(self):
+ def save(self, *args, **kwargs):
if not self.id:
if not self.group.moderation_enabled:
self.date_joined = datetime.now()
- super(Membership, self).save()
+ super(Membership, self).save(*args, **kwargs)
@property
def is_approved(self):
{% extends "im/account_base.html" %}
+{% load filters %}
+
{% block page.body %}
<div class="maincol {% block innerpage.class %}{% endblock %}">
<table class="zebra-striped id-sorted">
<th>Type: {{object.kind}}</th>
</tr>
<tr>
- <th>Issue date: {{object.issue_date}}</th>
+ <th>Issue date: {{object.issue_date|date:"d/m/Y"}}</th>
</tr>
<tr>
- <th>Expiration date: {{object.expiration_date}}</th>
+ <th>Expiration date: {{object.expiration_date|date:"d/m/Y"}}</th>
</tr>
<tr>
<th>Moderation: {% if object.moderation_enabled%}Yes{% else %}No{% endif %}</th>
<tr>
<th>Owner: {% for o in object.owner.all %}
{% if user == o %}
- Me!
+ Me
{% else%}
{{o.realname}} ({{o.email}})
{% endfor %}
</th>
</tr>
+ <tr>
+ <th>Enabled: {% if object.is_enabled %}Yes{% else %}No{% endif %}</th>
+ </tr>
</table>
<div class="section">
<h2>Members:</h2>
{% else %}
<td>Pending</td>
{% if user in m.group.owner.all %}
- <td><a href="{% url approve_member m.id %}">Approve</a></td>
- <td><a href="{% url disapprove_member m.id %}">Disapprove</a></td>
+ <td><a href="{% url approve_member m.group.id m.person.id %}">Approve</a></td>
+ <td><a href="{% url disapprove_member m.group.id m.person.id %}">Disapprove</a></td>
{% endif %}
{% endif %}
{% endif %}
</tr>
</thead>
<tbody>
- {% for q in quota %}
+ {% for k in quota|dkeys %}
<tr>
- <td>{{q.resource.name}}</td>
- <td>{{q.limit}}</td>
+ <td>{{ k }}</td>
+ <td>{{ quota|lookup:k }}</td>
</tr>
{% endfor %}
</tbody>
<p>No policies</p>
{% endif %}
</div>
- {% if more_policies %}
+ {% if user in object.owner.all and more_policies %}
<div class="rightcol">
<form action="{% url group_policies_add object.id %}" method="post" class="innerlabels signup">{% csrf_token %}
<h2><span>NEW POLICY</span></h2>
<th>Expiration date</th>
<th>Owner?</th>
<th>Participants</th>
+ <th>Enabled?</th>
<th>Moderation?</th>
<th>Enrollment status</th>
</tr>
<tr>
<td><a class="extra-link" href="{% url group_detail o.id %}">{{o.name}}</a></td>
<td>{{o.kind}}</td>
- <td>{{o.issue_date|date:"D d M Y"}}</td>
- <td>{{o.expiration_date|date:"D d M Y"}}</td>
+ <td>{{o.issue_date|date:"d/m/Y"}}</td>
+ <td>{{o.expiration_date|date:"d/m/Y"}}</td>
<td>{% if user in o.owner.all %}Yes{% else %}No{% endif %}</td>
<td>{{ o.approved_members|length }}/{{ o.members|length }}</td>
+ <td>{% if o.is_enabled %}Yes{% else %}No{% endif %}</td>
<td>{% if o.moderation_enabled%}Yes{% else %}No{% endif %}</td>
{% if user in o.approved_members %}
<td>Active</td>
--- /dev/null
+{% extends "im/account_base.html" %}
+
+{% load filters %}
+
+{% block page.body %}
+<div class="maincol {% block innerpage.class %}{% endblock %}">
+ <div class="section">
+ {% if quota %}
+ <table class="zebra-striped id-sorted">
+ <thead>
+ <tr>
+ <th>Resource</th>
+ <th>Limit</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for k in quota|dkeys %}
+ <tr>
+ <td>{{ k }}</td>
+ <td>{{ quota|lookup:k }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% else %}
+ <p>No policies</p>
+ {% endif %}
+ </div>
+</div>
+{% endblock %}
url(r'^approval_terms/?$', 'approval_terms', {}, name='latest_terms'),
url(r'^approval_terms/(?P<term_id>\d+)/?$', 'approval_terms'),
url(r'^password/?$', 'change_password', {}, name='password_change'),
+ url(r'^resources/?$', 'resource_list', {}, name='resource_list'),
url(r'^group/add/?$', 'group_add', {}, name='group_add'),
url(r'^group/list/?$', 'group_list', {}, name='group_list'),
url(r'^group/(?P<group_id>\d+)/?$', 'group_detail', {}, name='group_detail'),
- url(r'^group/(?P<group_id>\d+)/policies/list/?$', 'group_policies_list', {}, name='group_policies_list'),
+ #url(r'^group/(?P<group_id>\d+)/policies/list/?$', 'group_policies_list', {}, name='group_policies_list'),
url(r'^group/(?P<group_id>\d+)/policies/add/?$', 'group_policies_add', {}, name='group_policies_add'),
url(r'^group/search/?$', 'group_search', {}, name='group_search'),
url(r'^group/(?P<group_id>\d+)/join/?$', 'group_join', {}, name='group_join'),
url(r'^group/(?P<group_id>\d+)/leave/?$', 'group_leave', {}, name='group_leave'),
- url(r'^group/(?P<group_id>\d+)/request/approval/?$', 'group_approval_request', {}, name='group_approval_request'),
- url(r'^group/(?P<membership_id>\d+)/approve/?$', 'approve_member', {}, name='approve_member'),
- url(r'^group/(?P<membership_id>\d+)/disapprove/?$', 'disapprove_member', {}, name='disapprove_member'),
+ #url(r'^group/(?P<group_id>\d+)/request/approval/?$', 'group_approval_request', {}, name='group_approval_request'),
+ url(r'^group/(?P<group_id>\d+)/(?P<user_id>\d+)/approve/?$', 'approve_member', {}, name='approve_member'),
+ url(r'^group/(?P<group_id>\d+)/(?P<user_id>\d+)/disapprove/?$', 'disapprove_member', {}, name='disapprove_member'),
)
if EMAILCHANGE_ENABLED:
AstakosGroup.objects.all(),
object_id=group_id,
extra_context = {'form':get_astakos_group_policy_creation_form(group),
- 'quota':group.policies,
+ 'quota':group.quota,
'more_policies':group.has_undefined_policies})
@signed_terms_required
template_name = 'im/astakosgroup_detail.html',
post_save_redirect = reverse('group_detail', kwargs=dict(group_id=group_id)),
extra_context = {'group':group,
- 'quota':group.policies,
+ 'quota':group.quota,
'more_policies':group.has_undefined_policies})
@signed_terms_required
@login_required
def handle_membership():
def decorator(func):
@wraps(func)
- def wrapper(request, membership_id):
+ def wrapper(request, group_id, user_id):
try:
- m = Membership.objects.select_related().get(id=membership_id)
+ m = Membership.objects.select_related().get(group__id=group_id, person__id=user_id)
except Membership.DoesNotExist:
return HttpResponseBadRequest(_('Invalid membership.'))
else:
return render_response(template='im/astakosgroup_detail.html',
context_instance=get_context(request),
object=m.group,
- quota=m.group.policies,
+ quota=m.group.quota,
more_policies=m.group.has_undefined_policies)
return wrapper
return decorator
logger.exception(e)
msg = _('Something went wrong during %s\'s disapproval.' % realname)
messages.error(request, msg)
+
+@signed_terms_required
+@login_required
+def resource_list(request):
+ return render_response(template='im/astakosuserquota_list.html',
+ context_instance=get_context(request),
+ quota=request.user.quota)
\ No newline at end of file