Commit 31f8dfe2 authored by Jérome Perrin's avatar Jérome Perrin

BusinessTemplate: tolerate broken objects when updating their containers

Business template have some logic to keep uids when updating objects:
during installation, when an object is modified, business template first
remember the uids for this object and all its sub-objects,
replaces this object with the new version then recursively set the uid
on updated objects, so that updating an ERP5 document by business
template does not change its uid because this would break catalog.

When an object containing sub-objects is updated, it becomes a new
object in ZODB and sub-objects of the previous object are set as child
of the new object. This works even if the case of sub-objects being
instances of ZODB Broken class, except that the step where we restore
the uid fail as it's not allowed to modify a broken object.

Instead of unconditionnally setting the sub-objects uids, check that we
actually need to set it, because if it's already the expected value then
we don't need to touch the object.
parent a87da503
...@@ -6909,6 +6909,60 @@ class TestBusinessTemplate(BusinessTemplateMixin): ...@@ -6909,6 +6909,60 @@ class TestBusinessTemplate(BusinessTemplateMixin):
('Removed but should be kept', 'CatalogMethod')}, ('Removed but should be kept', 'CatalogMethod')},
new_bt.preinstall()) new_bt.preinstall())
def test_update_business_template_with_broken_objects(self):
"""Edge case test for a folder containing broken objects being updated.
In this scenario, a document containing random broken objects that are
not part of business template is being installed. The broken objects
are kept and do not prevent installing the business template.
"""
types_tool = self.portal.portal_types
types_tool.newContent('Geek Object', 'Base Type', type_class='Person')
types_tool.newContent(
'Geek Module',
'Base Type',
type_class='Folder',
type_filter_content_type=1,
type_allowed_content_type_list=('Geek Object',),
)
self.portal.newContent(portal_type='Geek Module', id='geek_module')
module_content = self.portal.geek_module.newContent(
portal_type='Geek Object',
id='1',
title='kept',
)
module_content_uid = module_content.getUid()
self.tic()
bt = self.portal.portal_templates.newContent(
portal_type='Business Template',
title=self.id(),
template_path_list=['geek_module'])
self.tic()
bt.build()
self.tic()
types_tool['Geek Object'].setTypeClass(' not exists - broken')
self.tic()
self.assertIsInstance(self.portal.geek_module['1'], ERP5BaseBroken)
export_dir = tempfile.mkdtemp()
try:
bt.export(path=export_dir, local=True)
self.tic()
new_bt = self.portal.portal_templates.download(
url='file://%s' % export_dir)
finally:
shutil.rmtree(export_dir)
new_bt.install()
# our broken document is still here and its properties have been kept
self.assertIsInstance(self.portal.geek_module['1'], ERP5BaseBroken)
self.assertEqual(module_content_uid, self.portal.geek_module['1'].uid)
self.assertEqual('kept', self.portal.geek_module['1'].title)
def test_BusinessTemplateWithTest(self): def test_BusinessTemplateWithTest(self):
sequence_list = SequenceList() sequence_list = SequenceList()
sequence_string = '\ sequence_string = '\
......
...@@ -1295,7 +1295,8 @@ class ObjectTemplateItem(BaseTemplateItem): ...@@ -1295,7 +1295,8 @@ class ObjectTemplateItem(BaseTemplateItem):
if uid is None: if uid is None:
return 0 return 0
else: else:
document.uid = uid if getattr(aq_base(document), 'uid', None) != uid:
document.uid = uid
return 1 return 1
groups = {} groups = {}
old_groups = {} old_groups = {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment