diff --git a/product/ERP5/Document/BusinessTemplate.py b/product/ERP5/Document/BusinessTemplate.py index 5ae1949989f028796e34af41bb3f698b5845490c..736e1d24731e5321f424ed379770577ec6306f82 100644 --- a/product/ERP5/Document/BusinessTemplate.py +++ b/product/ERP5/Document/BusinessTemplate.py @@ -62,6 +62,7 @@ from Products.ERP5Type.Utils import readLocalTest, \ from Products.ERP5Type.Utils import convertToUpperCase from Products.ERP5Type import Permissions, PropertySheet, interfaces from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Type.dynamic.lazy_class import ERP5BaseBroken from Products.ERP5Type.dynamic.portal_type_class import synchronizeDynamicModules from Products.ERP5Type.Core.PropertySheet import PropertySheet as PropertySheetDocument from Products.ERP5Type.TransactionalVariable import getTransactionalVariable @@ -656,6 +657,21 @@ class BaseTemplateItem(Implicit, Persistent): else: return context + def _resetDynamicModules(self): + # before any import, flush all ZODB caches to force a DB reload + # otherwise we could have objects trying to get commited while + # holding reference to a class that is no longer the same one as + # the class in its import location and pickle doesn't tolerate it. + # First we do a savepoint to dump dirty objects to temporary + # storage, so that all references to them can be freed. + transaction.savepoint(optimistic=True) + # Then we need to flush from all caches, not only the one from this + # connection + portal = self.getPortalObject() + portal._p_jar.db().cacheMinimize() + synchronizeDynamicModules(portal, force=True) + gc.collect() + class ObjectTemplateItem(BaseTemplateItem): """ This class is used for generic objects and as a subclass. @@ -1001,6 +1017,10 @@ class ObjectTemplateItem(BaseTemplateItem): root_document_path = '/%s/%s' % (portal.getId(), root_path) recursiveUnindex(catalog, item_path, root_document_path) + def fixBrokenObject(self, obj): + if isinstance(obj, ERP5BaseBroken): + self._resetDynamicModules() + def install(self, context, trashbin, **kw): self.beforeInstall() update_dict = kw.get('object_to_update') @@ -1109,6 +1129,7 @@ class ObjectTemplateItem(BaseTemplateItem): # install object obj = self._objects[path] + self.fixBrokenObject(obj) # XXX Following code make Python Scripts compile twice, because # _getCopy returns a copy without the result of the compilation. # A solution could be to add a specific _getCopy method to @@ -2165,7 +2186,6 @@ class PortalTypeTemplateItem(ObjectTemplateItem): self._workflow_chain_archive[portal_type] context.portal_workflow.manage_changeWorkflows(default_chain, props=chain_dict) - # XXX : this method is kept temporarily, but can be removed once all bt5 are # re-exported with separated workflow-chain information def _importFile(self, file_name, file): @@ -3524,21 +3544,6 @@ class FilesystemDocumentTemplateItem(BaseTemplateItem): {self._getKey(path) : ['Removed', self.__class__.__name__[:-12]]}) return modified_object_list - def _resetDynamicModules(self): - # before any import, flush all ZODB caches to force a DB reload - # otherwise we could have objects trying to get commited while - # holding reference to a class that is no longer the same one as - # the class in its import location and pickle doesn't tolerate it. - # First we do a savepoint to dump dirty objects to temporary - # storage, so that all references to them can be freed. - transaction.savepoint(optimistic=True) - # Then we need to flush from all caches, not only the one from this - # connection - portal = self.getPortalObject() - portal._p_jar.db().cacheMinimize() - synchronizeDynamicModules(portal, force=True) - gc.collect() - def install(self, context, trashbin, **kw): update_dict = kw.get('object_to_update') force = kw.get('force') diff --git a/product/ERP5/tests/testBusinessTemplate.py b/product/ERP5/tests/testBusinessTemplate.py index 4e31be66556c882c8aa6c5d7c0442479428d1a59..d74313f4610e6b1a9ed67cbb09045f126d14645a 100644 --- a/product/ERP5/tests/testBusinessTemplate.py +++ b/product/ERP5/tests/testBusinessTemplate.py @@ -39,6 +39,7 @@ from Products.ERP5Type.tests.Sequence import SequenceList, Sequence from urllib import pathname2url from Products.ERP5Type.Globals import PersistentMapping from Products.CMFCore.Expression import Expression +from Products.ERP5Type.dynamic.lazy_class import ERP5BaseBroken from Products.ERP5Type.tests.utils import LogInterceptor from Products.ERP5Type.Workflow import addWorkflowByType import shutil @@ -6337,6 +6338,32 @@ class TestBusinessTemplate(BusinessTemplateMixin): # check both File instances no longer behave like being overriden self.assertFalse(getattr(portal.another_file, 'isClassOverriden', False)) + def test_168_CheckPortalTypeAndPathInSameBusinessTemplate(self): + """ + Make sure we can define a portal type and instance of that portal type + in same bt. It already happened that this failed with error : + BrokenModified: Can't change broken objects + + It might sound similar to test_167, but we had cases not working even + though test_167 was running fine (due to additional steps that were + doing more reset of components) + """ + template_tool = self.portal.portal_templates + bt_path = os.path.join(os.path.dirname(__file__), 'test_data', + 'BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate') + bt = template_tool.download(bt_path) + foo_in_bt = bt._path_item._objects["foo"] + # Force evaluation of a method to force unghosting of the class + getattr(foo_in_bt, "getTitle", None) + self.assertTrue(isinstance(foo_in_bt, ERP5BaseBroken)) + self.commit() + bt.install(force=1) + self.commit() + foo_in_portal = self.portal.foo + self.assertFalse(isinstance(foo_in_portal, ERP5BaseBroken)) + self.assertEqual("Foo", foo_in_portal.getPortalType()) + self.uninstallBusinessTemplate('test_168_CheckPortalTypeAndPathInSameBusinessTemplate') + def test_type_provider(self): self.portal.newContent(id='dummy_type_provider', portal_type="Types Tool") type_provider = self.portal.dummy_type_provider diff --git a/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/PathTemplateItem/foo.xml b/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/PathTemplateItem/foo.xml new file mode 100644 index 0000000000000000000000000000000000000000..9996537e1f8ab955f97b5a7bf4819c3537f10c44 --- /dev/null +++ b/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/PathTemplateItem/foo.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="Foo" module="erp5.portal_type"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>id</string> </key> + <value> <string>foo</string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/PortalTypeTemplateItem/portal_types/Foo.xml b/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/PortalTypeTemplateItem/portal_types/Foo.xml new file mode 100644 index 0000000000000000000000000000000000000000..93d08ce682deeb031f00e79da68c9f11404b99f8 --- /dev/null +++ b/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/PortalTypeTemplateItem/portal_types/Foo.xml @@ -0,0 +1,24 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="Base Type" module="erp5.portal_type"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>id</string> </key> + <value> <string>Foo</string> </value> + </item> + <item> + <key> <string>portal_type</string> </key> + <value> <string>Foo Type</string> </value> + </item> + <item> + <key> <string>type_class</string> </key> + <value> <string>XMLObject</string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/bt/template_format_version b/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/bt/template_format_version new file mode 100644 index 0000000000000000000000000000000000000000..56a6051ca2b02b04ef92d5150c9ef600403cb1de --- /dev/null +++ b/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/bt/template_format_version @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/bt/template_path_list b/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/bt/template_path_list new file mode 100644 index 0000000000000000000000000000000000000000..257cc5642cb1a054f08cc83f2d943e56fd3ebe99 --- /dev/null +++ b/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/bt/template_path_list @@ -0,0 +1 @@ +foo diff --git a/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/bt/template_portal_type_id_list b/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/bt/template_portal_type_id_list new file mode 100644 index 0000000000000000000000000000000000000000..bc56c4d89448a963d0b6486989809d653dab9a97 --- /dev/null +++ b/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/bt/template_portal_type_id_list @@ -0,0 +1 @@ +Foo diff --git a/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/bt/title b/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/bt/title new file mode 100644 index 0000000000000000000000000000000000000000..d6a0b5295acc8bd67fe48d497a06b9d10403af15 --- /dev/null +++ b/product/ERP5/tests/test_data/BusinessTemplate_test_168_CheckPortalTypeAndPathInSameBusinessTemplate/bt/title @@ -0,0 +1 @@ +test_168_CheckPortalTypeAndPathInSameBusinessTemplate \ No newline at end of file