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
from Products.PythonScripts.PythonScript import PythonScript
from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
from Products.ERP5Type.Cache import transactional_cached
from Products.ERP5Type.Message import translateString
from Products.ERP5Type.Utils import readLocalDocument, \
writeLocalDocument, \
importLocalDocument, \
......@@ -77,7 +78,6 @@ customImporters={
from Products.ERP5Type.patches.WorkflowTool import WorkflowHistoryList
from zLOG import LOG, WARNING, INFO
from warnings import warn
from gzip import GzipFile
from lxml.etree import parse
from xml.sax.saxutils import escape
from Products.CMFCore.Expression import Expression
......@@ -88,6 +88,8 @@ import transaction
import threading
from ZODB.broken import Broken
from Products.ERP5.genbt5list import BusinessTemplateRevision, \
item_name_list, item_set
CACHE_DATABASE_PATH = None
try:
......@@ -303,18 +305,13 @@ def unregisterSkinFolderId(skin_tool, skin_folder_id, skin_selection_list):
deleteSkinSelection(skin_tool, skin_selection)
skin_tool.getPortalObject().changeSkin(None)
class BusinessTemplateArchive:
class BusinessTemplateArchive(object):
"""
This is the base class for all Business Template archives
"""
def _initCreation(self, path, **kw):
def __init__(self, path, **kw):
self.path = path
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)
self.revision = BusinessTemplateRevision()
def addObject(self, obj, name, path=None, ext='.xml'):
if path:
......@@ -329,15 +326,23 @@ class BusinessTemplateArchive:
if not isinstance(obj, str):
obj.seek(0)
obj = obj.read()
self.revision.hash(path, obj)
self._writeString(obj, path)
else:
if isinstance(obj, str):
self.revision.hash(path, obj)
obj = StringIO(obj)
else:
obj.seek(0)
self.revision.hash(path, obj.read())
write(obj, path)
def finishCreation(self):
pass
def getRevision(self):
return self.revision.digest()
class BusinessTemplateFolder(BusinessTemplateArchive):
"""
Class archiving business template into a folder tree
......@@ -352,39 +357,32 @@ class BusinessTemplateFolder(BusinessTemplateArchive):
finally:
f.close()
def _initImport(self, file, path, **kw):
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):
def importFiles(self, item):
"""
Import file from a local folder
"""
class_name = item.__class__.__name__
root_path_len = self.root_path_len
prefix_len = root_path_len + len(class_name) + len(os.sep)
join = os.path.join
item_name = item.__class__.__name__
root = join(os.path.normpath(self.path), item_name, '')
root_path_len = len(root)
if CACHE_DATABASE_PATH:
try:
cache_database.db = gdbm.open(CACHE_DATABASE_PATH, 'cf')
except gdbm.error:
cache_database.db = gdbm.open(CACHE_DATABASE_PATH, 'nf')
try:
for file_path in self.file_list_dict.get(class_name, ()):
if os.path.isfile(file_path):
file = open(file_path, 'rb')
try:
file_name = file_path[prefix_len:]
for root, dirs, files in os.walk(root):
for file_name in files:
file_name = join(root, file_name)
with open(file_name, 'rb') as f:
file_name = posixpath.normpath(file_name[root_path_len:])
if '%' in file_name:
file_name = unquote(file_name)
item._importFile(file_name, file)
finally:
file.close()
elif item_name == 'bt' and file_name == 'revision':
continue
self.revision.hash(item_name + '/' + file_name, f.read())
f.seek(0)
item._importFile(file_name, f)
finally:
if hasattr(cache_database, 'db'):
cache_database.db.close()
......@@ -395,12 +393,21 @@ class BusinessTemplateTarball(BusinessTemplateArchive):
Class archiving businnes template into a tarball file
"""
def _initCreation(self, **kw):
BusinessTemplateArchive._initCreation(self, **kw)
# init tarfile obj
def __init__(self, path, creation=0, importing=0, **kw):
super(BusinessTemplateTarball, self).__init__(path, **kw)
if creation:
self.fobj = StringIO()
self.tar = tarfile.open('', 'w:gz', self.fobj)
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):
if self.path:
......@@ -416,27 +423,21 @@ class BusinessTemplateTarball(BusinessTemplateArchive):
self.tar.close()
return self.fobj
def _initImport(self, file, **kw):
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):
def importFiles(self, item):
"""
Import all file from the archive to the site
"""
extractfile = self.tar.extractfile
for file_name, info in self.item_dict.get(item.__class__.__name__, ()):
item._importFile(file_name, extractfile(info))
item_name = item.__class__.__name__
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 TemplateConflictError(Exception): pass
......@@ -535,7 +536,7 @@ class BaseTemplateItem(Implicit, Persistent):
return self._objects.keys()
def importFile(self, bta, **kw):
bta.importFiles(item=self)
bta.importFiles(self)
def _removeAllButLastWorkflowHistory(self, obj):
workflow_history = getattr(obj, 'workflow_history', None)
......@@ -4677,6 +4678,13 @@ class LocalRolesTemplateItem(BaseTemplateItem):
delattr(obj, '__ac_local_roles_group_id_dict__')
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):
"""
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
, '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):
XMLObject.__init__(self, *args, **kw)
self._clean()
......@@ -4845,23 +4795,10 @@ Business Template is a set of definitions, such as skins, portal types and categ
self.workflow_history[
'business_template_installation_workflow'] = None
security.declareProtected(Permissions.AccessContentsInformation,
'getRevision')
def getRevision(self):
"""returns the revision property.
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)
def getShortRevision(self):
"""Returned a shortened revision"""
r = self.getRevision()
return r and r[:5]
security.declareProtected(Permissions.ManagePortal, 'storeTemplateItemData')
def storeTemplateItemData(self):
......@@ -4984,7 +4921,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
pass
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
"""
......@@ -4993,19 +4930,11 @@ Business Template is a set of definitions, such as skins, portal types and categ
# Make sure that everything is sane.
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.storeTemplateItemData()
# Build each part
for item_name in self._item_name_list:
for item_name in item_name_list:
item = getattr(self, item_name)
if item is None:
continue
......@@ -5014,6 +4943,8 @@ Business Template is a set of definitions, such as skins, portal types and categ
item.build(self)
# update _p_jar property of objects cleaned by removeProperties
transaction.savepoint(optimistic=True)
if update_revision:
self._export()
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
else:
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)
installed_item = getattr(installed_bt, item_name, 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
# Install everything
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)
if item is not None:
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
# remove object from old business template
if len(remove_object_dict):
# 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)
if item is not None:
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
not remove all items.
"""
# 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)
if item is not None:
item.trash(
......@@ -5220,7 +5151,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
"""
# Uninstall 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)
if item is not None:
item.uninstall(self, **kw)
......@@ -5248,7 +5179,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
if hasattr(self, attr):
delattr(self, attr)
# Secondly, make attributes empty.
for item_name in self._item_name_list:
for item_name in item_name_list:
setattr(self, item_name, None)
security.declareProtected(Permissions.ManagePortal, 'clean')
......@@ -5490,12 +5421,16 @@ Business Template is a set of definitions, such as skins, portal types and categ
if os.path.isdir(bt_path):
return bt_path
@transactional_cached(lambda self, vcs=None, path=None: (self, vcs, path))
def getVcsTool(self, vcs=None, path=None):
@transactional_cached(lambda self, vcs=None, path=None, restricted=False:
(self, vcs, path, restricted))
def _getVcsTool(self, vcs=None, path=None, restricted=False):
from Products.ERP5VCS.WorkingCopy import getVcsTool
if not (path or vcs):
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):
# 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
if self.getBuildingState() != 'built':
raise TemplateConditionError, \
'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 local:
# we export into a folder tree
bta = BusinessTemplateFolder(creation=1, path=path)
bta = BusinessTemplateFolder(path, creation=1)
else:
# We export BT into a tarball file
if path is None:
path = self.getTitle()
bta = BusinessTemplateTarball(creation=1, path=path)
bta = BusinessTemplateTarball(path, creation=1)
# export bt
for prop in self.propertyMap():
prop_type = prop['type']
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'):
continue
value = self.getProperty(id)
......@@ -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='')
# Export each part
for item_name in self._item_name_list:
for item_name in item_name_list:
item = getattr(self, item_name, None)
if item is not None:
item.export(context=self, bta=bta)
self._setRevision(bta.getRevision())
return bta.finishCreation()
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
"""
if dir:
bta = BusinessTemplateFolder(importing=1, file=file, path=root_path)
bta = (BusinessTemplateFolder if os.path.isdir(path) else
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:
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()
......@@ -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,
(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)
# this check is due to backwards compatability when there can be a
# 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
for module_id in module_id_list:
del sys.modules[module_id]
self._setRevision(bta.getRevision())
def getItemsList(self):
"""Return list of items in business template
"""
items_list = []
for item_name in self._item_name_list:
for item_name in item_name_list:
item = getattr(self, item_name, None)
if item is not None:
items_list.extend(item.getKeys())
......@@ -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
# a default class value to None
for key in BusinessTemplate._item_name_list:
for key in item_name_list:
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
import os
import shutil
import sys
import tarfile
from Acquisition import Implicit, Explicit
from AccessControl import ClassSecurityInfo
......@@ -45,6 +44,7 @@ from Products.ERP5Type.Tool.BaseTool import BaseTool
from Products.ERP5Type.Cache import transactional_cached
from Products.ERP5Type import Permissions
from Products.ERP5.Document.BusinessTemplate import BusinessTemplateMissingDependency
from Products.ERP5.genbt5list import generateInformation
from Acquisition import aq_base
from tempfile import mkstemp, mkdtemp
from Products.ERP5 import _dtmldir
......@@ -56,7 +56,6 @@ from xml.dom.minidom import parse
from xml.parsers.expat import ExpatError
import struct
import cPickle
import posixpath
from base64 import b64encode, b64decode
from Products.ERP5Type.Message import translateString
from zLOG import LOG, INFO, WARNING
......@@ -126,23 +125,19 @@ class TemplateTool (BaseTool):
# However, that unlikely happens, and using a Z SQL Method has a
# potential danger because business templates may exchange catalog
# methods, so the database could be broken temporarily.
latest_bt = None
latest_revision = 0
for bt in self.contentValues(filter={'portal_type':'Business Template'}):
last_bt = last_time = None
for bt in self.objectValues(portal_type='Business Template'):
if bt.getTitle() == title or title in bt.getProvisionList():
installation_state = bt.getInstallationState()
if installation_state == 'installed':
latest_bt = bt
break
elif strict is False and installation_state == 'replaced':
revision = bt.getRevision()
try:
revision = int(revision)
except ValueError:
continue
if revision > latest_revision:
latest_bt = bt
return latest_bt
state = bt.getInstallationState()
if state == 'installed':
return bt
if state == 'replaced' and not strict:
t = bt.workflow_history \
['business_template_installation_workflow'][-1]['time']
if last_time < t:
last_bt = bt
last_time = t
return last_bt
def getInstalledBusinessTemplatesList(self):
"""Deprecated.
......@@ -182,20 +177,12 @@ class TemplateTool (BaseTool):
return bt.getRevision()
return None
def getBuiltBusinessTemplatesList(self):
"""Deprecated.
"""
DeprecationWarning('getBuiltBusinessTemplatesList is deprecated; Use getBuiltBusinessTemplateList instead.', DeprecationWarning)
return self.getBuiltBusinessTemplateList()
def getBuiltBusinessTemplateList(self):
"""Get the list of built and not installed business templates.
"""
built_bts = []
for bt in self.contentValues(portal_type='Business Template'):
if bt.getInstallationState() == 'not_installed' and bt.getBuildingState() == 'built':
built_bts.append(bt)
return built_bts
return [bt for bt in self.objectValues(portal_type='Business Template')
if bt.getInstallationState() == 'not_installed' and
bt.getBuildingState() == 'built']
@property
def asRepository(self):
......@@ -203,7 +190,7 @@ class TemplateTool (BaseTool):
"""Export business template by their title
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
"""
def __before_publishing_traverse__(self, self2, request):
......@@ -215,9 +202,9 @@ class TemplateTool (BaseTool):
last_bt = None, None
for bt in self.aq_parent.searchFolder(title=title):
bt = bt.getObject()
revision = int(bt.getRevision())
if last_bt[0] < revision and bt.getInstallationState() != 'deleted':
last_bt = revision, bt
modified = bt.getModificationDate()
if last_bt[0] < modified and bt.getInstallationState() != 'deleted':
last_bt = modified, bt
if last_bt[1] is None:
return RESPONSE.notFoundError(title)
RESPONSE.setHeader('Content-type', 'application/data')
......@@ -298,52 +285,6 @@ class TemplateTool (BaseTool):
self.deleteContent(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' )
def manage_download(self, url, id=None, REQUEST=None):
"""The management interface for download.
......@@ -362,56 +303,9 @@ class TemplateTool (BaseTool):
def _download_local(self, path, bt_id):
"""Download Business Template from local directory or file
"""
if os.path.isdir(os.path.normpath(path)):
path = os.path.normpath(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)
bt = self.newContent(bt_id, 'Business Template')
bt.importFile(path)
return bt
else:
# this should be a file
return self._importBT(path, bt_id)
def _download_url(self, url, bt_id):
tempid, temppath = mkstemp()
......@@ -437,18 +331,6 @@ class TemplateTool (BaseTool):
finally:
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' )
def download(self, url, id=None, REQUEST=None):
"""
......@@ -463,13 +345,9 @@ class TemplateTool (BaseTool):
id = self.generateNewId()
urltype, name = splittype(url)
# Windows compatibility
if WIN:
if os.path.isdir(os.path.normpath(url)) or \
os.path.isfile(os.path.normpath(url)):
urltype = 'file'
name = os.path.normpath(url)
if WIN and urltype and '\\' in name:
urltype = None
name = url
if urltype and urltype != 'file':
if '/portal_templates/asRepository/' in url:
# In this case, the downloaded BT is already built.
......@@ -479,7 +357,7 @@ class TemplateTool (BaseTool):
return self[self._setObject(id, bt)]
bt = self._download_url(url, id)
else:
bt = self._download_local(name, id)
bt = self._download_local(os.path.normpath(name), id)
bt.build(no_action=True)
return bt
......@@ -687,9 +565,12 @@ class TemplateTool (BaseTool):
'updateRepositoryBusinessTemplateList' )
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.
For local repositories, if bt5list is missing or if genbt5list > 1,
bt5list is automatically generated (but not saved on disk).
"""
self.repository_dict = PersistentMapping()
property_list = ('title', 'version', 'revision', 'description', 'license',
......@@ -697,9 +578,19 @@ class TemplateTool (BaseTool):
#LOG('updateRepositoryBusiessTemplateList', 0,
# 'repository_list = %r' % (repository_list,))
for repository in repository_list:
url = '/'.join([repository, 'bt5list'])
f = urlopen(url)
property_dict_list = []
urltype, url = splittype(repository)
if WIN and urltype and '\\' in url:
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:
doc = parse(f)
......@@ -713,6 +604,7 @@ class TemplateTool (BaseTool):
else:
raise RuntimeError, 'Invalid repository: %s' % repository
try:
property_dict_list = []
root = doc.documentElement
for template in root.getElementsByTagName("template"):
id = template.getAttribute('id')
......@@ -1060,9 +952,6 @@ class TemplateTool (BaseTool):
update_only: return only bt that needs to be updated
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
result_list = []
template_set = None
......@@ -1089,14 +978,8 @@ class TemplateTool (BaseTool):
# if this business template is newer.
previous_repository, previous_property_dict = \
template_item_dict[title]
diff_version = self.compareVersions(previous_property_dict['version'],
property_dict['version'])
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']):
if self.compareVersions(previous_property_dict['version'],
property_dict['version']) < 0:
template_item_dict[title] = (repository, property_dict)
# Next, select only updated business templates.
if update_only:
......@@ -1109,9 +992,8 @@ class TemplateTool (BaseTool):
if diff_version < 0:
template_item_list.append((repository, property_dict))
elif diff_version == 0 \
and installed_bt.getRevision() \
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))
elif template_list is not None:
template_item_list.append((repository, property_dict))
......@@ -1119,29 +1001,24 @@ class TemplateTool (BaseTool):
# Create temporary Business Template objects for displaying.
for repository, property_dict in template_item_list:
property_dict = property_dict.copy()
id = property_dict['id']
filename = property_dict['id']
del property_dict['id']
revision = property_dict['revision']
version_state = 'new'
id = filename = property_dict.pop('id')
installed_bt = \
self.getInstalledBusinessTemplate(property_dict['title'])
if installed_bt is not None:
installed_version = installed_bt.getVersion()
installed_revision = installed_bt.getRevision()
result = self.compareVersions(installed_revision, revision)
if result == 0:
installed_revision = installed_bt.getShortRevision()
if installed_bt.getRevision() == property_dict['revision']:
version_state = 'present'
elif result < 0:
version_state = 'old'
else:
version_state = 'different'
else:
installed_version = ''
installed_revision = ''
version_state_title = version_state_title_dict[version_state]
version_state = 'new'
uid = self.encodeRepositoryBusinessTemplateUid(repository, id)
obj = newTempBusinessTemplate(self, 'temp_' + uid,
version_state = version_state,
version_state_title = version_state_title,
version_state_title=version_state.title(),
filename = filename,
installed_version = installed_version,
installed_revision = installed_revision,
......@@ -1206,10 +1083,9 @@ class TemplateTool (BaseTool):
return 0
def _getBusinessTemplateUrlDict(self, newest_only=False):
def _getBusinessTemplateUrlDict(self):
business_template_url_dict = {}
for bt in self.getRepositoryBusinessTemplateList(\
newest_only=newest_only):
for bt in self.getRepositoryBusinessTemplateList():
url, name = self.decodeRepositoryBusinessTemplateUid(bt.getUid())
if name.endswith('.bt5'):
name = name[:-4]
......@@ -1221,14 +1097,11 @@ class TemplateTool (BaseTool):
security.declareProtected(Permissions.ManagePortal,
'installBusinessTemplatesFromRepositories')
def installBusinessTemplatesFromRepositories(self, template_list,
only_newer=True, update_catalog=_MARKER, activate=False,
install_dependency=False):
def installBusinessTemplatesFromRepositories(self, *args, **kw):
"""Deprecated.
"""
DeprecationWarning('installBusinessTemplatesFromRepositories is deprecated; Use self.installBusinessTemplateListFromRepository instead.', DeprecationWarning)
return self.installBusinessTemplateListFromRepository(template_list,
only_newer, update_catalog, activate, install_dependency)
return self.installBusinessTemplateListFromRepository(*args, **kw)
security.declareProtected(Permissions.ManagePortal,
'resolveBusinessTemplateListDependency')
......@@ -1293,7 +1166,7 @@ class TemplateTool (BaseTool):
security.declareProtected(Permissions.ManagePortal,
'installBusinessTemplateListFromRepository')
def installBusinessTemplateListFromRepository(self, template_list,
only_newer=True, update_catalog=_MARKER, activate=False,
only_different=True, update_catalog=_MARKER, activate=False,
install_dependency=False):
"""Installs template_list from configured repositories by default only newest"""
# XXX-Luke: This method could replace
......@@ -1303,12 +1176,13 @@ class TemplateTool (BaseTool):
operation_log = []
resolved_template_list = self.resolveBusinessTemplateListDependency(
template_list)
installed_bt5_set = set([x.title
for x in self.getInstalledBusinessTemplatesList()])
installed_bt5_dict = dict((x.getTitle(), x.getRevision())
for x in self.getInstalledBusinessTemplateList())
if only_different:
template_url_dict = self._getBusinessTemplateUrlDict()
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
if not checkAvailability(i[1].replace(".bt5", ""))]
......@@ -1317,17 +1191,14 @@ class TemplateTool (BaseTool):
"Impossible to install, please install the following dependencies before: %s" \
% [x[1] for x in missing_dependency_list]
template_url_dict = self._getBusinessTemplateUrlDict()
activate_kw = dict(activity="SQLQueue", tag="start_%s" % (time.time()))
for repository, bt_id in resolved_template_list:
if only_different:
bt = template_url_dict.get(bt_id)
if bt is not None and bt_id in installed_bt5_set:
revision = int(bt['revision'])
installed_bt5 = self.getInstalledBusinessTemplate(bt_id)
if int(installed_bt5.getRevision()) <= revision and only_newer:
if bt is not None and bt['revision'] == installed_bt5_dict.get(bt_id):
continue
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:
param_dict["update_catalog"] = update_catalog
......@@ -1340,7 +1211,7 @@ class TemplateTool (BaseTool):
else:
document = self.updateBusinessTemplateFromUrl(**param_dict)
operation_log.append('Installed %s with revision %s' % (
document.getTitle(), document.getRevision()))
document.getTitle(), document.getShortRevision()))
return operation_log
......@@ -1354,7 +1225,7 @@ class TemplateTool (BaseTool):
reinstall=False,
active_process=None,
force_keep_list=None,
only_newer=True):
only_different=True):
"""
This method download and install a bt5, from a URL.
......@@ -1390,18 +1261,13 @@ class TemplateTool (BaseTool):
if reinstall:
install_kw = None
else:
if only_different:
previous_bt5 = self.getInstalledBusinessTemplate(bt_title)
if (previous_bt5 is not None) and only_newer:
try:
imported_revision = int(imported_bt5.getRevision())
previous_revision = int(previous_bt5.getRevision())
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))
if previous_bt5 and \
imported_bt5.getRevision() == previous_bt5.getRevision():
log("%s is already installed with revision %s"
% (bt_title, imported_bt5.getShortRevision()))
return imported_bt5
except ValueError:
pass
install_kw = {}
for listbox_line in imported_bt5.BusinessTemplate_getModifiedObject():
......
......@@ -31,130 +31,200 @@
"""Generate repository information on Business Templates.
"""
import posixpath
import tarfile
import os
import sys
import tempfile
import shutil
import cgi
property_list = '''
from base64 import b64encode
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
version
revision
description
license
dependency_list
test_dependency_list
provision_list
copyright_list
'''.strip().splitlines()
bt_title_path = os.path.join('bt', 'title')
'''.split())
def info(message):
"""Print a message to stdout.
"""
sys.stdout.write(message)
def __init__(self):
self.revision = BusinessTemplateRevision()
def err(message):
"""Print a message to stderr.
"""
sys.stderr.write(message)
def readProperty(property_dict, property_name, property_file):
def _read(self, path, file):
try:
text = property_file.read()
if property_name.endswith('_list'):
property_dict[property_name[:-5]] = text.splitlines()
else:
property_dict[property_name] = text
text = file.read()
finally:
property_file.close()
def readBusinessTemplate(tar):
"""Read an archived Business Template info.
"""
property_dict = {}
file.close()
if path.startswith('bt/'):
name = path[3:]
if name in self.property_list:
if name.endswith('_list'):
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:
name_list = info.name.split('/')
if len(name_list) == 3 and name_list[1] == 'bt' and name_list[2] in property_list:
property_file = tar.extractfile(info)
property_name = name_list[2]
readProperty(property_dict, property_name, property_file)
return property_dict
def readBusinessTemplateDirectory(dir):
"""Read Business Template Directory info.
"""
property_dict = {}
for property_name in property_list:
filename = os.path.join(dir, 'bt', property_name)
if os.path.isfile(filename):
property_file = open(filename, 'rb')
readProperty(property_dict, property_name, property_file)
return property_dict
def generateInformation(fd):
os.write(fd, '<?xml version="1.0"?>\n')
os.write(fd, '<repository>\n')
for file in sorted(os.listdir(os.getcwd())):
if file.endswith('.bt5'):
info('Reading %s... ' % (file,))
if not info.isdir():
name = info.name.split('/', 1)[1]
if name.split('/', 1)[0] in item_set:
self._read(name, tar.extractfile(info))
return iter(self)
@classmethod
def fromDir(cls, dir):
"""Read Business Template Directory info"""
self = cls()
lstrip_len = len(dir + os.sep)
for root, dirs, files in os.walk(dir):
if root:
for path in files:
path = os.path.join(root, path)
self._read(posixpath.normpath(path[lstrip_len:]), open(path, 'rb'))
else:
dirs[:] = item_set.intersection(dirs)
return iter(self)
def generateInformation(dir, info=id, err=None):
xml = StringIO()
xml.write('<?xml version="1.0"?>\n<repository>\n')
for name in sorted(os.listdir(dir)):
path = os.path.join(dir, name)
if name.endswith('.bt5'):
info('Reading %s... ' % name)
try:
tar = tarfile.open(file, 'r:gz')
tar = tarfile.open(path, 'r:gz')
except tarfile.TarError:
err('An error happened in %s; skipping\n' % (file,))
if err:
err('An error happened in %s; skipping\n' % name)
continue
raise
try:
property_dict = readBusinessTemplate(tar)
property_list = BusinessTemplate.fromTar(tar)
finally:
tar.close()
elif os.path.isfile(os.path.join(file, bt_title_path)):
info('Reading Directory %s... ' % (file,))
property_dict = readBusinessTemplateDirectory(file)
elif os.path.isfile(os.path.join(path, 'bt', 'title')):
info('Reading Directory %s... ' % name)
property_list = BusinessTemplate.fromDir(path)
else:
continue
os.write(fd, ' <template id="%s">\n' % (file,))
for property_id, property_value in sorted(property_dict.items()):
if type(property_value) is str:
os.write(fd, ' <%s>%s</%s>\n' % (
property_id, cgi.escape(property_value), property_id))
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')
xml.write(' <template id="%s">\n' % name)
for k, v in property_list:
for v in (v,) if type(v) is str else v:
xml.write(' <%s>%s</%s>\n' % (k, cgi.escape(v), k))
xml.write(' </template>\n')
info('done\n')
os.write(fd, '</repository>\n')
xml.write('</repository>\n')
return xml
def main():
if len(sys.argv) < 2:
dir_list = ['.']
else:
dir_list = sys.argv[1:]
def main(dir_list=None, **kw):
if dir_list is None:
kw.setdefault('info', sys.stdout.write)
kw.setdefault('err', sys.stderr.write)
dir_list = sys.argv[1:] or '.'
cur_umask = os.umask(0666)
os.umask(cur_umask)
cwd = os.getcwd()
for d in dir_list:
os.chdir(d)
try:
fd, path = tempfile.mkstemp(dir='.')
bt5list = generateInformation(d, **kw).getvalue()
d = os.path.join(d, 'bt5list.new')
try:
generateInformation(fd)
os.fchmod(fd, 0666 & ~cur_umask)
os.rename(path, 'bt5list')
except:
os.remove(path)
raise
finally:
os.close(fd)
with open(d, 'wb') as f:
f.write(bt5list)
os.rename(d, d[:-4])
finally:
os.chdir(cwd)
try:
os.remove(d)
except OSError:
pass
if __name__ == "__main__":
main()
......@@ -109,7 +109,7 @@
<string>my_id</string>
<string>my_title</string>
<string>my_version</string>
<string>my_revision</string>
<string>my_short_revision</string>
<string>my_translated_building_state_title</string>
<string>my_translated_installation_state_title</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 @@
<string>Version</string>
</tuple>
<tuple>
<string>revision</string>
<string>short_revision</string>
<string>Revision</string>
</tuple>
<tuple>
......
......@@ -340,7 +340,7 @@
<string>Version</string>
</tuple>
<tuple>
<string>revision</string>
<string>short_revision</string>
<string>Revision</string>
</tuple>
<tuple>
......
......@@ -343,14 +343,14 @@
<string>version</string>
<string>Version</string>
</tuple>
<tuple>
<string>revision</string>
<string>Revision</string>
</tuple>
<tuple>
<string>installed_version</string>
<string>Installed Version</string>
</tuple>
<tuple>
<string>short_revision</string>
<string>Revision</string>
</tuple>
<tuple>
<string>installed_revision</string>
<string>Installed Revision</string>
......@@ -367,10 +367,6 @@
<string>license</string>
<string>License</string>
</tuple>
<tuple>
<string>version_state_title</string>
<string>State</string>
</tuple>
</list>
</value>
</item>
......
bin/genbt5list
\ No newline at end of file
......@@ -2847,24 +2847,6 @@ class BusinessTemplateMixin(ERP5TypeTestCase, LogInterceptor):
self.assertTrue(base_category_obj is not None)
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):
""" Check if bt has no missing dependency
"""
......@@ -5149,26 +5131,6 @@ class TestBusinessTemplate(BusinessTemplateMixin):
sequence_list.addSequenceString(sequence_string)
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):
"""Test if a new Business Template has no dependencies"""
sequence_list = SequenceList()
......
......@@ -31,7 +31,7 @@ import os
import shutil
import unittest
import random
import transaction
import tempfile
from App.config import getConfiguration
from Products.ERP5VCS.WorkingCopy import getVcsTool
......@@ -99,32 +99,23 @@ class TestTemplateTool(ERP5TypeTestCase):
def testUpdateBT5FromRepository(self, quiet=quiet, run=run_all_test):
""" Test the list of bt5 returned for upgrade """
# edit bt5 revision so that it will be marked as updatable
bt_list = self.templates_tool.searchFolder(title='erp5_base')
self.assertEqual(len(bt_list), 1)
erp5_base = bt_list[0].getObject()
try:
erp5_base.edit(revision=0)
erp5_base = self.templates_tool.getInstalledBusinessTemplate('erp5_base',
strict=True)
erp5_base._setRevision('')
updatable_bt_list = \
self.templates_tool.getRepositoryBusinessTemplateList(update_only=True)
self.assertEqual(
[i.title for i in updatable_bt_list if i.title == "erp5_base"],
["erp5_base"])
self.assertTrue("erp5_base" in (bt.getTitle() for bt in
self.templates_tool.getRepositoryBusinessTemplateList(update_only=True)))
erp5_base.replace()
updatable_bt_list = \
self.templates_tool.getRepositoryBusinessTemplateList(update_only=True)
self.assertEqual(
[i.title for i in updatable_bt_list if i.title == "erp5_base"],
[])
finally:
erp5_base.edit(revision=int(erp5_base.getRevision()) + 10)
self.assertFalse("erp5_base" in (bt.getTitle() for bt in
self.templates_tool.getRepositoryBusinessTemplateList(update_only=True)))
self.abort()
def test_download_http(self):
test_web = self.portal.portal_templates.download(
'http://www.erp5.org/dists/snapshot/test_bt5/test_web.bt5')
self.assertEqual(test_web.getPortalType(), 'Business Template')
self.assertEqual(test_web.getTitle(), 'test_web')
self.assertTrue(test_web.getRevision())
self.assertEqual(len(test_web.getRevision()), 28)
def _svn_setup_ssl(self):
"""
......@@ -148,20 +139,20 @@ class TestTemplateTool(ERP5TypeTestCase):
test_web = self.portal.portal_templates.download(bt5_url)
self.assertEqual(test_web.getPortalType(), 'Business Template')
self.assertEqual(test_web.getTitle(), 'test_web')
self.assertTrue(test_web.getRevision())
self.assertEqual(len(test_web.getRevision()), 28)
def test_updateBusinessTemplateFromUrl_simple(self):
"""
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.
"""
self._svn_setup_ssl()
template_tool = self.portal.portal_templates
old_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style')
# change revision to an old revision
old_bt.setRevision(0.0001)
# fake different revision
old_bt.setRevision('')
url = 'https://svn.erp5.org/repos/public/erp5/trunk/bt5/erp5_csv_style'
template_tool.updateBusinessTemplateFromUrl(url)
new_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style')
......@@ -170,7 +161,7 @@ class TestTemplateTool(ERP5TypeTestCase):
# Test Another time with definning an ID
old_bt = new_bt
old_bt.setRevision(0.0002)
old_bt.setRevision('')
template_tool.updateBusinessTemplateFromUrl(url, id="new_erp5_csv_style")
new_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style')
self.assertNotEquals(old_bt, new_bt)
......@@ -184,8 +175,7 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertEqual(old_bt, new_bt)
self.assertEqual('erp5_csv_style', new_bt.getTitle())
self.assertEqual('new_erp5_csv_style', new_bt.getId())
not_installed_bt5 = getattr(template_tool, "not_installed_bt5", None)
self.assertNotEquals(not_installed_bt5, None)
not_installed_bt5 = template_tool['not_installed_bt5']
self.assertEqual('erp5_csv_style', not_installed_bt5.getTitle())
self.assertEqual(not_installed_bt5.getInstallationState(),
"not_installed")
......@@ -204,10 +194,8 @@ class TestTemplateTool(ERP5TypeTestCase):
keep_original_list=keep_original_list)
bt = template_tool.getInstalledBusinessTemplate('test_core')
self.assertNotEquals(None, bt)
erp5_test = getattr(self.portal.portal_skins, 'erp5_test', None)
self.assertNotEquals(None, erp5_test)
test_file = getattr(erp5_test, 'test_file', None)
self.assertEqual(None, test_file)
erp5_test = self.portal.portal_skins['erp5_test']
self.assertFalse(erp5_test.hasObject('test_file'))
def test_updateBusinessTemplateFromUrl_after_before_script(self):
"""
......@@ -248,48 +236,6 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertEqual(bt.getChangeLog(), '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):
"""Tests compare version on template tool. """
compareVersions = self.getPortal().portal_templates.compareVersions
......@@ -320,12 +266,45 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertEqual(None, self.getPortal()\
.portal_templates.getInstalledBusinessTemplate('erp5_toto'))
def test_getInstalledBusinessTemplateRevision(self):
self.assertTrue(300 < self.getPortal()\
.portal_templates.getInstalledBusinessTemplateRevision('erp5_core'))
self.assertEqual(None, self.getPortal()\
.portal_templates.getInstalledBusinessTemplateRevision('erp5_toto'))
def test_revision(self):
template_tool = self.portal.portal_templates
getInstalledRevision = template_tool.getInstalledBusinessTemplateRevision
self.assertEqual(None, getInstalledRevision('erp5_toto'))
available_bt, = template_tool.getRepositoryBusinessTemplateList(
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):
templates_tool = self.getPortal().portal_templates
......@@ -479,9 +458,9 @@ class TestTemplateTool(ERP5TypeTestCase):
bt_old = self.templates_tool.getInstalledBusinessTemplate(bt5_name, strict=True)
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(
[bt5_name], only_newer=False)
[bt5_name], only_different=False)
self.assertTrue("Installed %s with" % bt5_name in operation_log[-1])
bt_new = self.templates_tool.getInstalledBusinessTemplate(bt5_name,
......@@ -498,7 +477,7 @@ class TestTemplateTool(ERP5TypeTestCase):
bt = template_tool.getInstalledBusinessTemplate(bt5_name)
self.assertEqual(bt, None)
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])
bt = template_tool.getInstalledBusinessTemplate(bt5_name)
......@@ -514,7 +493,7 @@ class TestTemplateTool(ERP5TypeTestCase):
bt5_name = 'erp5_odt_style'
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])
bt = template_tool.getInstalledBusinessTemplate(bt5_name)
self.assertEqual(bt.getTitle(), bt5_name)
......@@ -524,7 +503,7 @@ class TestTemplateTool(ERP5TypeTestCase):
# Install again should not force catalog to be updated
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])
bt = template_tool.getInstalledBusinessTemplate(bt5_name)
self.assertNotEquals(bt, None)
......@@ -609,7 +588,7 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertNotEquals(bt, None)
bt = template_tool.getInstalledBusinessTemplate("erp5_workflow")
self.assertNotEquals(bt, None)
transaction.abort()
self.abort()
# 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
......@@ -625,22 +604,23 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertNotEquals(bt, None)
bt = template_tool.getInstalledBusinessTemplate("erp5_workflow")
self.assertNotEquals(bt, None)
transaction.abort()
self.abort()
def test_installBusinessTemplateListFromRepository_ignore_when_installed(self):
"""Check that install one business template, this method does not download
many business templates that are already installed
"""
template_tool = self.portal.portal_templates
# Delete not installed bt5 to check easily if more not installed was
# created
for bt5 in template_tool.getBuiltBusinessTemplateList():
bt5.delete()
bt5_name_list = ['erp5_calendar']
template_tool.installBusinessTemplateListFromRepository(bt5_name_list,
before = dict((bt.getTitle(), bt.getId())
for bt in template_tool.getInstalledBusinessTemplateList())
bt_title = 'erp5_calendar'
template_tool.installBusinessTemplateListFromRepository([bt_title],
install_dependency=True)
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):
"""Check sorting of a list of business template by their dependencies
......
......@@ -492,43 +492,15 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase):
% ', '.join(not_found_list))
return new_template_list
def setupAutomaticBusinessTemplateRepository(self, accept_public=True,
searchable_business_template_list=None):
# Try to setup some valid Repository List by reusing ERP5TypeTestCase API.
# if accept_public we can accept public repository can be set, otherwise
# we let failure happens.
if searchable_business_template_list is None:
searchable_business_template_list = ["erp5_base"]
# Assume that the public official repository is a valid repository
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 setupAutomaticBusinessTemplateRepository(self,
searchable_business_template_list=("erp5_base",)):
template_tool = self.portal.portal_templates
bt_set = set(searchable_business_template_list).difference(x['title']
for x in template_tool.repository_dict.itervalues() for x in x)
if bt_set:
template_tool.updateRepositoryBusinessTemplateList(set(
os.path.dirname(x[0]) for x in self._getBTPathAndIdList(bt_set)),
genbt5list=1)
def failIfDifferentSet(self, a, b, msg=""):
if not msg:
......
......@@ -316,6 +316,11 @@ class Git(WorkingCopy):
#except AttributeError:
# 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=()):
context = self.aq_parent
request = context.REQUEST
......
......@@ -65,10 +65,17 @@ class Subversion(WorkingCopy):
_login_cookie_name = 'erp5_subversion_login'
_ssl_trust_cookie_name = 'erp5_subversion_ssl_trust'
def __init__(self, path):
WorkingCopy.__init__(self, path)
if path and not os.path.exists(os.path.join(self.working_copy, '.svn')):
raise NotAWorkingCopyError(self.working_copy)
def __init__(self, *args, **kw):
WorkingCopy.__init__(self, *args, **kw)
try:
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):
"""Set login information.
......@@ -159,6 +166,12 @@ class Subversion(WorkingCopy):
def getRemoteComment(self):
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):
return self._getClient().export(path, url)
......
......@@ -89,9 +89,9 @@ class WorkingCopy(Implicit):
if cls.reference:
cls._registry.append((cls.reference, cls))
def __init__(self, path=None):
def __init__(self, path=None, restricted=False):
if path:
self.working_copy = self.checkWorkingPath(path)
self.working_copy = self.checkWorkingPath(path, restricted)
def getWorkingCopyList(self):
working_copy_list = []
......@@ -111,7 +111,7 @@ class WorkingCopy(Implicit):
os.mkdir(path)
self.working_copy = path
def checkWorkingPath(self, path):
def checkWorkingPath(self, path, restricted):
# First remove any '..' to prevent escaping.
# Note that 'normpath' ignore symlinks so it would not do it correctly.
parts = path.split(os.sep)
......@@ -124,11 +124,14 @@ class WorkingCopy(Implicit):
# Allow symlinks inside instance home.
path = os.path.normpath(path)
real_path = os.path.realpath(path)
for allowed in getConfiguration().instancehome, gettempdir():
if issubdir(allowed, path) or issubdir(allowed, real_path):
return real_path
if restricted and not any(
issubdir(allowed, path) or issubdir(allowed, real_path)
for allowed in (getConfiguration().instancehome, gettempdir())):
raise Unauthorized("Unauthorized access to path %r."
" 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):
try:
......@@ -186,7 +189,7 @@ class WorkingCopy(Implicit):
"""
if business_template.getBuildingState() == 'draft':
business_template.edit()
business_template.build()
business_template.build(update_revision=False)
self._export(business_template)
def _export(self, business_template):
......@@ -199,16 +202,6 @@ class WorkingCopy(Implicit):
def update(self, keep=False):
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):
try:
hasDiff = aq_base(self).__hasDiff
......@@ -328,7 +321,7 @@ class WorkingCopy(Implicit):
title='tmp_bt_revert',
template_path_list=path_added_list)
tmp_bt.edit()
tmp_bt.build()
tmp_bt.build(update_revision=False)
# Install then uninstall it to remove objects from ZODB
tmp_bt.install()
tmp_bt.uninstall()
......@@ -338,16 +331,16 @@ class WorkingCopy(Implicit):
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:
for x in WorkingCopy._registry:
if x[0] == vcs:
return x[1](path)
return x[1](path, restricted)
raise ValueError("Unsupported Version Control System: %s" % vcs)
elif path:
for x in WorkingCopy._registry:
try:
return x[1](path)
return x[1](path, restricted)
except NotAWorkingCopyError:
pass
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