[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