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