From c5fafe5c3fe2f71fb7b6f37922aeab1b7cb95b3e Mon Sep 17 00:00:00 2001 From: Leif Johansson Date: Wed, 4 Aug 2010 14:14:48 +0200 Subject: break out invitation --- coip/apps/auth/views.py | 10 ++++----- coip/apps/membership/forms.py | 9 ++++----- coip/apps/membership/models.py | 27 +++++++++++++++++-------- coip/apps/membership/views.py | 43 +++++++++++---------------------------- coip/apps/name/forms.py | 3 +++ coip/apps/name/models.py | 45 +++++++++++++++++++++++++++++------------ coip/apps/name/views.py | 36 +++++++++++++++++++++------------ coip/apps/userprofile/models.py | 6 ++++-- coip/apps/userprofile/views.py | 12 +++++++++-- 9 files changed, 112 insertions(+), 79 deletions(-) diff --git a/coip/apps/auth/views.py b/coip/apps/auth/views.py index 094e974..1582a5e 100644 --- a/coip/apps/auth/views.py +++ b/coip/apps/auth/views.py @@ -8,6 +8,7 @@ from coip.apps.userprofile.models import UserProfile from django.contrib.auth.models import User from coip.apps.auth.utils import anonid from coip.apps.name.models import lookup +import datetime def meta(request,attr): v = request.META.get(attr) @@ -50,11 +51,10 @@ def accounts_login_federated(request): if update: request.user.save() - profile.save() - - #autocreate a few personal namespaces - lookup('user:'+profile.identifier,True,'system:anyuser#l '+request.user+'#rw') - lookup(request.user,True,'system:anyuser#l '+request.user+'#rw') + + # Allow auto_now to kick in for the lastupdated field + #profile.lastupdated = datetime.datetime.now() + profile.save() next = request.session.get("after_login_redirect", None) if next is not None: diff --git a/coip/apps/membership/forms.py b/coip/apps/membership/forms.py index c74307b..512647f 100644 --- a/coip/apps/membership/forms.py +++ b/coip/apps/membership/forms.py @@ -5,13 +5,12 @@ Created on Jun 23, 2010 ''' from django import forms from coip.apps.membership.models import Membership -from django.forms.widgets import Textarea class MembershipForm(forms.ModelForm): class Meta: model = Membership -class InvitationForm(forms.Form): - email = forms.EmailField() - expires = forms.DateTimeField() - message = forms.CharField(widget=Textarea) \ No newline at end of file +class InvitationForm(forms.ModelForm): + class Meta: + model = Membership + fields = ['email'] \ No newline at end of file diff --git a/coip/apps/membership/models.py b/coip/apps/membership/models.py index 935a253..9ff6a89 100644 --- a/coip/apps/membership/models.py +++ b/coip/apps/membership/models.py @@ -13,15 +13,13 @@ class Membership(models.Model): ''' Membership in a namespace/group ''' - user = models.ForeignKey(User,unique=True,blank=True,related_name='user') - inviter = models.ForeignKey(User,unique=True,blank=True,related_name='inviter') + user = models.ForeignKey(User,blank=True,null=True,related_name='user') name = models.ForeignKey(Name,related_name='memberships') - email = models.EmailField(blank=True,null=True) - nonce = models.CharField(max_length=255,blank=True,null=True) enabled = models.BooleanField() + hidden = models.BooleanField() timecreated = models.DateTimeField(auto_now_add=True) lastupdated = models.DateTimeField(auto_now=True) - expires = models.DateTimeField(blank=True) + expires = models.DateTimeField(blank=True,null=True) def __unicode__(self): return "%s in %s" % (self.user,self.name) @@ -34,8 +32,21 @@ class Membership(models.Model): return "active" else: return "inactive"; + +def add_member(name,user): + (m,created) = Membership.objects.get_or_create(user=user,name=name) + if created or not m.enabled: + m.enabled = True + m.save() + +def disable_member(name,user): + m = Membership.objects.get(name=name,user=user) + if m: + m.enabled = False + m.save() - def send_email(self): - pprint("sent email to "+self.to) - return +def remove_member(name,user): + m = Membership.objects.get(name=name,user=user) + if m: + m.delete() diff --git a/coip/apps/membership/views.py b/coip/apps/membership/views.py index 6a500d9..e479015 100644 --- a/coip/apps/membership/views.py +++ b/coip/apps/membership/views.py @@ -3,37 +3,18 @@ Created on Jun 23, 2010 @author: leifj ''' -from django.contrib.auth.decorators import login_required +from django.shortcuts import get_object_or_404 from coip.apps.membership.models import Membership -from coip.apps.membership.forms import InvitationForm -from django.http import HttpResponseRedirect -from coip.apps.auth.utils import nonce -from coip.multiresponse import respond_to +from coip.multiresponse import render403, respond_to -#@login_required -#def memberships(request,name): -# -# Membership.objects.get(name) +def show(request,id): + membership = get_object_or_404(Membership,pk=id) + name = membership.name + if not name.has_permission(request.user,'r'): + return render403("You do not have permission to view membership information for %s" % (name)) - -@login_required -def invite(request): - user = request.user - if request.method == 'POST': - form = InvitationForm(request.POST) - if form.is_valid(): - email = form.cleaned_data["email"] - expires = form.cleaned_data["expires"] - message = form.cleaned_data["message"] - membership = Membership(inviter=user,email=email,message=message,expires=expires,nonce=nonce()) - membership.save() - membership.send_email() - return HttpResponseRedirect("/membership/id/"+membership.id) - else: - form = InvitationForm({}); - - return respond_to(request,{'text/html': 'apps/invitation/create.html'},{'form': form}) - -def accept(request,nonce): - user = request.user - membership = Membership.objects.get(nonce=nonce) \ No newline at end of file + return respond_to({'text/html': 'apps/membership/membership.html'}, + {'membership': membership, + 'render': {'edit': name.has_permission(request.user,'w'), + 'delete': name.has_permission(request.user,'d'), + 'disable': name.has_permission(request.user,'d')}}) \ No newline at end of file diff --git a/coip/apps/name/forms.py b/coip/apps/name/forms.py index 47c7795..a68102e 100644 --- a/coip/apps/name/forms.py +++ b/coip/apps/name/forms.py @@ -16,11 +16,14 @@ class AttributeForm(forms.ModelForm): model = Attribute class NameEditForm(forms.ModelForm): + description = forms.CharField(widget=forms.Textarea(attrs={'cols': 85, 'rows': 10})) + class Meta: model = Name fields = ['short','description'] class NewNameForm(forms.ModelForm): + description = forms.CharField(widget=forms.Textarea(attrs={'cols': 85, 'rows': 10})) class Meta: model = Name fields = ['type','value','short','description'] diff --git a/coip/apps/name/models.py b/coip/apps/name/models.py index 0011955..e803c0b 100644 --- a/coip/apps/name/models.py +++ b/coip/apps/name/models.py @@ -8,6 +8,7 @@ import re from pprint import pprint from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist +from django.db.models.signals import post_init, pre_save class Attribute(models.Model): name = models.CharField(unique=True,max_length=255) @@ -25,9 +26,9 @@ class Name(models.Model): type = models.ForeignKey(Attribute, blank=True, null=True,related_name='names') value = models.CharField(max_length=255) parent = models.ForeignKey('self', blank=True, null=True,related_name='children') - acl = models.TextField(blank=True) # fully-qualified-name '#' rights short = models.CharField(max_length=64,blank=True) creator = models.ForeignKey(User,blank=True, null=True) + display = models.TextField(editable=False) description = models.TextField(blank=True) timecreated = models.DateTimeField(auto_now_add=True) lastupdated = models.DateTimeField(auto_now=True) @@ -45,6 +46,9 @@ class Name(models.Model): return self.value def __unicode__(self): + return self.display + + def display_str(self): n = self str = "" while n: @@ -68,8 +72,10 @@ class Name(models.Model): c.remove(recursive) self.delete() - def copy_acl(self): - return self.acl + def copyacl(self,name): + for ace in name.lsacl(): + self.setacl(ace.dst,ace.data) + def link(self,dst,type,data): if not self.has_link(dst,NameLink.part_of,data): @@ -87,10 +93,14 @@ class Name(models.Model): return NameLink.objects.filter(src=self,dst=dst,type=type,data=data).count() > 0 def setacl(self,name,perm): - link = NameLink.objects.get_or_create(src=self,dst=name,type=NameLink.access_control) + (link,b) = NameLink.objects.get_or_create(src=self,dst=name,type=NameLink.access_control) save = False + if not link.data: + link.data = '' + save = True for p in perm: - if not link.data.find(p): + pprint(p) + if link.data.find(p) == -1: link.data = link.data+p save = True if save: @@ -122,6 +132,7 @@ class Name(models.Model): # TODO: reverse order of test for production system - will spead-up superuser-test and it is cheap #pprint(NameLink.objects.filter(src=self,type=NameLink.access_control,data=perm,dst__memberships__user=user)) # user is superuser or acl is on implicit group or user is member of acl group + anyuser = lookup("system:anyuser",True) if NameLink.objects.filter(src=self,dst=anyuser,type=NameLink.access_control,data__contains=perm).count() > 0: return True if NameLink.objects.filter(src=self,type=NameLink.access_control,data__contains=perm,dst__memberships__user=user).count() > 0: @@ -135,6 +146,11 @@ class Name(models.Model): def permitted_children(self,user,perm): return filter(lambda s: s.has_permission(user,perm),self.children.all()) +def set_display(sender,**kwargs): + kwargs['instance'].display = kwargs['instance'].display_str() + +pre_save.connect(set_display,sender=Name) + class NameLink(models.Model): src = models.ForeignKey(Name,related_name='sources') dst = models.ForeignKey(Name,related_name='destinations') @@ -179,18 +195,22 @@ def traverse(name,callable,user,depth,includeroot=False): # TODO - remove system user dependency -def walkto(root,nameparts,autocreate=False,autoacl='l'): +def walkto(root,nameparts,autocreate=False): name = None for n in nameparts: (a,eq,v) = n.partition('=') + pprint("walkto %s -> %s" % (root,n)) if v: attribute = Attribute.objects.get(name=a) try: name = Name.objects.get(parent=root,type=attribute.id,value=v) except ObjectDoesNotExist,e: if autocreate: - name = Name(parent=root,creator=None,type=attribute.id,value=v,acl=autoacl) + name = Name(parent=root,creator=None,type=attribute.id,value=v) name.save() + if root: + name.copyacl(root) + else: raise e else: @@ -198,18 +218,17 @@ def walkto(root,nameparts,autocreate=False,autoacl='l'): name = Name.objects.get(parent=root,type=None,value=a) except ObjectDoesNotExist,e: if autocreate: - name = Name(parent=root,creator=None,type=None,value=a,acl=autoacl) + name = Name(parent=root,creator=None,type=None,value=a) name.save() + if root: + name.copyacl(root) else: raise e root = name return name -def lookup(name,autocreate=False,autoacl='l'): - return walkto(None,nameparts=re.compile('[;:]').split(name),autocreate=autocreate,autoacl=autoacl) +def lookup(name,autocreate=False): + return walkto(None,nameparts=re.compile('[;:]').split(name),autocreate=autocreate) def attribute(a): Attribute.objects.get_or_create(name=a) - -#sysuser = User.objects.get_or_create(username='system',first_name='COIP System',last_name='User',password="(not used)") -anyuser = lookup("system:anyuser",True) \ No newline at end of file diff --git a/coip/apps/name/views.py b/coip/apps/name/views.py index a59190b..a8c17de 100644 --- a/coip/apps/name/views.py +++ b/coip/apps/name/views.py @@ -6,9 +6,9 @@ Created on Jul 6, 2010 from coip.apps.name.models import Name, lookup, traverse from django.core.exceptions import ObjectDoesNotExist from django.http import HttpResponseNotFound, HttpResponseForbidden,\ - HttpResponseRedirect + HttpResponseRedirect, Http404 from django.contrib.auth.decorators import login_required -from coip.multiresponse import respond_to, json_response +from coip.multiresponse import respond_to, json_response, render403 from pprint import pprint from coip.apps.name.forms import NameEditForm, NewNameForm, NameDeleteForm from twisted.python.reflect import ObjectNotFound @@ -21,7 +21,7 @@ def delete(request,id): return HttpResponseNotFound() if not name.has_permission(request.user,'d'): - return HttpResponseForbidden() + return render403() if request.method == 'POST': form = NameDeleteForm(request.POST) @@ -63,10 +63,11 @@ def add(request,id): return HttpResponseForbidden('You are not allowed to create names') if request.method == 'POST': - name = Name(parent=parent,creator=request.user,acl=parent.copy_acl()) + name = Name(parent=parent,creator=request.user) form = NewNameForm(request.POST,instance=name) if form.is_valid(): - form.save() + name = form.save() + name.copyacl(name.parent) return HttpResponseRedirect("/name/id/%d" % name.id) else: form = NewNameForm() @@ -101,18 +102,27 @@ def show_root(request): def show(request,name): if not name: - return HttpResponseNotFound() + raise Http404() if name.has_permission(request.user,'r'): + memberships = None + invitations = None + if name.has_permission(request.user,'l'): + memberships = name.memberships + invitations = name.invitations return respond_to(request, {'text/html': 'apps/name/name.html'}, - {'name': name, - 'memberships': name.memberships, - 'delete': name.has_permission(request.user,'d'), - 'insert': name.has_permission(request.user,'i'), - 'edit': name.has_permission(request.user,'w')}) + {'name': name, + 'memberships':memberships, + 'invitations':invitations, + 'render': {'delete': name.has_permission(request.user,'d'), + 'insert': name.has_permission(request.user,'i'), + 'edit': name.has_permission(request.user,'w'), + 'invite': name.has_permission(request.user,'i'), + 'up': name.parent and name.parent.has_permission(request.user,'r')} + }) else: - return HttpResponseForbidden() + return render403() @login_required def show_by_name(request,n=None): @@ -121,7 +131,7 @@ def show_by_name(request,n=None): try: return show(request,lookup(n)) except ObjectDoesNotExist: - return HttpResponseNotFound() + return HttpResponseNotFound() @login_required def show_by_id(request,id=None): diff --git a/coip/apps/userprofile/models.py b/coip/apps/userprofile/models.py index 2387e36..76473b1 100644 --- a/coip/apps/userprofile/models.py +++ b/coip/apps/userprofile/models.py @@ -17,6 +17,10 @@ class UserProfile(models.Model): def __unicode__(self): return "%s [%s] - %s" % (self.identifier,self.user.username,self.display_name) + + +def last_used_profile(user): + return UserProfile.objects.filter(user=user).order_by('lastupdated')[0] class PKey(models.Model): user_profile = models.ForeignKey(UserProfile,related_name='keys') @@ -26,5 +30,3 @@ class PKey(models.Model): def __unicode__(self): return "A merge-key for "+self.user_profile - - \ No newline at end of file diff --git a/coip/apps/userprofile/views.py b/coip/apps/userprofile/views.py index 2ea4e2a..282c572 100644 --- a/coip/apps/userprofile/views.py +++ b/coip/apps/userprofile/views.py @@ -7,12 +7,12 @@ from django.contrib.auth.decorators import login_required from coip.apps.userprofile.models import PKey from django.http import HttpResponseRedirect from coip.multiresponse import respond_to -from coip.apps.membership.models import Membership +from coip.apps.membership.models import Membership, add_member from coip.apps.userprofile.utils import user_profile from django.core.exceptions import ObjectDoesNotExist from pprint import pprint from coip.apps.auth.utils import nonce -from coip.apps.name.models import Name, NameLink +from coip.apps.name.models import Name, NameLink, lookup @login_required def merge(request,pkey=None): @@ -39,6 +39,14 @@ def home(request): except ObjectDoesNotExist: pass + anyuser = lookup("system:anyuser",True) + profile = user_profile(request) + home = lookup('user:'+request.user.username,autocreate=True) + home.short = "Home of %s (%s)" % (profile.display_name,profile.identifier) + home.save() + add_member(home,profile.user) + home.setacl(home,"rliw") + names = [(link.src,link.data) for link in NameLink.objects.filter(dst__memberships__user=request.user,type=NameLink.access_control).all()] return respond_to(request, {'text/html': 'apps/userprofile/home.html'},{'memberships': memberships,'names': names}) -- cgit v1.1