[Zope-Checkins] SVN: Zope/branches/Zope-2_8-branch/ - Collector #1792: applied patch for broken ZClasses

Andreas Jung andreas at andreas-jung.com
Sat Jun 11 02:20:44 EDT 2005


Log message for revision 30751:
        - Collector #1792: applied patch for broken ZClasses
  

Changed:
  U   Zope/branches/Zope-2_8-branch/doc/CHANGES.txt
  U   Zope/branches/Zope-2_8-branch/lib/python/App/FactoryDispatcher.py
  U   Zope/branches/Zope-2_8-branch/lib/python/ZClasses/ZClass.py
  U   Zope/branches/Zope-2_8-branch/lib/python/ZClasses/ZClass.txt
  U   Zope/branches/Zope-2_8-branch/lib/python/ZClasses/_pmc.py
  U   Zope/branches/Zope-2_8-branch/lib/python/ZClasses/_pmc.txt

-=-
Modified: Zope/branches/Zope-2_8-branch/doc/CHANGES.txt
===================================================================
--- Zope/branches/Zope-2_8-branch/doc/CHANGES.txt	2005-06-10 23:57:01 UTC (rev 30750)
+++ Zope/branches/Zope-2_8-branch/doc/CHANGES.txt	2005-06-11 06:20:42 UTC (rev 30751)
@@ -22,10 +22,12 @@
 
    - Collector #1233: port ZOPE_CONFIG patch from Zope 2.7 to Zope 2.8
 
-  Zope 2.8.0 (to be released 2005/06/11)
+  Zope 2.8.0 (2005/06/11)
 
     Bugs Fixed
 
+      - Collector #1792: applied patch for broken ZClasses
+
       - doc/FAQ.txt updated: should bear some resemblance to reality now.
         (PCGI stuff removed; error information updated; PID information
         updated; upgrade procedure added; some common version questions added.)

Modified: Zope/branches/Zope-2_8-branch/lib/python/App/FactoryDispatcher.py
===================================================================
--- Zope/branches/Zope-2_8-branch/lib/python/App/FactoryDispatcher.py	2005-06-10 23:57:01 UTC (rev 30750)
+++ Zope/branches/Zope-2_8-branch/lib/python/App/FactoryDispatcher.py	2005-06-11 06:20:42 UTC (rev 30751)
@@ -78,7 +78,7 @@
             m=d[name]
             w=getattr(m, '_permissionMapper', None)
             if w is not None:
-                m=ofWrapper(aqwrap(m, getattr(w,'aq_base',w), self))
+                m=aqwrap(m, getattr(w,'aq_base',w), self)
 
             return m
 
@@ -102,10 +102,3 @@
         d = update_menu and '/manage_main?update_menu=1' or '/manage_main'
         REQUEST['RESPONSE'].redirect(self.DestinationURL()+d)
 
-
-import ExtensionClass
-class ofWrapper(ExtensionClass.Base):
-    def __init__(self, o):
-        self._o=o
-
-    def __of__(self, parent): return self.__dict__['_o']

Modified: Zope/branches/Zope-2_8-branch/lib/python/ZClasses/ZClass.py
===================================================================
--- Zope/branches/Zope-2_8-branch/lib/python/ZClasses/ZClass.py	2005-06-10 23:57:01 UTC (rev 30750)
+++ Zope/branches/Zope-2_8-branch/lib/python/ZClasses/ZClass.py	2005-06-11 06:20:42 UTC (rev 30751)
@@ -17,6 +17,7 @@
 
 from ZPublisher.mapply import mapply
 from ExtensionClass import Base
+from Acquisition import aq_base
 from App.FactoryDispatcher import FactoryDispatcher
 from ComputedAttribute import ComputedAttribute
 from Products.PythonScripts.PythonScript import PythonScript
@@ -388,7 +389,7 @@
             product=product,
             id=self.id,
             meta_type=z.meta_type or '',
-            meta_class=self,
+            meta_class=aq_base(self),
             )
 
     def _unregister(self):
@@ -448,10 +449,7 @@
         screen of the new instance's parent Folder. Otherwise,
         the instance will be returned.
         """
-        i=mapply(self._zclass_, (), REQUEST)
-        try: i._setId(id)
-        except AttributeError:
-            i.id=id
+        i = self.fromRequest(id, REQUEST)
         folder=durl=None
         if hasattr(self, 'Destination'):
             d=self.Destination
@@ -461,6 +459,8 @@
         if not hasattr(folder,'_setObject'):
             folder=folder.aq_parent
 
+        # An object is not guarenteed to have the id we passed in.
+        id = i.getId()
         folder._setObject(id, i)
 
         if RESPONSE is not None:
@@ -468,16 +468,23 @@
             except: durl=REQUEST['URL3']
             RESPONSE.redirect(durl+'/manage_workspace')
         else:
-            # An object is not guarenteed to have the id we passed in.
-            id = i.getId()
             return folder._getOb(id)
 
     index_html=createInObjectManager
 
     def fromRequest(self, id=None, REQUEST={}):
-        i=mapply(self._zclass_, (), REQUEST)
-        if id is not None and (not hasattr(i, 'id') or not i.id): i.id=id
-
+        if self._zclass_.__init__ is object.__init__:
+            # there is no user defined __init__, avoid calling
+            # mapply then, as it would fail while trying
+            # to figure out the function properties
+            i = self._zclass_()
+        else:
+            i = mapply(self._zclass_, (), REQUEST)
+        if id is not None:
+            try:
+                i._setId(id)
+            except AttributeError:
+                i.id = id
         return i
 
     def __call__(self, *args, **kw):

Modified: Zope/branches/Zope-2_8-branch/lib/python/ZClasses/ZClass.txt
===================================================================
--- Zope/branches/Zope-2_8-branch/lib/python/ZClasses/ZClass.txt	2005-06-10 23:57:01 UTC (rev 30750)
+++ Zope/branches/Zope-2_8-branch/lib/python/ZClasses/ZClass.txt	2005-06-11 06:20:42 UTC (rev 30751)
@@ -18,6 +18,8 @@
     >>> conn.root()['Application'] = app
     >>> from OFS.Application import initialize
     >>> initialize(app)
+    >>> app.manage_addFolder('sandbox')
+    >>> sandbox = app.sandbox
 
 Once we have an object space, we need to create a product to hold the ZClass:
 
@@ -28,8 +30,91 @@
 
     >>> test.manage_addZClass('C', zope_object=True,  CreateAFactory=True)
 
-Having created a ZClass, we can create an instance:
+Having created a ZClass, we can create an instance.
 
+When setting the 'CreateAFactory' flag, a factory will be created which
+can be called from the web to create a new instance of 'C'. It also places
+an option for adding 'C's in the Add-menu in the ZMI.
+
+    >>> factory = test.C_factory
+
+    >>> factory.__class__
+    <class 'App.Factory.Factory'>
+
+The other objects created are:
+
+    - a permission "C_add_permission" required to access the factory,
+    >>> test.C_add_permission.meta_type
+    'Zope Permission'
+
+    - an initial add-form "C_addForm" which calls
+    >>> test.C_addForm.meta_type
+    'DTML Method'
+
+    - the add-script "C_add" for creating a new 'C' instance
+    >>> test.C_add.meta_type
+    'Script (Python)'
+
+The factory stores the name of the initial page and its permission name:
+
+    >>> factory.initial, factory.permission
+    ('C_addForm', 'Add Cs')
+
+We only need a simple add-script in this scenario:
+
+    >>> factory.initial = 'C_add'
+
+    >>> test.C_add.ZPythonScript_edit('',
+    ...     '##parameters=dispatcher, request\n'
+    ...     'return container.C.createInObjectManager('
+    ...          'request["id"], request)\n')
+    ...
+
+Anonymous users are usually not allowed to create new content:
+
+    add a new user...
+    >>> app.acl_users._addUser('admin', 'pass', 'pass', (), ())
+    >>> user = app.acl_users.getUser('admin').__of__(app.acl_users)
+
+    log in as user 'admin'...
+    >>> from AccessControl.SecurityManagement import newSecurityManager
+    >>> newSecurityManager(None, user)
+
+Now simulate a browser request to add a 'C' instance with id 'z':
+
+    >>> request = {'id': 'z'}
+    >>> sandbox.manage_addProduct['test'].C_factory.index_html(request)
+    Traceback (most recent call last):
+    ...
+    Unauthorized: You are not allowed to access 'C' in this context
+
+All right, allow the admin user to 'Add Cs':
+
+    >>> bool(user.has_permission('Add Cs', sandbox))
+    False
+
+    >>> sandbox.manage_addLocalRoles('admin', ['Admin'])
+    >>> sandbox.manage_permission('Add Cs', roles=['Admin'])
+    
+    >>> bool(user.has_permission('Add Cs', sandbox))
+    True
+
+Try again:
+    
+    >>> request = {'id': 'z'}
+    >>> sandbox.manage_addProduct['test'].C_factory.index_html(request)
+    <C at /sandbox/z>
+    >>> app.sandbox.z
+    <C at /sandbox/z>
+
+Log out:
+
+    >>> from AccessControl.SecurityManagement import noSecurityManager
+    >>> noSecurityManager()
+
+
+From python there is a much simpler way for creating a ZClass instance:
+
     >>> c = test.C()
     >>> c._setId('c')
     >>> app._setObject('c', c)

Modified: Zope/branches/Zope-2_8-branch/lib/python/ZClasses/_pmc.py
===================================================================
--- Zope/branches/Zope-2_8-branch/lib/python/ZClasses/_pmc.py	2005-06-10 23:57:01 UTC (rev 30750)
+++ Zope/branches/Zope-2_8-branch/lib/python/ZClasses/_pmc.py	2005-06-11 06:20:42 UTC (rev 30751)
@@ -56,15 +56,20 @@
         ExtensionClass.pmc_init_of(result)
         return result
 
+    # copy_reg.py:_slotnames() tries to use this attribute as a cache.
+    # Dont allow this attribute to be written as it may cause us
+    # to register with the data_manager.
+    __slotnames__ = property(None)
+
     def __setattr__(self, name, v):
+        super(ZClassPersistentMetaClass, self).__setattr__(name, v)
         if not ((name.startswith('_p_') or name.startswith('_v'))):
             self._p_maybeupdate(name)
-        super(ZClassPersistentMetaClass, self).__setattr__(name, v)
 
     def __delattr__(self, name):
+        super(ZClassPersistentMetaClass, self).__delattr__(name)
         if not ((name.startswith('_p_') or name.startswith('_v'))):
             self._p_maybeupdate(name)
-        super(ZClassPersistentMetaClass, self).__delattr__(name)
     
     def __setstate__(self, state):
         try:
@@ -92,6 +97,11 @@
 
             for k in to_remove:
                 delattr(self, k)
+            
+            try:
+                del cdict['__slotnames__']
+            except KeyError:
+                pass
 
             for k, v in cdict.items():
                 setattr(self, k, v)

Modified: Zope/branches/Zope-2_8-branch/lib/python/ZClasses/_pmc.txt
===================================================================
--- Zope/branches/Zope-2_8-branch/lib/python/ZClasses/_pmc.txt	2005-06-10 23:57:01 UTC (rev 30750)
+++ Zope/branches/Zope-2_8-branch/lib/python/ZClasses/_pmc.txt	2005-06-11 06:20:42 UTC (rev 30751)
@@ -205,12 +205,38 @@
 
     >>> connection.root()['P'] = P
 
+    >>> tm.commit()
+
+The persistence variables are as expected:
+
+    >>> P._p_oid
+    '\x00\x00\x00\x00\x00\x00\x00\x02'
+    >>> P._p_jar is not None
+    True
+    >>> P._p_serial is not None
+    True
+    >>> P._p_changed
+    False
+
+    >>> initial_serial = P._p_serial
+
     >>> import persistent.mapping
     >>> connection.root()['obs'] = persistent.mapping.PersistentMapping()
     >>> p = P('p')
     >>> connection.root()['obs']['p'] = p
     >>> tm.commit()
 
+The class will be unaffected:
+
+    >>> P._p_oid
+    '\x00\x00\x00\x00\x00\x00\x00\x02'
+    >>> P._p_jar is not None
+    True
+    >>> P._p_serial == initial_serial
+    True
+    >>> P._p_changed
+    False
+
 You might be wondering why we didn't just stick 'p' into the root
 object. We created an intermediate persistent object instead.  We are
 storing persistent classes in the root object. To create a ghost for a



More information about the Zope-Checkins mailing list