include slightly modified copy of five.intid
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@3685 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
		
							parent
							
								
									50570a44f1
								
							
						
					
					
						commit
						d1110c410e
					
				
					 20 changed files with 1246 additions and 0 deletions
				
			
		
							
								
								
									
										336
									
								
								z2/intid/README.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								z2/intid/README.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,336 @@ | ||||||
|  | Usage | ||||||
|  | ===== | ||||||
|  | 
 | ||||||
|  | First, let make sure the ofs utility provides the interface:: | ||||||
|  | 
 | ||||||
|  |     >>> from zope.app.intid.interfaces import IIntIds | ||||||
|  |     >>> from five.intid import site | ||||||
|  |     >>> import five.intid.tests as tests | ||||||
|  |     >>> from zope.interface.verify import verifyObject | ||||||
|  |     >>> from zope.component import getAllUtilitiesRegisteredFor | ||||||
|  |     >>> from zope.app.component.hooks import setSite | ||||||
|  |     >>> tests.setUp(self.app) | ||||||
|  | 
 | ||||||
|  | Content added before the utility won't be registered(until explicitly | ||||||
|  | called to). We'll set some up now for later | ||||||
|  | 
 | ||||||
|  |     >>> tests.manage_addSimpleContent(self.folder, 'mycont1', "My Content") | ||||||
|  |     >>> content1 = self.folder.mycont1 | ||||||
|  | 
 | ||||||
|  | `five.intid.site` has convenience functions for adding, get and | ||||||
|  | removing an IntId utility: `add_intid`, `get_intid`, `del_intid`. | ||||||
|  | 
 | ||||||
|  | You can install the utility in a specific location:: | ||||||
|  | 
 | ||||||
|  |     >>> site.add_intids(self.folder) | ||||||
|  |     >>> folder_intids = site.get_intids(self.folder) | ||||||
|  |     >>> verifyObject(IIntIds, folder_intids) | ||||||
|  |     True | ||||||
|  | 
 | ||||||
|  | You can tell `add_intids` to find the site root, and install there. | ||||||
|  | It will be available everywhere:: | ||||||
|  | 
 | ||||||
|  |     >>> site.add_intids(self.folder, findroot=True) | ||||||
|  |     >>> root_intids = site.get_intids(self.app) | ||||||
|  |     >>> root_intids | ||||||
|  |     <...IntIds ...> | ||||||
|  |     >>> folder_intids is root_intids | ||||||
|  |     False | ||||||
|  | 
 | ||||||
|  | And finally, do a remove:: | ||||||
|  | 
 | ||||||
|  |     >>> site.del_intids(self.folder, findroot=True) | ||||||
|  |     >>> site.get_intids(self.app) | ||||||
|  |     Traceback (most recent call last): | ||||||
|  |     ... | ||||||
|  |     ComponentLookupError: (<InterfaceClass zope.app.intid.interfaces.IIntIds>, '') | ||||||
|  | 
 | ||||||
|  | Before we look at intid events, we need to set the traversal | ||||||
|  | hook. Once we have done this, when we ask for all registered Intids, | ||||||
|  | we will get the utility from test folder:: | ||||||
|  | 
 | ||||||
|  |     >>> setSite(self.folder) | ||||||
|  |     >>> tuple(getAllUtilitiesRegisteredFor(IIntIds)) | ||||||
|  |     (<...IntIds ...>,) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | When we add content, event will be fired to add keyreference for said | ||||||
|  | objects the utilities (currently, our content and the utility are | ||||||
|  | registered):: | ||||||
|  | 
 | ||||||
|  |     >>> from five.intid.lsm import USE_LSM | ||||||
|  |     >>> tests.manage_addSimpleContent(self.folder, 'mycont2', "My Content") | ||||||
|  |     >>> content2 = self.folder.mycont2 | ||||||
|  |     >>> intid = site.get_intids(self.folder) | ||||||
|  |     >>> if USE_LSM: | ||||||
|  |     ...     len(intid.items()) == 1 | ||||||
|  |     ... else: | ||||||
|  |     ...     len(intid.items()) == 2 | ||||||
|  |     True | ||||||
|  | 
 | ||||||
|  | Pre-existing content will raise a keyerror if passed to the intid | ||||||
|  | utility:: | ||||||
|  | 
 | ||||||
|  |     >>> intid.getId(content1) | ||||||
|  |     Traceback (most recent call last): | ||||||
|  |     ... | ||||||
|  |     KeyError: <SimpleContent at /test_folder_1_/mycont1> | ||||||
|  | 
 | ||||||
|  | We can call the keyreferences, and get the objects back:: | ||||||
|  | 
 | ||||||
|  |     >>> if USE_LSM: | ||||||
|  |     ...     intid.items()[0][1]() | ||||||
|  |     ... else: | ||||||
|  |     ...     intid.items()[1][1]() | ||||||
|  |     <SimpleContent at /test_folder_1_/mycont2> | ||||||
|  | 
 | ||||||
|  | we can get an object's `intid` from the utility like so:: | ||||||
|  | 
 | ||||||
|  |     >>> ob_id = intid.getId(content2) | ||||||
|  | 
 | ||||||
|  | and get an object back like this:: | ||||||
|  | 
 | ||||||
|  |     >>> intid.getObject(ob_id) | ||||||
|  |     <SimpleContent at /test_folder_1_/mycont2> | ||||||
|  | 
 | ||||||
|  | these objects are aquisition wrapped on retrieval:: | ||||||
|  | 
 | ||||||
|  |     >>> type(intid.getObject(ob_id)) | ||||||
|  |     <type 'ImplicitAcquirerWrapper'> | ||||||
|  | 
 | ||||||
|  | We can even turn an unwrapped object into a wrapped object by | ||||||
|  | resolving it from it's intid, also the intid utility should work | ||||||
|  | even if it is unwrapped:: | ||||||
|  | 
 | ||||||
|  |     >>> from Acquisition import aq_base | ||||||
|  |     >>> resolved = intid.getObject(intid.getId(aq_base(content2))) | ||||||
|  |     >>> type(resolved) | ||||||
|  |     <type 'ImplicitAcquirerWrapper'> | ||||||
|  |     >>> unwrapped = aq_base(intid) | ||||||
|  |     >>> unwrapped.getObject(ob_id) == resolved | ||||||
|  |     True | ||||||
|  |     >>> unwrapped.getId(content2) == ob_id | ||||||
|  |     True | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | When an object is added or removed, subscribers add it to the intid | ||||||
|  | utility, and fire an event is fired | ||||||
|  | (zope.app.intid.interfaces.IIntIdAddedEvent, | ||||||
|  | zope.app.intid.interfaces.IIntIdRemovedEvent respectively). | ||||||
|  | 
 | ||||||
|  | `five.intid` hooks up these events to redispatch as object events. The | ||||||
|  | tests hook up a simple subscriber to verify that the intid object | ||||||
|  | events are fired (these events are useful for catalogish tasks).  | ||||||
|  | 
 | ||||||
|  |     >>> tests.NOTIFIED[0] | ||||||
|  |     '<SimpleContent at mycont2> <...IntIdAddedEvent instance at ...' | ||||||
|  | 
 | ||||||
|  | Registering and unregistering objects does not fire these events:: | ||||||
|  | 
 | ||||||
|  |     >>> tests.NOTIFIED[0] = "No change" | ||||||
|  |     >>> uid = intid.register(content1) | ||||||
|  |     >>> intid.getObject(uid) | ||||||
|  |     <SimpleContent at /test_folder_1_/mycont1> | ||||||
|  | 
 | ||||||
|  |     >>> tests.NOTIFIED[0] | ||||||
|  |     'No change' | ||||||
|  | 
 | ||||||
|  |     >>> intid.unregister(content1) | ||||||
|  |     >>> intid.getObject(uid) | ||||||
|  |     Traceback (most recent call last): | ||||||
|  |     ... | ||||||
|  |     KeyError: ... | ||||||
|  | 
 | ||||||
|  |     >>> tests.NOTIFIED[0] | ||||||
|  |     'No change' | ||||||
|  | 
 | ||||||
|  | Renaming an object should not break the rewrapping of the object: | ||||||
|  | 
 | ||||||
|  |     >>> self.setRoles(['Manager']) | ||||||
|  |     >>> folder.mycont2.meta_type = 'Folder' # We need a metatype to move | ||||||
|  |     >>> folder.manage_renameObject('mycont2','mycont_new') | ||||||
|  |     >>> moved = intid.getObject(ob_id) | ||||||
|  |     >>> moved | ||||||
|  |     <SimpleContent at /test_folder_1_/mycont_new> | ||||||
|  | 
 | ||||||
|  | Nor should moving it: | ||||||
|  | 
 | ||||||
|  |     >>> from OFS.Folder import manage_addFolder | ||||||
|  |     >>> manage_addFolder(self.folder, 'folder2', "folder 2") | ||||||
|  |     >>> cut = folder.manage_cutObjects(['mycont_new']) | ||||||
|  |     >>> ignore = folder.folder2.manage_pasteObjects(cut) | ||||||
|  |     >>> moved = intid.getObject(ob_id) | ||||||
|  |     >>> moved | ||||||
|  |     <SimpleContent at /test_folder_1_/folder2/mycont_new> | ||||||
|  |     >>> moved.aq_parent | ||||||
|  |     <Folder at /test_folder_1_/folder2> | ||||||
|  | 
 | ||||||
|  | Let's move it back: | ||||||
|  | 
 | ||||||
|  |     >>> cut = folder.folder2.manage_cutObjects(['mycont_new']) | ||||||
|  |     >>> ignore = folder.manage_pasteObjects(cut) | ||||||
|  |     >>> folder.manage_renameObject('mycont_new','mycont2') | ||||||
|  | 
 | ||||||
|  | We can create an object without acquisition so we can be able to | ||||||
|  | add intid to it : | ||||||
|  | 
 | ||||||
|  |     >>> from five.intid.tests import DemoPersistent | ||||||
|  |     >>> demo1 = DemoPersistent() | ||||||
|  |     >>> demo1.__parent__ = self.app | ||||||
|  |     >>> from zope.event import notify | ||||||
|  |     >>> from zope.app.container.contained import ObjectAddedEvent | ||||||
|  |     >>> notify(ObjectAddedEvent(demo1)) | ||||||
|  |     >>> nowrappid = intid.getId(demo1) | ||||||
|  |     >>> demo1 == intid.getObject(nowrappid) | ||||||
|  |     True | ||||||
|  | 
 | ||||||
|  | This is a good time to take a look at keyreferences, the core part of | ||||||
|  | this system. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Key References in Zope2 | ||||||
|  | ======================= | ||||||
|  | 
 | ||||||
|  | Key references are hashable objects returned by IKeyReference.  The | ||||||
|  | hash produced is a unique identifier for whatever the object is | ||||||
|  | referencing(another zodb object, a hook for sqlobject, etc). | ||||||
|  | 
 | ||||||
|  | object retrieval in intid occurs by calling a key reference. This | ||||||
|  | implementation is slightly different than the zope3 due to | ||||||
|  | acquisition. | ||||||
|  | 
 | ||||||
|  | The factories returned by IKeyReference must persist and this dictates | ||||||
|  | being especially careful about references to acquisition wrapped | ||||||
|  | objects as well as return acq wrapped objects as usually expected in | ||||||
|  | zope2. | ||||||
|  | 
 | ||||||
|  |     >>> ref = intid.refs[ob_id] | ||||||
|  |     >>> ref | ||||||
|  |     <five.intid.keyreference.KeyReferenceToPersistent object at ...> | ||||||
|  | 
 | ||||||
|  | The reference object holds a reference to the unwrapped target object | ||||||
|  | and a property to fetch the app(also, not wrapped ie <type 'ImplicitAcquirerWrapper'>):: | ||||||
|  | 
 | ||||||
|  |     >>> ref.object | ||||||
|  |     <SimpleContent at mycont2> | ||||||
|  | 
 | ||||||
|  |     >>> type(ref.object) | ||||||
|  |     <class 'Products.Five.tests.testing.simplecontent.SimpleContent'> | ||||||
|  |   | ||||||
|  |     >>> ref.root | ||||||
|  |     <Application at > | ||||||
|  | 
 | ||||||
|  | Calling the reference object (or the property wrapped_object) will | ||||||
|  | return an acquisition object wrapped object (wrapped as it was | ||||||
|  | created):: | ||||||
|  | 
 | ||||||
|  |     >>> ref.wrapped_object == ref() | ||||||
|  |     True | ||||||
|  |      | ||||||
|  |     >>> ref() | ||||||
|  |     <SimpleContent at /test_folder_1_/mycont2> | ||||||
|  |      | ||||||
|  |     >>> type(ref()) | ||||||
|  |     <type 'ImplicitAcquirerWrapper'> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | The resolution mechanism tries its best to end up with the current | ||||||
|  | request at the end of the acquisition chain, just as it would be | ||||||
|  | under noraml circumstances:: | ||||||
|  | 
 | ||||||
|  |     >>> ref.wrapped_object.aq_chain[-1] | ||||||
|  |     <ZPublisher.BaseRequest.RequestContainer object at ...> | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  | The hash calculation is a combination of the database name and the | ||||||
|  | object's persistent object id(oid):: | ||||||
|  | 
 | ||||||
|  |     >>> ref.dbname | ||||||
|  |     'unnamed' | ||||||
|  | 
 | ||||||
|  |     >>> hash((ref.dbname, ref.object._p_oid)) == hash(ref) | ||||||
|  |     True | ||||||
|  | 
 | ||||||
|  |     >>> tests.tearDown() | ||||||
|  | 
 | ||||||
|  | Acquisition Loops | ||||||
|  | ================= | ||||||
|  | 
 | ||||||
|  | five.intid detects loops in acquisition chains in both aq_parent and | ||||||
|  | __parent__. | ||||||
|  | 
 | ||||||
|  | Setup a loop:: | ||||||
|  | 
 | ||||||
|  |     >>> import Acquisition | ||||||
|  |     >>> class Acq(Acquisition.Acquirer): pass | ||||||
|  |     >>> foo = Acq() | ||||||
|  |     >>> foo.bar = Acq() | ||||||
|  |     >>> foo.__parent__ = foo.bar | ||||||
|  | 
 | ||||||
|  | Looking for the root on an object with an acquisition loop will raise | ||||||
|  | an error:: | ||||||
|  | 
 | ||||||
|  |     >>> from five.intid import site | ||||||
|  |     >>> site.get_root(foo.bar) | ||||||
|  |     Traceback (most recent call last): | ||||||
|  |     ... | ||||||
|  |     AttributeError: __parent__ loop found | ||||||
|  | 
 | ||||||
|  | Looking for the connection on an object with an acquisition loop will | ||||||
|  | simply return None:: | ||||||
|  | 
 | ||||||
|  |     >>> from five.intid import keyreference | ||||||
|  |     >>> keyreference.connectionOfPersistent(foo.bar) | ||||||
|  | 
 | ||||||
|  | Unreferenceable | ||||||
|  | =============== | ||||||
|  | 
 | ||||||
|  | Some objects implement IPersistent but are never actually persisted, or | ||||||
|  | contain references to such objects. Specifically, CMFCore directory views | ||||||
|  | contain FSObjects that are never persisted, and DirectoryViewSurrogates | ||||||
|  | that contain references to such objects. Because FSObjects are never actually | ||||||
|  | persisted, five.intid's assumption that it can add a  | ||||||
|  | 
 | ||||||
|  | For such objects, the unreferenceable module provides no-op subcribers and | ||||||
|  | adapters to omit such objects from five.intid handling. | ||||||
|  | 
 | ||||||
|  |     >>> from zope import interface, component | ||||||
|  |     >>> from five.intid import unreferenceable | ||||||
|  | 
 | ||||||
|  |     >>> from Products.CMFCore import FSPythonScript | ||||||
|  |     >>> foo = FSPythonScript.FSPythonScript('foo', __file__) | ||||||
|  |     >>> self.app._setObject('foo', foo) | ||||||
|  |     'foo' | ||||||
|  | 
 | ||||||
|  |     >>> keyref = unreferenceable.KeyReferenceNever(self.app.foo) | ||||||
|  |     Traceback (most recent call last): | ||||||
|  |     ... | ||||||
|  |     NotYet | ||||||
|  |     >>> foo in self.app._p_jar._registered_objects | ||||||
|  |     False | ||||||
|  | 
 | ||||||
|  | Objects with no id | ||||||
|  | ================== | ||||||
|  | 
 | ||||||
|  | It is possible to attempt to get a key reference for an object that has not | ||||||
|  | yet been properly added to a container, but would otherwise have a path. | ||||||
|  | In this case, we raise the NotYet exception to let the calling code defer | ||||||
|  | as necessary, since the key reference would otherwise resolve the wrong | ||||||
|  | object (the parent, to be precise) from an incorrect path. | ||||||
|  | 
 | ||||||
|  |     >>> from zope.app.keyreference.interfaces import IKeyReference | ||||||
|  |     >>> from five.intid.keyreference import KeyReferenceToPersistent | ||||||
|  |     >>> from zope.component import provideAdapter | ||||||
|  |     >>> provideAdapter(KeyReferenceToPersistent) | ||||||
|  | 
 | ||||||
|  |     >>> from OFS.SimpleItem import SimpleItem | ||||||
|  |     >>> item = SimpleItem('').__of__(self.folder) | ||||||
|  |     >>> '/'.join(item.getPhysicalPath()) | ||||||
|  |     '/test_folder_1_/' | ||||||
|  |      | ||||||
|  |     >>> IKeyReference(item) | ||||||
|  |     Traceback (most recent call last): | ||||||
|  |     ... | ||||||
|  |     NotYet: <SimpleItem at > | ||||||
							
								
								
									
										1
									
								
								z2/intid/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								z2/intid/__init__.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | # | ||||||
							
								
								
									
										51
									
								
								z2/intid/base.zcml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								z2/intid/base.zcml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | ||||||
|  | <configure xmlns="http://namespaces.zope.org/zope" | ||||||
|  |            xmlns:browser="http://namespaces.zope.org/browser" | ||||||
|  |            xmlns:five="http://namespaces.zope.org/five"> | ||||||
|  | 
 | ||||||
|  |   <include package="zope.app.keyreference" /> | ||||||
|  | 
 | ||||||
|  |   <browser:page | ||||||
|  |       name="index.html" | ||||||
|  |       for="zope.app.intid.interfaces.IIntIds" | ||||||
|  |       permission="five.ManageSite" | ||||||
|  |       class=".browser.IntIdsView" | ||||||
|  |       template="registrations.pt" | ||||||
|  |      /> | ||||||
|  | 
 | ||||||
|  |   <browser:page | ||||||
|  |       name="populate" | ||||||
|  |       for="zope.app.intid.interfaces.IIntIds" | ||||||
|  |       permission="five.ManageSite" | ||||||
|  |       class=".browser.IntIdsView" | ||||||
|  |       attribute="populate" | ||||||
|  |      /> | ||||||
|  | 
 | ||||||
|  |   <browser:page | ||||||
|  |      for="OFS.interfaces.IFolder" | ||||||
|  |      class=".site.FiveIntIdsInstall" | ||||||
|  |      permission="five.ManageSite" | ||||||
|  |      template="install.pt" | ||||||
|  |      name="install-intids.html" | ||||||
|  |      /> | ||||||
|  | 
 | ||||||
|  |   <browser:page | ||||||
|  |      for="OFS.interfaces.IApplication" | ||||||
|  |      class=".site.FiveIntIdsInstall" | ||||||
|  |      permission="five.ManageSite" | ||||||
|  |      template="install.pt" | ||||||
|  |      name="install-intids.html" | ||||||
|  |      /> | ||||||
|  | 
 | ||||||
|  |   <subscriber | ||||||
|  |      for="zope.app.intid.interfaces.IIntIdAddedEvent" | ||||||
|  |      handler="zope.component.event.objectEventNotify" | ||||||
|  |      /> | ||||||
|  | 
 | ||||||
|  |   <subscriber | ||||||
|  |      for="zope.app.intid.interfaces.IIntIdRemovedEvent"  | ||||||
|  |      handler="zope.component.event.objectEventNotify" | ||||||
|  |      /> | ||||||
|  | 
 | ||||||
|  |   <includeOverrides file="overrides.zcml" package="cybertools.z2.intid"/> | ||||||
|  | 
 | ||||||
|  | </configure> | ||||||
							
								
								
									
										5
									
								
								z2/intid/browser.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								z2/intid/browser.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | from zope.app.intid.browser import IntIdsView | ||||||
|  | from Products.Five import BrowserView | ||||||
|  | 
 | ||||||
|  | class FiveIntIdsView(IntIdsView, BrowserView): | ||||||
|  |     """ utility view for five """ | ||||||
							
								
								
									
										49
									
								
								z2/intid/cmfdirectoryview.zcml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								z2/intid/cmfdirectoryview.zcml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | <configure  | ||||||
|  |     xmlns="http://namespaces.zope.org/zope" | ||||||
|  |     xmlns:zcml="http://namespaces.zope.org/zcml" | ||||||
|  |     zcml:condition="installed Products.CMFCore"> | ||||||
|  |      | ||||||
|  |   <!-- DirectoryViews --> | ||||||
|  |   <subscriber | ||||||
|  |       handler=".unreferenceable.addIntIdSubscriber" | ||||||
|  |       for="Products.CMFCore.interfaces.IDirectoryView | ||||||
|  |            zope.app.container.interfaces.IObjectAddedEvent" | ||||||
|  |       /> | ||||||
|  |   <subscriber | ||||||
|  |       handler=".unreferenceable.removeIntIdSubscriber" | ||||||
|  |       for="Products.CMFCore.interfaces.IDirectoryView | ||||||
|  |            zope.app.container.interfaces.IObjectRemovedEvent" | ||||||
|  |       /> | ||||||
|  |   <subscriber | ||||||
|  |       handler=".unreferenceable.moveIntIdSubscriber" | ||||||
|  |       for="Products.CMFCore.interfaces.IDirectoryView | ||||||
|  |            zope.app.container.interfaces.IObjectMovedEvent" | ||||||
|  |       /> | ||||||
|  |   <adapter  | ||||||
|  |       factory=".unreferenceable.KeyReferenceNever" | ||||||
|  |       for="Products.CMFCore.interfaces.IDirectoryView" | ||||||
|  |       trusted="y" | ||||||
|  |       /> | ||||||
|  | 
 | ||||||
|  |   <!-- FSObject --> | ||||||
|  |   <subscriber | ||||||
|  |       handler=".unreferenceable.addIntIdSubscriber" | ||||||
|  |       for="Products.CMFCore.FSObject.FSObject | ||||||
|  |            zope.app.container.interfaces.IObjectAddedEvent" | ||||||
|  |       /> | ||||||
|  |   <subscriber | ||||||
|  |       handler=".unreferenceable.removeIntIdSubscriber" | ||||||
|  |       for="Products.CMFCore.FSObject.FSObject | ||||||
|  |            zope.app.container.interfaces.IObjectRemovedEvent" | ||||||
|  |       /> | ||||||
|  |   <subscriber | ||||||
|  |       handler=".unreferenceable.moveIntIdSubscriber" | ||||||
|  |       for="Products.CMFCore.FSObject.FSObject | ||||||
|  |            zope.app.container.interfaces.IObjectMovedEvent" | ||||||
|  |       /> | ||||||
|  |   <adapter  | ||||||
|  |       factory=".unreferenceable.KeyReferenceNever" | ||||||
|  |       for="Products.CMFCore.FSObject.FSObject" | ||||||
|  |       trusted="y" | ||||||
|  |       /> | ||||||
|  | </configure> | ||||||
							
								
								
									
										15
									
								
								z2/intid/configure.zcml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								z2/intid/configure.zcml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | <configure xmlns="http://namespaces.zope.org/zope" | ||||||
|  |            xmlns:browser="http://namespaces.zope.org/browser" | ||||||
|  |            xmlns:five="http://namespaces.zope.org/five" | ||||||
|  |            xmlns:zcml="http://namespaces.zope.org/zcml"> | ||||||
|  | 
 | ||||||
|  |   <!-- All Core Configuration --> | ||||||
|  |   <include file="base.zcml" package="cybertools.z2.intid" /> | ||||||
|  | 
 | ||||||
|  |   <!-- Event Subscribers --> | ||||||
|  |   <include file="subscriber.zcml" package="cybertools.z2.intid" /> | ||||||
|  | 
 | ||||||
|  |   <!-- CMFCore directoryview exceptions --> | ||||||
|  |   <!--<include file="cmfdirectoryview.zcml" />--> | ||||||
|  | 
 | ||||||
|  | </configure> | ||||||
							
								
								
									
										15
									
								
								z2/intid/install.pt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								z2/intid/install.pt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | <html> | ||||||
|  |   <head> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <div tal:condition="view/installed">the intids is installed</div> | ||||||
|  |     <div> | ||||||
|  |       <form method=POST tal:condition="not:view/installed"> | ||||||
|  | 	<input type="submit"  | ||||||
|  | 	       value="Install intid utility"  | ||||||
|  | 	       name="install" | ||||||
|  | 	       style="border: 2px solid #F99; background-color: red"/> | ||||||
|  |       </form> | ||||||
|  |     </div> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										135
									
								
								z2/intid/intid.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								z2/intid/intid.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,135 @@ | ||||||
|  | from Globals import InitializeClass | ||||||
|  | from persistent import Persistent | ||||||
|  | from Acquisition import Explicit | ||||||
|  | from zope.app import zapi | ||||||
|  | from zope.app.intid import IntIds as z3IntIds | ||||||
|  | from zope.app.intid.interfaces import IIntIds | ||||||
|  | from zope.app.intid.interfaces import IntIdAddedEvent, IntIdRemovedEvent | ||||||
|  | from zope.app.container.interfaces import IObjectAddedEvent, IObjectRemovedEvent | ||||||
|  | from zope.app.keyreference.interfaces import IKeyReference, NotYet | ||||||
|  | from zope.event import notify | ||||||
|  | from zope.interface import implements | ||||||
|  | 
 | ||||||
|  | _marker = [] | ||||||
|  | 
 | ||||||
|  | class IntIds(z3IntIds): | ||||||
|  |     """ zope2ish intid utility """ | ||||||
|  |     implements(IIntIds) | ||||||
|  | 
 | ||||||
|  |     meta_type="IntId Utility" | ||||||
|  | 
 | ||||||
|  |     def __init__(self, id_=IIntIds.__name__): | ||||||
|  |         self.id = self.__name__ = id_ | ||||||
|  |         super(IntIds, self).__init__() | ||||||
|  | 
 | ||||||
|  |     def getId(self, ob=_marker): | ||||||
|  |         # Compatibility with SimpleItem | ||||||
|  |         if ob is _marker: | ||||||
|  |             return self.__name__ | ||||||
|  |         return z3IntIds.getId(self, ob) | ||||||
|  | 
 | ||||||
|  |     def register(self, ob): | ||||||
|  |         key = IKeyReference(ob) | ||||||
|  |         res = self.ids.get(key, None) | ||||||
|  |         if res is not None: | ||||||
|  |             return res | ||||||
|  |         uid = self._generateId() | ||||||
|  |         self.refs[uid] = key | ||||||
|  |         self.ids[key] = uid | ||||||
|  |         return uid | ||||||
|  | 
 | ||||||
|  |     def unregister(self, ob): | ||||||
|  |         key = IKeyReference(ob, None) | ||||||
|  |         if key is None: | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         uid = self.ids[key] | ||||||
|  |         del self.refs[uid] | ||||||
|  |         del self.ids[key] | ||||||
|  | 
 | ||||||
|  | InitializeClass(IntIds) | ||||||
|  | 
 | ||||||
|  | class OFSIntIds(IntIds, Explicit): | ||||||
|  |     """Mixin acquisition for non-lsm sites""" | ||||||
|  | 
 | ||||||
|  |     def manage_fixupOwnershipAfterAdd(self): | ||||||
|  |         pass | ||||||
|  |     def wl_isLocked(self): | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  | InitializeClass(OFSIntIds) | ||||||
|  | 
 | ||||||
|  | # @@ these are "sloppy" subscribers that let objects that have not | ||||||
|  | # been properly added to the db by | ||||||
|  | def addIntIdSubscriber(ob, event): | ||||||
|  |     """A subscriber to ObjectAddedEvent | ||||||
|  | 
 | ||||||
|  |     Registers the object added in all unique id utilities and fires | ||||||
|  |     an event for the catalogs. | ||||||
|  |     """ | ||||||
|  |     utilities = tuple(zapi.getAllUtilitiesRegisteredFor(IIntIds)) | ||||||
|  |     if utilities: # assert that there are any utilites | ||||||
|  |         key = None | ||||||
|  |         try: | ||||||
|  |             key = IKeyReference(ob, None) | ||||||
|  |         except NotYet: | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  |         # Register only objects that adapt to key reference | ||||||
|  |         if key is not None: | ||||||
|  |             for utility in utilities: | ||||||
|  |                 utility.register(key) | ||||||
|  |             # Notify the catalogs that this object was added. | ||||||
|  |             notify(IntIdAddedEvent(ob, event)) | ||||||
|  | 
 | ||||||
|  | def removeIntIdSubscriber(ob, event): | ||||||
|  |     """A subscriber to ObjectRemovedEvent | ||||||
|  | 
 | ||||||
|  |     Removes the unique ids registered for the object in all the unique | ||||||
|  |     id utilities. | ||||||
|  |     """ | ||||||
|  |     utilities = tuple(zapi.getAllUtilitiesRegisteredFor(IIntIds)) | ||||||
|  |     if utilities: | ||||||
|  |         key = None | ||||||
|  |         try: | ||||||
|  |             key = IKeyReference(ob, None) | ||||||
|  |         except NotYet: # @@ temporary fix | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  |         # Register only objects that adapt to key reference | ||||||
|  |         if key is not None: | ||||||
|  |             # Notify the catalogs that this object is about to be removed. | ||||||
|  |             notify(IntIdRemovedEvent(ob, event)) | ||||||
|  |             for utility in utilities: | ||||||
|  |                 try: | ||||||
|  |                     utility.unregister(key) | ||||||
|  |                 except KeyError: | ||||||
|  |                     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def moveIntIdSubscriber(ob, event): | ||||||
|  |     """A subscriber to ObjectMovedEvent | ||||||
|  | 
 | ||||||
|  |     Updates the stored path for the object in all the unique | ||||||
|  |     id utilities. | ||||||
|  |     """ | ||||||
|  |     if IObjectRemovedEvent.providedBy(event) or \ | ||||||
|  |            IObjectAddedEvent.providedBy(event): | ||||||
|  |         return | ||||||
|  |     utilities = tuple(zapi.getAllUtilitiesRegisteredFor(IIntIds)) | ||||||
|  |     if utilities: | ||||||
|  |         key = None | ||||||
|  |         try: | ||||||
|  |             key = IKeyReference(ob, None) | ||||||
|  |         except NotYet: # @@ temporary fix | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  |         # Update objects that adapt to key reference | ||||||
|  |         if key is not None: | ||||||
|  |             for utility in utilities: | ||||||
|  |                 try: | ||||||
|  |                     uid = utility.getId(ob) | ||||||
|  |                     utility.refs[uid] = key | ||||||
|  |                     utility.ids[key] = uid | ||||||
|  |                 except KeyError: | ||||||
|  |                     pass | ||||||
							
								
								
									
										115
									
								
								z2/intid/keyreference.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								z2/intid/keyreference.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,115 @@ | ||||||
|  | from Acquisition import aq_base, aq_chain | ||||||
|  | from ZODB.interfaces import IConnection | ||||||
|  | from ZPublisher.BaseRequest import RequestContainer | ||||||
|  | from zExceptions import NotFound | ||||||
|  | from persistent import IPersistent | ||||||
|  | from zope.component import adapter, adapts | ||||||
|  | from zope.app.component.hooks import getSite | ||||||
|  | from zope.interface import implements, implementer | ||||||
|  | from zope.app.keyreference.interfaces import IKeyReference, NotYet | ||||||
|  | from zope.app.keyreference.persistent import KeyReferenceToPersistent | ||||||
|  | from site import get_root, aq_iter | ||||||
|  | from zope.app.container.interfaces import IObjectAddedEvent | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @adapter(IPersistent) | ||||||
|  | @implementer(IConnection) | ||||||
|  | def connectionOfPersistent(obj): | ||||||
|  |     """ zope2 cxn fetcher for wrapped items """ | ||||||
|  |     for parent in aq_iter(obj): | ||||||
|  |         conn = getattr(parent, '_p_jar', None) | ||||||
|  |         if conn is not None: | ||||||
|  |             return conn | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @adapter(IPersistent, IObjectAddedEvent) | ||||||
|  | def add_object_to_connection(ob, event): | ||||||
|  |     """Pre-add new objects to their persistence connection""" | ||||||
|  |     connection = IConnection(ob, None) | ||||||
|  |     if None is not connection: | ||||||
|  |         connection.add(aq_base(ob)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class KeyReferenceToPersistent(KeyReferenceToPersistent): | ||||||
|  |     """a zope2ish implementation of keyreferences that unwraps objects | ||||||
|  |     that have Acquisition wrappers | ||||||
|  | 
 | ||||||
|  |     These references compare by _p_oids of the objects they reference. | ||||||
|  | 
 | ||||||
|  |     @@ cache IConnection as a property and volative attr? | ||||||
|  |     """ | ||||||
|  |     implements(IKeyReference) | ||||||
|  |     adapts(IPersistent) | ||||||
|  | 
 | ||||||
|  |     key_type_id = 'five.intid.keyreference' | ||||||
|  | 
 | ||||||
|  |     def __init__(self, wrapped_obj): | ||||||
|  |         # make sure our object is wrapped by containment only | ||||||
|  |         try: | ||||||
|  |             self.path = '/'.join(wrapped_obj.getPhysicalPath()) | ||||||
|  |         except AttributeError: | ||||||
|  |             self.path = None | ||||||
|  |          | ||||||
|  |         # If the path ends with /, it means the object had an empty id. | ||||||
|  |         # This means it's not yet added to the container, and so we have | ||||||
|  |         # to defer. | ||||||
|  |         if self.path is not None and self.path.endswith('/'): | ||||||
|  |             raise NotYet(wrapped_obj) | ||||||
|  |         self.object = aq_base(wrapped_obj) | ||||||
|  |         connection = IConnection(wrapped_obj, None) | ||||||
|  | 
 | ||||||
|  |         if not getattr(self.object, '_p_oid', None): | ||||||
|  |             if connection is None: | ||||||
|  |                 raise NotYet(wrapped_obj) | ||||||
|  |             connection.add(self.object) | ||||||
|  |          | ||||||
|  |         try: | ||||||
|  |             self.root_oid = get_root(wrapped_obj)._p_oid | ||||||
|  |         except AttributeError: | ||||||
|  |             # If the object is unwrapped we can try to use the Site from the | ||||||
|  |             # threadlocal as our acquisition context, hopefully it's not | ||||||
|  |             # something odd. | ||||||
|  |             self.root_oid = get_root(getSite())._p_oid | ||||||
|  |         self.oid = self.object._p_oid | ||||||
|  |         self.dbname = connection.db().database_name | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def root(self): | ||||||
|  |         return IConnection(self.object)[self.root_oid] | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def wrapped_object(self): | ||||||
|  |         if self.path is None: | ||||||
|  |             return self.object | ||||||
|  |         try: | ||||||
|  |             obj = self.root.unrestrictedTraverse(self.path) | ||||||
|  |         except (NotFound, AttributeError,): | ||||||
|  |             return self.object | ||||||
|  |         chain = aq_chain(obj) | ||||||
|  |         # Try to ensure we have a request at the acquisition root | ||||||
|  |         # by using the one from getSite | ||||||
|  |         if not len(chain) or not isinstance(chain[-1], RequestContainer): | ||||||
|  |             site = getSite() | ||||||
|  |             site_chain = aq_chain(site) | ||||||
|  |             if len(site_chain) and isinstance(site_chain[-1], | ||||||
|  |                                               RequestContainer): | ||||||
|  |                 req = site_chain[-1] | ||||||
|  |                 new_obj = req | ||||||
|  |                 # rebuld the chain with the request at the bottom | ||||||
|  |                 for item in reversed(chain): | ||||||
|  |                     new_obj = aq_base(item).__of__(new_obj) | ||||||
|  |                 obj = new_obj | ||||||
|  |         return obj | ||||||
|  | 
 | ||||||
|  |     def __call__(self): | ||||||
|  |         return self.wrapped_object | ||||||
|  | 
 | ||||||
|  |     def __hash__(self): | ||||||
|  |         return hash((self.dbname, | ||||||
|  |                      self.object._p_oid, | ||||||
|  |                      )) | ||||||
|  | 
 | ||||||
|  |     def __cmp__(self, other): | ||||||
|  |         if self.key_type_id == other.key_type_id: | ||||||
|  |             return cmp((self.dbname,self.oid), (other.dbname, other.oid)) | ||||||
|  |         return cmp(self.key_type_id, other.key_type_id) | ||||||
							
								
								
									
										13
									
								
								z2/intid/lsm.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								z2/intid/lsm.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | ||||||
|  | """ | ||||||
|  | five.localsitemanager/PersistentComponents compatibility support. | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     from five.localsitemanager import ( | ||||||
|  |         make_objectmanager_site as make_site, ) | ||||||
|  | except ImportError: | ||||||
|  |     USE_LSM = False | ||||||
|  |     from Products.Five.site.localsite import ( | ||||||
|  |         enableLocalSiteHook as make_site, ) | ||||||
|  | else: | ||||||
|  |     USE_LSM = True | ||||||
							
								
								
									
										10
									
								
								z2/intid/overrides.zcml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								z2/intid/overrides.zcml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | <configure xmlns="http://namespaces.zope.org/zope"> | ||||||
|  | 
 | ||||||
|  | <adapter  | ||||||
|  |     factory=".keyreference.KeyReferenceToPersistent" | ||||||
|  |     trusted="y" | ||||||
|  |     /> | ||||||
|  | 
 | ||||||
|  | <adapter factory=".keyreference.connectionOfPersistent" /> | ||||||
|  | 
 | ||||||
|  | </configure> | ||||||
							
								
								
									
										24
									
								
								z2/intid/registrations.pt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								z2/intid/registrations.pt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | ||||||
|  | <html metal:use-macro="context/@@standard_macros/view" | ||||||
|  |     i18n:domain="zope"> | ||||||
|  |   <body> | ||||||
|  |   <div metal:fill-slot="body"> | ||||||
|  |   <div metal:define-macro="body"> | ||||||
|  |       <p i18n:translate=""><span i18n:name="count" | ||||||
|  |          tal:replace="view/len" /> objects</p> | ||||||
|  | 
 | ||||||
|  |       <table id="sortable" class="listing" summary="Content listing" | ||||||
|  |              tal:condition="request/testing|nothing" | ||||||
|  |              i18n:attributes="summary"> | ||||||
|  |         <tr><th i18n:translate="">ID</th><th | ||||||
|  |                 i18n:translate="">Object</th></tr> | ||||||
|  |         <tr tal:repeat="row view/_items"> | ||||||
|  |           <td tal:content="python:row[0]" /> | ||||||
|  |           <td><a tal:content="python:row[1]" | ||||||
|  |                  tal:attributes="href python:row[1]" /></td> | ||||||
|  |         </tr> | ||||||
|  |       </table> | ||||||
|  |   </div> | ||||||
|  |   </div> | ||||||
|  |   </body> | ||||||
|  | 
 | ||||||
|  | </html> | ||||||
							
								
								
									
										133
									
								
								z2/intid/site.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								z2/intid/site.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,133 @@ | ||||||
|  | from Acquisition import aq_base, aq_chain | ||||||
|  | from Products.Five import BrowserView | ||||||
|  | from zope.app.intid.interfaces import IIntIds | ||||||
|  | from zope.app.component.hooks import setSite, setHooks | ||||||
|  | from zope.app.component.interfaces import ISite | ||||||
|  | from zope.component.interfaces import ComponentLookupError | ||||||
|  | from zope.component import getUtility, getSiteManager | ||||||
|  | from OFS.interfaces import IApplication | ||||||
|  | from intid import IntIds, OFSIntIds | ||||||
|  | from lsm import make_site, USE_LSM | ||||||
|  | from utils import aq_iter | ||||||
|  | 
 | ||||||
|  | class FiveIntIdsInstall(BrowserView): | ||||||
|  |     @property | ||||||
|  |     def context(self): | ||||||
|  |         return self._context[0] | ||||||
|  | 
 | ||||||
|  |     def __init__(self, context, request): | ||||||
|  |         self._context = context, | ||||||
|  |         self.request = request | ||||||
|  |         doinstall = self.request.get('install', None) | ||||||
|  |         if doinstall: | ||||||
|  |             self.install() | ||||||
|  | 
 | ||||||
|  |     def install(self): | ||||||
|  |         add_intids(self.context, findroot=False) | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def installed(self): | ||||||
|  |         installed = False | ||||||
|  |         try: | ||||||
|  |             intids = getUtility(IIntIds) | ||||||
|  |             if intids is not None: | ||||||
|  |                 if USE_LSM: | ||||||
|  |                     sm = self.context.getSiteManager() | ||||||
|  |                     if 'intids' in sm.objectIds(): | ||||||
|  |                         installed = True | ||||||
|  |                 else: | ||||||
|  |                     installed = True | ||||||
|  |         except ComponentLookupError, e: | ||||||
|  |             pass | ||||||
|  |         return installed | ||||||
|  | 
 | ||||||
|  | def initializeSite(site, sethook=False, **kw): | ||||||
|  |     make_site(site) | ||||||
|  |     if sethook: | ||||||
|  |          setHooks() | ||||||
|  |     setSite(site) | ||||||
|  | 
 | ||||||
|  | def xx_get_root(app): | ||||||
|  |     for parent in aq_iter(app, error=AttributeError): | ||||||
|  |         if IApplication.providedBy(parent): | ||||||
|  |             return parent | ||||||
|  |     raise AttributeError, 'No application found' | ||||||
|  | 
 | ||||||
|  | def get_root(obj): | ||||||
|  |     for p in aq_chain(obj): | ||||||
|  |         if IApplication.providedBy(p): | ||||||
|  |             return p | ||||||
|  |     raise AttributeError, 'No application found' | ||||||
|  | 
 | ||||||
|  | def addUtility(site, interface, klass, name='', ofs_name='', findroot=True): | ||||||
|  |     """ | ||||||
|  |     add local utility in zope2 | ||||||
|  |     """ | ||||||
|  |     app = site | ||||||
|  |     if findroot: | ||||||
|  |         app = get_root(site) | ||||||
|  | 
 | ||||||
|  |     # If we have the zope Application and the utility is not yet | ||||||
|  |     # registered, then register it. | ||||||
|  |     assert app is not None, TypeError("app is None") | ||||||
|  | 
 | ||||||
|  |     if not ISite.providedBy(app): | ||||||
|  |         initializeSite(app, sethook=False) | ||||||
|  | 
 | ||||||
|  |     sm = app.getSiteManager() | ||||||
|  |     obj = None | ||||||
|  |     if USE_LSM: | ||||||
|  |         # Try to get the utility from OFS directly in case it is | ||||||
|  |         # stored, but not registered | ||||||
|  |         ofs_name = ofs_name or name | ||||||
|  |         obj = getattr(aq_base(sm), ofs_name, None) | ||||||
|  |     if sm.queryUtility(interface, | ||||||
|  |                        name=name, | ||||||
|  |                        default=None) is None: | ||||||
|  |         # Register the utility if it is not yet registered | ||||||
|  |         if obj is None: | ||||||
|  |             if name: | ||||||
|  |                 obj = klass(name) | ||||||
|  |             else: | ||||||
|  |                 obj = klass() | ||||||
|  |         if USE_LSM: | ||||||
|  |             sm.registerUtility(provided=interface, component=obj, | ||||||
|  |                                name=name) | ||||||
|  |         else: | ||||||
|  |             #sm.registerUtility(interface, obj, name=name)  ###hm | ||||||
|  |             sm.registerUtility(provided=interface, component=obj, | ||||||
|  |                                name=name) | ||||||
|  |     elif USE_LSM and obj is None: | ||||||
|  |         # Get the utility if registered, but not yet stored in the LSM | ||||||
|  |         obj = sm.queryUtility(interface, name=name) | ||||||
|  | 
 | ||||||
|  |     # Make sure we store the utility permanently in the OFS so we don't loose | ||||||
|  |     # intids on uninstall | ||||||
|  |     if USE_LSM: | ||||||
|  |         if ofs_name and ofs_name not in sm.objectIds(): | ||||||
|  |             sm._setObject(ofs_name, aq_base(obj), set_owner=False, | ||||||
|  |                           suppress_events=True) | ||||||
|  | 
 | ||||||
|  | from intid import IIntIds, OFSIntIds | ||||||
|  | from zope.component import getUtility | ||||||
|  | 
 | ||||||
|  | def add_intids(site, findroot=False): | ||||||
|  |     if USE_LSM: | ||||||
|  |         klass = IntIds | ||||||
|  |     else: | ||||||
|  |         klass = OFSIntIds | ||||||
|  |     addUtility(site, IIntIds, klass, ofs_name='intids', findroot=findroot) | ||||||
|  | 
 | ||||||
|  | def get_intids(context=None): | ||||||
|  |     return getUtility(IIntIds, context=context) | ||||||
|  | 
 | ||||||
|  | def del_intids(context=None, findroot=False): | ||||||
|  |     if findroot: | ||||||
|  |         context = get_root(context) | ||||||
|  |     utility = get_intids(context) | ||||||
|  |     if USE_LSM: | ||||||
|  |         getSiteManager(context).unregisterUtility(component=utility, | ||||||
|  |                                                   provided=IIntIds) | ||||||
|  |     else: | ||||||
|  |         parent = utility.aq_parent | ||||||
|  |         parent.manage_delObjects([utility.__name__]) | ||||||
							
								
								
									
										18
									
								
								z2/intid/subscriber.zcml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								z2/intid/subscriber.zcml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | <configure xmlns="http://namespaces.zope.org/zope"> | ||||||
|  | 	 | ||||||
|  |   <subscriber | ||||||
|  |      handler=".intid.addIntIdSubscriber" | ||||||
|  |      for="persistent.IPersistent | ||||||
|  |           zope.app.container.interfaces.IObjectAddedEvent" | ||||||
|  |       /> | ||||||
|  |   <subscriber | ||||||
|  |       handler=".intid.removeIntIdSubscriber" | ||||||
|  |       for="persistent.IPersistent | ||||||
|  |            zope.app.container.interfaces.IObjectRemovedEvent" | ||||||
|  |       /> | ||||||
|  |   <subscriber | ||||||
|  |       handler=".intid.moveIntIdSubscriber" | ||||||
|  |       for="OFS.interfaces.ITraversable | ||||||
|  |            zope.app.container.interfaces.IObjectMovedEvent" | ||||||
|  |       /> | ||||||
|  | </configure> | ||||||
							
								
								
									
										16
									
								
								z2/intid/test.zcml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								z2/intid/test.zcml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | <configure xmlns="http://namespaces.zope.org/zope" | ||||||
|  |            xmlns:five="http://namespaces.zope.org/five"> | ||||||
|  |   <include file="subscriber.zcml" /> | ||||||
|  |   <include file="cmfdirectoryview.zcml" /> | ||||||
|  |   <include package="zope.app.keyreference" /> | ||||||
|  |   <subscriber | ||||||
|  |       handler=".tests.setNotified" | ||||||
|  |       for="zope.app.intid.interfaces.IIntIdAddedEvent" | ||||||
|  |       /> | ||||||
|  |   <subscriber | ||||||
|  |       handler=".tests.setNotified" | ||||||
|  |       for="zope.app.intid.interfaces.IIntIdRemovedEvent" | ||||||
|  |       /> | ||||||
|  |    | ||||||
|  |   <includeOverrides file="overrides.zcml" package="five.intid"/> | ||||||
|  | </configure> | ||||||
							
								
								
									
										168
									
								
								z2/intid/tracking.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								z2/intid/tracking.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,168 @@ | ||||||
|  | Tracking Object Additions, Deletions, and Moves | ||||||
|  | =============================================== | ||||||
|  | 
 | ||||||
|  | Unique ID utilities track object add moves.  Let's look at an | ||||||
|  | example. First, we'll create a unique Id utility: | ||||||
|  | 
 | ||||||
|  |   >>> print http(r""" | ||||||
|  |   ... POST /++etc++site/default/@@contents.html HTTP/1.1 | ||||||
|  |   ... Authorization: Basic mgr:mgrpw | ||||||
|  |   ... Referer: http://localhost:8081/++etc++site/default/@@contents.html | ||||||
|  |   ... | ||||||
|  |   ... type_name=BrowserAdd__zope.app.intid.IntIds&new_value=""") | ||||||
|  |   HTTP/1.1 303 See Other | ||||||
|  |   ... | ||||||
|  |   Location: http://localhost/++etc++site/default/IntIds/@@registration.html | ||||||
|  |   ... | ||||||
|  | 
 | ||||||
|  |   >>> print http(r""" | ||||||
|  |   ... POST /++etc++site/default/IntIds/addRegistration.html HTTP/1.1 | ||||||
|  |   ... Authorization: Basic mgr:mgrpw | ||||||
|  |   ... Referer: http://localhost:8081/++etc++site/default/IntIds/ | ||||||
|  |   ... Content-Type: multipart/form-data; boundary=----------CedQTrEQIEPbgfYhvcITAhQi2aJdgu3tYfJ0WYQmkpLQTt6OTOpd5GJ | ||||||
|  |   ...  | ||||||
|  |   ... ------------CedQTrEQIEPbgfYhvcITAhQi2aJdgu3tYfJ0WYQmkpLQTt6OTOpd5GJ | ||||||
|  |   ... Content-Disposition: form-data; name="field.name" | ||||||
|  |   ...  | ||||||
|  |   ... IntIds | ||||||
|  |   ... ------------CedQTrEQIEPbgfYhvcITAhQi2aJdgu3tYfJ0WYQmkpLQTt6OTOpd5GJ | ||||||
|  |   ... Content-Disposition: form-data; name="field.provided" | ||||||
|  |   ...  | ||||||
|  |   ... zope.app.intid.interfaces.IIntIds | ||||||
|  |   ... ------------CedQTrEQIEPbgfYhvcITAhQi2aJdgu3tYfJ0WYQmkpLQTt6OTOpd5GJ | ||||||
|  |   ... Content-Disposition: form-data; name="field.provided-empty-marker" | ||||||
|  |   ...  | ||||||
|  |   ... 1 | ||||||
|  |   ... ------------CedQTrEQIEPbgfYhvcITAhQi2aJdgu3tYfJ0WYQmkpLQTt6OTOpd5GJ | ||||||
|  |   ... Content-Disposition: form-data; name="field.status" | ||||||
|  |   ...  | ||||||
|  |   ... Active | ||||||
|  |   ... ------------CedQTrEQIEPbgfYhvcITAhQi2aJdgu3tYfJ0WYQmkpLQTt6OTOpd5GJ | ||||||
|  |   ... Content-Disposition: form-data; name="field.status-empty-marker" | ||||||
|  |   ...  | ||||||
|  |   ... 1 | ||||||
|  |   ... ------------CedQTrEQIEPbgfYhvcITAhQi2aJdgu3tYfJ0WYQmkpLQTt6OTOpd5GJ | ||||||
|  |   ... Content-Disposition: form-data; name="field.permission" | ||||||
|  |   ...  | ||||||
|  |   ...  | ||||||
|  |   ... ------------CedQTrEQIEPbgfYhvcITAhQi2aJdgu3tYfJ0WYQmkpLQTt6OTOpd5GJ | ||||||
|  |   ... Content-Disposition: form-data; name="field.permission-empty-marker" | ||||||
|  |   ...  | ||||||
|  |   ... 1 | ||||||
|  |   ... ------------CedQTrEQIEPbgfYhvcITAhQi2aJdgu3tYfJ0WYQmkpLQTt6OTOpd5GJ | ||||||
|  |   ... Content-Disposition: form-data; name="UPDATE_SUBMIT" | ||||||
|  |   ...  | ||||||
|  |   ... Add | ||||||
|  |   ... ------------CedQTrEQIEPbgfYhvcITAhQi2aJdgu3tYfJ0WYQmkpLQTt6OTOpd5GJ-- | ||||||
|  |   ... """) | ||||||
|  |   HTTP/1.1 303 See Other | ||||||
|  |   ... | ||||||
|  |   Location: @@SelectedManagementView.html | ||||||
|  |   ... | ||||||
|  | 
 | ||||||
|  | Now, we'll add a few folders: | ||||||
|  | 
 | ||||||
|  |   >>> print http(r""" | ||||||
|  |   ... POST /@@contents.html HTTP/1.1 | ||||||
|  |   ... Authorization: Basic mgr:mgrpw | ||||||
|  |   ... Content-Length: 64 | ||||||
|  |   ... Content-Type: application/x-www-form-urlencoded | ||||||
|  |   ...  | ||||||
|  |   ... type_name=BrowserAdd__zope.app.folder.folder.Folder&new_value=f1""") | ||||||
|  |   HTTP/1.1 303 See Other | ||||||
|  |   ... | ||||||
|  | 
 | ||||||
|  |   >>> print http(r""" | ||||||
|  |   ... POST /f1/@@contents.html HTTP/1.1 | ||||||
|  |   ... Authorization: Basic mgr:mgrpw | ||||||
|  |   ... Content-Length: 64 | ||||||
|  |   ... Content-Type: application/x-www-form-urlencoded | ||||||
|  |   ...  | ||||||
|  |   ... type_name=BrowserAdd__zope.app.folder.folder.Folder&new_value=f1""") | ||||||
|  |   HTTP/1.1 303 See Other | ||||||
|  |   ... | ||||||
|  | 
 | ||||||
|  |   >>> print http(r""" | ||||||
|  |   ... POST /f1/@@contents.html HTTP/1.1 | ||||||
|  |   ... Authorization: Basic mgr:mgrpw | ||||||
|  |   ... Content-Length: 64 | ||||||
|  |   ... Content-Type: application/x-www-form-urlencoded | ||||||
|  |   ...  | ||||||
|  |   ... type_name=BrowserAdd__zope.app.folder.folder.Folder&new_value=f2""") | ||||||
|  |   HTTP/1.1 303 See Other | ||||||
|  |   ... | ||||||
|  | 
 | ||||||
|  |   >>> print http(r""" | ||||||
|  |   ... POST /f1/f1/@@contents.html HTTP/1.1 | ||||||
|  |   ... Authorization: Basic mgr:mgrpw | ||||||
|  |   ... Content-Length: 64 | ||||||
|  |   ... Content-Type: application/x-www-form-urlencoded | ||||||
|  |   ...  | ||||||
|  |   ... type_name=BrowserAdd__zope.app.folder.folder.Folder&new_value=f1""") | ||||||
|  |   HTTP/1.1 303 See Other | ||||||
|  |   ... | ||||||
|  | 
 | ||||||
|  | Now, if we look at the index page for the unique id utility, we'll see | ||||||
|  | the objects we added: | ||||||
|  | 
 | ||||||
|  |   >>> print http(r""" | ||||||
|  |   ... GET /++etc++site/default/IntIds/@@index.html?testing=1 HTTP/1.1 | ||||||
|  |   ... Authorization: Basic mgr:mgrpw | ||||||
|  |   ... Referer: http://localhost:8081/++etc++site/default/@@contents.html | ||||||
|  |   ... """) | ||||||
|  |   HTTP/1.1 200 Ok | ||||||
|  |   ...4 objects... | ||||||
|  |   .../f1... | ||||||
|  |   .../f1/f1... | ||||||
|  |   .../f1/f2... | ||||||
|  |   .../f1/f1/f1... | ||||||
|  | 
 | ||||||
|  | If we move the top object: | ||||||
|  | 
 | ||||||
|  |   >>> print http(r""" | ||||||
|  |   ... POST /@@contents.html HTTP/1.1 | ||||||
|  |   ... Authorization: Basic mgr:mgrpw | ||||||
|  |   ... Content-Length: 40 | ||||||
|  |   ... Content-Type: application/x-www-form-urlencoded | ||||||
|  |   ... Referer: http://localhost:8081/@@contents.html | ||||||
|  |   ...  | ||||||
|  |   ... new_value%3Alist=f2&rename_ids%3Alist=f1""") | ||||||
|  |   HTTP/1.1 303 See Other | ||||||
|  |   ... | ||||||
|  | 
 | ||||||
|  | We'll see that reflected in the utility: | ||||||
|  | 
 | ||||||
|  |   >>> print http(r""" | ||||||
|  |   ... GET /++etc++site/default/IntIds/@@index.html?testing=1 HTTP/1.1 | ||||||
|  |   ... Authorization: Basic mgr:mgrpw | ||||||
|  |   ... Referer: http://localhost:8081/++etc++site/default/@@contents.html | ||||||
|  |   ... """) | ||||||
|  |   HTTP/1.1 200 Ok | ||||||
|  |   ...4 objects... | ||||||
|  |   .../f2... | ||||||
|  |   .../f2/f1... | ||||||
|  |   .../f2/f2... | ||||||
|  |   .../f2/f1/f1... | ||||||
|  | 
 | ||||||
|  | And if we delete the top object: | ||||||
|  | 
 | ||||||
|  |   >>> print http(r""" | ||||||
|  |   ... POST /@@contents.html HTTP/1.1 | ||||||
|  |   ... Authorization: Basic mgr:mgrpw | ||||||
|  |   ... Content-Length: 44 | ||||||
|  |   ... Content-Type: application/x-www-form-urlencoded | ||||||
|  |   ... Referer: http://localhost:8081/@@contents.html | ||||||
|  |   ...  | ||||||
|  |   ... ids%3Alist=f2&container_delete_button=Delete""") | ||||||
|  |   HTTP/1.1 303 See Other | ||||||
|  |   ... | ||||||
|  | 
 | ||||||
|  | all of the objects will go away: | ||||||
|  | 
 | ||||||
|  |   >>> print http(r""" | ||||||
|  |   ... GET /++etc++site/default/IntIds/@@index.html?testing=1 HTTP/1.1 | ||||||
|  |   ... Authorization: Basic mgr:mgrpw | ||||||
|  |   ... Referer: http://localhost:8081/++etc++site/default/@@contents.html | ||||||
|  |   ... """) | ||||||
|  |   HTTP/1.1 200 Ok | ||||||
|  |   ...0 objects... | ||||||
							
								
								
									
										31
									
								
								z2/intid/unreferenceable.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								z2/intid/unreferenceable.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | # Sometimes persistent classes are never meant to be persisted. The most | ||||||
|  | # common example are CMFCore directory views and filesystem objects. | ||||||
|  | # Register specific handlers that are no-ops to circumvent | ||||||
|  | from zope.interface import implements | ||||||
|  | from zope.app.keyreference.interfaces import IKeyReference, NotYet | ||||||
|  | 
 | ||||||
|  | def addIntIdSubscriber(ob, event): | ||||||
|  |     return | ||||||
|  | 
 | ||||||
|  | def removeIntIdSubscriber(ob, event): | ||||||
|  |     return | ||||||
|  | 
 | ||||||
|  | def moveIntIdSubscriber(ob, event): | ||||||
|  |     return | ||||||
|  | 
 | ||||||
|  | class KeyReferenceNever(object): | ||||||
|  |     """A keyreference that is never ready""" | ||||||
|  |     implements(IKeyReference) | ||||||
|  |      | ||||||
|  |     key_type_id = 'five.intid.cmfexceptions.keyreference' | ||||||
|  |      | ||||||
|  |     def __init__(self, obj): | ||||||
|  |         raise NotYet() | ||||||
|  |      | ||||||
|  |     def __call__(self): | ||||||
|  |         return None | ||||||
|  |          | ||||||
|  |     def __cmp__(self, other): | ||||||
|  |         if self.key_type_id == other.key_type_id: | ||||||
|  |             return cmp(self, other) | ||||||
|  |         return cmp(self.key_type_id, other.key_type_id) | ||||||
							
								
								
									
										23
									
								
								z2/intid/utils.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								z2/intid/utils.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | ||||||
|  | from Acquisition import aq_base, aq_inner, IAcquirer | ||||||
|  | 
 | ||||||
|  | def aq_iter(obj, error=None): | ||||||
|  |     if not (IAcquirer.providedBy(obj) or hasattr(obj, '__parent__')): | ||||||
|  |         raise TypeError("%s not acquisition wrapped" %obj) | ||||||
|  | 
 | ||||||
|  |     # adapted from alecm's 'listen' | ||||||
|  |     seen = set() | ||||||
|  |     # get the inner-most wrapper (maybe save some cycles, and prevent | ||||||
|  |     # bogus loop detection) | ||||||
|  |     cur = aq_inner(obj) | ||||||
|  |     while cur is not None: | ||||||
|  |         yield cur | ||||||
|  |         seen.add(id(aq_base(cur))) | ||||||
|  |         cur = getattr(cur, 'aq_parent', getattr(cur, '__parent__', None)) | ||||||
|  |         if id(aq_base(cur)) in seen: | ||||||
|  |             # avoid loops resulting from acquisition-less views | ||||||
|  |             # whose __parent__ points to | ||||||
|  |             # the context whose aq_parent points to the view | ||||||
|  |             if error is not None: | ||||||
|  |                 raise error, '__parent__ loop found' | ||||||
|  |             break | ||||||
|  | 
 | ||||||
							
								
								
									
										28
									
								
								z2/intid/xx_ftests.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								z2/intid/xx_ftests.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | ############################################################################## | ||||||
|  | # | ||||||
|  | # Copyright (c) 2004 Zope Corporation and Contributors. | ||||||
|  | # All Rights Reserved. | ||||||
|  | # | ||||||
|  | # This software is subject to the provisions of the Zope Public License, | ||||||
|  | # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution. | ||||||
|  | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED | ||||||
|  | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
|  | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS | ||||||
|  | # FOR A PARTICULAR PURPOSE. | ||||||
|  | # | ||||||
|  | ############################################################################## | ||||||
|  | """Int Id Utility Functional Tests | ||||||
|  | 
 | ||||||
|  | $Id$ | ||||||
|  | """ | ||||||
|  | import unittest | ||||||
|  | from zope.app.testing import functional | ||||||
|  | 
 | ||||||
|  | def test_suite(): | ||||||
|  |     return unittest.TestSuite(( | ||||||
|  |         functional.FunctionalDocFileSuite('tracking.txt'), | ||||||
|  |         )) | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     unittest.main(defaultTest='test_suite') | ||||||
|  | 
 | ||||||
							
								
								
									
										60
									
								
								z2/intid/xx_tests.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								z2/intid/xx_tests.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | ||||||
|  | import doctest | ||||||
|  | from zope.app.testing import placelesssetup | ||||||
|  | from persistent import Persistent | ||||||
|  | from zope.app.component.hooks import setHooks, setSite | ||||||
|  | 
 | ||||||
|  | from Products.Five.tests.testing.simplecontent import ( | ||||||
|  |     SimpleContent, | ||||||
|  |     ISimpleContent, | ||||||
|  |     manage_addSimpleContent, | ||||||
|  |     ) | ||||||
|  | from Products.Five import zcml | ||||||
|  | from five.intid.lsm import USE_LSM | ||||||
|  | from five.intid import site | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS | ||||||
|  | 
 | ||||||
|  | class DemoPersistent(Persistent): | ||||||
|  |     """ Demo persistent object """ | ||||||
|  |     test = 'test object' | ||||||
|  |     __name__ = 'Test Object' | ||||||
|  | 
 | ||||||
|  | NOTIFIED=[None] | ||||||
|  | 
 | ||||||
|  | def setNotified(event): | ||||||
|  |     NOTIFIED[0] = "%s %s" %(event.object, event) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def setUp(app): | ||||||
|  |     # enable zcml and site hooks | ||||||
|  |     placelesssetup.setUp() | ||||||
|  |     import Products.Five | ||||||
|  |     from five import intid | ||||||
|  |     zcml.load_config('meta.zcml', Products.Five) | ||||||
|  |     zcml.load_config('configure.zcml', Products.Five) | ||||||
|  |     zcml.load_config('test.zcml', intid) | ||||||
|  |     if not USE_LSM: | ||||||
|  |         # monkey in our hooks | ||||||
|  |         from Products.Five.site.metaconfigure import classSiteHook | ||||||
|  |         from Products.Five.site.localsite import FiveSite | ||||||
|  |         from zope.interface import classImplements | ||||||
|  |         from zope.app.component.interfaces import IPossibleSite | ||||||
|  |         klass = app.__class__ | ||||||
|  |         classSiteHook(klass, FiveSite) | ||||||
|  |         classImplements(klass, IPossibleSite) | ||||||
|  |     setHooks() | ||||||
|  | 
 | ||||||
|  | def tearDown(): | ||||||
|  |     placelesssetup.tearDown() | ||||||
|  | 
 | ||||||
|  | def test_suite(): | ||||||
|  |     import unittest | ||||||
|  |     from Testing.ZopeTestCase import FunctionalDocFileSuite | ||||||
|  |     from zope.testing.doctest import DocTestSuite | ||||||
|  |     integration = FunctionalDocFileSuite( | ||||||
|  |         'README.txt', | ||||||
|  |         package='five.intid', | ||||||
|  |         optionflags=optionflags | ||||||
|  |         ) | ||||||
|  |     return unittest.TestSuite((integration,)) | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 helmutm
						helmutm