[Erp5-report] r35110 jerome - in /erp5/trunk/products/ERP5: Document/ tests/

nobody at svn.erp5.org nobody at svn.erp5.org
Fri May 7 14:36:08 CEST 2010


Author: jerome
Date: Fri May  7 14:36:04 2010
New Revision: 35110

URL: http://svn.erp5.org?rev=35110&view=rev
Log:
When exporting a type information, export it in the context in its type
provider. When installing a type information, register its provider if it's not
already registered.


Modified:
    erp5/trunk/products/ERP5/Document/BusinessTemplate.py
    erp5/trunk/products/ERP5/tests/testBusinessTemplate.py

Modified: erp5/trunk/products/ERP5/Document/BusinessTemplate.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Document/BusinessTemplate.py?rev=35110&r1=35109&r2=35110&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/Document/BusinessTemplate.py [utf8] (original)
+++ erp5/trunk/products/ERP5/Document/BusinessTemplate.py [utf8] Fri May  7 14:36:04 2010
@@ -31,7 +31,7 @@
 from Shared.DC.ZRDB.Connection import Connection as RDBConnection
 from Products.ERP5Type.DiffUtils import DiffFile
 from Products.ERP5Type.Globals import Persistent, PersistentMapping
-from Acquisition import Implicit, aq_base
+from Acquisition import Implicit, aq_base, aq_inner, aq_parent
 from AccessControl import ClassSecurityInfo
 from Products.CMFCore.utils import getToolByName
 from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
@@ -854,11 +854,12 @@
     """
     pass
 
-  def onNewObject(self):
+  def onNewObject(self, obj):
     """
       Installation hook.
       Called when installation process determined that object to install is
       new on current site (it's not replacing an existing object).
+      `obj` parameter is the newly created object in its acquisition context.
       Can be overridden by subclasses.
     """
     pass
@@ -947,9 +948,10 @@
           saved_uid_dict = {}
           subobjects_dict = {}
           portal_type_dict = {}
-          # Object already exists
           old_obj = container._getOb(object_id, None)
+          object_existed = old_obj is not None
           if old_obj is not None:
+            # Object already exists
             recurse(saveHook, old_obj)
             if getattr(aq_base(old_obj), 'groups', None) is not None:
               # we must keep original order groups
@@ -969,8 +971,7 @@
               portal_type_dict['workflow_chain'] = \
                 getChainByType(context)[1].get('chain_' + object_id, '')
             container.manage_delObjects([object_id])
-          else:
-            self.onNewObject()
+
           # install object
           obj = self._objects[path]
           if getattr(obj, 'meta_type', None) == 'Script (Python)':
@@ -997,6 +998,11 @@
             LOG("BT, install", 0, object_id)
             raise
           obj = container._getOb(object_id)
+
+          if not object_existed:
+            # A new object was added, call the hook
+            self.onNewObject(obj)
+
           # mark a business template installation so in 'PortalType_afterClone' scripts
           # we can implement logical for reseting or not attributes (i.e reference).
           self.REQUEST.set('is_business_template_installation', 1)
@@ -1414,7 +1420,7 @@
   def beforeInstall(self):
     self._installed_new_category = False
 
-  def onNewObject(self):
+  def onNewObject(self, obj):
     self._installed_new_category = True
 
   def afterInstall(self):
@@ -1753,6 +1759,10 @@
     p = context.getPortalObject()
     for relative_url in self._archive.keys():
       obj = p.unrestrictedTraverse(relative_url)
+      # normalize relative_url, not all type informations are stored in
+      # "portal_types"
+      relative_url = '%s/%s' % (obj.getPhysicalPath()[-2:])
+
       obj = obj._getCopy(context)
       # obj is in ghost state and an attribute must be accessed
       # so that obj.__dict__ does not return an empty dict
@@ -1768,6 +1778,17 @@
           delattr(obj, attr)
       self._objects[relative_url] = obj
       obj.wl_clearLocks()
+  
+  def onNewObject(self, obj):
+    """ When we install a type which is contained in a type provider not
+    registered on types tool, register the type provider.
+    """
+    portal = obj.getPortalObject()
+    types_tool = portal.portal_types
+    type_container_id = obj.getParentId()
+    if type_container_id not in types_tool.type_provider_list:
+      types_tool.type_provider_list = tuple(types_tool.type_provider_list) + (
+          type_container_id,)
 
   # XXX : this method is kept temporarily, but can be removed once all bt5 are
   # re-exported with separated workflow-chain information
@@ -1938,7 +1959,7 @@
                                   (wf_id, portal_type)
             chain_dict[chain_key] = self._objects[path]
         else:
-          if portal_type not in context.portal_types.objectIds():
+          if context.portal_types.getTypeInfo(portal_type) is None:
             raise ValueError('Cannot chain workflow %r to non existing '
                            'portal type %r' % (self._chain_string_separator\
                                                      .join(self._objects[path])
@@ -2043,16 +2064,15 @@
 
   def build(self, context, **kw):
     types_tool = getToolByName(self.getPortalObject(), 'portal_types')
-    types_list = list(types_tool.objectIds())
     for key in self._archive.keys():
       try:
         portal_type, allowed_type = key.split(' | ')
       except ValueError:
         raise ValueError('Invalid item %r in %s' % (key, self.name))
+      ob = types_tool.getTypeInfo(portal_type)
       # check properties corresponds to what is defined in site
-      if not portal_type in types_list:
+      if ob is None:
         raise ValueError, "Portal Type %s not found in site" %(portal_type,)
-      ob = types_tool._getOb(portal_type)
       prop_value = getattr(ob, self.class_property, ())
       if not allowed_type in prop_value and not self.is_bt_for_diff:
         raise ValueError, "%s %s not found in portal type %s" % (
@@ -2151,15 +2171,14 @@
           action = update_dict[key]
           if action == 'nothing':
             continue
-        try:
-          portal_id = key.split('/')[-1]
-          portal_type = types_tool._getOb(portal_id)
-        except (AttributeError, KeyError):
+        portal_id = key.split('/')[-1]
+        type_information = types_tool.getTypeInfo(portal_id)
+        if type_information is None:
           raise AttributeError, "Portal type '%s' not found while " \
               "installing %s" % (portal_id, self.getTitle())
         property_list = self._objects.get(key, [])
         old_property_list = old_objects.get(key, ())
-        object_property_list = getattr(portal_type, self.class_property, ())
+        object_property_list = getattr(type_information, self.class_property, ())
         # merge differences between portal types properties
         # for example:
         # * current value : [A,B,C]
@@ -2169,7 +2188,7 @@
         for id in object_property_list:
           if id not in property_list and id not in old_property_list:
             property_list.append(id)
-        setattr(portal_type, self.class_property, tuple(property_list))
+        setattr(type_information, self.class_property, tuple(property_list))
 
   def uninstall(self, context, **kw):
     object_path = kw.get('object_path', None)
@@ -2180,19 +2199,19 @@
     else:
       object_key_list = self._objects.keys()
     for key in object_key_list:
-      try:
-        portal_id = key.split('/')[-1]
-        portal_type = types_tool._getOb(portal_id)
-      except (AttributeError,  KeyError):
-        LOG("portal types not found : ", 100, portal_id)
+      portal_id = key.split('/')[-1]
+      type_information = types_tool.getTypeInfo(portal_id)
+      if type_information is None:
+        LOG("BusinessTemplate", WARNING,
+            "Portal type %r not found while uninstalling" % (portal_id,))
         continue
       property_list = self._objects[key]
-      original_property_list = list(getattr(portal_type,
+      original_property_list = list(getattr(type_information,
                                     self.class_property, ()))
       for id in property_list:
         if id in original_property_list:
           original_property_list.remove(id)
-      setattr(portal_type, self.class_property, tuple(original_property_list))
+      setattr(type_information, self.class_property, tuple(original_property_list))
 
 
 class PortalTypeHiddenContentTypeTemplateItem(PortalTypeAllowedContentTypeTemplateItem):
@@ -2588,7 +2607,7 @@
     Gets action copy from action provider given the action id or reference
     """
     # Several tools still use CMF actions
-    if obj.getParentId() == 'portal_types':
+    if interfaces.ITypeProvider.providedBy(obj.getParentValue()):
       return self._getPortalTypeActionCopy(obj, value)
     else:
       return self._getPortalToolActionCopy(obj, context, value)
@@ -2600,6 +2619,8 @@
       url, value = id.split(' | ')
       url = posixpath.split(url)
       obj = p.unrestrictedTraverse(url)
+      # normalize url
+      url = obj.getPhysicalPath()[-2:]
       action = self._getActionCopy(obj, context, value)
       if action is None:
         if self.is_bt_for_diff:
@@ -2625,7 +2646,7 @@
           path, id = id.rsplit('/', 1)
           container = p.unrestrictedTraverse(path)
 
-          if container.getParentId() == 'portal_types':
+          if interfaces.ITypeProvider.providedBy(aq_parent(aq_inner(container))):
             # XXX future BT should use 'reference' instead of 'id'
             reference = getattr(obj, 'reference', None) or obj.id
             portal_type_dict.setdefault(path, {})[reference] = obj
@@ -2742,7 +2763,7 @@
         obj = p.unrestrictedTraverse(relative_url, None)
         # Several tools still use CMF actions
         if obj is not None:
-          is_new_action = obj.getParentId() == 'portal_types'
+          is_new_action = interfaces.ITypeProvider.providedBy(obj.getParentValue())
           key = is_new_action and 'reference' or 'id'
       else:
         relative_url, key, value = self._splitPath(id)
@@ -2753,8 +2774,8 @@
           if getattr(action_list[index], key, None) == value:
             obj.deleteActions(selections=(index,))
             break
-      LOG('BusinessTemplate', 100,
-          'unable to uninstall action at %s, ignoring' % relative_url )
+      LOG('BusinessTemplate', WARNING,
+          'Unable to uninstall action at %s, ignoring' % relative_url )
     BaseTemplateItem.uninstall(self, context, **kw)
 
 class PortalTypeRolesTemplateItem(BaseTemplateItem):
@@ -2768,6 +2789,8 @@
     for relative_url in self._archive.keys():
       obj = p.unrestrictedTraverse("portal_types/%s" %
           relative_url.split('/', 1)[1])
+      # normalize url
+      relative_url = '%s/%s' % (obj.getPhysicalPath()[-2:])
       self._objects[relative_url] = type_role_list = []
       for role in obj.getRoleInformationList():
         type_role_dict = {}
@@ -2880,6 +2903,9 @@
           type_roles_list = self._objects[roles_path] or []
           for role_property_dict in type_roles_list:
             obj._importRole(role_property_dict)
+        else:
+          raise AttributeError("Path '%r' not found while "
+                               "installing roles" % (path, ))
 
   def uninstall(self, context, **kw):
     p = context.getPortalObject()

Modified: erp5/trunk/products/ERP5/tests/testBusinessTemplate.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/tests/testBusinessTemplate.py?rev=35110&r1=35109&r2=35110&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/tests/testBusinessTemplate.py [utf8] (original)
+++ erp5/trunk/products/ERP5/tests/testBusinessTemplate.py [utf8] Fri May  7 14:36:04 2010
@@ -43,6 +43,7 @@
 from Products.ERP5Type.Globals import PersistentMapping
 from Products.CMFCore.Expression import Expression
 from Products.ERP5Type.tests.utils import LogInterceptor
+from Products.ERP5Type.Tool.TypesTool import TypeProvider
 from Products.ERP5Type.Workflow import addWorkflowByType
 from Products.ERP5Type.tests.backportUnittest import expectedFailure
 import shutil
@@ -50,11 +51,17 @@
 import gc
 import random
 import string
+import tempfile
+import glob
 
 from MethodObject import Method
 from Persistence import Persistent
 
 WORKFLOW_TYPE = 'erp5_workflow' 
+
+class DummyTypeProvider(TypeProvider):
+  id = 'dummy_type_provider'
+
 
 class TestBusinessTemplate(ERP5TypeTestCase, LogInterceptor):
   """
@@ -6378,6 +6385,124 @@
     self.assertFalse(getattr(portal.some_file, 'isClassOverriden', False))
     self.assertFalse(getattr(portal.another_file, 'isClassOverriden', False))
 
+  def test_type_provider(self):
+    self.portal._setObject('dummy_type_provider', DummyTypeProvider())
+    type_provider = self.portal.dummy_type_provider
+    types_tool = self.portal.portal_types
+
+    registered_type_provider_list = types_tool.type_provider_list
+    # register this type provider
+    types_tool.type_provider_list = (
+        'dummy_type_provider',) + registered_type_provider_list
+
+    dummy_type = type_provider.newContent(
+                             portal_type='Base Type',
+                             id='Dummy Type',
+                             type_factory_method_id='addFolder',
+                             type_property_sheet_list=('Reference',),
+                             type_base_category_list=('source',),
+                             type_allowed_content_type_list=('Dummy Type',),
+                             type_hidden_content_type_list=('Dummy Type',) )
+
+    dummy_type.newContent(portal_type='Action Information',
+                          reference='view',
+                          title='View', )
+
+    dummy_type.newContent(portal_type='Role Information',
+                          title='Dummy Role Definition',
+                          role_name_list=('Assignee', ))
+
+    pw = self.getWorkflowTool()
+    cbt = pw._chains_by_type.copy()
+    props = {}
+    for id, wf_ids in cbt.items():
+      props['chain_%s' % id] = ','.join(wf_ids)
+    props['chain_Dummy Type'] = 'edit_workflow'
+    pw.manage_changeWorkflows('', props=props)
+    self.assertEquals(('edit_workflow', ), pw.getChainFor('Dummy Type'))
+
+    bt = self.portal.portal_templates.newContent(
+                          portal_type='Business Template',
+                          title='test_bt',
+                          template_tool_id_list=('dummy_type_provider', ),
+                          template_portal_type_id_list=('Dummy Type',),
+                          template_portal_type_role_list=('Dummy Type', ),
+                          template_portal_type_workflow_chain_list=(
+                             'Dummy Type | edit_workflow',),
+                          template_portal_type_allowed_content_type_list=(
+                             'Dummy Type | Dummy Type',),
+                          template_portal_type_hidden_content_type_list=(
+                             'Dummy Type | Dummy Type',),
+                          template_portal_type_property_sheet_list=(
+                             'Dummy Type | Reference',),
+                          template_portal_type_base_category_list=(
+                             'Dummy Type | source',),
+                          template_action_path_list=(
+                             'Dummy Type | view',),)
+    self.stepTic()
+    bt.build()
+    self.stepTic()
+    export_dir = tempfile.mkdtemp()
+    try:
+      bt.export(path=export_dir, local=True)
+      self.stepTic()
+      # portal type template item are exported in their physical location
+      for template_item in ('PortalTypeTemplateItem',
+                            'ActionTemplateItem',):
+        self.assertEquals(['dummy_type_provider'],
+            [os.path.basename(f) for f in
+              glob.glob('%s/%s/*' % (export_dir, template_item))])
+      new_bt = self.portal.portal_templates.download(
+                        url='file:/%s' % export_dir)
+    finally:
+      shutil.rmtree(export_dir)
+
+    # uninstall the workflow chain 
+    pw._chains_by_type = cbt
+    # unregister type provider
+    types_tool.type_provider_list = registered_type_provider_list
+    # uninstall the type provider (this will also uninstall the contained types)
+    self.portal.manage_delObjects(['dummy_type_provider'])
+    self.stepTic()
+    
+    new_bt.install()
+
+    type_provider = self.portal._getOb('dummy_type_provider', None)
+    self.assertNotEqual(None, type_provider)
+
+    # This type provider, will be automatically registered on types tool during
+    # business template installation, because it contains type information
+    self.assertTrue('dummy_type_provider' in types_tool.type_provider_list)
+    # The type is reinstalled
+    self.assertTrue('Dummy Type' in type_provider.objectIds())
+    # is available from types tool
+    self.assertTrue('Dummy Type' in [ti.getId() for
+                    ti in types_tool.listTypeInfo()])
+    
+    dummy_type = types_tool.getTypeInfo('Dummy Type')
+    self.assertNotEquals(None, dummy_type)
+    # all the configuration from the type is still here
+    self.assertEquals(['Reference'], dummy_type.getTypePropertySheetList())
+    self.assertEquals(['source'], dummy_type.getTypeBaseCategoryList())
+    self.assertEquals(['Dummy Type'], dummy_type.getTypeAllowedContentTypeList())
+    self.assertEquals(['Dummy Type'], dummy_type.getTypeHiddenContentTypeList())
+
+    action_list = dummy_type.contentValues(portal_type='Action Information')
+    self.assertEquals(['View'], [action.getTitle() for action in action_list])
+    self.assertEquals(['view'], [action.getReference() for action in action_list])
+    
+    role_list = dummy_type.contentValues(portal_type='Role Information')
+    self.assertEquals(['Dummy Role Definition'],
+                      [role.getTitle() for role in role_list])
+    
+    self.assertEquals(('edit_workflow',), pw.getChainFor('Dummy Type'))
+    
+    # and our type can be used
+    instance = self.portal.newContent(portal_type='Dummy Type',
+                                      id='test_document')
+    instance.setSourceReference('OK')
+    self.assertEquals('OK', instance.getSourceReference())
+
 
 def test_suite():
   suite = unittest.TestSuite()




More information about the Erp5-report mailing list