Commit 238b4332 authored by Romain Courteaud's avatar Romain Courteaud

Continued to make BusinessTemplate more generic.

Added a diff method to compare current installed version of a BusinessTemplate
with _btsave_ version.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@3766 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 3ff80c38
...@@ -33,15 +33,25 @@ from AccessControl import ClassSecurityInfo ...@@ -33,15 +33,25 @@ from AccessControl import ClassSecurityInfo
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName
from Products.CMFCore.WorkflowCore import WorkflowMethod from Products.CMFCore.WorkflowCore import WorkflowMethod
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5Type.Utils import readLocalPropertySheet, writeLocalPropertySheet, importLocalPropertySheet, removeLocalPropertySheet from Products.ERP5Type.Utils import readLocalPropertySheet, \
from Products.ERP5Type.Utils import readLocalExtension, writeLocalExtension, removeLocalExtension writeLocalPropertySheet, \
from Products.ERP5Type.Utils import readLocalTest, writeLocalTest, removeLocalTest importLocalPropertySheet, \
from Products.ERP5Type.Utils import readLocalDocument, writeLocalDocument, importLocalDocument, removeLocalDocument removeLocalPropertySheet
from Products.ERP5Type.Utils import readLocalExtension, writeLocalExtension, \
removeLocalExtension
from Products.ERP5Type.Utils import readLocalTest, writeLocalTest, \
removeLocalTest
from Products.ERP5Type.Utils import readLocalDocument, writeLocalDocument, \
importLocalDocument, removeLocalDocument
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
import cStringIO import cStringIO
import fnmatch import fnmatch
import re import re
from Products.ERP5Type.Cache import clearCache from Products.ERP5Type.Cache import clearCache
from DateTime import DateTime
from OFS import XMLExportImport
from cStringIO import StringIO
import difflib
from zLOG import LOG from zLOG import LOG
...@@ -72,6 +82,9 @@ class BaseTemplateItem(Implicit, Persistent): ...@@ -72,6 +82,9 @@ class BaseTemplateItem(Implicit, Persistent):
# trash is quite similar to uninstall. # trash is quite similar to uninstall.
return self.uninstall(context, new_item=new_item, trash=1, **kw) return self.uninstall(context, new_item=new_item, trash=1, **kw)
def diff(self, **kw):
return ''
class ObjectTemplateItem(BaseTemplateItem): class ObjectTemplateItem(BaseTemplateItem):
""" """
This class is used for generic objects and as a subclass. This class is used for generic objects and as a subclass.
...@@ -112,7 +125,9 @@ class ObjectTemplateItem(BaseTemplateItem): ...@@ -112,7 +125,9 @@ class ObjectTemplateItem(BaseTemplateItem):
container_path = relative_url.split('/')[0:-1] container_path = relative_url.split('/')[0:-1]
object_id = relative_url.split('/')[-1] object_id = relative_url.split('/')[-1]
container = portal.unrestrictedTraverse(container_path) container = portal.unrestrictedTraverse(container_path)
#LOG('Installing' , 0, '%s in %s with %s' % (self.id, container.getPhysicalPath(), self.export_string)) # LOG('Installing' , 0,
# '%s in %s with %s' % \
# (self.id, container.getPhysicalPath(), self.export_string))
container_ids = container.objectIds() container_ids = container.objectIds()
if object_id in container_ids: # Object already exists if object_id in container_ids: # Object already exists
self._backupObject(container, object_id) self._backupObject(container, object_id)
...@@ -125,8 +140,10 @@ class ObjectTemplateItem(BaseTemplateItem): ...@@ -125,8 +140,10 @@ class ObjectTemplateItem(BaseTemplateItem):
object.manage_afterClone(object) object.manage_afterClone(object)
object.wl_clearLocks() object.wl_clearLocks()
if object.meta_type in ('Z SQL Method',): if object.meta_type in ('Z SQL Method',):
# It is necessary to make sure that the sql connection in this method is valid. # It is necessary to make sure that the sql connection
sql_connection_list = portal.objectIds(spec=('Z MySQL Database Connection',)) # in this method is valid.
sql_connection_list = portal.objectIds(
spec=('Z MySQL Database Connection',))
if object.connection_id not in sql_connection_list: if object.connection_id not in sql_connection_list:
object.connection_id = sql_connection_list[0] object.connection_id = sql_connection_list[0]
...@@ -145,9 +162,107 @@ class ObjectTemplateItem(BaseTemplateItem): ...@@ -145,9 +162,107 @@ class ObjectTemplateItem(BaseTemplateItem):
container.manage_delObjects([object_id]) container.manage_delObjects([object_id])
except: except:
pass pass
BaseTemplateItem.uninstall(self, context, **kw) BaseTemplateItem.uninstall(self, context, **kw)
def _compareObjects(self, object1, object2, btsave_object_included=0):
"""
Execute a diff between 2 objects,
and return a string diff.
"""
xml_dict = {
str(object1): None,
str(object2): None,
}
# Generate XML
for object in (object1, object2):
string_io = StringIO()
XMLExportImport.exportXML(object._p_jar, object._p_oid, string_io)
object_xml = string_io.getvalue()
string_io.close()
object_xml_lines = object_xml.splitlines(1)
xml_dict[str(object)] = object_xml_lines
# Make diff between XML
diff_instance = difflib.Differ()
diff_list = list(diff_instance.compare(xml_dict[str(object1)],
xml_dict[str(object2)]))
diff_list = [x for x in diff_list if x[0] != ' ']
# Dirty patch to remove useless diff message (id different)
if btsave_object_included==1:
if len(diff_list) == 3:
if '_btsave_' in diff_list[1]:
diff_list = []
# Return string
result = '%s' % ''.join(diff_list)
return result
def diff(self, archive_variable='_archive', max_deep=0, verbose=0):
"""
Show all __btsave__ created, and make a diff between
the current and the old version.
"""
result = ''
portal = self.getPortalObject()
# Browse all items stored
for relative_url, object in getattr(self, archive_variable).items():
object = portal.unrestrictedTraverse(relative_url)
container_path = relative_url.split('/')[0:-1]
object_id = relative_url.split('/')[-1]
container = portal.unrestrictedTraverse(container_path)
container_ids = container.objectIds()
# Search _btsave_ object
compare_object_couple_list = []
btsave_id_list = []
n = 1
new_object_id = '%s_btsave_%s' % (object_id, n)
while new_object_id in container_ids:
# Found _btsave_ object
btsave_id_list.append(new_object_id)
compare_object_couple_list.append(
(object, portal.unrestrictedTraverse(
container_path+[new_object_id])))
n += 1
new_object_id = '%s_btsave_%s' % (object_id, n)
if n == 1:
result += "$$$ Added: %s $$$\n" % \
('/'.join(container_path+[object_id]))
result += '%s\n' % ('-'*80)
# Find all objects to compare
deep = 0
while deep != max_deep:
new_compare_object_couple_list = []
for new_object, btsave_object in compare_object_couple_list:
btsave_object_content_id_list = btsave_object.objectIds()
for new_object_content_id in new_object.objectIds():
if new_object_content_id in btsave_object_content_id_list:
new_compare_object_couple_list.append(
(getattr(new_object, new_object_content_id),
getattr(btsave_object, new_object_content_id)))
btsave_object_content_id_list.remove(new_object_content_id)
else:
result += "$$$ Added: %s/%s $$$\n" % \
(new_object.absolute_url(), new_object_content_id)
result += '%s\n' % ('-'*80)
for btsave_object_id in btsave_object_content_id_list:
result += "$$$ Removed: %s/%s $$$\n" % \
(btsave_object.absolute_url(), btsave_object_id)
result += '%s\n' % ('-'*80)
if new_compare_object_couple_list == []:
deep = max_deep
else:
compare_object_couple_list = new_compare_object_couple_list
deep += 1
# Now, we can compare all objects requested
for new_object, btsave_object in compare_object_couple_list:
tmp_diff = self._compareObjects(new_object, btsave_object,
btsave_object_included=1)
if tmp_diff != '':
result += "$$$ %s $$$\n$$$ %s $$$\n" % \
(new_object.absolute_url(),
btsave_object.absolute_url())
if verbose == 1:
result += tmp_diff
result += '%s\n' % ('-'*80)
return result
class PathTemplateItem(ObjectTemplateItem): class PathTemplateItem(ObjectTemplateItem):
""" """
...@@ -308,12 +423,14 @@ class SkinTemplateItem(ObjectTemplateItem): ...@@ -308,12 +423,14 @@ class SkinTemplateItem(ObjectTemplateItem):
ObjectTemplateItem.uninstall(self, context, **kw) ObjectTemplateItem.uninstall(self, context, **kw)
def diff(self, max_deep=1, **kw):
return ObjectTemplateItem.diff(self, max_deep=max_deep, **kw)
class WorkflowTemplateItem(ObjectTemplateItem):
def __init__(self, id_list, **kw): class WorkflowTemplateItem(ObjectTemplateItem):
ObjectTemplateItem.__init__(self, id_list, tool_id='portal_workflow', **kw)
def __init__(self, id_list, tool_id='portal_workflow', **kw):
return ObjectTemplateItem.__init__(self, id_list, tool_id=tool_id, **kw)
class PortalTypeTemplateItem(ObjectTemplateItem): class PortalTypeTemplateItem(ObjectTemplateItem):
...@@ -387,7 +504,6 @@ class PortalTypeTemplateItem(ObjectTemplateItem): ...@@ -387,7 +504,6 @@ class PortalTypeTemplateItem(ObjectTemplateItem):
chain_dict['chain_%s' % portal_type] = self._workflow_chain_archive[portal_type] chain_dict['chain_%s' % portal_type] = self._workflow_chain_archive[portal_type]
context.portal_workflow.manage_changeWorkflows(default_chain,props=chain_dict) context.portal_workflow.manage_changeWorkflows(default_chain,props=chain_dict)
class CatalogMethodTemplateItem(ObjectTemplateItem): class CatalogMethodTemplateItem(ObjectTemplateItem):
def __init__(self, id_list, tool_id='portal_catalog', **kw): def __init__(self, id_list, tool_id='portal_catalog', **kw):
...@@ -550,7 +666,6 @@ class CatalogMethodTemplateItem(ObjectTemplateItem): ...@@ -550,7 +666,6 @@ class CatalogMethodTemplateItem(ObjectTemplateItem):
ObjectTemplateItem.uninstall(self, context, **kw) ObjectTemplateItem.uninstall(self, context, **kw)
class ActionTemplateItem(BaseTemplateItem): class ActionTemplateItem(BaseTemplateItem):
def _splitPath(self, path): def _splitPath(self, path):
...@@ -668,6 +783,8 @@ class SitePropertyTemplateItem(BaseTemplateItem): ...@@ -668,6 +783,8 @@ class SitePropertyTemplateItem(BaseTemplateItem):
class ModuleTemplateItem(BaseTemplateItem): class ModuleTemplateItem(BaseTemplateItem):
def diff(self, max_deep=1, **kw):
return ''
def build(self, context, **kw): def build(self, context, **kw):
BaseTemplateItem.build(self, context, **kw) BaseTemplateItem.build(self, context, **kw)
p = context.getPortalObject() p = context.getPortalObject()
...@@ -720,93 +837,55 @@ class ModuleTemplateItem(BaseTemplateItem): ...@@ -720,93 +837,55 @@ class ModuleTemplateItem(BaseTemplateItem):
pass pass
class DocumentTemplateItem(BaseTemplateItem): class DocumentTemplateItem(BaseTemplateItem):
local_file_reader_name = 'readLocalDocument'
local_file_writer_name = 'writeLocalDocument'
local_file_importer_name = 'importLocalDocument'
local_file_remover_name = 'removeLocalDocument'
def build(self, context, **kw): def build(self, context, **kw):
BaseTemplateItem.build(self, context, **kw) BaseTemplateItem.build(self, context, **kw)
for id in self._archive.keys(): for id in self._archive.keys():
self._archive[id] = readLocalDocument(id) self._archive[id] = globals()[self.local_file_reader_name](id)
def install(self, context, **kw): def install(self, context, **kw):
BaseTemplateItem.install(self, context, **kw) BaseTemplateItem.install(self, context, **kw)
for id,text in self._archive.items(): for id,text in self._archive.items():
writeLocalDocument(id, text, create=1) # This raises an exception if the file exists. # This raises an exception if the file exists.
importLocalDocument(id) globals()[self.local_file_writer_name](id, text, create=1)
if self.local_file_importer_name is not None:
globals()[self.local_file_importer_name](id)
def uninstall(self, context, **kw): def uninstall(self, context, **kw):
for id in self._archive.keys(): for id in self._archive.keys():
try: try:
removeLocalDocument(id) globals()[self.local_file_importer_name](id)
except OSError: except OSError:
pass pass
BaseTemplateItem.uninstall(self, context, **kw) BaseTemplateItem.uninstall(self, context, **kw)
class PropertySheetTemplateItem(DocumentTemplateItem):
class PropertySheetTemplateItem(BaseTemplateItem): local_file_reader_name = 'readLocalPropertySheet'
local_file_writer_name = 'writeLocalPropertySheet'
def build(self, context, **kw): local_file_importer_name = 'importLocalPropertySheet'
BaseTemplateItem.build(self, context, **kw) local_file_remover_name = 'removeLocalPropertySheet'
for id in self._archive.keys():
self._archive[id] = readLocalPropertySheet(id) class ExtensionTemplateItem(DocumentTemplateItem):
local_file_reader_name = 'readLocalExtension'
def install(self, context, **kw): local_file_writer_name = 'writeLocalExtension'
BaseTemplateItem.install(self, context, **kw) # XXX is this method a error or ?
for id,text in self._archive.items(): local_file_importer_name = 'importLocalPropertySheet'
writeLocalPropertySheet(id, text, create=1) # This raises an exception if the file exists. local_file_remover_name = 'removeLocalExtension'
importLocalPropertySheet(id)
class TestTemplateItem(DocumentTemplateItem):
def uninstall(self, context, **kw): local_file_reader_name = 'readLocalTest'
for id in self._archive.keys(): local_file_writer_name = 'writeLocalTest'
try: # XXX is this a error ?
removeLocalPropertySheet(id) local_file_importer_name = None
except OSError: local_file_remover_name = 'removeLocalTest'
class ProductTemplateItem(BaseTemplateItem):
# XXX Not implemented yet
pass pass
BaseTemplateItem.uninstall(self, context, **kw)
class ExtensionTemplateItem(BaseTemplateItem):
def build(self, context, **kw):
BaseTemplateItem.build(self, context, **kw)
for id in self._archive.keys():
self._archive[id] = readLocalExtension(id)
def install(self, context, **kw):
BaseTemplateItem.install(self, context, **kw)
for id,text in self._archive.items():
writeLocalExtension(id, text, create=1) # This raises an exception if the file exists.
importLocalPropertySheet(id)
def uninstall(self, context, **kw):
for id in self._archive.keys():
try:
removeLocalExtension(id)
except OSError:
pass
BaseTemplateItem.uninstall(self, context, **kw)
class TestTemplateItem(BaseTemplateItem):
def build(self, context, **kw):
BaseTemplateItem.build(self, context, **kw)
for id in self._archive.keys():
self._archive[id] = readLocalTest(id)
def install(self, context, **kw):
BaseTemplateItem.install(self, context, **kw)
for id,text in self._archive.items():
writeLocalTest(id, text, create=1) # This raises an exception if the file exists.
def uninstall(self, context, **kw):
for id in self._archive.keys():
try:
removeLocalTest(id)
except OSError:
pass
BaseTemplateItem.uninstall(self, context, **kw)
class ProductTemplateItem(BaseTemplateItem): pass # Not implemented yet
class RoleTemplateItem(BaseTemplateItem): class RoleTemplateItem(BaseTemplateItem):
...@@ -844,7 +923,6 @@ class RoleTemplateItem(BaseTemplateItem): ...@@ -844,7 +923,6 @@ class RoleTemplateItem(BaseTemplateItem):
del roles[role] del roles[role]
p.__ac_roles__ = tuple(roles.keys()) p.__ac_roles__ = tuple(roles.keys())
class CatalogResultKeyTemplateItem(BaseTemplateItem): class CatalogResultKeyTemplateItem(BaseTemplateItem):
def install(self, context, **kw): def install(self, context, **kw):
...@@ -958,7 +1036,6 @@ class CatalogResultTableTemplateItem(BaseTemplateItem): ...@@ -958,7 +1036,6 @@ class CatalogResultTableTemplateItem(BaseTemplateItem):
catalog.sql_search_tables = sql_search_tables catalog.sql_search_tables = sql_search_tables
BaseTemplateItem.uninstall(self, context, **kw) BaseTemplateItem.uninstall(self, context, **kw)
class MessageTranslationTemplateItem(BaseTemplateItem): class MessageTranslationTemplateItem(BaseTemplateItem):
def build(self, context, **kw): def build(self, context, **kw):
...@@ -966,11 +1043,14 @@ class MessageTranslationTemplateItem(BaseTemplateItem): ...@@ -966,11 +1043,14 @@ class MessageTranslationTemplateItem(BaseTemplateItem):
localizer = context.getPortalObject().Localizer localizer = context.getPortalObject().Localizer
for lang in self._archive.keys(): for lang in self._archive.keys():
self._archive[lang] = PersistentMapping() self._archive[lang] = PersistentMapping()
# Export only erp5_ui at the moment. This is safer against information leak. # Export only erp5_ui at the moment.
# This is safer against information leak.
for catalog in ('erp5_ui', ): for catalog in ('erp5_ui', ):
LOG('MessageTranslationTemplateItem build', 0, 'catalog = %r' % (catalog,)) LOG('MessageTranslationTemplateItem build', 0,
'catalog = %r' % (catalog,))
mc = localizer._getOb(catalog) mc = localizer._getOb(catalog)
LOG('MessageTranslationTemplateItem build', 0, 'mc = %r' % (mc,)) LOG('MessageTranslationTemplateItem build', 0,
'mc = %r' % (mc,))
self._archive[lang][catalog] = mc.manage_export(lang) self._archive[lang][catalog] = mc.manage_export(lang)
def install(self, context, **kw): def install(self, context, **kw):
...@@ -986,7 +1066,6 @@ class MessageTranslationTemplateItem(BaseTemplateItem): ...@@ -986,7 +1066,6 @@ class MessageTranslationTemplateItem(BaseTemplateItem):
mc.manage_addLanguage(lang) mc.manage_addLanguage(lang)
mc.manage_import(lang, po) mc.manage_import(lang, po)
class BusinessTemplate(XMLObject): class BusinessTemplate(XMLObject):
""" """
A business template allows to construct ERP5 modules A business template allows to construct ERP5 modules
...@@ -1389,14 +1468,16 @@ class BusinessTemplate(XMLObject): ...@@ -1389,14 +1468,16 @@ class BusinessTemplate(XMLObject):
""" """
return self._getOrderedList('template_message_translation') return self._getOrderedList('template_message_translation')
def diff(self): def diff(self, verbose=0):
""" """
Return a 'diff' of the business template compared to the Return a 'diff' of the business template compared to the
__btsave__ version. __btsave__ version.
""" """
diff_message = '%s : %s\n%s\n' % (self.getPath(), DateTime(),
'='*80)
# Diff everything # Diff everything
# for item_name in self._item_name_list[::-1]: for item_name in self._item_name_list:
# item = getattr(self, item_name) item = getattr(self, item_name)
# if item is not None: if item is not None:
# item.uninstall(local_configuration) diff_message += item.diff(verbose=verbose)
pass return diff_message
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