work in progress: user management via IPerson adapters for concepts
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1208 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
b79666d1b5
commit
966a75f166
5 changed files with 240 additions and 24 deletions
129
organize/README.txt
Normal file
129
organize/README.txt
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
===============================================================
|
||||||
|
loops - Linked Objects for Organization and Processing Services
|
||||||
|
===============================================================
|
||||||
|
|
||||||
|
($Id$)
|
||||||
|
|
||||||
|
Note: This packages depends on cybertools.organize.
|
||||||
|
|
||||||
|
Letz's do some basic set up
|
||||||
|
|
||||||
|
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
||||||
|
>>> site = placefulSetUp(True)
|
||||||
|
|
||||||
|
>>> from zope import component, interface
|
||||||
|
>>> from zope.app import zapi
|
||||||
|
|
||||||
|
and setup a simple loops site with a concept manager and some concepts:
|
||||||
|
|
||||||
|
>>> from loops import Loops
|
||||||
|
>>> site['loops'] = Loops()
|
||||||
|
>>> loopsRoot = site['loops']
|
||||||
|
|
||||||
|
>>> from cybertools.relation.interfaces import IRelationRegistry
|
||||||
|
>>> from cybertools.relation.registry import DummyRelationRegistry
|
||||||
|
>>> relations = DummyRelationRegistry()
|
||||||
|
>>> component.provideUtility(relations, IRelationRegistry)
|
||||||
|
|
||||||
|
>>> from loops.concept import ConceptManager, Concept
|
||||||
|
>>> loopsRoot['concepts'] = ConceptManager()
|
||||||
|
>>> concepts = loopsRoot['concepts']
|
||||||
|
|
||||||
|
>>> concepts['hasType'] = Concept(u'has type')
|
||||||
|
>>> concepts['type'] = Concept(u'Type')
|
||||||
|
>>> type = concepts['type']
|
||||||
|
>>> type.conceptType = type
|
||||||
|
|
||||||
|
>>> concepts['person'] = Concept(u'Person')
|
||||||
|
>>> person = concepts['person']
|
||||||
|
>>> person.conceptType = type
|
||||||
|
|
||||||
|
>>> johnC = Concept(u'John')
|
||||||
|
>>> concepts['john'] = johnC
|
||||||
|
>>> johnC.conceptType = person
|
||||||
|
|
||||||
|
|
||||||
|
Organizations: Persons (and Users), Institutions, Addresses...
|
||||||
|
==============================================================
|
||||||
|
|
||||||
|
The classes used in this package are just adapters to IConcept.
|
||||||
|
|
||||||
|
>>> from loops.interfaces import IConcept
|
||||||
|
>>> from loops.organize.interfaces import IPerson
|
||||||
|
>>> from loops.organize.party import Person
|
||||||
|
>>> component.provideAdapter(Person, (IConcept,), IPerson)
|
||||||
|
|
||||||
|
>>> john = IPerson(johnC)
|
||||||
|
>>> john.title
|
||||||
|
u'John'
|
||||||
|
>>> john.firstName = u'John'
|
||||||
|
>>> johnC._firstName
|
||||||
|
u'John'
|
||||||
|
>>> john.lastName is None
|
||||||
|
True
|
||||||
|
>>> john.someOtherAttribute
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AttributeError: someOtherAttribute
|
||||||
|
|
||||||
|
We can use the age calculations from the base Person class:
|
||||||
|
|
||||||
|
>>> from datetime import date
|
||||||
|
>>> john.birthDate = date(1980, 3, 26)
|
||||||
|
>>> john.ageAt(date(2006, 5, 12))
|
||||||
|
26
|
||||||
|
>>> john.age >= 26
|
||||||
|
True
|
||||||
|
|
||||||
|
A person can be associated with a user of the system by setting the
|
||||||
|
userId attribute; this will also register the person concept in the
|
||||||
|
corresponding principal annotations so that there is a fast way to find
|
||||||
|
the person(s) belonging to a user/principal.
|
||||||
|
|
||||||
|
For testing, we first have to provide the needed utilities and settings
|
||||||
|
(in real life this is all done during Zope startup):
|
||||||
|
|
||||||
|
>>> from zope.app.security.interfaces import IAuthentication
|
||||||
|
>>> from zope.app.security.principalregistry import PrincipalRegistry
|
||||||
|
>>> auth = PrincipalRegistry()
|
||||||
|
>>> component.provideUtility(auth, IAuthentication)
|
||||||
|
|
||||||
|
>>> from zope.app.principalannotation.interfaces import IPrincipalAnnotationUtility
|
||||||
|
>>> from zope.app.principalannotation import PrincipalAnnotationUtility
|
||||||
|
>>> principalAnnotations = PrincipalAnnotationUtility()
|
||||||
|
>>> component.provideUtility(principalAnnotations, IPrincipalAnnotationUtility)
|
||||||
|
|
||||||
|
>>> principal = auth.definePrincipal('users.john', u'John', login='john')
|
||||||
|
|
||||||
|
>>> john.userId = 'users.john'
|
||||||
|
|
||||||
|
>>> annotations = principalAnnotations.getAnnotationsById('users.john')
|
||||||
|
>>> annotations.get('loops.organize.person') == relations.getUniqueIdForObject(johnC)
|
||||||
|
True
|
||||||
|
|
||||||
|
Change a userId assignment:
|
||||||
|
|
||||||
|
>>> principal = auth.definePrincipal('users.johnny', u'Johnny', login='johnny')
|
||||||
|
>>> john.userId = 'users.johnny'
|
||||||
|
|
||||||
|
>>> annotations = principalAnnotations.getAnnotationsById('users.johnny')
|
||||||
|
>>> annotations.get('loops.organize.person') == relations.getUniqueIdForObject(johnC)
|
||||||
|
True
|
||||||
|
>>> annotations = principalAnnotations.getAnnotationsById('users.john')
|
||||||
|
>>> annotations.get('loops.organize.person') is None
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> john.userId = None
|
||||||
|
>>> annotations = principalAnnotations.getAnnotationsById('users.johnny')
|
||||||
|
>>> annotations.get('loops.organize.person') is None
|
||||||
|
True
|
||||||
|
|
||||||
|
Deleting a person with a userId assigned removes the corresponding
|
||||||
|
principal annotation:
|
||||||
|
|
||||||
|
|
||||||
|
Fin de partie
|
||||||
|
=============
|
||||||
|
|
||||||
|
>>> placefulTearDown()
|
||||||
|
|
|
@ -9,13 +9,14 @@
|
||||||
<!-- adapters -->
|
<!-- adapters -->
|
||||||
|
|
||||||
<zope:adapter factory="loops.organize.party.Person"
|
<zope:adapter factory="loops.organize.party.Person"
|
||||||
|
provides="loops.organize.interfaces.IPerson"
|
||||||
trusted="True" />
|
trusted="True" />
|
||||||
|
|
||||||
<zope:class class="loops.organize.party.Person">
|
<zope:class class="loops.organize.party.Person">
|
||||||
<require permission="zope.View"
|
<require permission="zope.View"
|
||||||
interface="cybertools.organize.interfaces.IPerson" />
|
interface="loops.organize.interfaces.IPerson" />
|
||||||
<require permission="zope.ManageContent"
|
<require permission="zope.ManageContent"
|
||||||
set_schema="cybertools.organize.interfaces.IPerson" />
|
set_schema="loops.organize.interfaces.IPerson" />
|
||||||
</zope:class>
|
</zope:class>
|
||||||
|
|
||||||
</configure>
|
</configure>
|
||||||
|
|
44
organize/interfaces.py
Normal file
44
organize/interfaces.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Interfaces for organizational stuff like persons and addresses.
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
||||||
|
from zope.interface import Interface, Attribute
|
||||||
|
from zope import schema
|
||||||
|
from zope.i18nmessageid import MessageFactory
|
||||||
|
from cybertools.organize.interfaces import IPerson as IBasePerson
|
||||||
|
|
||||||
|
_ = MessageFactory('zope')
|
||||||
|
|
||||||
|
|
||||||
|
class IPerson(IBasePerson):
|
||||||
|
""" Resembles a human being with a name (first and last name),
|
||||||
|
a birth date, and a set of addresses. This interface only
|
||||||
|
lists fields used in addidtion to those provided by the
|
||||||
|
basic cybertools.organize package.
|
||||||
|
"""
|
||||||
|
|
||||||
|
userId = schema.TextLine(
|
||||||
|
title=_(u'User ID'),
|
||||||
|
description=_(u'The principal id of a user that should '
|
||||||
|
'be associated with this person.'),
|
||||||
|
required=False,)
|
|
@ -22,48 +22,91 @@ Adapters for IConcept providing interfaces from the cybertools.organize package.
|
||||||
$Id$
|
$Id$
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from zope import interface, component
|
||||||
from zope.app import zapi
|
from zope.app import zapi
|
||||||
|
from zope.app.principalannotation import annotations
|
||||||
|
from zope.app.security.interfaces import IAuthentication, PrincipalLookupError
|
||||||
from zope.component import adapts
|
from zope.component import adapts
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from zope.cachedescriptors.property import Lazy
|
from zope.cachedescriptors.property import Lazy
|
||||||
from zope.security.proxy import removeSecurityProxy
|
from zope.security.proxy import removeSecurityProxy
|
||||||
|
|
||||||
from cybertools.organize.interfaces import IPerson
|
#from cybertools.organize.interfaces import IPerson
|
||||||
|
from cybertools.organize.party import Person as BasePerson
|
||||||
|
from cybertools.relation.interfaces import IRelationRegistry
|
||||||
from loops.interfaces import IConcept
|
from loops.interfaces import IConcept
|
||||||
from loops.type import TypeInterfaceSourceList
|
from loops.type import TypeInterfaceSourceList
|
||||||
|
from loops.organize.interfaces import IPerson
|
||||||
|
|
||||||
|
|
||||||
|
# register IPerson as a type interface - (TODO: use a function for this)
|
||||||
|
|
||||||
TypeInterfaceSourceList.typeInterfaces += (IPerson,)
|
TypeInterfaceSourceList.typeInterfaces += (IPerson,)
|
||||||
|
|
||||||
|
|
||||||
class Person(object):
|
class Person(BasePerson):
|
||||||
""" typeInterface adapter for concepts of type 'person'.
|
""" typeInterface adapter for concepts of type 'person'.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
implements(IPerson)
|
implements(IPerson)
|
||||||
adapts(IConcept)
|
adapts(IConcept)
|
||||||
|
|
||||||
|
__attributes = ('context', '__parent__', 'userId',)
|
||||||
|
|
||||||
def __init__(self, context):
|
def __init__(self, context):
|
||||||
self.context = context
|
self.context = context # to get the permission stuff right
|
||||||
self.__parent__ = context # to get the permission stuff right
|
self.__parent__ = context
|
||||||
|
|
||||||
def getFirstName(self):
|
def __getattr__(self, attr):
|
||||||
return getattr(self.context, '_firstName', u'')
|
self.checkAttr(attr)
|
||||||
def setFirstName(self, firstName):
|
return getattr(self.context, '_' + attr, None)
|
||||||
self.context._firstName = firstName
|
|
||||||
firstName = property(getFirstName, setFirstName)
|
|
||||||
|
|
||||||
def getLastName(self):
|
def __setattr__(self, attr, value):
|
||||||
return getattr(self.context, '_lastName', u'')
|
if attr in self.__attributes:
|
||||||
def setLastName(self, lastName):
|
object.__setattr__(self, attr, value)
|
||||||
self.context._lastName = lastName
|
else:
|
||||||
lastName = property(getLastName, setLastName)
|
self.checkAttr(attr)
|
||||||
|
setattr(self.context, '_' + attr, value)
|
||||||
def getBirthDate(self):
|
|
||||||
return getattr(self.context, '_birthDate', u'')
|
|
||||||
def setBirthDate(self, birthDate):
|
|
||||||
self.context._birthDate = birthDate
|
|
||||||
birthDate = property(getBirthDate, setBirthDate)
|
|
||||||
|
|
||||||
|
def checkAttr(self, attr):
|
||||||
|
if attr not in list(IPerson) + list(IConcept):
|
||||||
|
raise AttributeError(attr)
|
||||||
|
|
||||||
|
def getUserId(self):
|
||||||
|
return getattr(self.context, '_userId', None)
|
||||||
|
def setUserId(self, userId):
|
||||||
|
auth = self.authentication
|
||||||
|
oldUserId = self.userId
|
||||||
|
if oldUserId and oldUserId != userId:
|
||||||
|
principal = self.getPrincipalForUserId(oldUserId)
|
||||||
|
if principal is not None:
|
||||||
|
pa = annotations(principal)
|
||||||
|
pa['loops.organize.person'] = None
|
||||||
|
if userId:
|
||||||
|
principal = auth.getPrincipal(userId)
|
||||||
|
pa = annotations(principal)
|
||||||
|
relations = component.getUtility(IRelationRegistry)
|
||||||
|
uid = relations.getUniqueIdForObject(self.context)
|
||||||
|
pa['loops.organize.person'] = uid
|
||||||
|
self.context._userId = userId
|
||||||
|
userId = property(getUserId, setUserId)
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def authentication(self):
|
||||||
|
return component.getUtility(IAuthentication, context=self.context)
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def principal(self):
|
||||||
|
return self.getPrincipalForUserId()
|
||||||
|
|
||||||
|
def getPrincipalForUserId(self, userId=None):
|
||||||
|
userId = userId or self.userId
|
||||||
|
if not userId:
|
||||||
|
return None
|
||||||
|
auth = self.authentication
|
||||||
|
try:
|
||||||
|
return auth.getPrincipal(userId)
|
||||||
|
except PrincipalLookupError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,7 @@ def test_suite():
|
||||||
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
|
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
|
||||||
return unittest.TestSuite((
|
return unittest.TestSuite((
|
||||||
unittest.makeSuite(Test),
|
unittest.makeSuite(Test),
|
||||||
#DocFileSuite('../README.txt', optionflags=flags),
|
DocFileSuite('README.txt', optionflags=flags),
|
||||||
#DocFileSuite('../helpers.txt', optionflags=flags),
|
|
||||||
))
|
))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
Loading…
Add table
Reference in a new issue