=============================================================== loops - Linked Objects for Organization and Processing Services =============================================================== ($Id$) Note: This packages depends on cybertools.organize. Let's do some basic setup >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown >>> site = placefulSetUp(True) >>> from zope import component, interface and set up a simple loops site with a concept manager and some concepts (with all the type machinery, what in real life is done via standard ZCML setup): >>> from loops.organize.setup import SetupManager >>> component.provideAdapter(SetupManager, name='organize') >>> from loops.tests.setup import TestSite >>> t = TestSite(site) >>> concepts, resources, views = t.setup() >>> from loops import util >>> loopsRoot = site['loops'] >>> loopsId = util.getUidForObject(loopsRoot) >>> type = concepts['type'] >>> person = concepts['person'] >>> from loops.concept import Concept >>> johnC = concepts['john'] = Concept(u'John') >>> 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') >>> from loops.organize.party import ANNOTATION_KEY >>> annotations[ANNOTATION_KEY][loopsId] == 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[ANNOTATION_KEY][loopsId] == johnC True >>> annotations = principalAnnotations.getAnnotationsById('users.john') >>> annotations[ANNOTATION_KEY][loopsId] is None True >>> john.userId = None >>> annotations = principalAnnotations.getAnnotationsById('users.johnny') >>> annotations[ANNOTATION_KEY][loopsId] is None True Deleting a person with a userId assigned removes the corresponding principal annotation: >>> from zope.app.container.interfaces import IObjectRemovedEvent >>> from zope.app.container.contained import ObjectRemovedEvent >>> from zope.event import notify >>> from zope.interface import Interface >>> from loops.organize.party import removePersonReferenceFromPrincipal >>> from zope.app.testing import ztapi >>> ztapi.subscribe([IConcept, IObjectRemovedEvent], None, ... removePersonReferenceFromPrincipal) >>> john.userId = 'users.john' >>> annotations = principalAnnotations.getAnnotationsById('users.john') >>> annotations[ANNOTATION_KEY][loopsId] == johnC True >>> del concepts['john'] >>> annotations = principalAnnotations.getAnnotationsById('users.john') >>> annotations[ANNOTATION_KEY][loopsId] is None True If we try to assign a userId of a principal that already has a person concept assigned we should get an error: >>> johnC = concepts['john'] = Concept(u'John') >>> johnC.conceptType = person >>> john = IPerson(johnC) >>> john.userId = 'users.john' >>> marthaC = concepts['martha'] = Concept(u'Martha') >>> marthaC.conceptType = person >>> martha = IPerson(marthaC) >>> martha.userId = 'users.john' Traceback (most recent call last): ... ValueError: ... Member Registrations ==================== The member registration needs the whole pluggable authentication stuff with a principal folder: >>> from zope.app.appsetup.bootstrap import ensureUtility >>> from zope.app.authentication.authentication import PluggableAuthentication >>> ensureUtility(site, IAuthentication, '', PluggableAuthentication, ... copy_to_zlog=False, asObject=True) <...PluggableAuthentication...> >>> pau = component.getUtility(IAuthentication, context=site) >>> from zope.app.authentication.principalfolder import PrincipalFolder >>> from zope.app.authentication.interfaces import IAuthenticatorPlugin >>> pFolder = PrincipalFolder('loops.') >>> pau['loops'] = pFolder >>> pau.authenticatorPlugins = ('loops',) In addition, we have to create at least one node in the view space and register an IMemberRegistrationManager adapter for the loops root object: >>> from loops.view import Node >>> menu = views['menu'] = Node('Home') >>> menu.nodeType = 'menu' >>> from loops.organize.member import MemberRegistrationManager >>> component.provideAdapter(MemberRegistrationManager) Now we can enter the registration info for a new member (after having made sure that a principal object can be served by a corresponding factory): >>> from zope.app.authentication.principalfolder import FoundPrincipalFactory >>> component.provideAdapter(FoundPrincipalFactory) >>> data = {'loginName': u'newuser', ... 'password': u'quack', ... 'passwordConfirm': u'quack', ... 'lastName': u'Sawyer', ... 'firstName': u'Tom', ... 'email': u'tommy@sawyer.com', ... 'action': 'update',} and register it. >>> from zope.publisher.browser import TestRequest >>> request = TestRequest(form=data) >>> from loops.organize.browser import MemberRegistration >>> regView = MemberRegistration(menu, request) >>> regView.update() False >>> from loops.common import adapted >>> person = concepts['person.newuser'] >>> pa = adapted(person) >>> pa.lastName, pa.userId (u'Sawyer', u'loops.newuser') Now we can also retrieve it from the authentication utility: >>> pau.getPrincipal('loops.newuser').title u'Tom Sawyer' Change Password --------------- >>> data = {'oldPassword': u'tiger', ... 'password': u'lion', ... 'passwordConfirm': u'lion', ... 'action': 'update'} >>> request = TestRequest(form=data) We need a principal for testing the login stuff: >>> from zope.app.authentication.principalfolder import InternalPrincipal >>> principal = InternalPrincipal('scott', 'tiger', 'Scotty') >>> request.setPrincipal(principal) >>> from cybertools.composer.schema.factory import SchemaFactory >>> from cybertools.composer.schema.field import FieldInstance >>> component.provideAdapter(SchemaFactory) >>> component.provideAdapter(FieldInstance) >>> from loops.organize.browser import PasswordChange >>> pwcView = PasswordChange(menu, request) >>> pwcView.update() False Fin de partie ============= >>> placefulTearDown()