Commit 2eb560f6 authored by Julien Muchembled's avatar Julien Muchembled

Merge branch to remove 'bt/revision' files

parents c727c78a db6a2d07
10
\ No newline at end of file
7
\ No newline at end of file
7
\ No newline at end of file
1581
\ No newline at end of file
22
\ No newline at end of file
280
\ No newline at end of file
98
\ No newline at end of file
12
\ No newline at end of file
709
\ No newline at end of file
503
\ No newline at end of file
591
\ No newline at end of file
25
\ No newline at end of file
14
\ No newline at end of file
15
\ No newline at end of file
1
\ No newline at end of file
380
\ No newline at end of file
392
\ No newline at end of file
1
\ No newline at end of file
552
\ No newline at end of file
65
\ No newline at end of file
438
\ No newline at end of file
705
\ No newline at end of file
17
\ No newline at end of file
11
\ No newline at end of file
111
\ No newline at end of file
27
\ No newline at end of file
16
\ No newline at end of file
14
\ No newline at end of file
625
\ No newline at end of file
153
\ No newline at end of file
1283
\ No newline at end of file
30
\ No newline at end of file
24
\ No newline at end of file
766
\ No newline at end of file
15
\ No newline at end of file
34
\ No newline at end of file
681
\ No newline at end of file
10
\ No newline at end of file
27
\ No newline at end of file
19
\ No newline at end of file
24
\ No newline at end of file
185
\ No newline at end of file
154
\ No newline at end of file
6
\ No newline at end of file
5
\ No newline at end of file
396
\ No newline at end of file
294
\ No newline at end of file
3
\ No newline at end of file
20
\ No newline at end of file
23
\ No newline at end of file
3
\ No newline at end of file
1885
\ No newline at end of file
170
\ No newline at end of file
768
\ No newline at end of file
42
\ No newline at end of file
20
\ No newline at end of file
41
\ No newline at end of file
36
\ No newline at end of file
7
\ No newline at end of file
11
\ No newline at end of file
1
\ No newline at end of file
72
\ No newline at end of file
13
\ No newline at end of file
481
\ No newline at end of file
2
\ No newline at end of file
260
\ No newline at end of file
123
\ No newline at end of file
416
\ No newline at end of file
112
\ No newline at end of file
29
\ No newline at end of file
595
\ No newline at end of file
142
\ No newline at end of file
36
\ No newline at end of file
6
\ No newline at end of file
582
\ No newline at end of file
35
\ No newline at end of file
24
\ No newline at end of file
2
\ No newline at end of file
838
\ No newline at end of file
28
\ No newline at end of file
12
\ No newline at end of file
943
\ No newline at end of file
223
\ No newline at end of file
55
\ No newline at end of file
70
\ No newline at end of file
18
\ No newline at end of file
16
\ No newline at end of file
14
\ No newline at end of file
177
\ No newline at end of file
95
\ No newline at end of file
6
\ No newline at end of file
14
\ No newline at end of file
31
\ No newline at end of file
122
\ No newline at end of file
7
\ No newline at end of file
17
\ No newline at end of file
269
\ No newline at end of file
639
\ No newline at end of file
292
\ No newline at end of file
96
\ No newline at end of file
2
\ No newline at end of file
400
\ No newline at end of file
67
\ No newline at end of file
613
\ No newline at end of file
13
\ No newline at end of file
4
\ No newline at end of file
1118
\ No newline at end of file
93
\ No newline at end of file
10
\ No newline at end of file
71
\ No newline at end of file
77
\ No newline at end of file
40
\ No newline at end of file
54
\ No newline at end of file
38
\ No newline at end of file
417
\ No newline at end of file
175
\ No newline at end of file
38
\ No newline at end of file
39
\ No newline at end of file
10
\ No newline at end of file
33
\ No newline at end of file
6
\ No newline at end of file
8
\ No newline at end of file
6
\ No newline at end of file
8
\ No newline at end of file
6
\ No newline at end of file
80
\ No newline at end of file
...@@ -40,6 +40,7 @@ from Products.CMFCore.utils import getToolByName ...@@ -40,6 +40,7 @@ from Products.CMFCore.utils import getToolByName
from Products.PythonScripts.PythonScript import PythonScript from Products.PythonScripts.PythonScript import PythonScript
from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
from Products.ERP5Type.Cache import transactional_cached from Products.ERP5Type.Cache import transactional_cached
from Products.ERP5Type.Message import translateString
from Products.ERP5Type.Utils import readLocalDocument, \ from Products.ERP5Type.Utils import readLocalDocument, \
writeLocalDocument, \ writeLocalDocument, \
importLocalDocument, \ importLocalDocument, \
...@@ -77,7 +78,6 @@ customImporters={ ...@@ -77,7 +78,6 @@ customImporters={
from Products.ERP5Type.patches.WorkflowTool import WorkflowHistoryList from Products.ERP5Type.patches.WorkflowTool import WorkflowHistoryList
from zLOG import LOG, WARNING, INFO from zLOG import LOG, WARNING, INFO
from warnings import warn from warnings import warn
from gzip import GzipFile
from lxml.etree import parse from lxml.etree import parse
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
from Products.CMFCore.Expression import Expression from Products.CMFCore.Expression import Expression
...@@ -88,6 +88,8 @@ import transaction ...@@ -88,6 +88,8 @@ import transaction
import threading import threading
from ZODB.broken import Broken from ZODB.broken import Broken
from Products.ERP5.genbt5list import BusinessTemplateRevision, \
item_name_list, item_set
CACHE_DATABASE_PATH = None CACHE_DATABASE_PATH = None
try: try:
...@@ -303,18 +305,13 @@ def unregisterSkinFolderId(skin_tool, skin_folder_id, skin_selection_list): ...@@ -303,18 +305,13 @@ def unregisterSkinFolderId(skin_tool, skin_folder_id, skin_selection_list):
deleteSkinSelection(skin_tool, skin_selection) deleteSkinSelection(skin_tool, skin_selection)
skin_tool.getPortalObject().changeSkin(None) skin_tool.getPortalObject().changeSkin(None)
class BusinessTemplateArchive: class BusinessTemplateArchive(object):
""" """
This is the base class for all Business Template archives This is the base class for all Business Template archives
""" """
def _initCreation(self, path, **kw): def __init__(self, path, **kw):
self.path = path self.path = path
self.revision = BusinessTemplateRevision()
def __init__(self, creation=0, importing=0, file=None, path=None, **kw):
if creation:
self._initCreation(path=path, **kw)
elif importing:
self._initImport(file=file, path=path, **kw)
def addObject(self, obj, name, path=None, ext='.xml'): def addObject(self, obj, name, path=None, ext='.xml'):
if path: if path:
...@@ -329,15 +326,23 @@ class BusinessTemplateArchive: ...@@ -329,15 +326,23 @@ class BusinessTemplateArchive:
if not isinstance(obj, str): if not isinstance(obj, str):
obj.seek(0) obj.seek(0)
obj = obj.read() obj = obj.read()
self.revision.hash(path, obj)
self._writeString(obj, path) self._writeString(obj, path)
else: else:
if isinstance(obj, str): if isinstance(obj, str):
self.revision.hash(path, obj)
obj = StringIO(obj) obj = StringIO(obj)
else:
obj.seek(0)
self.revision.hash(path, obj.read())
write(obj, path) write(obj, path)
def finishCreation(self): def finishCreation(self):
pass pass
def getRevision(self):
return self.revision.digest()
class BusinessTemplateFolder(BusinessTemplateArchive): class BusinessTemplateFolder(BusinessTemplateArchive):
""" """
Class archiving business template into a folder tree Class archiving business template into a folder tree
...@@ -352,39 +357,32 @@ class BusinessTemplateFolder(BusinessTemplateArchive): ...@@ -352,39 +357,32 @@ class BusinessTemplateFolder(BusinessTemplateArchive):
finally: finally:
f.close() f.close()
def _initImport(self, file, path, **kw): def importFiles(self, item):
root_path_len = len(os.path.normpath(os.path.join(path, '_'))) - 1
self.root_path_len = root_path_len
d = {}
for f in file:
f = os.path.normpath(f)
klass = f[root_path_len:].split(os.sep, 1)[0]
d.setdefault(klass, []).append(f)
self.file_list_dict = d
def importFiles(self, item, **kw):
""" """
Import file from a local folder Import file from a local folder
""" """
class_name = item.__class__.__name__ join = os.path.join
root_path_len = self.root_path_len item_name = item.__class__.__name__
prefix_len = root_path_len + len(class_name) + len(os.sep) root = join(os.path.normpath(self.path), item_name, '')
root_path_len = len(root)
if CACHE_DATABASE_PATH: if CACHE_DATABASE_PATH:
try: try:
cache_database.db = gdbm.open(CACHE_DATABASE_PATH, 'cf') cache_database.db = gdbm.open(CACHE_DATABASE_PATH, 'cf')
except gdbm.error: except gdbm.error:
cache_database.db = gdbm.open(CACHE_DATABASE_PATH, 'nf') cache_database.db = gdbm.open(CACHE_DATABASE_PATH, 'nf')
try: try:
for file_path in self.file_list_dict.get(class_name, ()): for root, dirs, files in os.walk(root):
if os.path.isfile(file_path): for file_name in files:
file = open(file_path, 'rb') file_name = join(root, file_name)
try: with open(file_name, 'rb') as f:
file_name = file_path[prefix_len:] file_name = posixpath.normpath(file_name[root_path_len:])
if '%' in file_name: if '%' in file_name:
file_name = unquote(file_name) file_name = unquote(file_name)
item._importFile(file_name, file) elif item_name == 'bt' and file_name == 'revision':
finally: continue
file.close() self.revision.hash(item_name + '/' + file_name, f.read())
f.seek(0)
item._importFile(file_name, f)
finally: finally:
if hasattr(cache_database, 'db'): if hasattr(cache_database, 'db'):
cache_database.db.close() cache_database.db.close()
...@@ -395,12 +393,21 @@ class BusinessTemplateTarball(BusinessTemplateArchive): ...@@ -395,12 +393,21 @@ class BusinessTemplateTarball(BusinessTemplateArchive):
Class archiving businnes template into a tarball file Class archiving businnes template into a tarball file
""" """
def _initCreation(self, **kw): def __init__(self, path, creation=0, importing=0, **kw):
BusinessTemplateArchive._initCreation(self, **kw) super(BusinessTemplateTarball, self).__init__(path, **kw)
# init tarfile obj if creation:
self.fobj = StringIO() self.fobj = StringIO()
self.tar = tarfile.open('', 'w:gz', self.fobj) self.tar = tarfile.open('', 'w:gz', self.fobj)
self.time = time.time() self.time = time.time()
elif importing:
self.tar = tarfile.open(path, 'r:gz')
self.item_dict = item_dict = defaultdict(list)
for info in self.tar.getmembers():
if info.isreg():
path = info.name.split('/')
if path[0] == '.':
del path[0]
item_dict[path[1]].append(('/'.join(path[2:]), info))
def _writeFile(self, obj, path): def _writeFile(self, obj, path):
if self.path: if self.path:
...@@ -416,27 +423,21 @@ class BusinessTemplateTarball(BusinessTemplateArchive): ...@@ -416,27 +423,21 @@ class BusinessTemplateTarball(BusinessTemplateArchive):
self.tar.close() self.tar.close()
return self.fobj return self.fobj
def _initImport(self, file, **kw): def importFiles(self, item):
self.tar = tarfile.TarFile(fileobj=StringIO(GzipFile(fileobj=file).read()))
self.item_dict = {}
setdefault = self.item_dict.setdefault
for info in self.tar.getmembers():
if info.isreg():
path = info.name.split('/')
if path[0] == '.':
del path[0]
file_name = '/'.join(path[2:])
if '%' in file_name:
file_name = unquote(file_name)
setdefault(path[1], []).append((file_name, info))
def importFiles(self, item, **kw):
""" """
Import all file from the archive to the site Import all file from the archive to the site
""" """
extractfile = self.tar.extractfile extractfile = self.tar.extractfile
for file_name, info in self.item_dict.get(item.__class__.__name__, ()): item_name = item.__class__.__name__
item._importFile(file_name, extractfile(info)) for file_name, info in self.item_dict.get(item_name, ()):
if '%' in file_name:
file_name = unquote(file_name)
elif item_name == 'bt' and file_name == 'revision':
continue
f = extractfile(info)
self.revision.hash(item_name + '/' + file_name, f.read())
f.seek(0)
item._importFile(file_name, f)
class TemplateConditionError(Exception): pass class TemplateConditionError(Exception): pass
class TemplateConflictError(Exception): pass class TemplateConflictError(Exception): pass
...@@ -535,7 +536,7 @@ class BaseTemplateItem(Implicit, Persistent): ...@@ -535,7 +536,7 @@ class BaseTemplateItem(Implicit, Persistent):
return self._objects.keys() return self._objects.keys()
def importFile(self, bta, **kw): def importFile(self, bta, **kw):
bta.importFiles(item=self) bta.importFiles(self)
def _removeAllButLastWorkflowHistory(self, obj): def _removeAllButLastWorkflowHistory(self, obj):
workflow_history = getattr(obj, 'workflow_history', None) workflow_history = getattr(obj, 'workflow_history', None)
...@@ -4677,6 +4678,13 @@ class LocalRolesTemplateItem(BaseTemplateItem): ...@@ -4677,6 +4678,13 @@ class LocalRolesTemplateItem(BaseTemplateItem):
delattr(obj, '__ac_local_roles_group_id_dict__') delattr(obj, '__ac_local_roles_group_id_dict__')
obj.reindexObject() obj.reindexObject()
class bt(dict):
"""Fake 'bt' item to read bt/* files through BusinessTemplateArchive"""
def _importFile(self, file_name, file):
self[file_name] = file.read()
class BusinessTemplate(XMLObject): class BusinessTemplate(XMLObject):
""" """
A business template allows to construct ERP5 modules A business template allows to construct ERP5 modules
...@@ -4768,64 +4776,6 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -4768,64 +4776,6 @@ Business Template is a set of definitions, such as skins, portal types and categ
, 'filter_content_types' : 1 , 'filter_content_types' : 1
} }
# This is a global variable
# Order is important for installation
# We want to have:
# * workflow and portal_type* before ZODB Component {Document,Extension...}
# * path after module, because path can be module content
# * path after categories, because path can be categories content
# * path after portal types roles so that roles in the current bt can be used
# * path before workflow chain, because path can be a portal type
# (until chains are set on portal types with categories)
# * skin after paths, because we can install a custom connection string as
# path and use it with SQLMethods in a skin.
# ( and more )
_item_name_list = [
'_registered_version_priority_selection_item',
'_workflow_item',
'_product_item',
'_document_item',
'_property_sheet_item',
'_constraint_item',
'_extension_item',
'_test_item',
'_role_item',
'_tool_item',
'_message_translation_item',
'_site_property_item',
'_portal_type_item',
#'_portal_type_workflow_chain_item',
'_portal_type_allowed_content_type_item',
'_portal_type_hidden_content_type_item',
'_portal_type_property_sheet_item',
'_portal_type_base_category_item',
'_category_item',
'_module_item',
'_portal_type_roles_item',
'_path_item',
'_skin_item',
'_registered_skin_selection_item',
'_preference_item',
'_action_item',
'_local_roles_item',
'_portal_type_workflow_chain_item',
'_catalog_method_item',
'_catalog_result_key_item',
'_catalog_related_key_item',
'_catalog_result_table_item',
'_catalog_search_key_item',
'_catalog_keyword_key_item',
'_catalog_datetime_key_item',
'_catalog_full_text_key_item',
'_catalog_request_key_item',
'_catalog_multivalue_key_item',
'_catalog_topic_key_item',
'_catalog_scriptable_key_item',
'_catalog_role_key_item',
'_catalog_local_role_key_item',
'_catalog_security_uid_column_item',
]
def __init__(self, *args, **kw): def __init__(self, *args, **kw):
XMLObject.__init__(self, *args, **kw) XMLObject.__init__(self, *args, **kw)
self._clean() self._clean()
...@@ -4845,23 +4795,10 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -4845,23 +4795,10 @@ Business Template is a set of definitions, such as skins, portal types and categ
self.workflow_history[ self.workflow_history[
'business_template_installation_workflow'] = None 'business_template_installation_workflow'] = None
security.declareProtected(Permissions.AccessContentsInformation, def getShortRevision(self):
'getRevision') """Returned a shortened revision"""
def getRevision(self): r = self.getRevision()
"""returns the revision property. return r and r[:5]
This is a workaround for #461.
"""
return self._baseGetRevision()
def updateRevisionNumber(self):
"""Increment bt revision number.
"""
revision_number = self.getRevision()
if revision_number is None or revision_number.strip() == '':
revision_number = 1
else:
revision_number = int(revision_number)+1
self.setRevision(revision_number)
security.declareProtected(Permissions.ManagePortal, 'storeTemplateItemData') security.declareProtected(Permissions.ManagePortal, 'storeTemplateItemData')
def storeTemplateItemData(self): def storeTemplateItemData(self):
...@@ -4984,7 +4921,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -4984,7 +4921,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
pass pass
security.declareProtected(Permissions.ManagePortal, 'build') security.declareProtected(Permissions.ManagePortal, 'build')
def build(self, no_action=0): def build(self, no_action=0, update_revision=True):
""" """
Copy existing portal objects to self Copy existing portal objects to self
""" """
...@@ -4993,19 +4930,11 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -4993,19 +4930,11 @@ Business Template is a set of definitions, such as skins, portal types and categ
# Make sure that everything is sane. # Make sure that everything is sane.
self.clean() self.clean()
try:
from Products.ERP5VCS.WorkingCopy import NotAWorkingCopyError
try:
self.setRevision(self.getVcsTool().newRevision())
except (NotAWorkingCopyError, IOError):
raise ImportError
except ImportError:
self.updateRevisionNumber()
self._setTemplateFormatVersion(1) self._setTemplateFormatVersion(1)
self.storeTemplateItemData() self.storeTemplateItemData()
# Build each part # Build each part
for item_name in self._item_name_list: for item_name in item_name_list:
item = getattr(self, item_name) item = getattr(self, item_name)
if item is None: if item is None:
continue continue
...@@ -5014,6 +4943,8 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5014,6 +4943,8 @@ Business Template is a set of definitions, such as skins, portal types and categ
item.build(self) item.build(self)
# update _p_jar property of objects cleaned by removeProperties # update _p_jar property of objects cleaned by removeProperties
transaction.savepoint(optimistic=True) transaction.savepoint(optimistic=True)
if update_revision:
self._export()
def publish(self, url, username=None, password=None): def publish(self, url, username=None, password=None):
""" """
...@@ -5083,7 +5014,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5083,7 +5014,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
else: else:
installed_bt = self.portal_templates._getOb(INSTALLED_BT_FOR_DIFF) installed_bt = self.portal_templates._getOb(INSTALLED_BT_FOR_DIFF)
for item_name in self._item_name_list: for item_name in item_name_list:
new_item = getattr(self, item_name, None) new_item = getattr(self, item_name, None)
installed_item = getattr(installed_bt, item_name, None) installed_item = getattr(installed_bt, item_name, None)
if new_item is not None: if new_item is not None:
...@@ -5138,7 +5069,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5138,7 +5069,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
# Install everything # Install everything
if len(object_to_update) or force: if len(object_to_update) or force:
for item_name in self._item_name_list: for item_name in item_name_list:
item = getattr(self, item_name, None) item = getattr(self, item_name, None)
if item is not None: if item is not None:
item.install(self, force=force, object_to_update=object_to_update, item.install(self, force=force, object_to_update=object_to_update,
...@@ -5163,7 +5094,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5163,7 +5094,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
# remove object from old business template # remove object from old business template
if len(remove_object_dict): if len(remove_object_dict):
# XXX: this code assumes that there is an installed_bt # XXX: this code assumes that there is an installed_bt
for item_name in reversed(installed_bt._item_name_list): for item_name in reversed(item_name_list):
item = getattr(installed_bt, item_name, None) item = getattr(installed_bt, item_name, None)
if item is not None: if item is not None:
item.remove(self, remove_object_dict=remove_object_dict, trashbin=trashbin) item.remove(self, remove_object_dict=remove_object_dict, trashbin=trashbin)
...@@ -5207,7 +5138,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5207,7 +5138,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
not remove all items. not remove all items.
""" """
# Trash everything # Trash everything
for item_name in self._item_name_list[::-1]: for item_name in reversed(item_name_list):
item = getattr(self, item_name, None) item = getattr(self, item_name, None)
if item is not None: if item is not None:
item.trash( item.trash(
...@@ -5220,7 +5151,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5220,7 +5151,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
""" """
# Uninstall everything # Uninstall everything
# Trash everything # Trash everything
for item_name in self._item_name_list[::-1]: for item_name in reversed(item_name_list):
item = getattr(self, item_name, None) item = getattr(self, item_name, None)
if item is not None: if item is not None:
item.uninstall(self, **kw) item.uninstall(self, **kw)
...@@ -5248,7 +5179,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5248,7 +5179,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
if hasattr(self, attr): if hasattr(self, attr):
delattr(self, attr) delattr(self, attr)
# Secondly, make attributes empty. # Secondly, make attributes empty.
for item_name in self._item_name_list: for item_name in item_name_list:
setattr(self, item_name, None) setattr(self, item_name, None)
security.declareProtected(Permissions.ManagePortal, 'clean') security.declareProtected(Permissions.ManagePortal, 'clean')
...@@ -5490,12 +5421,16 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5490,12 +5421,16 @@ Business Template is a set of definitions, such as skins, portal types and categ
if os.path.isdir(bt_path): if os.path.isdir(bt_path):
return bt_path return bt_path
@transactional_cached(lambda self, vcs=None, path=None: (self, vcs, path)) @transactional_cached(lambda self, vcs=None, path=None, restricted=False:
def getVcsTool(self, vcs=None, path=None): (self, vcs, path, restricted))
def _getVcsTool(self, vcs=None, path=None, restricted=False):
from Products.ERP5VCS.WorkingCopy import getVcsTool from Products.ERP5VCS.WorkingCopy import getVcsTool
if not (path or vcs): if not (path or vcs):
path = self.getExportPath() path = self.getExportPath()
return getVcsTool(vcs=vcs, path=path).__of__(self) return getVcsTool(vcs, path, restricted).__of__(self)
def getVcsTool(self, vcs=None, path=None):
return self._getVcsTool(vcs, path, True)
def isVcsType(self, *vcs): def isVcsType(self, *vcs):
# could be moved to Products.ERP5.Base.Base # could be moved to Products.ERP5.Base.Base
...@@ -5513,22 +5448,24 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5513,22 +5448,24 @@ Business Template is a set of definitions, such as skins, portal types and categ
if self.getBuildingState() != 'built': if self.getBuildingState() != 'built':
raise TemplateConditionError, \ raise TemplateConditionError, \
'Business Template must be built before export' 'Business Template must be built before export'
return self._export(path, local, bta)
def _export(self, path=None, local=0, bta=None):
if bta is None: if bta is None:
if local: if local:
# we export into a folder tree # we export into a folder tree
bta = BusinessTemplateFolder(creation=1, path=path) bta = BusinessTemplateFolder(path, creation=1)
else: else:
# We export BT into a tarball file # We export BT into a tarball file
if path is None: if path is None:
path = self.getTitle() path = self.getTitle()
bta = BusinessTemplateTarball(creation=1, path=path) bta = BusinessTemplateTarball(path, creation=1)
# export bt # export bt
for prop in self.propertyMap(): for prop in self.propertyMap():
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', if id in ('id', 'uid', 'rid', 'sid', 'id_group', 'last_id', 'revision',
'install_object_list_list', 'id_generator', 'bt_for_diff'): 'install_object_list_list', 'id_generator', 'bt_for_diff'):
continue continue
value = self.getProperty(id) value = self.getProperty(id)
...@@ -5540,22 +5477,50 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5540,22 +5477,50 @@ Business Template is a set of definitions, such as skins, portal types and categ
bta.addObject('\n'.join(value), name=id, path='bt', ext='') bta.addObject('\n'.join(value), name=id, path='bt', ext='')
# Export each part # Export each part
for item_name in self._item_name_list: for item_name in item_name_list:
item = getattr(self, item_name, None) item = getattr(self, item_name, None)
if item is not None: if item is not None:
item.export(context=self, bta=bta) item.export(context=self, bta=bta)
self._setRevision(bta.getRevision())
return bta.finishCreation() return bta.finishCreation()
security.declareProtected(Permissions.ManagePortal, 'importFile') security.declareProtected(Permissions.ManagePortal, 'importFile')
def importFile(self, dir = 0, file=None, root_path=None): def importFile(self, path):
""" """
Import all xml files in Business Template Import all xml files in Business Template
""" """
if dir: bta = (BusinessTemplateFolder if os.path.isdir(path) else
bta = BusinessTemplateFolder(importing=1, file=file, path=root_path) BusinessTemplateTarball)(path, importing=1)
bt_item = bt()
bta.importFiles(bt_item)
prop_dict = {}
for prop in self.propertyMap():
pid = prop['id']
if pid != 'id':
prop_type = prop['type']
value = bt_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'):
prop_dict[pid[:-5]] = (value or '').splitlines()
self._edit(**prop_dict)
from Products.ERP5VCS.WorkingCopy import NotAWorkingCopyError
try:
vcs_tool = self._getVcsTool(path=path)
except NotAWorkingCopyError:
pass
else: else:
bta = BusinessTemplateTarball(importing=1, file=file) comment = translateString(
'Downloaded from ${type} repository at revision ${revision}',
mapping={'type': vcs_tool.title,
'revision': vcs_tool.getRevision(True)})
workflow_tool = self.getPortalObject().portal_workflow
workflow_tool.business_template_building_workflow.notifyWorkflowMethod(
self, 'edit', kw={'comment': comment})
self.storeTemplateItemData() self.storeTemplateItemData()
...@@ -5572,7 +5537,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5572,7 +5537,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
setattr(module, template_id, type(template_id, setattr(module, template_id, type(template_id,
(SimpleItem.SimpleItem,), {'__module__': module_id})) (SimpleItem.SimpleItem,), {'__module__': module_id}))
for item_name in self._item_name_list: for item_name in item_name_list:
item_object = getattr(self, item_name, None) item_object = getattr(self, item_name, None)
# this check is due to backwards compatability when there can be a # this check is due to backwards compatability when there can be a
# difference between install erp5_property_sheets (esp. BusinessTemplate # difference between install erp5_property_sheets (esp. BusinessTemplate
...@@ -5585,11 +5550,13 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5585,11 +5550,13 @@ Business Template is a set of definitions, such as skins, portal types and categ
for module_id in module_id_list: for module_id in module_id_list:
del sys.modules[module_id] del sys.modules[module_id]
self._setRevision(bta.getRevision())
def getItemsList(self): def getItemsList(self):
"""Return list of items in business template """Return list of items in business template
""" """
items_list = [] items_list = []
for item_name in self._item_name_list: for item_name in item_name_list:
item = getattr(self, item_name, None) item = getattr(self, item_name, None)
if item is not None: if item is not None:
items_list.extend(item.getKeys()) items_list.extend(item.getKeys())
...@@ -6054,5 +6021,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6054,5 +6021,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
# Block acquisition on all _item_name_list properties by setting # Block acquisition on all _item_name_list properties by setting
# a default class value to None # a default class value to None
for key in BusinessTemplate._item_name_list: for key in item_name_list:
setattr(BusinessTemplate, key, None) setattr(BusinessTemplate, key, None)
# Check naming convention of items.
assert item_set.issubset(globals()), item_set.difference(globals())
...@@ -33,7 +33,6 @@ from App.config import getConfiguration ...@@ -33,7 +33,6 @@ from App.config import getConfiguration
import os import os
import shutil import shutil
import sys import sys
import tarfile
from Acquisition import Implicit, Explicit from Acquisition import Implicit, Explicit
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
...@@ -45,6 +44,7 @@ from Products.ERP5Type.Tool.BaseTool import BaseTool ...@@ -45,6 +44,7 @@ from Products.ERP5Type.Tool.BaseTool import BaseTool
from Products.ERP5Type.Cache import transactional_cached from Products.ERP5Type.Cache import transactional_cached
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5.Document.BusinessTemplate import BusinessTemplateMissingDependency from Products.ERP5.Document.BusinessTemplate import BusinessTemplateMissingDependency
from Products.ERP5.genbt5list import generateInformation
from Acquisition import aq_base from Acquisition import aq_base
from tempfile import mkstemp, mkdtemp from tempfile import mkstemp, mkdtemp
from Products.ERP5 import _dtmldir from Products.ERP5 import _dtmldir
...@@ -56,7 +56,6 @@ from xml.dom.minidom import parse ...@@ -56,7 +56,6 @@ from xml.dom.minidom import parse
from xml.parsers.expat import ExpatError from xml.parsers.expat import ExpatError
import struct import struct
import cPickle import cPickle
import posixpath
from base64 import b64encode, b64decode from base64 import b64encode, b64decode
from Products.ERP5Type.Message import translateString from Products.ERP5Type.Message import translateString
from zLOG import LOG, INFO, WARNING from zLOG import LOG, INFO, WARNING
...@@ -126,23 +125,19 @@ class TemplateTool (BaseTool): ...@@ -126,23 +125,19 @@ class TemplateTool (BaseTool):
# However, that unlikely happens, and using a Z SQL Method has a # However, that unlikely happens, and using a Z SQL Method has a
# potential danger because business templates may exchange catalog # potential danger because business templates may exchange catalog
# methods, so the database could be broken temporarily. # methods, so the database could be broken temporarily.
latest_bt = None last_bt = last_time = None
latest_revision = 0 for bt in self.objectValues(portal_type='Business Template'):
for bt in self.contentValues(filter={'portal_type':'Business Template'}):
if bt.getTitle() == title or title in bt.getProvisionList(): if bt.getTitle() == title or title in bt.getProvisionList():
installation_state = bt.getInstallationState() state = bt.getInstallationState()
if installation_state == 'installed': if state == 'installed':
latest_bt = bt return bt
break if state == 'replaced' and not strict:
elif strict is False and installation_state == 'replaced': t = bt.workflow_history \
revision = bt.getRevision() ['business_template_installation_workflow'][-1]['time']
try: if last_time < t:
revision = int(revision) last_bt = bt
except ValueError: last_time = t
continue return last_bt
if revision > latest_revision:
latest_bt = bt
return latest_bt
def getInstalledBusinessTemplatesList(self): def getInstalledBusinessTemplatesList(self):
"""Deprecated. """Deprecated.
...@@ -182,20 +177,12 @@ class TemplateTool (BaseTool): ...@@ -182,20 +177,12 @@ class TemplateTool (BaseTool):
return bt.getRevision() return bt.getRevision()
return None return None
def getBuiltBusinessTemplatesList(self):
"""Deprecated.
"""
DeprecationWarning('getBuiltBusinessTemplatesList is deprecated; Use getBuiltBusinessTemplateList instead.', DeprecationWarning)
return self.getBuiltBusinessTemplateList()
def getBuiltBusinessTemplateList(self): def getBuiltBusinessTemplateList(self):
"""Get the list of built and not installed business templates. """Get the list of built and not installed business templates.
""" """
built_bts = [] return [bt for bt in self.objectValues(portal_type='Business Template')
for bt in self.contentValues(portal_type='Business Template'): if bt.getInstallationState() == 'not_installed' and
if bt.getInstallationState() == 'not_installed' and bt.getBuildingState() == 'built': bt.getBuildingState() == 'built']
built_bts.append(bt)
return built_bts
@property @property
def asRepository(self): def asRepository(self):
...@@ -203,7 +190,7 @@ class TemplateTool (BaseTool): ...@@ -203,7 +190,7 @@ class TemplateTool (BaseTool):
"""Export business template by their title """Export business template by their title
Provides a view of template tool allowing a user to download the last Provides a view of template tool allowing a user to download the last
revision of a business template with a URL like: edited business template with a URL like:
http://.../erp5/portal_templates/asRepository/erp5_core http://.../erp5/portal_templates/asRepository/erp5_core
""" """
def __before_publishing_traverse__(self, self2, request): def __before_publishing_traverse__(self, self2, request):
...@@ -215,9 +202,9 @@ class TemplateTool (BaseTool): ...@@ -215,9 +202,9 @@ class TemplateTool (BaseTool):
last_bt = None, None last_bt = None, None
for bt in self.aq_parent.searchFolder(title=title): for bt in self.aq_parent.searchFolder(title=title):
bt = bt.getObject() bt = bt.getObject()
revision = int(bt.getRevision()) modified = bt.getModificationDate()
if last_bt[0] < revision and bt.getInstallationState() != 'deleted': if last_bt[0] < modified and bt.getInstallationState() != 'deleted':
last_bt = revision, bt last_bt = modified, bt
if last_bt[1] is None: if last_bt[1] is None:
return RESPONSE.notFoundError(title) return RESPONSE.notFoundError(title)
RESPONSE.setHeader('Content-type', 'application/data') RESPONSE.setHeader('Content-type', 'application/data')
...@@ -298,52 +285,6 @@ class TemplateTool (BaseTool): ...@@ -298,52 +285,6 @@ class TemplateTool (BaseTool):
self.deleteContent(id) self.deleteContent(id)
self._importObjectFromFile(StringIO(export_string), id=id) self._importObjectFromFile(StringIO(export_string), id=id)
def _importBT(self, path=None, id=id):
"""
Import template from a temp file (as uploaded by the user)
"""
with open(path, 'rb') as file:
# read magic key to determine wich kind of bt we use
file.seek(0)
magic = file.read(5)
# XXX: should really check for a magic and offer a falback if it
# doens't correspond to anything handled.
tar = tarfile.open(path, 'r:gz')
dir_name = tar.members[0].name.split(posixpath.sep, 1)[0]
try:
# create bt object
bt = self.newContent(portal_type='Business Template', id=id)
prop_dict = {}
for prop in bt.propertyMap():
prop_type = prop['type']
pid = prop['id']
prop_path = posixpath.join(dir_name, 'bt', pid)
try:
info = tar.getmember(prop_path)
value = tar.extractfile(info).read()
except KeyError:
value = None
if value is 'None':
# At export time, we used to export non-existent properties:
# str(obj.getProperty('non-existing')) == 'None'
# Discard them
continue
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'):
prop_dict[pid[:-5]] = (value or '').splitlines()
prop_dict.pop('id', '')
bt.edit(**prop_dict)
# import all other files from bt
with open(path, 'rb') as fobj:
bt.importFile(file=fobj)
finally:
tar.close()
return bt
security.declareProtected( Permissions.ManagePortal, 'manage_download' ) security.declareProtected( Permissions.ManagePortal, 'manage_download' )
def manage_download(self, url, id=None, REQUEST=None): def manage_download(self, url, id=None, REQUEST=None):
"""The management interface for download. """The management interface for download.
...@@ -362,56 +303,9 @@ class TemplateTool (BaseTool): ...@@ -362,56 +303,9 @@ class TemplateTool (BaseTool):
def _download_local(self, path, bt_id): def _download_local(self, path, bt_id):
"""Download Business Template from local directory or file """Download Business Template from local directory or file
""" """
if os.path.isdir(os.path.normpath(path)): bt = self.newContent(bt_id, 'Business Template')
path = os.path.normpath(path) bt.importFile(path)
def callback(file_list, directory, files):
for excluded_directory in ('CVS', '.svn'):
try:
files.remove(excluded_directory)
except ValueError:
pass
for file in files:
absolute_path = os.path.join(directory, file)
if os.path.isfile(absolute_path):
file_list.append(absolute_path)
file_list = []
os.path.walk(path, callback, file_list)
file_list.sort()
# import bt object
bt = self.newContent(portal_type='Business Template', id=bt_id)
bt_path = os.path.join(path, 'bt')
# import properties
prop_dict = {}
for prop in bt.propertyMap():
prop_type = prop['type']
pid = prop['id']
prop_path = os.path.join('.', bt_path, pid)
if not os.path.exists(prop_path):
value = None
else:
with open(prop_path, 'rb') as f:
value = f.read()
if value is 'None':
# At export time, we used to export non-existent properties:
# str(obj.getProperty('non-existing')) == 'None'
# Discard them
value = None
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'):
prop_dict[pid[:-5]] = (value or '').splitlines()
prop_dict.pop('id', '')
bt.edit(**prop_dict)
# import all others objects
bt.importFile(dir=True, file=file_list, root_path=path)
return bt return bt
else:
# this should be a file
return self._importBT(path, bt_id)
def _download_url(self, url, bt_id): def _download_url(self, url, bt_id):
tempid, temppath = mkstemp() tempid, temppath = mkstemp()
...@@ -437,18 +331,6 @@ class TemplateTool (BaseTool): ...@@ -437,18 +331,6 @@ class TemplateTool (BaseTool):
finally: finally:
shutil.rmtree(svn_checkout_tmp_dir) shutil.rmtree(svn_checkout_tmp_dir)
def assertBtPathExists(self, url):
"""
Check if bt is present on the system
"""
urltype, name = splittype(url)
# Windows compatibility
if WIN:
if os.path.isdir(os.path.normpath(url)) or \
os.path.isfile(os.path.normpath(url)):
name = os.path.normpath(url)
return os.path.exists(os.path.normpath(name))
security.declareProtected( 'Import/Export objects', 'download' ) security.declareProtected( 'Import/Export objects', 'download' )
def download(self, url, id=None, REQUEST=None): def download(self, url, id=None, REQUEST=None):
""" """
...@@ -463,13 +345,9 @@ class TemplateTool (BaseTool): ...@@ -463,13 +345,9 @@ class TemplateTool (BaseTool):
id = self.generateNewId() id = self.generateNewId()
urltype, name = splittype(url) urltype, name = splittype(url)
# Windows compatibility if WIN and urltype and '\\' in name:
if WIN: urltype = None
if os.path.isdir(os.path.normpath(url)) or \ name = url
os.path.isfile(os.path.normpath(url)):
urltype = 'file'
name = os.path.normpath(url)
if urltype and urltype != 'file': if urltype and urltype != 'file':
if '/portal_templates/asRepository/' in url: if '/portal_templates/asRepository/' in url:
# In this case, the downloaded BT is already built. # In this case, the downloaded BT is already built.
...@@ -479,7 +357,7 @@ class TemplateTool (BaseTool): ...@@ -479,7 +357,7 @@ class TemplateTool (BaseTool):
return self[self._setObject(id, bt)] return self[self._setObject(id, bt)]
bt = self._download_url(url, id) bt = self._download_url(url, id)
else: else:
bt = self._download_local(name, id) bt = self._download_local(os.path.normpath(name), id)
bt.build(no_action=True) bt.build(no_action=True)
return bt return bt
...@@ -687,9 +565,12 @@ class TemplateTool (BaseTool): ...@@ -687,9 +565,12 @@ class TemplateTool (BaseTool):
'updateRepositoryBusinessTemplateList' ) 'updateRepositoryBusinessTemplateList' )
def updateRepositoryBusinessTemplateList(self, repository_list, def updateRepositoryBusinessTemplateList(self, repository_list,
REQUEST=None, RESPONSE=None, **kw): REQUEST=None, RESPONSE=None, genbt5list=0, **kw):
""" """
Update the information on Business Templates from repositories. Update the information on Business Templates from repositories.
For local repositories, if bt5list is missing or if genbt5list > 1,
bt5list is automatically generated (but not saved on disk).
""" """
self.repository_dict = PersistentMapping() self.repository_dict = PersistentMapping()
property_list = ('title', 'version', 'revision', 'description', 'license', property_list = ('title', 'version', 'revision', 'description', 'license',
...@@ -697,9 +578,19 @@ class TemplateTool (BaseTool): ...@@ -697,9 +578,19 @@ class TemplateTool (BaseTool):
#LOG('updateRepositoryBusiessTemplateList', 0, #LOG('updateRepositoryBusiessTemplateList', 0,
# 'repository_list = %r' % (repository_list,)) # 'repository_list = %r' % (repository_list,))
for repository in repository_list: for repository in repository_list:
url = '/'.join([repository, 'bt5list']) urltype, url = splittype(repository)
f = urlopen(url) if WIN and urltype and '\\' in url:
property_dict_list = [] urltype = None
url = repository
if urltype and urltype != 'file':
f = urlopen(repository + '/bt5list')
else:
bt5list = os.path.join(url, 'bt5list')
if genbt5list > os.path.exists(bt5list):
f = generateInformation(url)
f.seek(0)
else:
f = open(bt5list, 'rb')
try: try:
try: try:
doc = parse(f) doc = parse(f)
...@@ -713,6 +604,7 @@ class TemplateTool (BaseTool): ...@@ -713,6 +604,7 @@ class TemplateTool (BaseTool):
else: else:
raise RuntimeError, 'Invalid repository: %s' % repository raise RuntimeError, 'Invalid repository: %s' % repository
try: try:
property_dict_list = []
root = doc.documentElement root = doc.documentElement
for template in root.getElementsByTagName("template"): for template in root.getElementsByTagName("template"):
id = template.getAttribute('id') id = template.getAttribute('id')
...@@ -1060,9 +952,6 @@ class TemplateTool (BaseTool): ...@@ -1060,9 +952,6 @@ class TemplateTool (BaseTool):
update_only: return only bt that needs to be updated update_only: return only bt that needs to be updated
template_list: only returns bt within the given list template_list: only returns bt within the given list
""" """
version_state_title_dict = { 'new' : 'New', 'present' : 'Present',
'old' : 'Old' }
from Products.ERP5Type.Document import newTempBusinessTemplate from Products.ERP5Type.Document import newTempBusinessTemplate
result_list = [] result_list = []
template_set = None template_set = None
...@@ -1089,14 +978,8 @@ class TemplateTool (BaseTool): ...@@ -1089,14 +978,8 @@ class TemplateTool (BaseTool):
# if this business template is newer. # if this business template is newer.
previous_repository, previous_property_dict = \ previous_repository, previous_property_dict = \
template_item_dict[title] template_item_dict[title]
diff_version = self.compareVersions(previous_property_dict['version'], if self.compareVersions(previous_property_dict['version'],
property_dict['version']) property_dict['version']) < 0:
if diff_version < 0:
template_item_dict[title] = (repository, property_dict)
elif diff_version == 0 \
and previous_property_dict['revision'] \
and property_dict['revision'] \
and int(previous_property_dict['revision']) < int(property_dict['revision']):
template_item_dict[title] = (repository, property_dict) template_item_dict[title] = (repository, property_dict)
# Next, select only updated business templates. # Next, select only updated business templates.
if update_only: if update_only:
...@@ -1109,9 +992,8 @@ class TemplateTool (BaseTool): ...@@ -1109,9 +992,8 @@ class TemplateTool (BaseTool):
if diff_version < 0: if diff_version < 0:
template_item_list.append((repository, property_dict)) template_item_list.append((repository, property_dict))
elif diff_version == 0 \ elif diff_version == 0 \
and installed_bt.getRevision() \
and property_dict['revision'] \ and property_dict['revision'] \
and int(installed_bt.getRevision()) < int(property_dict['revision']): and installed_bt.getRevision() != property_dict['revision']:
template_item_list.append((repository, property_dict)) template_item_list.append((repository, property_dict))
elif template_list is not None: elif template_list is not None:
template_item_list.append((repository, property_dict)) template_item_list.append((repository, property_dict))
...@@ -1119,29 +1001,24 @@ class TemplateTool (BaseTool): ...@@ -1119,29 +1001,24 @@ class TemplateTool (BaseTool):
# Create temporary Business Template objects for displaying. # Create temporary Business Template objects for displaying.
for repository, property_dict in template_item_list: for repository, property_dict in template_item_list:
property_dict = property_dict.copy() property_dict = property_dict.copy()
id = property_dict['id'] id = filename = property_dict.pop('id')
filename = property_dict['id']
del property_dict['id']
revision = property_dict['revision']
version_state = 'new'
installed_bt = \ installed_bt = \
self.getInstalledBusinessTemplate(property_dict['title']) self.getInstalledBusinessTemplate(property_dict['title'])
if installed_bt is not None: if installed_bt is not None:
installed_version = installed_bt.getVersion() installed_version = installed_bt.getVersion()
installed_revision = installed_bt.getRevision() installed_revision = installed_bt.getShortRevision()
result = self.compareVersions(installed_revision, revision) if installed_bt.getRevision() == property_dict['revision']:
if result == 0:
version_state = 'present' version_state = 'present'
elif result < 0: else:
version_state = 'old' version_state = 'different'
else: else:
installed_version = '' installed_version = ''
installed_revision = '' installed_revision = ''
version_state_title = version_state_title_dict[version_state] version_state = 'new'
uid = self.encodeRepositoryBusinessTemplateUid(repository, id) uid = self.encodeRepositoryBusinessTemplateUid(repository, id)
obj = newTempBusinessTemplate(self, 'temp_' + uid, obj = newTempBusinessTemplate(self, 'temp_' + uid,
version_state = version_state, version_state = version_state,
version_state_title = version_state_title, version_state_title=version_state.title(),
filename = filename, filename = filename,
installed_version = installed_version, installed_version = installed_version,
installed_revision = installed_revision, installed_revision = installed_revision,
...@@ -1206,10 +1083,9 @@ class TemplateTool (BaseTool): ...@@ -1206,10 +1083,9 @@ class TemplateTool (BaseTool):
return 0 return 0
def _getBusinessTemplateUrlDict(self, newest_only=False): def _getBusinessTemplateUrlDict(self):
business_template_url_dict = {} business_template_url_dict = {}
for bt in self.getRepositoryBusinessTemplateList(\ for bt in self.getRepositoryBusinessTemplateList():
newest_only=newest_only):
url, name = self.decodeRepositoryBusinessTemplateUid(bt.getUid()) url, name = self.decodeRepositoryBusinessTemplateUid(bt.getUid())
if name.endswith('.bt5'): if name.endswith('.bt5'):
name = name[:-4] name = name[:-4]
...@@ -1221,14 +1097,11 @@ class TemplateTool (BaseTool): ...@@ -1221,14 +1097,11 @@ class TemplateTool (BaseTool):
security.declareProtected(Permissions.ManagePortal, security.declareProtected(Permissions.ManagePortal,
'installBusinessTemplatesFromRepositories') 'installBusinessTemplatesFromRepositories')
def installBusinessTemplatesFromRepositories(self, template_list, def installBusinessTemplatesFromRepositories(self, *args, **kw):
only_newer=True, update_catalog=_MARKER, activate=False,
install_dependency=False):
"""Deprecated. """Deprecated.
""" """
DeprecationWarning('installBusinessTemplatesFromRepositories is deprecated; Use self.installBusinessTemplateListFromRepository instead.', DeprecationWarning) DeprecationWarning('installBusinessTemplatesFromRepositories is deprecated; Use self.installBusinessTemplateListFromRepository instead.', DeprecationWarning)
return self.installBusinessTemplateListFromRepository(template_list, return self.installBusinessTemplateListFromRepository(*args, **kw)
only_newer, update_catalog, activate, install_dependency)
security.declareProtected(Permissions.ManagePortal, security.declareProtected(Permissions.ManagePortal,
'resolveBusinessTemplateListDependency') 'resolveBusinessTemplateListDependency')
...@@ -1293,7 +1166,7 @@ class TemplateTool (BaseTool): ...@@ -1293,7 +1166,7 @@ class TemplateTool (BaseTool):
security.declareProtected(Permissions.ManagePortal, security.declareProtected(Permissions.ManagePortal,
'installBusinessTemplateListFromRepository') 'installBusinessTemplateListFromRepository')
def installBusinessTemplateListFromRepository(self, template_list, def installBusinessTemplateListFromRepository(self, template_list,
only_newer=True, update_catalog=_MARKER, activate=False, only_different=True, update_catalog=_MARKER, activate=False,
install_dependency=False): install_dependency=False):
"""Installs template_list from configured repositories by default only newest""" """Installs template_list from configured repositories by default only newest"""
# XXX-Luke: This method could replace # XXX-Luke: This method could replace
...@@ -1303,12 +1176,13 @@ class TemplateTool (BaseTool): ...@@ -1303,12 +1176,13 @@ class TemplateTool (BaseTool):
operation_log = [] operation_log = []
resolved_template_list = self.resolveBusinessTemplateListDependency( resolved_template_list = self.resolveBusinessTemplateListDependency(
template_list) template_list)
installed_bt5_dict = dict((x.getTitle(), x.getRevision())
installed_bt5_set = set([x.title for x in self.getInstalledBusinessTemplateList())
for x in self.getInstalledBusinessTemplatesList()]) if only_different:
template_url_dict = self._getBusinessTemplateUrlDict()
def checkAvailability(bt_title): def checkAvailability(bt_title):
return bt_title in template_list or bt_title in installed_bt5_set return bt_title in template_list or bt_title in installed_bt5_dict
missing_dependency_list = [i for i in resolved_template_list missing_dependency_list = [i for i in resolved_template_list
if not checkAvailability(i[1].replace(".bt5", ""))] if not checkAvailability(i[1].replace(".bt5", ""))]
...@@ -1317,17 +1191,14 @@ class TemplateTool (BaseTool): ...@@ -1317,17 +1191,14 @@ class TemplateTool (BaseTool):
"Impossible to install, please install the following dependencies before: %s" \ "Impossible to install, please install the following dependencies before: %s" \
% [x[1] for x in missing_dependency_list] % [x[1] for x in missing_dependency_list]
template_url_dict = self._getBusinessTemplateUrlDict()
activate_kw = dict(activity="SQLQueue", tag="start_%s" % (time.time())) activate_kw = dict(activity="SQLQueue", tag="start_%s" % (time.time()))
for repository, bt_id in resolved_template_list: for repository, bt_id in resolved_template_list:
if only_different:
bt = template_url_dict.get(bt_id) bt = template_url_dict.get(bt_id)
if bt is not None and bt_id in installed_bt5_set: if bt is not None and bt['revision'] == installed_bt5_dict.get(bt_id):
revision = int(bt['revision'])
installed_bt5 = self.getInstalledBusinessTemplate(bt_id)
if int(installed_bt5.getRevision()) <= revision and only_newer:
continue continue
bt_url = '%s/%s' % (repository, bt_id) bt_url = '%s/%s' % (repository, bt_id)
param_dict = dict(download_url=bt_url, only_newer=only_newer) param_dict = dict(download_url=bt_url, only_different=only_different)
if update_catalog is not _MARKER: if update_catalog is not _MARKER:
param_dict["update_catalog"] = update_catalog param_dict["update_catalog"] = update_catalog
...@@ -1340,7 +1211,7 @@ class TemplateTool (BaseTool): ...@@ -1340,7 +1211,7 @@ class TemplateTool (BaseTool):
else: else:
document = self.updateBusinessTemplateFromUrl(**param_dict) document = self.updateBusinessTemplateFromUrl(**param_dict)
operation_log.append('Installed %s with revision %s' % ( operation_log.append('Installed %s with revision %s' % (
document.getTitle(), document.getRevision())) document.getTitle(), document.getShortRevision()))
return operation_log return operation_log
...@@ -1354,7 +1225,7 @@ class TemplateTool (BaseTool): ...@@ -1354,7 +1225,7 @@ class TemplateTool (BaseTool):
reinstall=False, reinstall=False,
active_process=None, active_process=None,
force_keep_list=None, force_keep_list=None,
only_newer=True): only_different=True):
""" """
This method download and install a bt5, from a URL. This method download and install a bt5, from a URL.
...@@ -1390,18 +1261,13 @@ class TemplateTool (BaseTool): ...@@ -1390,18 +1261,13 @@ class TemplateTool (BaseTool):
if reinstall: if reinstall:
install_kw = None install_kw = None
else: else:
if only_different:
previous_bt5 = self.getInstalledBusinessTemplate(bt_title) previous_bt5 = self.getInstalledBusinessTemplate(bt_title)
if (previous_bt5 is not None) and only_newer: if previous_bt5 and \
try: imported_bt5.getRevision() == previous_bt5.getRevision():
imported_revision = int(imported_bt5.getRevision()) log("%s is already installed with revision %s"
previous_revision = int(previous_bt5.getRevision()) % (bt_title, imported_bt5.getShortRevision()))
if imported_revision <= previous_revision:
log("%s is already installed with revision %i, which is same or "
"newer revision than new revision %i." % (bt_title,
previous_revision, imported_revision))
return imported_bt5 return imported_bt5
except ValueError:
pass
install_kw = {} install_kw = {}
for listbox_line in imported_bt5.BusinessTemplate_getModifiedObject(): for listbox_line in imported_bt5.BusinessTemplate_getModifiedObject():
......
...@@ -31,130 +31,200 @@ ...@@ -31,130 +31,200 @@
"""Generate repository information on Business Templates. """Generate repository information on Business Templates.
""" """
import posixpath
import tarfile import tarfile
import os import os
import sys import sys
import tempfile
import shutil
import cgi import cgi
from base64 import b64encode
property_list = ''' from cStringIO import StringIO
from hashlib import sha1
from urllib import unquote
# Order is important for installation
# We want to have:
# * workflow and portal_type* before ZODB Component {Document,Extension...}
# * path after module, because path can be module content
# * path after categories, because path can be categories content
# * path after portal types roles so that roles in the current bt can be used
# * path before workflow chain, because path can be a portal type
# (until chains are set on portal types with categories)
# * skin after paths, because we can install a custom connection string as
# path and use it with SQLMethods in a skin.
# ( and more )
item_name_list = (
'registered_version_priority_selection',
'workflow',
'product',
'document',
'property_sheet',
'constraint',
'extension',
'test',
'role',
'tool',
'message_translation',
'site_property',
'portal_type',
'portal_type_allowed_content_type',
'portal_type_hidden_content_type',
'portal_type_property_sheet',
'portal_type_base_category',
'category',
'module',
'portal_type_roles',
'path',
'skin',
'registered_skin_selection',
'preference',
'action',
'local_roles',
'portal_type_workflow_chain',
'catalog_method',
'catalog_result_key',
'catalog_related_key',
'catalog_result_table',
'catalog_search_key',
'catalog_keyword_key',
'catalog_datetime_key',
'catalog_full_text_key',
'catalog_request_key',
'catalog_multivalue_key',
'catalog_topic_key',
'catalog_scriptable_key',
'catalog_role_key',
'catalog_local_role_key',
'catalog_security_uid_column',
)
item_set = set(('CatalogDateTimeKey' if x == 'catalog_datetime_key' else
''.join(map(str.title, x.split('_')))) + 'TemplateItem'
for x in item_name_list)
item_set.add('bt')
item_name_list = tuple('_%s_item' % x for x in item_name_list)
class BusinessTemplateRevision(list):
def hash(self, path, text):
self.append((path, sha1(text).digest()))
def digest(self):
self.sort()
return b64encode(sha1('\0'.join(h + p for (h, p) in self)).digest())
class BusinessTemplate(dict):
property_list = frozenset('''
title title
version version
revision
description description
license license
dependency_list dependency_list
test_dependency_list test_dependency_list
provision_list provision_list
copyright_list copyright_list
'''.strip().splitlines() '''.split())
bt_title_path = os.path.join('bt', 'title')
def info(message): def __init__(self):
"""Print a message to stdout. self.revision = BusinessTemplateRevision()
"""
sys.stdout.write(message)
def err(message): def _read(self, path, file):
"""Print a message to stderr.
"""
sys.stderr.write(message)
def readProperty(property_dict, property_name, property_file):
try: try:
text = property_file.read() text = file.read()
if property_name.endswith('_list'):
property_dict[property_name[:-5]] = text.splitlines()
else:
property_dict[property_name] = text
finally: finally:
property_file.close() file.close()
if path.startswith('bt/'):
def readBusinessTemplate(tar): name = path[3:]
"""Read an archived Business Template info. if name in self.property_list:
""" if name.endswith('_list'):
property_dict = {} self[name[:-5]] = text.splitlines()
else:
self[name] = text
elif name == 'revision':
return
self.revision.hash(unquote(path) if '%' in path else path, text)
def __iter__(self):
self['revision'] = self.revision.digest()
return iter(sorted(self.iteritems()))
@classmethod
def fromTar(cls, tar):
"""Read an archived Business Template info"""
self = cls()
for info in tar: for info in tar:
name_list = info.name.split('/') if not info.isdir():
if len(name_list) == 3 and name_list[1] == 'bt' and name_list[2] in property_list: name = info.name.split('/', 1)[1]
property_file = tar.extractfile(info) if name.split('/', 1)[0] in item_set:
property_name = name_list[2] self._read(name, tar.extractfile(info))
readProperty(property_dict, property_name, property_file) return iter(self)
return property_dict @classmethod
def fromDir(cls, dir):
def readBusinessTemplateDirectory(dir): """Read Business Template Directory info"""
"""Read Business Template Directory info. self = cls()
""" lstrip_len = len(dir + os.sep)
property_dict = {} for root, dirs, files in os.walk(dir):
for property_name in property_list: if root:
filename = os.path.join(dir, 'bt', property_name) for path in files:
if os.path.isfile(filename): path = os.path.join(root, path)
property_file = open(filename, 'rb') self._read(posixpath.normpath(path[lstrip_len:]), open(path, 'rb'))
readProperty(property_dict, property_name, property_file) else:
dirs[:] = item_set.intersection(dirs)
return property_dict return iter(self)
def generateInformation(fd): def generateInformation(dir, info=id, err=None):
os.write(fd, '<?xml version="1.0"?>\n') xml = StringIO()
os.write(fd, '<repository>\n') xml.write('<?xml version="1.0"?>\n<repository>\n')
for name in sorted(os.listdir(dir)):
for file in sorted(os.listdir(os.getcwd())): path = os.path.join(dir, name)
if file.endswith('.bt5'): if name.endswith('.bt5'):
info('Reading %s... ' % (file,)) info('Reading %s... ' % name)
try: try:
tar = tarfile.open(file, 'r:gz') tar = tarfile.open(path, 'r:gz')
except tarfile.TarError: except tarfile.TarError:
err('An error happened in %s; skipping\n' % (file,)) if err:
err('An error happened in %s; skipping\n' % name)
continue continue
raise
try: try:
property_dict = readBusinessTemplate(tar) property_list = BusinessTemplate.fromTar(tar)
finally: finally:
tar.close() tar.close()
elif os.path.isfile(os.path.join(file, bt_title_path)): elif os.path.isfile(os.path.join(path, 'bt', 'title')):
info('Reading Directory %s... ' % (file,)) info('Reading Directory %s... ' % name)
property_dict = readBusinessTemplateDirectory(file) property_list = BusinessTemplate.fromDir(path)
else: else:
continue continue
os.write(fd, ' <template id="%s">\n' % (file,)) xml.write(' <template id="%s">\n' % name)
for property_id, property_value in sorted(property_dict.items()): for k, v in property_list:
if type(property_value) is str: for v in (v,) if type(v) is str else v:
os.write(fd, ' <%s>%s</%s>\n' % ( xml.write(' <%s>%s</%s>\n' % (k, cgi.escape(v), k))
property_id, cgi.escape(property_value), property_id)) xml.write(' </template>\n')
else:
for value in property_value:
os.write(fd, ' <%s>%s</%s>\n' % (
property_id, cgi.escape(value), property_id))
os.write(fd, ' </template>\n')
info('done\n') info('done\n')
os.write(fd, '</repository>\n') xml.write('</repository>\n')
return xml
def main(): def main(dir_list=None, **kw):
if len(sys.argv) < 2: if dir_list is None:
dir_list = ['.'] kw.setdefault('info', sys.stdout.write)
else: kw.setdefault('err', sys.stderr.write)
dir_list = sys.argv[1:] dir_list = sys.argv[1:] or '.'
cur_umask = os.umask(0666)
os.umask(cur_umask)
cwd = os.getcwd()
for d in dir_list: for d in dir_list:
os.chdir(d) bt5list = generateInformation(d, **kw).getvalue()
try: d = os.path.join(d, 'bt5list.new')
fd, path = tempfile.mkstemp(dir='.')
try: try:
generateInformation(fd) with open(d, 'wb') as f:
os.fchmod(fd, 0666 & ~cur_umask) f.write(bt5list)
os.rename(path, 'bt5list') os.rename(d, d[:-4])
except:
os.remove(path)
raise
finally:
os.close(fd)
finally: finally:
os.chdir(cwd) try:
os.remove(d)
except OSError:
pass
if __name__ == "__main__": if __name__ == "__main__":
main() main()
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
<string>my_id</string> <string>my_id</string>
<string>my_title</string> <string>my_title</string>
<string>my_version</string> <string>my_version</string>
<string>my_revision</string> <string>my_short_revision</string>
<string>my_translated_building_state_title</string> <string>my_translated_building_state_title</string>
<string>my_translated_installation_state_title</string> <string>my_translated_installation_state_title</string>
<string>my_description</string> <string>my_description</string>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="StringField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_revision</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
<item>
<key> <string>too_long</string> </key>
<value> <string>Too much input was given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>truncate</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>truncate</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>the number of revision used by the business template. This number increases each time we commit a modification</string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Revision Number</string> </value>
</item>
<item>
<key> <string>truncate</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>description</string>
<string>editable</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_short_revision</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string>A shortened hash of the contents of the Business Template. The hash is computed at download, build and export.</string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Revision Number</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -349,7 +349,7 @@ ...@@ -349,7 +349,7 @@
<string>Version</string> <string>Version</string>
</tuple> </tuple>
<tuple> <tuple>
<string>revision</string> <string>short_revision</string>
<string>Revision</string> <string>Revision</string>
</tuple> </tuple>
<tuple> <tuple>
......
...@@ -340,7 +340,7 @@ ...@@ -340,7 +340,7 @@
<string>Version</string> <string>Version</string>
</tuple> </tuple>
<tuple> <tuple>
<string>revision</string> <string>short_revision</string>
<string>Revision</string> <string>Revision</string>
</tuple> </tuple>
<tuple> <tuple>
......
...@@ -343,14 +343,14 @@ ...@@ -343,14 +343,14 @@
<string>version</string> <string>version</string>
<string>Version</string> <string>Version</string>
</tuple> </tuple>
<tuple>
<string>revision</string>
<string>Revision</string>
</tuple>
<tuple> <tuple>
<string>installed_version</string> <string>installed_version</string>
<string>Installed Version</string> <string>Installed Version</string>
</tuple> </tuple>
<tuple>
<string>short_revision</string>
<string>Revision</string>
</tuple>
<tuple> <tuple>
<string>installed_revision</string> <string>installed_revision</string>
<string>Installed Revision</string> <string>Installed Revision</string>
...@@ -367,10 +367,6 @@ ...@@ -367,10 +367,6 @@
<string>license</string> <string>license</string>
<string>License</string> <string>License</string>
</tuple> </tuple>
<tuple>
<string>version_state_title</string>
<string>State</string>
</tuple>
</list> </list>
</value> </value>
</item> </item>
......
bin/genbt5list
\ No newline at end of file
...@@ -2847,24 +2847,6 @@ class BusinessTemplateMixin(ERP5TypeTestCase, LogInterceptor): ...@@ -2847,24 +2847,6 @@ class BusinessTemplateMixin(ERP5TypeTestCase, LogInterceptor):
self.assertTrue(base_category_obj is not None) self.assertTrue(base_category_obj is not None)
self.assertEqual(len(base_category_obj.objectIds()), 0) self.assertEqual(len(base_category_obj.objectIds()), 0)
def stepCheckInitialRevision(self, sequence=None, **kw):
""" Check if revision of a new bt is an empty string
"""
bt = sequence.get('current_bt')
self.assertEqual(bt.getRevision(), '')
def stepCheckFirstRevision(self, sequence=None, **kw):
""" Check if revision of the bt is 1
"""
bt = sequence.get('current_bt')
self.assertEqual(bt.getRevision(), '1')
def stepCheckSecondRevision(self, sequence=None, **kw):
""" Check if revision of the bt is 2
"""
bt = sequence.get('current_bt')
self.assertEqual(bt.getRevision(), '2')
def stepCheckNoMissingDependencies(self, sequence=None, **kw): def stepCheckNoMissingDependencies(self, sequence=None, **kw):
""" Check if bt has no missing dependency """ Check if bt has no missing dependency
""" """
...@@ -5149,26 +5131,6 @@ class TestBusinessTemplate(BusinessTemplateMixin): ...@@ -5149,26 +5131,6 @@ class TestBusinessTemplate(BusinessTemplateMixin):
sequence_list.addSequenceString(sequence_string) sequence_list.addSequenceString(sequence_string)
sequence_list.play(self) sequence_list.play(self)
# test of portal types
def test_22_RevisionNumberIsIncremented(self):
"""Test is revision number is incremented with the bt is built"""
sequence_list = SequenceList()
sequence_string = '\
CreatePortalType \
CreateNewBusinessTemplate \
UseExportBusinessTemplate \
CheckInitialRevision \
BuildBusinessTemplate \
CheckBuiltBuildingState \
stepCheckFirstRevision \
BuildBusinessTemplate \
stepCheckSecondRevision \
RemoveBusinessTemplate \
RemovePortalType \
'
sequence_list.addSequenceString(sequence_string)
sequence_list.play(self)
def test_23_CheckNoDependencies(self): def test_23_CheckNoDependencies(self):
"""Test if a new Business Template has no dependencies""" """Test if a new Business Template has no dependencies"""
sequence_list = SequenceList() sequence_list = SequenceList()
......
...@@ -31,7 +31,7 @@ import os ...@@ -31,7 +31,7 @@ import os
import shutil import shutil
import unittest import unittest
import random import random
import transaction import tempfile
from App.config import getConfiguration from App.config import getConfiguration
from Products.ERP5VCS.WorkingCopy import getVcsTool from Products.ERP5VCS.WorkingCopy import getVcsTool
...@@ -99,32 +99,23 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -99,32 +99,23 @@ class TestTemplateTool(ERP5TypeTestCase):
def testUpdateBT5FromRepository(self, quiet=quiet, run=run_all_test): def testUpdateBT5FromRepository(self, quiet=quiet, run=run_all_test):
""" Test the list of bt5 returned for upgrade """ """ Test the list of bt5 returned for upgrade """
# edit bt5 revision so that it will be marked as updatable # edit bt5 revision so that it will be marked as updatable
bt_list = self.templates_tool.searchFolder(title='erp5_base') erp5_base = self.templates_tool.getInstalledBusinessTemplate('erp5_base',
self.assertEqual(len(bt_list), 1) strict=True)
erp5_base = bt_list[0].getObject() erp5_base._setRevision('')
try:
erp5_base.edit(revision=0)
updatable_bt_list = \ self.assertTrue("erp5_base" in (bt.getTitle() for bt in
self.templates_tool.getRepositoryBusinessTemplateList(update_only=True) self.templates_tool.getRepositoryBusinessTemplateList(update_only=True)))
self.assertEqual(
[i.title for i in updatable_bt_list if i.title == "erp5_base"],
["erp5_base"])
erp5_base.replace() erp5_base.replace()
updatable_bt_list = \ self.assertFalse("erp5_base" in (bt.getTitle() for bt in
self.templates_tool.getRepositoryBusinessTemplateList(update_only=True) self.templates_tool.getRepositoryBusinessTemplateList(update_only=True)))
self.assertEqual( self.abort()
[i.title for i in updatable_bt_list if i.title == "erp5_base"],
[])
finally:
erp5_base.edit(revision=int(erp5_base.getRevision()) + 10)
def test_download_http(self): def test_download_http(self):
test_web = self.portal.portal_templates.download( test_web = self.portal.portal_templates.download(
'http://www.erp5.org/dists/snapshot/test_bt5/test_web.bt5') 'http://www.erp5.org/dists/snapshot/test_bt5/test_web.bt5')
self.assertEqual(test_web.getPortalType(), 'Business Template') self.assertEqual(test_web.getPortalType(), 'Business Template')
self.assertEqual(test_web.getTitle(), 'test_web') self.assertEqual(test_web.getTitle(), 'test_web')
self.assertTrue(test_web.getRevision()) self.assertEqual(len(test_web.getRevision()), 28)
def _svn_setup_ssl(self): def _svn_setup_ssl(self):
""" """
...@@ -148,20 +139,20 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -148,20 +139,20 @@ class TestTemplateTool(ERP5TypeTestCase):
test_web = self.portal.portal_templates.download(bt5_url) test_web = self.portal.portal_templates.download(bt5_url)
self.assertEqual(test_web.getPortalType(), 'Business Template') self.assertEqual(test_web.getPortalType(), 'Business Template')
self.assertEqual(test_web.getTitle(), 'test_web') self.assertEqual(test_web.getTitle(), 'test_web')
self.assertTrue(test_web.getRevision()) self.assertEqual(len(test_web.getRevision()), 28)
def test_updateBusinessTemplateFromUrl_simple(self): def test_updateBusinessTemplateFromUrl_simple(self):
""" """
Test updateBusinessTemplateFromUrl method Test updateBusinessTemplateFromUrl method
By default if a new business template has revision >= previous one By default if a new business template has revision != previous one
the new bt5 is not installed, only imported. the new bt5 is not installed, only imported.
""" """
self._svn_setup_ssl() self._svn_setup_ssl()
template_tool = self.portal.portal_templates template_tool = self.portal.portal_templates
old_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style') old_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style')
# change revision to an old revision # fake different revision
old_bt.setRevision(0.0001) old_bt.setRevision('')
url = 'https://svn.erp5.org/repos/public/erp5/trunk/bt5/erp5_csv_style' url = 'https://svn.erp5.org/repos/public/erp5/trunk/bt5/erp5_csv_style'
template_tool.updateBusinessTemplateFromUrl(url) template_tool.updateBusinessTemplateFromUrl(url)
new_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style') new_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style')
...@@ -170,7 +161,7 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -170,7 +161,7 @@ class TestTemplateTool(ERP5TypeTestCase):
# Test Another time with definning an ID # Test Another time with definning an ID
old_bt = new_bt old_bt = new_bt
old_bt.setRevision(0.0002) old_bt.setRevision('')
template_tool.updateBusinessTemplateFromUrl(url, id="new_erp5_csv_style") template_tool.updateBusinessTemplateFromUrl(url, id="new_erp5_csv_style")
new_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style') new_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style')
self.assertNotEquals(old_bt, new_bt) self.assertNotEquals(old_bt, new_bt)
...@@ -184,8 +175,7 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -184,8 +175,7 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertEqual(old_bt, new_bt) self.assertEqual(old_bt, new_bt)
self.assertEqual('erp5_csv_style', new_bt.getTitle()) self.assertEqual('erp5_csv_style', new_bt.getTitle())
self.assertEqual('new_erp5_csv_style', new_bt.getId()) self.assertEqual('new_erp5_csv_style', new_bt.getId())
not_installed_bt5 = getattr(template_tool, "not_installed_bt5", None) not_installed_bt5 = template_tool['not_installed_bt5']
self.assertNotEquals(not_installed_bt5, None)
self.assertEqual('erp5_csv_style', not_installed_bt5.getTitle()) self.assertEqual('erp5_csv_style', not_installed_bt5.getTitle())
self.assertEqual(not_installed_bt5.getInstallationState(), self.assertEqual(not_installed_bt5.getInstallationState(),
"not_installed") "not_installed")
...@@ -204,10 +194,8 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -204,10 +194,8 @@ class TestTemplateTool(ERP5TypeTestCase):
keep_original_list=keep_original_list) keep_original_list=keep_original_list)
bt = template_tool.getInstalledBusinessTemplate('test_core') bt = template_tool.getInstalledBusinessTemplate('test_core')
self.assertNotEquals(None, bt) self.assertNotEquals(None, bt)
erp5_test = getattr(self.portal.portal_skins, 'erp5_test', None) erp5_test = self.portal.portal_skins['erp5_test']
self.assertNotEquals(None, erp5_test) self.assertFalse(erp5_test.hasObject('test_file'))
test_file = getattr(erp5_test, 'test_file', None)
self.assertEqual(None, test_file)
def test_updateBusinessTemplateFromUrl_after_before_script(self): def test_updateBusinessTemplateFromUrl_after_before_script(self):
""" """
...@@ -248,48 +236,6 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -248,48 +236,6 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertEqual(bt.getChangeLog(), 'MODIFIED') self.assertEqual(bt.getChangeLog(), 'MODIFIED')
self.assertEqual(portal.getTitle(), 'MODIFIED') self.assertEqual(portal.getTitle(), 'MODIFIED')
def test_updateBusinessTemplateFromUrl_stringCastingBug(self):
pt = self.getTemplateTool()
template = pt.newContent(portal_type='Business Template')
self.assertTrue(template.getBuildingState() == 'draft')
self.assertTrue(template.getInstallationState() == 'not_installed')
title = 'install_casting_to_int_bug_check'
template.edit(title=title,
version='1.0',
description='bt for unit_test')
self.commit()
template.build()
self.commit()
cfg = getConfiguration()
template_path = os.path.join(cfg.instancehome, 'tests', '%s' % (title,))
# remove previous version of bt it exists
if os.path.exists(template_path):
shutil.rmtree(template_path)
template.export(path=template_path, local=1)
self.assertTrue(os.path.exists(template_path))
# setup version '9'
first_revision = '9'
open(os.path.join(template_path, 'bt', 'revision'), 'w').write(first_revision)
pt.updateBusinessTemplateFromUrl(template_path)
new_bt = pt.getInstalledBusinessTemplate(title)
self.assertEqual(new_bt.getRevision(), first_revision)
# setup revision '11', becasue: '11' < '9' (string comp), but 11 > 9 (int comp)
second_revision = '11'
self.assertTrue(second_revision < first_revision)
self.assertTrue(int(second_revision) > int(first_revision))
open(os.path.join(template_path, 'bt', 'revision'), 'w').write(second_revision)
pt.updateBusinessTemplateFromUrl(template_path)
newer_bt = pt.getInstalledBusinessTemplate(title)
self.assertNotEqual(new_bt, newer_bt)
self.assertEqual(newer_bt.getRevision(), second_revision)
def test_CompareVersions(self): def test_CompareVersions(self):
"""Tests compare version on template tool. """ """Tests compare version on template tool. """
compareVersions = self.getPortal().portal_templates.compareVersions compareVersions = self.getPortal().portal_templates.compareVersions
...@@ -320,12 +266,45 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -320,12 +266,45 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertEqual(None, self.getPortal()\ self.assertEqual(None, self.getPortal()\
.portal_templates.getInstalledBusinessTemplate('erp5_toto')) .portal_templates.getInstalledBusinessTemplate('erp5_toto'))
def test_getInstalledBusinessTemplateRevision(self): def test_revision(self):
self.assertTrue(300 < self.getPortal()\ template_tool = self.portal.portal_templates
.portal_templates.getInstalledBusinessTemplateRevision('erp5_core')) getInstalledRevision = template_tool.getInstalledBusinessTemplateRevision
self.assertEqual(None, getInstalledRevision('erp5_toto'))
self.assertEqual(None, self.getPortal()\ available_bt, = template_tool.getRepositoryBusinessTemplateList(
.portal_templates.getInstalledBusinessTemplateRevision('erp5_toto')) template_list=('test_core',))
revision = available_bt.getRevision()
self.assertEqual('PN8VPt52MbdHtxfjKvL+MBsNbzM=', revision)
installed_bt = template_tool.download("%s/%s" % (available_bt.repository,
available_bt.filename))
self.assertEqual(revision, installed_bt.getRevision())
installed_bt.install()
self.assertEqual(revision, getInstalledRevision('test_core'))
bt = installed_bt.Base_createCloneDocument(batch_mode=1)
bt.build(update_revision=False)
root = tempfile.mkdtemp()
try:
bt.export(root, local=1)
with open(os.path.join(root, 'bt', 'title')) as f:
self.assertTrue('test_core', f.read())
# We don't export revision anymore.
self.assertFalse(os.path.exists(os.path.join(root, 'bt', 'revision')))
# Computed at download ...
self.assertEqual(revision, template_tool.download(root).getRevision())
finally:
shutil.rmtree(root)
bt._setVersion("2.0")
# ... at building by default ...
bt.build()
revision = bt.getRevision()
self.assertEqual('tPNr/gGXaa0fYCsFUWe8nqzSNLc=', revision)
self.portal.portal_skins.erp5_test.manage_renameObject('test_file',
'test_file2')
bt.build(update_revision=False)
self.assertEqual(revision, bt.getRevision())
# ... and at export.
bt.export(str(random.random()))
self.assertEqual('Nup/xsO1xpsmdJ5GTdknuVJyOr8=', bt.getRevision())
self.abort()
def test_getInstalledBusinessTemplateList(self): def test_getInstalledBusinessTemplateList(self):
templates_tool = self.getPortal().portal_templates templates_tool = self.getPortal().portal_templates
...@@ -479,9 +458,9 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -479,9 +458,9 @@ class TestTemplateTool(ERP5TypeTestCase):
bt_old = self.templates_tool.getInstalledBusinessTemplate(bt5_name, strict=True) bt_old = self.templates_tool.getInstalledBusinessTemplate(bt5_name, strict=True)
self.assertEqual(bt.getId(), bt_old.getId()) self.assertEqual(bt.getId(), bt_old.getId())
# Repeat operation, new bt5 should be inslalled due only_newer = False # Repeat operation, new bt5 should be inslalled due only_different = False
operation_log = self.templates_tool.installBusinessTemplateListFromRepository( operation_log = self.templates_tool.installBusinessTemplateListFromRepository(
[bt5_name], only_newer=False) [bt5_name], only_different=False)
self.assertTrue("Installed %s with" % bt5_name in operation_log[-1]) self.assertTrue("Installed %s with" % bt5_name in operation_log[-1])
bt_new = self.templates_tool.getInstalledBusinessTemplate(bt5_name, bt_new = self.templates_tool.getInstalledBusinessTemplate(bt5_name,
...@@ -498,7 +477,7 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -498,7 +477,7 @@ class TestTemplateTool(ERP5TypeTestCase):
bt = template_tool.getInstalledBusinessTemplate(bt5_name) bt = template_tool.getInstalledBusinessTemplate(bt5_name)
self.assertEqual(bt, None) self.assertEqual(bt, None)
operation_log = template_tool.installBusinessTemplateListFromRepository([bt5_name], operation_log = template_tool.installBusinessTemplateListFromRepository([bt5_name],
only_newer=False, update_catalog=0) only_different=False, update_catalog=0)
self.assertTrue("Installed %s with" % bt5_name in operation_log[0]) self.assertTrue("Installed %s with" % bt5_name in operation_log[0])
bt = template_tool.getInstalledBusinessTemplate(bt5_name) bt = template_tool.getInstalledBusinessTemplate(bt5_name)
...@@ -514,7 +493,7 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -514,7 +493,7 @@ class TestTemplateTool(ERP5TypeTestCase):
bt5_name = 'erp5_odt_style' bt5_name = 'erp5_odt_style'
operation_log = template_tool.installBusinessTemplateListFromRepository([bt5_name], operation_log = template_tool.installBusinessTemplateListFromRepository([bt5_name],
only_newer=False, update_catalog=1) only_different=False, update_catalog=1)
self.assertTrue("Installed %s with" % bt5_name in operation_log[-1]) self.assertTrue("Installed %s with" % bt5_name in operation_log[-1])
bt = template_tool.getInstalledBusinessTemplate(bt5_name) bt = template_tool.getInstalledBusinessTemplate(bt5_name)
self.assertEqual(bt.getTitle(), bt5_name) self.assertEqual(bt.getTitle(), bt5_name)
...@@ -524,7 +503,7 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -524,7 +503,7 @@ class TestTemplateTool(ERP5TypeTestCase):
# Install again should not force catalog to be updated # Install again should not force catalog to be updated
operation_log = template_tool.installBusinessTemplateListFromRepository( operation_log = template_tool.installBusinessTemplateListFromRepository(
[bt5_name], only_newer=False) [bt5_name], only_different=False)
self.assertTrue("Installed %s with" % bt5_name in operation_log[-1]) self.assertTrue("Installed %s with" % bt5_name in operation_log[-1])
bt = template_tool.getInstalledBusinessTemplate(bt5_name) bt = template_tool.getInstalledBusinessTemplate(bt5_name)
self.assertNotEquals(bt, None) self.assertNotEquals(bt, None)
...@@ -609,7 +588,7 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -609,7 +588,7 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertNotEquals(bt, None) self.assertNotEquals(bt, None)
bt = template_tool.getInstalledBusinessTemplate("erp5_workflow") bt = template_tool.getInstalledBusinessTemplate("erp5_workflow")
self.assertNotEquals(bt, None) self.assertNotEquals(bt, None)
transaction.abort() self.abort()
# Same as above but also check that dependencies are properly resolved if # Same as above but also check that dependencies are properly resolved if
# one of the dependency is explicitly added to the list of bt5 to be # one of the dependency is explicitly added to the list of bt5 to be
...@@ -625,22 +604,23 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -625,22 +604,23 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertNotEquals(bt, None) self.assertNotEquals(bt, None)
bt = template_tool.getInstalledBusinessTemplate("erp5_workflow") bt = template_tool.getInstalledBusinessTemplate("erp5_workflow")
self.assertNotEquals(bt, None) self.assertNotEquals(bt, None)
transaction.abort() self.abort()
def test_installBusinessTemplateListFromRepository_ignore_when_installed(self): def test_installBusinessTemplateListFromRepository_ignore_when_installed(self):
"""Check that install one business template, this method does not download """Check that install one business template, this method does not download
many business templates that are already installed many business templates that are already installed
""" """
template_tool = self.portal.portal_templates template_tool = self.portal.portal_templates
# Delete not installed bt5 to check easily if more not installed was before = dict((bt.getTitle(), bt.getId())
# created for bt in template_tool.getInstalledBusinessTemplateList())
for bt5 in template_tool.getBuiltBusinessTemplateList(): bt_title = 'erp5_calendar'
bt5.delete() template_tool.installBusinessTemplateListFromRepository([bt_title],
bt5_name_list = ['erp5_calendar']
template_tool.installBusinessTemplateListFromRepository(bt5_name_list,
install_dependency=True) install_dependency=True)
self.tic() self.tic()
self.assertEquals(template_tool.getBuiltBusinessTemplateList(), []) after = dict((bt.getTitle(), bt.getId())
for bt in template_tool.getInstalledBusinessTemplateList())
del after[bt_title]
self.assertEqual(before, after)
def test_sortBusinessTemplateList(self): def test_sortBusinessTemplateList(self):
"""Check sorting of a list of business template by their dependencies """Check sorting of a list of business template by their dependencies
......
...@@ -492,43 +492,15 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase): ...@@ -492,43 +492,15 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase):
% ', '.join(not_found_list)) % ', '.join(not_found_list))
return new_template_list return new_template_list
def setupAutomaticBusinessTemplateRepository(self, accept_public=True, def setupAutomaticBusinessTemplateRepository(self,
searchable_business_template_list=None): searchable_business_template_list=("erp5_base",)):
# Try to setup some valid Repository List by reusing ERP5TypeTestCase API. template_tool = self.portal.portal_templates
# if accept_public we can accept public repository can be set, otherwise bt_set = set(searchable_business_template_list).difference(x['title']
# we let failure happens. for x in template_tool.repository_dict.itervalues() for x in x)
if searchable_business_template_list is None: if bt_set:
searchable_business_template_list = ["erp5_base"] template_tool.updateRepositoryBusinessTemplateList(set(
os.path.dirname(x[0]) for x in self._getBTPathAndIdList(bt_set)),
# Assume that the public official repository is a valid repository genbt5list=1)
public_bt5_repository_list = ['http://www.erp5.org/dists/snapshot/bt5/']
template_list = []
for bt_id in searchable_business_template_list:
bt_template_list = self._getBTPathAndIdList([bt_id])
if len(bt_template_list):
template_list.append(bt_template_list[0])
if len(template_list) > 0:
bt5_repository_path_list = ["/".join(x[0].split("/")[:-1])
for x in template_list]
if accept_public:
try:
self.portal.portal_templates.updateRepositoryBusinessTemplateList(
bt5_repository_path_list, None)
except (RuntimeError, IOError), e:
# If bt5 repository is not a repository use public one.
self.portal.portal_templates.updateRepositoryBusinessTemplateList(
public_bt5_repository_list)
else:
self.portal.portal_templates.updateRepositoryBusinessTemplateList(
bt5_repository_path_list, None)
elif accept_public:
self.portal.portal_templates.updateRepositoryBusinessTemplateList(
public_bt5_repository_list)
else:
raise ValueError("ERP5 was unable to determinate a valid local " + \
"repository, please check your environment or " + \
"use accept_public as True")
def failIfDifferentSet(self, a, b, msg=""): def failIfDifferentSet(self, a, b, msg=""):
if not msg: if not msg:
......
...@@ -316,6 +316,11 @@ class Git(WorkingCopy): ...@@ -316,6 +316,11 @@ class Git(WorkingCopy):
#except AttributeError: #except AttributeError:
# pass # pass
def getRevision(self, dirty=False):
if dirty and self._git('diff-index', '--quiet', 'HEAD').wait():
return self.git('rev-parse', '--short', 'HEAD') + '+'
return self.git('rev-parse', 'HEAD')
def commit(self, changelog, added=(), modified=(), removed=()): def commit(self, changelog, added=(), modified=(), removed=()):
context = self.aq_parent context = self.aq_parent
request = context.REQUEST request = context.REQUEST
......
...@@ -65,10 +65,17 @@ class Subversion(WorkingCopy): ...@@ -65,10 +65,17 @@ class Subversion(WorkingCopy):
_login_cookie_name = 'erp5_subversion_login' _login_cookie_name = 'erp5_subversion_login'
_ssl_trust_cookie_name = 'erp5_subversion_ssl_trust' _ssl_trust_cookie_name = 'erp5_subversion_ssl_trust'
def __init__(self, path): def __init__(self, *args, **kw):
WorkingCopy.__init__(self, path) WorkingCopy.__init__(self, *args, **kw)
if path and not os.path.exists(os.path.join(self.working_copy, '.svn')): try:
raise NotAWorkingCopyError(self.working_copy) path = self.working_copy
except AttributeError:
return
from pysvn import ClientError
try:
self.getRevision()
except (ClientError, KeyError):
raise NotAWorkingCopyError(path)
def setLogin(self, realm, user, password): def setLogin(self, realm, user, password):
"""Set login information. """Set login information.
...@@ -159,6 +166,12 @@ class Subversion(WorkingCopy): ...@@ -159,6 +166,12 @@ class Subversion(WorkingCopy):
def getRemoteComment(self): def getRemoteComment(self):
return 'r%s' % self.info()['revision'] return 'r%s' % self.info()['revision']
def getRevision(self, dirty=False):
r = self.info()['commit_revision']
if dirty and self._getClient().status(self.working_copy, get_all=False):
return "%s+" % r
return r
def export(self, path, url): def export(self, path, url):
return self._getClient().export(path, url) return self._getClient().export(path, url)
......
...@@ -89,9 +89,9 @@ class WorkingCopy(Implicit): ...@@ -89,9 +89,9 @@ class WorkingCopy(Implicit):
if cls.reference: if cls.reference:
cls._registry.append((cls.reference, cls)) cls._registry.append((cls.reference, cls))
def __init__(self, path=None): def __init__(self, path=None, restricted=False):
if path: if path:
self.working_copy = self.checkWorkingPath(path) self.working_copy = self.checkWorkingPath(path, restricted)
def getWorkingCopyList(self): def getWorkingCopyList(self):
working_copy_list = [] working_copy_list = []
...@@ -111,7 +111,7 @@ class WorkingCopy(Implicit): ...@@ -111,7 +111,7 @@ class WorkingCopy(Implicit):
os.mkdir(path) os.mkdir(path)
self.working_copy = path self.working_copy = path
def checkWorkingPath(self, path): def checkWorkingPath(self, path, restricted):
# First remove any '..' to prevent escaping. # First remove any '..' to prevent escaping.
# Note that 'normpath' ignore symlinks so it would not do it correctly. # Note that 'normpath' ignore symlinks so it would not do it correctly.
parts = path.split(os.sep) parts = path.split(os.sep)
...@@ -124,11 +124,14 @@ class WorkingCopy(Implicit): ...@@ -124,11 +124,14 @@ class WorkingCopy(Implicit):
# Allow symlinks inside instance home. # Allow symlinks inside instance home.
path = os.path.normpath(path) path = os.path.normpath(path)
real_path = os.path.realpath(path) real_path = os.path.realpath(path)
for allowed in getConfiguration().instancehome, gettempdir(): if restricted and not any(
if issubdir(allowed, path) or issubdir(allowed, real_path): issubdir(allowed, path) or issubdir(allowed, real_path)
return real_path for allowed in (getConfiguration().instancehome, gettempdir())):
raise Unauthorized("Unauthorized access to path %r." raise Unauthorized("Unauthorized access to path %r."
" It is NOT in your Zope home instance." % path) " It is NOT in your Zope home instance." % path)
if os.path.isdir(real_path):
return real_path
raise NotAWorkingCopyError(real_path)
def _getCookie(self, name, default=None): def _getCookie(self, name, default=None):
try: try:
...@@ -186,7 +189,7 @@ class WorkingCopy(Implicit): ...@@ -186,7 +189,7 @@ class WorkingCopy(Implicit):
""" """
if business_template.getBuildingState() == 'draft': if business_template.getBuildingState() == 'draft':
business_template.edit() business_template.edit()
business_template.build() business_template.build(update_revision=False)
self._export(business_template) self._export(business_template)
def _export(self, business_template): def _export(self, business_template):
...@@ -199,16 +202,6 @@ class WorkingCopy(Implicit): ...@@ -199,16 +202,6 @@ class WorkingCopy(Implicit):
def update(self, keep=False): def update(self, keep=False):
raise NotAWorkingCopyError raise NotAWorkingCopyError
def newRevision(self):
path = os.path.join('bt', 'revision')
try:
revision = int(self.showOld(path)) + 1
except NotVersionedError:
return 1
with open(os.path.join(self.working_copy, path), 'w') as file:
file.write(str(revision))
return revision
def hasDiff(self, path): def hasDiff(self, path):
try: try:
hasDiff = aq_base(self).__hasDiff hasDiff = aq_base(self).__hasDiff
...@@ -328,7 +321,7 @@ class WorkingCopy(Implicit): ...@@ -328,7 +321,7 @@ class WorkingCopy(Implicit):
title='tmp_bt_revert', title='tmp_bt_revert',
template_path_list=path_added_list) template_path_list=path_added_list)
tmp_bt.edit() tmp_bt.edit()
tmp_bt.build() tmp_bt.build(update_revision=False)
# Install then uninstall it to remove objects from ZODB # Install then uninstall it to remove objects from ZODB
tmp_bt.install() tmp_bt.install()
tmp_bt.uninstall() tmp_bt.uninstall()
...@@ -338,16 +331,16 @@ class WorkingCopy(Implicit): ...@@ -338,16 +331,16 @@ class WorkingCopy(Implicit):
installed_bt.reinstall(object_to_update=object_to_update, force=0) installed_bt.reinstall(object_to_update=object_to_update, force=0)
def getVcsTool(vcs=None, path=None): def getVcsTool(vcs=None, path=None, restricted=False):
if vcs: if vcs:
for x in WorkingCopy._registry: for x in WorkingCopy._registry:
if x[0] == vcs: if x[0] == vcs:
return x[1](path) return x[1](path, restricted)
raise ValueError("Unsupported Version Control System: %s" % vcs) raise ValueError("Unsupported Version Control System: %s" % vcs)
elif path: elif path:
for x in WorkingCopy._registry: for x in WorkingCopy._registry:
try: try:
return x[1](path) return x[1](path, restricted)
except NotAWorkingCopyError: except NotAWorkingCopyError:
pass pass
raise NotAWorkingCopyError(path) raise NotAWorkingCopyError(path)
......
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