Commit 4c9d9f0e authored by Jérome Perrin's avatar Jérome Perrin

Fix some business template update bugs

Several bug fixes for business template update bugs, including a fix for the problem preventing to update erp5_accounting_l10n_fr

See merge request nexedi/erp5!1112
parents 17a0a275 e86c6071
...@@ -212,7 +212,7 @@ class BusinessTemplateMixin(ERP5TypeTestCase, LogInterceptor): ...@@ -212,7 +212,7 @@ class BusinessTemplateMixin(ERP5TypeTestCase, LogInterceptor):
Get a business template at portal_templates Get a business template at portal_templates
""" """
template_tool = self.getTemplateTool() template_tool = self.getTemplateTool()
for bt in template_tool.objectValues(filter={'portal_type':'Business Template'}): for bt in template_tool.objectValues(filter={'portal_type': 'Business Template'}):
if bt.getTitle() == title: if bt.getTitle() == title:
return bt return bt
return None return None
...@@ -2640,7 +2640,7 @@ class BusinessTemplateMixin(ERP5TypeTestCase, LogInterceptor): ...@@ -2640,7 +2640,7 @@ class BusinessTemplateMixin(ERP5TypeTestCase, LogInterceptor):
import_bt = sequence.get('import_bt') import_bt = sequence.get('import_bt')
pc = self.getCatalogTool() pc = self.getCatalogTool()
catalog_id = pc.getSQLCatalog().id catalog_id = pc.getSQLCatalog().id
object_to_update = {'portal_catalog/'+catalog_id+'/z_another_fake_method':'install'} object_to_update = {'portal_catalog/'+catalog_id+'/z_another_fake_method': 'install'}
import_bt.install(object_to_update=object_to_update) import_bt.install(object_to_update=object_to_update)
def stepCreateNewBusinessTemplate(self, sequence=None, **kw): def stepCreateNewBusinessTemplate(self, sequence=None, **kw):
...@@ -6570,7 +6570,7 @@ class TestBusinessTemplate(BusinessTemplateMixin): ...@@ -6570,7 +6570,7 @@ class TestBusinessTemplate(BusinessTemplateMixin):
finally: finally:
shutil.rmtree(export_dir) shutil.rmtree(export_dir)
new_bt.install(force=0, object_to_update={'dummy_type_provider':'remove'}) new_bt.install(force=0, object_to_update={'dummy_type_provider': 'remove'})
self.assertNotEquals(None, types_tool.getTypeInfo('Base Category')) self.assertNotEquals(None, types_tool.getTypeInfo('Base Category'))
self.assertNotIn('dummy_type_provider', types_tool.type_provider_list) self.assertNotIn('dummy_type_provider', types_tool.type_provider_list)
...@@ -6854,6 +6854,86 @@ class TestBusinessTemplate(BusinessTemplateMixin): ...@@ -6854,6 +6854,86 @@ class TestBusinessTemplate(BusinessTemplateMixin):
{'test_document': ('Removed but should be kept', 'Path')}, {'test_document': ('Removed but should be kept', 'Path')},
new_bt.preinstall()) new_bt.preinstall())
def test_update_business_template_with_category_having_subcategory_tree_modified(self):
"""Non regression test for a case where some categories in a subtrees are added and
some are removed.
Updating from:
portal_categories/test_category/modified
portal_categories/test_category/modified/container_in_which_child_is_added
portal_categories/test_category/modified/container_in_which_child_is_added/child_kept
portal_categories/test_category/modified/removed
to:
portal_categories/test_category/modified <-- this will be modified
portal_categories/test_category/modified/container_in_which_child_is_added
portal_categories/test_category/modified/container_in_which_child_is_added/child_kept
portal_categories/test_category/modified/container_in_which_child_is_added/added
was causing when test_category was added both as a base category and as paths.
This was the cause of KeyError when updating erp5_accounting_l10n_fr
"""
portal_categories = self.portal.portal_categories
if 'test_category' in portal_categories.objectIds():
portal_categories.manage_delObjects(['test_category'])
base_category = portal_categories.newContent(portal_type='Base Category', id='test_category')
parent_category = base_category.newContent(portal_type='Category', id='modified')
parent_category.newContent(portal_type='Category', id='container_in_which_child_is_added')
parent_category.newContent(portal_type='Category', id='removed')
parent_category.container_in_which_child_is_added.newContent(portal_type='Category', id='child_kept')
business_template = self.portal.portal_templates.newContent(
portal_type='Business Template',
title=self.id(),
template_path_list=(
'portal_categories/test_category/**'
),
template_base_category_list=['test_category'],
)
self.tic()
business_template.build()
self.tic()
export_dir = tempfile.mkdtemp()
try:
business_template.export(path=export_dir, local=True)
self.tic()
new_business_template_version_1 = self.portal.portal_templates.download(
url='file://%s' % export_dir)
finally:
shutil.rmtree(export_dir)
# Apply the changes and build a second version of business template
parent_category.setTitle('modified')
parent_category.container_in_which_child_is_added.newContent(portal_type='Category', id='added')
parent_category.manage_delObjects(['removed'])
business_template.build()
self.tic()
export_dir = tempfile.mkdtemp()
try:
business_template.export(path=export_dir, local=True)
self.tic()
self.portal.portal_categories.manage_delObjects(['test_category'])
self.tic()
new_business_template_version_1.install()
self.tic()
portal_categories.test_category.modified.container_in_which_child_is_added.setTitle(
'This path should not be reinstalled during update'
)
self.tic()
self.portal.portal_templates.updateBusinessTemplateFromUrl(
download_url='file://%s' % export_dir
)
finally:
shutil.rmtree(export_dir)
self.tic()
self.assertEqual(
'This path should not be reinstalled during update',
portal_categories.test_category.modified.container_in_which_child_is_added.getTitle())
def test_update_business_template_with_template_keep_path_list_catalog_method(self): def test_update_business_template_with_template_keep_path_list_catalog_method(self):
"""Tests for `template_keep_path_list` feature for the special case of catalog methods """Tests for `template_keep_path_list` feature for the special case of catalog methods
""" """
...@@ -6909,6 +6989,60 @@ class TestBusinessTemplate(BusinessTemplateMixin): ...@@ -6909,6 +6989,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,6 +1295,7 @@ class ObjectTemplateItem(BaseTemplateItem): ...@@ -1295,6 +1295,7 @@ class ObjectTemplateItem(BaseTemplateItem):
if uid is None: if uid is None:
return 0 return 0
else: else:
if getattr(aq_base(document), 'uid', None) != uid:
document.uid = uid document.uid = uid
return 1 return 1
groups = {} groups = {}
...@@ -1559,7 +1560,7 @@ class ObjectTemplateItem(BaseTemplateItem): ...@@ -1559,7 +1560,7 @@ class ObjectTemplateItem(BaseTemplateItem):
if update_dict.has_key(widget_path) and update_dict[widget_path] in ('remove', 'save_and_remove'): if update_dict.has_key(widget_path) and update_dict[widget_path] in ('remove', 'save_and_remove'):
continue continue
widget_in_form = 0 widget_in_form = 0
for group_id, group_value_list in new_groups_dict.iteritems(): for group_value_list in new_groups_dict.values():
if widget_id in group_value_list: if widget_id in group_value_list:
widget_in_form = 1 widget_in_form = 1
break break
...@@ -1715,7 +1716,7 @@ class PathTemplateItem(ObjectTemplateItem): ...@@ -1715,7 +1716,7 @@ class PathTemplateItem(ObjectTemplateItem):
if len(id_list) == 0: if len(id_list) == 0:
return ['/'.join(relative_url_list)] return ['/'.join(relative_url_list)]
id = id_list[0] id = id_list[0]
if re.search('[\*\?\[\]]', id) is None: if re.search(r'[\*\?\[\]]', id) is None:
# If the id has no meta character, do not have to check all objects. # If the id has no meta character, do not have to check all objects.
obj = folder._getOb(id, None) obj = folder._getOb(id, None)
if obj is None: if obj is None:
...@@ -1772,7 +1773,7 @@ class PathTemplateItem(ObjectTemplateItem): ...@@ -1772,7 +1773,7 @@ class PathTemplateItem(ObjectTemplateItem):
# Ignore any object without PortalType (non-ERP5 objects) # Ignore any object without PortalType (non-ERP5 objects)
try: try:
portal_type = aq_base(obj).getPortalType() portal_type = aq_base(obj).getPortalType()
except Exception, e: except Exception:
pass pass
else: else:
if portal_type not in p.portal_types: if portal_type not in p.portal_types:
...@@ -1949,6 +1950,17 @@ class CategoryTemplateItem(ObjectTemplateItem): ...@@ -1949,6 +1950,17 @@ class CategoryTemplateItem(ObjectTemplateItem):
# reset accessors if we installed a new category # reset accessors if we installed a new category
self.portal_types.resetDynamicDocumentsOnceAtTransactionBoundary() self.portal_types.resetDynamicDocumentsOnceAtTransactionBoundary()
def install(self, context, trashbin, **kw):
# Only update base categories, the category they contain will be installed/updated
# as PathTemplateItem.install
kw['object_to_update'] = {
path: action
for (path, action) in kw['object_to_update'].items()
if path.split('/')[:-1] == ['portal_categories'] or path in self._objects
}
return super(CategoryTemplateItem, self).install(context, trashbin, **kw)
class SkinTemplateItem(ObjectTemplateItem): class SkinTemplateItem(ObjectTemplateItem):
def __init__(self, id_list, tool_id='portal_skins', **kw): def __init__(self, id_list, tool_id='portal_skins', **kw):
......
...@@ -80,9 +80,6 @@ class TrashTool(BaseTool): ...@@ -80,9 +80,6 @@ class TrashTool(BaseTool):
# backup the object # backup the object
# here we choose export/import to copy because cut/paste # here we choose export/import to copy because cut/paste
# do too many things and check for what we want to do # do too many things and check for what we want to do
obj = None
if object_id not in backup_object_container.objectIds():
# export object
object_path = container_path + [object_id] object_path = container_path + [object_id]
obj = self.unrestrictedTraverse(object_path, None) obj = self.unrestrictedTraverse(object_path, None)
if obj is not None: if obj is not None:
......
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