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:
helmutm 2006-05-12 19:44:29 +00:00
parent b79666d1b5
commit 966a75f166
5 changed files with 240 additions and 24 deletions

129
organize/README.txt Normal file
View 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()

View file

@ -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
View 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,)

View file

@ -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

View file

@ -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__':