Commit 3e91cdb0 authored by Ayush Tiwari's avatar Ayush Tiwari Committed by Ayush Tiwari

bt5_config: Added import function for BusinessManager and BusinessItem classes

parent 258f7eab
...@@ -57,6 +57,7 @@ from Products.ERP5Type import Permissions, PropertySheet, interfaces ...@@ -57,6 +57,7 @@ from Products.ERP5Type import Permissions, PropertySheet, interfaces
from AccessControl import ClassSecurityInfo, Unauthorized, getSecurityManager from AccessControl import ClassSecurityInfo, Unauthorized, getSecurityManager
from Acquisition import Implicit, aq_base, aq_inner, aq_parent from Acquisition import Implicit, aq_base, aq_inner, aq_parent
from Products.ERP5Type.Globals import InitializeClass from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from zLOG import LOG, INFO, WARNING from zLOG import LOG, INFO, WARNING
from Products.ERP5Type.patches.ppml import importXML from Products.ERP5Type.patches.ppml import importXML
from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
...@@ -143,7 +144,7 @@ class BusinessManager(XMLObject): ...@@ -143,7 +144,7 @@ class BusinessManager(XMLObject):
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
_properties = ( _properties = (
{'id': 'template_path', {'id': 'template_path_list',
'type': 'lines', 'type': 'lines',
'default': 'python: ()', 'default': 'python: ()',
'acquisition_base_category': (), 'acquisition_base_category': (),
...@@ -152,9 +153,19 @@ class BusinessManager(XMLObject): ...@@ -152,9 +153,19 @@ class BusinessManager(XMLObject):
'acquisition_accessor_id': 'getTemplatePathList', 'acquisition_accessor_id': 'getTemplatePathList',
'override': 1, 'override': 1,
'mode': 'w'}, 'mode': 'w'},
{'id': 'template_format_version',
'type': 'int',
'default': 'python: 3',
'acquisition_base_category': (),
'acquisition_portal_type': (),
'acquisition_depends': None,
'acquisition_accessor_id': 'getTemplateFormatVersion',
'override': 1,
'mode': 'w'},
) )
template_path_list = () template_path_list = ()
template_format_version = 3
status = 'uninstalled' status = 'uninstalled'
# Declarative security # Declarative security
...@@ -194,9 +205,10 @@ class BusinessManager(XMLObject): ...@@ -194,9 +205,10 @@ class BusinessManager(XMLObject):
def edit(self, **kw): def edit(self, **kw):
""" """
Explicilty edit the class instance Explicilty edit the class instance
XXX: No need of this class ? as we already have _edit from ERP5Type.Folder
""" """
if 'path_item_list' in kw: if 'template_path_list' in kw:
path_item_list = kw.pop('path_item_list') path_item_list = kw.pop('template_path_list')
self._setTemplatePathList(path_item_list) self._setTemplatePathList(path_item_list)
def _setTemplatePathList(self, path_item_list): def _setTemplatePathList(self, path_item_list):
...@@ -212,6 +224,16 @@ class BusinessManager(XMLObject): ...@@ -212,6 +224,16 @@ class BusinessManager(XMLObject):
result = tuple(result) result = tuple(result)
return result return result
def getTemplateFormatVersion(self):
return self.template_format_version
def _setTemplateFormatVersion(self, value):
self.template_format_version = int(value)
def propertyMap(self):
prop_map = super(BusinessManager, self).propertyMap()
final_prop_map = prop_map+self._properties
return final_prop_map
security.declareProtected(Permissions.ManagePortal, 'export') security.declareProtected(Permissions.ManagePortal, 'export')
def export(self, path=None, local=0, bma=None, **kw): def export(self, path=None, local=0, bma=None, **kw):
...@@ -221,7 +243,7 @@ class BusinessManager(XMLObject): ...@@ -221,7 +243,7 @@ class BusinessManager(XMLObject):
XXX: Are we planning to use something like archive for saving the exported XXX: Are we planning to use something like archive for saving the exported
objects inside a Business Manager objects inside a Business Manager
""" """
if not self.getState() == 'built': if not self.getStatus() == 'built':
raise ValueError, 'Manager not built properly' raise ValueError, 'Manager not built properly'
return self._export(path, local, bma, **kw) return self._export(path, local, bma, **kw)
...@@ -232,7 +254,7 @@ class BusinessManager(XMLObject): ...@@ -232,7 +254,7 @@ class BusinessManager(XMLObject):
# we export into a folder tree # we export into a folder tree
bma = BusinessManagerFolder(path, creation=1) bma = BusinessManagerFolder(path, creation=1)
else: else:
# We export BP into a tarball file # We export bm into a tarball file
if path is None: if path is None:
path = self.getTitle() path = self.getTitle()
bma = BusinessManagerTarball(path, creation=1) bma = BusinessManagerTarball(path, creation=1)
...@@ -242,15 +264,15 @@ class BusinessManager(XMLObject): ...@@ -242,15 +264,15 @@ class BusinessManager(XMLObject):
prop_type = prop['type'] prop_type = prop['type']
id = prop['id'] id = prop['id']
if id in ('id', 'uid', 'rid', 'sid', 'id_group', 'last_id', 'revision', if id in ('id', 'uid', 'rid', 'sid', 'id_group', 'last_id', 'revision',
'install_object_list_list', 'id_generator', 'bp_for_diff'): 'install_object_list_list', 'id_generator', 'bm_for_diff'):
continue continue
value = self.getProperty(id) value = self.getProperty(id)
if not value: if not value:
continue continue
if prop_type in ('text', 'string', 'int', 'boolean'): if prop_type in ('text', 'string', 'int', 'boolean'):
bpa.addObject(str(value), name=id, path='bp', ext='') bma.addObject(str(value), name=id, path='bm', ext='')
elif prop_type in ('lines', 'tokens'): elif prop_type in ('lines', 'tokens'):
bpa.addObject('\n'.join(value), name=id, path='bp', ext='') bma.addObject('\n'.join(value), name=id, path='bm', ext='')
# Export each part # Export each part
for item in self._path_item_list: for item in self._path_item_list:
...@@ -258,6 +280,42 @@ class BusinessManager(XMLObject): ...@@ -258,6 +280,42 @@ class BusinessManager(XMLObject):
return bma.finishCreation() return bma.finishCreation()
security.declareProtected(Permissions.ManagePortal, 'importFile')
def importFile(self, path):
"""
Import all xml files in Business Manager
"""
bma = (BusinessManagerFolder if os.path.isdir(path) else
BusinessManagerTarball)(path, importing=1)
bm_item = bm()
bma.importFiles(bm_item, parent=self)
prop_dict = {}
for prop in self.propertyMap():
pid = prop['id']
if pid != 'id':
prop_type = prop['type']
value = bm_item.get(pid)
if prop_type in ('text', 'string'):
prop_dict[pid] = value or ''
elif prop_type in ('int', 'boolean'):
prop_dict[pid] = value or 0
elif prop_type in ('lines', 'tokens'):
# XXX: Do add pid[:-5] after we switch to proper getters and setters
prop_dict[pid] = (value or '').splitlines()
# XXX: This is not working, needs to be fixed so as it copies all the
# properties from BMA to the newly created Business Manager
self._edit(**prop_dict)
self.storeTemplateData()
#workflow_tool = self.getPortalObject().portal_workflow
#workflow_tool.business_package_building_workflow.notifyWorkflowMethod(
# self, 'edit', kw={'comment': 'Downloaded'})
for item_object in self._path_item_list:
item_object.importFile(bma, parent=self)
# Set the status to uninstalled
self.setStatus('uninstalled')
def __add__(self, other): def __add__(self, other):
""" """
Adds the Business Item objects for the given Business Manager objects Adds the Business Item objects for the given Business Manager objects
...@@ -872,7 +930,7 @@ class BusinessItem(Implicit, Persistent): ...@@ -872,7 +930,7 @@ class BusinessItem(Implicit, Persistent):
path = self.__class__.__name__ + '/' path = self.__class__.__name__ + '/'
# We now will add the XML object and its sha hash while exporting the object # We now will add the XML object and its sha hash while exporting the object
# to Business package itself # to Business Manager itself
# Back compatibility with filesystem Documents # Back compatibility with filesystem Documents
key = self._path key = self._path
...@@ -920,7 +978,7 @@ class BusinessItem(Implicit, Persistent): ...@@ -920,7 +978,7 @@ class BusinessItem(Implicit, Persistent):
extension = self.guessExtensionOfDocument(obj, key, data extension = self.guessExtensionOfDocument(obj, key, data
if record_id == 'data' if record_id == 'data'
else None) else None)
bpa.addObject(StringIO(data), key, path=path, bma.addObject(StringIO(data), key, path=path,
ext='._xml' if extension == 'xml' else '.' + extension) ext='._xml' if extension == 'xml' else '.' + extension)
break break
# since we get the obj from context we should # since we get the obj from context we should
...@@ -932,6 +990,123 @@ class BusinessItem(Implicit, Persistent): ...@@ -932,6 +990,123 @@ class BusinessItem(Implicit, Persistent):
XMLExportImport.exportXML(obj._p_jar, obj._p_oid, f) XMLExportImport.exportXML(obj._p_jar, obj._p_oid, f)
bma.addObject(f, key, path=path) bma.addObject(f, key, path=path)
def importFile(self, bma, parent, **kw):
bma.importFiles(self, parent)
def _importFile(self, file_name, file_obj, parent):
obj_key, file_ext = os.path.splitext(file_name)
# id() for installing several bt5 in the same transaction
transactional_variable_obj_key = "%s-%s" % (id(self), obj_key)
if file_ext != '.xml':
# For ZODB Components: if .xml have been processed before, set the
# source code property, otherwise store it in a transactional variable
# so that it can be set once the .xml has been processed
data = file_obj.read()
try:
obj = self._objects[obj_key]
except KeyError:
getTransactionalVariable()[transactional_variable_obj_key] = data
else:
self._restoreSeparatelyExportedProperty(obj, data)
else:
connection = self.getConnection(parent)
__traceback_info__ = 'Importing %s' % file_name
if hasattr(cache_database, 'db') and isinstance(file_obj, file):
obj = connection.importFile(self._compileXML(file_obj))
else:
# FIXME: Why not use the importXML function directly? Are there any BT5s
# with actual .zexp files on the wild?
obj = connection.importFile(file_obj, customImporters=customImporters)
self._value = obj
data = getTransactionalVariable().get(transactional_variable_obj_key)
if data is not None:
self._restoreSeparatelyExportedProperty(obj, data)
def _restoreSeparatelyExportedProperty(self, obj, data):
unicode_data, property_name = SEPARATELY_EXPORTED_PROPERTY_DICT[
obj.__class__.__name__][1:]
if unicode_data:
data = data.decode(obj.output_encoding)
try:
setattr(obj, property_name, data)
except BrokenModified:
obj.__Broken_state__[property_name] = data
obj._p_changed = 1
else:
# Revert any work done by __setstate__.
# XXX: This is enough for all objects we currently split in 2 files,
# but __setstate__ could behave badly with the missing attribute
# and newly added types may require more than this.
self.removeProperties(obj, 1, keep_workflow_history=True)
def getConnection(self, obj):
while True:
connection = obj._p_jar
if connection is not None:
return connection
obj = obj.aq_parent
def _compileXML(self, file):
# This method converts XML to ZEXP. Because the conversion
# is quite heavy, a persistent cache database is used to
# store ZEXP, so the second run wouldn't have to re-generate
# identical data again.
#
# For now, a pair of the path to an XML file and its modification time
# are used as a unique key. In theory, a checksum of the content could
# be used instead, and it could be more reliable, as modification time
# might not be updated in some insane filesystems correctly. However,
# in practice, checksums consume a lot of CPU time, so when the cache
# does not hit, the increased overhead is significant. In addition, it
# does rarely happen that two XML files in Business Manager contain
# the same data, so it may not be expected to have more cache hits
# with this approach.
#
# The disadvantage is that this wouldn't work with the archive format,
# because each entry in an archive does not have a mtime in itself.
# However, the plan is to have an archive to retain ZEXP directly
# instead of XML, so the idea of caching would be completely useless
# with the archive format.
name = file.name
mtime = os.path.getmtime(file.name)
key = '%s:%s' % (name, mtime)
try:
return StringIO(cache_database.db[key])
except:
pass
from Shared.DC.xml import ppml
from OFS.XMLExportImport import start_zopedata, save_record, save_zopedata
import xml.parsers.expat
outfile=StringIO()
try:
data=file.read()
F=ppml.xmlPickler()
F.end_handlers['record'] = save_record
F.end_handlers['ZopeData'] = save_zopedata
F.start_handlers['ZopeData'] = start_zopedata
F.binary=1
F.file=outfile
p=xml.parsers.expat.ParserCreate('utf-8')
p.returns_unicode = False
p.CharacterDataHandler=F.handle_data
p.StartElementHandler=F.unknown_starttag
p.EndElementHandler=F.unknown_endtag
p.Parse(data)
try:
cache_database.db[key] = outfile.getvalue()
except:
pass
outfile.seek(0)
return outfile
except:
outfile.close()
raise
def getBusinessPath(self): def getBusinessPath(self):
return self._path return self._path
...@@ -956,7 +1131,7 @@ class BusinessItem(Implicit, Persistent): ...@@ -956,7 +1131,7 @@ class BusinessItem(Implicit, Persistent):
class BusinessManagerArchive(object): class BusinessManagerArchive(object):
""" """
This is the base class for all Business Template archives This is the base class for all Business Manager archives
""" """
def __init__(self, path, **kw): def __init__(self, path, **kw):
...@@ -989,7 +1164,7 @@ class BusinessManagerArchive(object): ...@@ -989,7 +1164,7 @@ class BusinessManagerArchive(object):
class BusinessManagerFolder(BusinessManagerArchive): class BusinessManagerFolder(BusinessManagerArchive):
""" """
Class archiving business template into a folder tree Class archiving business manager into a folder tree
""" """
def _writeString(self, obj, path): def _writeString(self, obj, path):
...@@ -1002,11 +1177,10 @@ class BusinessManagerFolder(BusinessManagerArchive): ...@@ -1002,11 +1177,10 @@ class BusinessManagerFolder(BusinessManagerArchive):
finally: finally:
f.close() f.close()
def importFiles(self, item): def importFiles(self, item, parent):
""" """
Import file from a local folder Import file from a local folder
""" """
join = os.path.join join = os.path.join
item_name = item.__class__.__name__ item_name = item.__class__.__name__
root = join(os.path.normpath(self.path), item_name, '') root = join(os.path.normpath(self.path), item_name, '')
...@@ -1028,7 +1202,7 @@ class BusinessManagerFolder(BusinessManagerArchive): ...@@ -1028,7 +1202,7 @@ class BusinessManagerFolder(BusinessManagerArchive):
continue continue
# self.revision.hash(item_name + '/' + file_name, f.read()) # self.revision.hash(item_name + '/' + file_name, f.read())
f.seek(0) f.seek(0)
item._importFile(file_name, f) item._importFile(file_name, f, parent)
finally: finally:
if hasattr(cache_database, 'db'): if hasattr(cache_database, 'db'):
cache_database.db.close() cache_database.db.close()
...@@ -1070,7 +1244,7 @@ class BusinessManagerTarball(BusinessManagerArchive): ...@@ -1070,7 +1244,7 @@ class BusinessManagerTarball(BusinessManagerArchive):
self.tar.close() self.tar.close()
return self.fobj return self.fobj
def importFiles(self, item): def importFiles(self, item, parent):
""" """
Import all file from the archive to the site Import all file from the archive to the site
""" """
...@@ -1084,15 +1258,15 @@ class BusinessManagerTarball(BusinessManagerArchive): ...@@ -1084,15 +1258,15 @@ class BusinessManagerTarball(BusinessManagerArchive):
f = extractfile(info) f = extractfile(info)
self.revision.hash(item_name + '/' + file_name, f.read()) self.revision.hash(item_name + '/' + file_name, f.read())
f.seek(0) f.seek(0)
item._importFile(file_name, f) item._importFile(file_name, f, parent)
class bm(dict): class bm(dict):
""" """
Fake 'bm' item to read bp/* files through BusinessManagerArchive Fake 'bm' item to read bm/* files through BusinessManagerArchive
""" """
def _importFile(self, file_name, file): def _importFile(self, file_name, file, parent):
self[file_name] = file.read() self[file_name] = file.read()
#InitializeClass(BusinessManager) #InitializeClass(BusinessManager)
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