Commit de9b52a4 authored by Vincent Pelletier's avatar Vincent Pelletier

Improve doctrgins.

Use getattr instead of hasattr to avoid the read conflict error not raised by hasattr.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@7025 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 82fe8c07
...@@ -56,7 +56,7 @@ except ImportError: ...@@ -56,7 +56,7 @@ except ImportError:
class LocalConfiguration(Implicit): class LocalConfiguration(Implicit):
""" """
Holds local configuration information Contains local configuration information
""" """
def __init__(self, **kw): def __init__(self, **kw):
self.__dict__.update(kw) self.__dict__.update(kw)
...@@ -69,15 +69,10 @@ class TemplateTool (BaseTool): ...@@ -69,15 +69,10 @@ class TemplateTool (BaseTool):
TemplateTool manages Business Templates. TemplateTool manages Business Templates.
TemplateTool provides some methods to deal with Business Templates: TemplateTool provides some methods to deal with Business Templates:
- download - download
- publish - publish
- install - install
- update - update
- save - save
""" """
id = 'portal_templates' id = 'portal_templates'
...@@ -97,28 +92,38 @@ class TemplateTool (BaseTool): ...@@ -97,28 +92,38 @@ class TemplateTool (BaseTool):
def getInstalledBusinessTemplate(self, title, **kw): def getInstalledBusinessTemplate(self, title, **kw):
""" """
Return a installed business template if any. Return an installed version of business template of a certain title.
""" """
# This can be slow if, say, 10000 business templates are present. # This can be slow if, say, 10000 business templates are present.
# However, that unlikely happens, and using a Z SQL Method has a potential danger # However, that unlikely happens, and using a Z SQL Method has a
# because business templates may exchange catalog methods, so the database could be # potential danger because business templates may exchange catalog
# broken temporarily. # methods, so the database could be broken temporarily.
for bt in self.contentValues(filter={'portal_type':'Business Template'}): for bt in self.contentValues(filter={'portal_type':'Business Template'}):
if bt.getInstallationState() == 'installed' and bt.getTitle() == title: if bt.getInstallationState() == 'installed' and bt.getTitle() == title:
return bt return bt
return None return None
def updateLocalConfiguration(self, template, **kw): def updateLocalConfiguration(self, template, **kw):
"""
Call the update method on the configuration, create if it doesn't
exists.
"""
template_id = template.getId() template_id = template.getId()
if not hasattr(self, '_local_configuration'): self._local_configuration = PersistentMapping() if getattr(self, '_local_configuration', None) is None:
self._local_configuration = PersistentMapping()
if not self._local_configuration.has_key(template_id): if not self._local_configuration.has_key(template_id):
self._local_configuration[template_id] = LocalConfiguration(**kw) self._local_configuration[template_id] = LocalConfiguration(**kw)
else: else:
self._local_configuration[template_id].update(**kw) self._local_configuration[template_id].update(**kw)
def getLocalConfiguration(self, template): def getLocalConfiguration(self, template):
"""
Return the configuration for the given business template, or None if
it's not defined.
"""
template_id = template.getId() template_id = template.getId()
if not hasattr(self, '_local_configuration'): self._local_configuration = PersistentMapping() if getattr(self, '_local_configuration', None) is None:
self._local_configuration = PersistentMapping()
local_configuration = self._local_configuration.get(template_id, None) local_configuration = self._local_configuration.get(template_id, None)
if local_configuration is not None: if local_configuration is not None:
return local_configuration.__of__(template) return local_configuration.__of__(template)
...@@ -127,27 +132,32 @@ class TemplateTool (BaseTool): ...@@ -127,27 +132,32 @@ class TemplateTool (BaseTool):
security.declareProtected( 'Import/Export objects', 'save' ) security.declareProtected( 'Import/Export objects', 'save' )
def save(self, business_template, REQUEST=None, RESPONSE=None): def save(self, business_template, REQUEST=None, RESPONSE=None):
""" """
Save BT in folder format Save the BusinessTemplate in the servers's filesystem.
""" """
cfg = getConfiguration() cfg = getConfiguration()
path = os.path.join(cfg.clienthome, '%s' % (business_template.getTitle(),)) path = os.path.join(cfg.clienthome,
'%s' % (business_template.getTitle(),))
path = pathname2url(path) path = pathname2url(path)
business_template.export(path=path, local=1) business_template.export(path=path, local=1)
if REQUEST is not None: if REQUEST is not None:
ret_url = business_template.absolute_url() + '/' + REQUEST.get('form_id', 'view') ret_url = '%s/%s?portal_status_message=Saved+in+%s+.' % \
qs = '?portal_status_message=Saved+in+%s+.' % pathname2url(path) (business_template.absolute_url(),
if RESPONSE is None: RESPONSE = REQUEST.RESPONSE REQUEST.get('form_id', 'view'), pathname2url(path))
return REQUEST.RESPONSE.redirect( ret_url + qs ) if RESPONSE is None:
RESPONSE = REQUEST.RESPONSE
return REQUEST.RESPONSE.redirect( ret_url )
security.declareProtected( 'Import/Export objects', 'export' ) security.declareProtected( 'Import/Export objects', 'export' )
def export(self, business_template, REQUEST=None, RESPONSE=None): def export(self, business_template, REQUEST=None, RESPONSE=None):
""" """
Export BT in tarball format Export the Business Template as a bt5 file and offer the user to
download it.
""" """
path = business_template.getTitle() path = business_template.getTitle()
path = pathname2url(path) path = pathname2url(path)
tmpdir_path = mkdtemp() # XXXXXXXXXXXXXXX Why is it necessary to create a temporary directory? tmpdir_path = mkdtemp() # XXX Why is it necessary to create a temporary
current_directory = os.getcwd() # XXXXXXXXXXXXXXXXXX not thread safe # directory?
current_directory = os.getcwd() # XXX not thread safe
os.chdir(tmpdir_path) os.chdir(tmpdir_path)
export_string = business_template.export(path=path) export_string = business_template.export(path=path)
os.chdir(current_directory) os.chdir(current_directory)
...@@ -165,17 +175,19 @@ class TemplateTool (BaseTool): ...@@ -165,17 +175,19 @@ class TemplateTool (BaseTool):
security.declareProtected( 'Import/Export objects', 'publish' ) security.declareProtected( 'Import/Export objects', 'publish' )
def publish(self, business_template, url, username=None, password=None): def publish(self, business_template, url, username=None, password=None):
""" """
Publish in a format or another Publish the given business template at the given URL.
""" """
business_template.build() business_template.build()
export_string = self.manage_exportObject(id=business_template.getId(), download=1) export_string = self.manage_exportObject(id=business_template.getId(),
download=1)
bt = Resource(url, username=username, password=password) bt = Resource(url, username=username, password=password)
bt.put(file=export_string, content_type='application/x-erp5-business-template') bt.put(file=export_string,
content_type='application/x-erp5-business-template')
business_template.setPublicationUrl(url) business_template.setPublicationUrl(url)
def update(self, business_template): def update(self, business_template):
""" """
Update an existing template Update an existing template from its publication URL.
""" """
url = business_template.getPublicationUrl() url = business_template.getPublicationUrl()
id = business_template.getId() id = business_template.getId()
...@@ -186,7 +198,7 @@ class TemplateTool (BaseTool): ...@@ -186,7 +198,7 @@ class TemplateTool (BaseTool):
def _importBT(self, path=None, id=id): def _importBT(self, path=None, id=id):
""" """
Import template from a temp file Import template from a temp file (as uploaded by the user)
""" """
file = open(path, 'r') file = open(path, 'r')
try: try:
...@@ -202,6 +214,8 @@ class TemplateTool (BaseTool): ...@@ -202,6 +214,8 @@ class TemplateTool (BaseTool):
bt.id = id # Make sure id is consistent bt.id = id # Make sure id is consistent
bt.setProperty('template_format_version', 0, type='int') bt.setProperty('template_format_version', 0, type='int')
else: # new version else: # new version
# 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') tar = tarfile.open(path, 'r:gz')
try: try:
# create bt object # create bt object
...@@ -217,7 +231,8 @@ class TemplateTool (BaseTool): ...@@ -217,7 +231,8 @@ class TemplateTool (BaseTool):
except KeyError: except KeyError:
continue continue
value = tar.extractfile(info).read() value = tar.extractfile(info).read()
if prop_type == 'text' or prop_type == 'string' or prop_type == 'int': if prop_type == 'text' or prop_type == 'string'
or prop_type == 'int':
prop_dict[pid] = value prop_dict[pid] = value
elif prop_type == 'lines' or prop_type == 'tokens': elif prop_type == 'lines' or prop_type == 'tokens':
prop_dict[pid[:-5]] = value.split(str(os.linesep)) prop_dict[pid[:-5]] = value.split(str(os.linesep))
...@@ -244,23 +259,27 @@ class TemplateTool (BaseTool): ...@@ -244,23 +259,27 @@ class TemplateTool (BaseTool):
if REQUEST is not None: if REQUEST is not None:
ret_url = self.absolute_url() + '/' + REQUEST.get('form_id', 'view') ret_url = self.absolute_url() + '/' + REQUEST.get('form_id', 'view')
REQUEST.RESPONSE.redirect("%s?portal_status_message=Business+Templates+Downloaded+Successfully" REQUEST.RESPONSE.redirect("%s?portal_status_message=Business+" \
"Templates+Downloaded+Successfully"
% ret_url) % ret_url)
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):
""" """
Download Business template, can be file or local directory Download Business Template from url, can be file or local directory
""" """
# For backward compatibility; If REQUEST is passed, it is likely from the management interface. # For backward compatibility: If REQUEST is passed, it is likely that we
# come from the management interface.
if REQUEST is not None: if REQUEST is not None:
return self.manage_download(url, id=id, REQUEST=REQUEST) return self.manage_download(url, id=id, REQUEST=REQUEST)
urltype, name = splittype(url) urltype, name = splittype(url)
if os.path.isdir(name): # new version of business template in plain format (folder) if os.path.isdir(name): # new version of business template in plain
# format (folder)
file_list = [] file_list = []
def callback(arg, directory, files): def callback(arg, directory, files):
if 'CVS' not in directory and '.svn' not in directory: if 'CVS' not in directory and '.svn' not in directory: # XXX:
# possible side-effects
for file in files: for file in files:
file_list.append(os.path.join(directory, file)) file_list.append(os.path.join(directory, file))
...@@ -305,14 +324,15 @@ class TemplateTool (BaseTool): ...@@ -305,14 +324,15 @@ class TemplateTool (BaseTool):
def importFile(self, import_file=None, id=None, REQUEST=None, **kw): def importFile(self, import_file=None, id=None, REQUEST=None, **kw):
""" """
Import Business template from one file Import Business Template from one file
""" """
if REQUEST is None: if REQUEST is None:
REQUEST = getattr(self, 'REQUEST', None) REQUEST = getattr(self, 'REQUEST', None)
if (import_file is None) or (len(import_file.read()) == 0) : if (import_file is None) or (len(import_file.read()) == 0) :
if REQUEST is not None : if REQUEST is not None :
REQUEST.RESPONSE.redirect("%s?portal_status_message=No+file+or+an+empty+file+was+specified" REQUEST.RESPONSE.redirect("%s?portal_status_message=No+file+or+an+" \
"empty+file+was+specified"
% self.absolute_url()) % self.absolute_url())
return return
else : else :
...@@ -335,21 +355,24 @@ class TemplateTool (BaseTool): ...@@ -335,21 +355,24 @@ class TemplateTool (BaseTool):
if REQUEST is not None: if REQUEST is not None:
ret_url = self.absolute_url() + '/' + REQUEST.get('form_id', 'view') ret_url = self.absolute_url() + '/' + REQUEST.get('form_id', 'view')
REQUEST.RESPONSE.redirect("%s?portal_status_message=Business+Templates+Imported+Successfully" REQUEST.RESPONSE.redirect("%s?portal_status_message=Business+" \
"Templates+Imported+Successfully"
% ret_url) % ret_url)
def runUnitTestList(self, test_list=[], **kwd) : def runUnitTestList(self, test_list=[], **kwd):
""" """
Runs Unit Tests related to this Business Template Runs Unit Tests related to this Business Template
""" """
# XXX: should check for file presence before trying to execute.
# XXX: should check if the unit test file is configured in the BT
from Products.ERP5Type.tests.runUnitTest import getUnitTestFile from Products.ERP5Type.tests.runUnitTest import getUnitTestFile
return os.popen('/usr/bin/python %s %s 2>&1' % (getUnitTestFile(), ' '.join(test_list))).read() return os.popen('/usr/bin/python %s %s 2>&1'
% (getUnitTestFile(), ' '.join(test_list))).read()
def diffObject(self, REQUEST, **kw): def diffObject(self, REQUEST, **kw):
""" """
Make diff between two objects Make diff between two objects, whose paths are stored in values bt1
and bt2 in the REQUEST object.
""" """
bt1_id = getattr(REQUEST, 'bt1', None) bt1_id = getattr(REQUEST, 'bt1', None)
bt2_id = getattr(REQUEST, 'bt2', None) bt2_id = getattr(REQUEST, 'bt2', None)
...@@ -360,13 +383,19 @@ class TemplateTool (BaseTool): ...@@ -360,13 +383,19 @@ class TemplateTool (BaseTool):
else: else:
return bt1.diffObject(REQUEST, compare_with=bt2_id) return bt1.diffObject(REQUEST, compare_with=bt2_id)
security.declareProtected( 'Import/Export objects', 'updateRepositoryBusinessTemplateList' ) security.declareProtected( 'Import/Export objects',
def updateRepositoryBusinessTemplateList(self, repository_list, REQUEST=None, RESPONSE=None, **kw): 'updateRepositoryBusinessTemplateList' )
"""Update the information on Business Templates in repositories.
def updateRepositoryBusinessTemplateList(self, repository_list,
REQUEST=None, RESPONSE=None, **kw):
"""
Update the information on Business Templates from repositories.
""" """
self.repository_dict = PersistentMapping() self.repository_dict = PersistentMapping()
property_list = ('title', 'version', 'description', 'license', 'dependency', 'copyright') property_list = ('title', 'version', 'description', 'license',
#LOG('updateRepositoryBusiessTemplateList', 0, 'repository_list = %r' % (repository_list,)) 'dependency', 'copyright')
#LOG('updateRepositoryBusiessTemplateList', 0,
# 'repository_list = %r' % (repository_list,))
for repository in repository_list: for repository in repository_list:
url = '/'.join([repository, 'bt5list']) url = '/'.join([repository, 'bt5list'])
f = urlopen(url) f = urlopen(url)
...@@ -394,11 +423,16 @@ class TemplateTool (BaseTool): ...@@ -394,11 +423,16 @@ class TemplateTool (BaseTool):
property_dict = {} property_dict = {}
property_dict['id'] = id property_dict['id'] = id
property_dict['title'] = temp_property_dict.get('title', [''])[0] property_dict['title'] = temp_property_dict.get('title', [''])[0]
property_dict['version'] = temp_property_dict.get('version', [''])[0] property_dict['version'] = \
property_dict['description'] = temp_property_dict.get('description', [''])[0] temp_property_dict.get('version', [''])[0]
property_dict['license'] = temp_property_dict.get('license', [''])[0] property_dict['description'] = \
property_dict['dependency_list'] = temp_property_dict.get('dependency', ()) temp_property_dict.get('description', [''])[0]
property_dict['copyright_list'] = temp_property_dict.get('copyright', ()) property_dict['license'] = \
temp_property_dict.get('license', [''])[0]
property_dict['dependency_list'] = \
temp_property_dict.get('dependency', ())
property_dict['copyright_list'] = \
temp_property_dict.get('copyright', ())
property_dict_list.append(property_dict) property_dict_list.append(property_dict)
finally: finally:
...@@ -410,26 +444,33 @@ class TemplateTool (BaseTool): ...@@ -410,26 +444,33 @@ class TemplateTool (BaseTool):
if REQUEST is not None: if REQUEST is not None:
ret_url = self.absolute_url() + '/' + REQUEST.get('form_id', 'view') ret_url = self.absolute_url() + '/' + REQUEST.get('form_id', 'view')
REQUEST.RESPONSE.redirect("%s?portal_status_message=Business+Templates+Updated+Successfully" REQUEST.RESPONSE.redirect("%s?portal_status_message=Business+" \
"Templates+Updated+Successfully"
% ret_url) % ret_url)
security.declareProtected( Permissions.AccessContentsInformation, 'getRepositoryList' ) security.declareProtected( Permissions.AccessContentsInformation,
'getRepositoryList' )
def getRepositoryList(self): def getRepositoryList(self):
"""Get the list of repositories. """
Get the list of repositories.
""" """
return self.repository_dict.keys() return self.repository_dict.keys()
security.declarePublic( 'decodeRepositoryBusinessTemplateUid' ) security.declarePublic( 'decodeRepositoryBusinessTemplateUid' )
def decodeRepositoryBusinessTemplateUid(self, uid): def decodeRepositoryBusinessTemplateUid(self, uid):
"""Decode the uid of a business template in a repository. Return a repository and an id. """
Decode the uid of a business template from a repository.
Return a repository and an id.
""" """
return cPickle.loads(b64decode(uid)) return cPickle.loads(b64decode(uid))
security.declareProtected( Permissions.AccessContentsInformation, 'getRepositoryBusinessTemplateList' ) security.declareProtected( Permissions.AccessContentsInformation,
'getRepositoryBusinessTemplateList' )
def getRepositoryBusinessTemplateList(self, update_only=0, **kw): def getRepositoryBusinessTemplateList(self, update_only=0, **kw):
"""Get the list of Business Templates in repositories. """Get the list of Business Templates in repositories.
""" """
version_state_title_dict = { 'new' : 'New', 'present' : 'Present', 'old' : 'Old' } version_state_title_dict = { 'new' : 'New', 'present' : 'Present',
'old' : 'Old' }
from Products.ERP5Type.Document import newTempBusinessTemplate from Products.ERP5Type.Document import newTempBusinessTemplate
template_list = [] template_list = []
...@@ -442,19 +483,24 @@ class TemplateTool (BaseTool): ...@@ -442,19 +483,24 @@ class TemplateTool (BaseTool):
for property_dict in property_dict_list: for property_dict in property_dict_list:
title = property_dict['title'] title = property_dict['title']
if title not in template_item_dict: if title not in template_item_dict:
# If this is the first time to see this business template, insert it. # If this is the first time to see this business template,
# insert it.
template_item_dict[title] = (repository, property_dict) template_item_dict[title] = (repository, property_dict)
else: else:
# If this business template has been seen before, insert it only if # If this business template has been seen before, insert it only
# this business template is newer. # if this business template is newer.
previous_repository, previous_property_dict = template_item_dict[title] previous_repository, previous_property_dict = \
if self.compareVersions(previous_property_dict['version'], property_dict['version']) < 0: template_item_dict[title]
if self.compareVersions(previous_property_dict['version'],
property_dict['version']) < 0:
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.
for repository, property_dict in template_item_dict.values(): for repository, property_dict in template_item_dict.values():
installed_bt = self.getInstalledBusinessTemplate(property_dict['title']) installed_bt = \
self.getInstalledBusinessTemplate(property_dict['title'])
if installed_bt is not None: if installed_bt is not None:
if self.compareVersions(installed_bt.getVersion(), property_dict['version']) < 0: if self.compareVersions(installed_bt.getVersion(),
property_dict['version']) < 0:
template_item_list.append((repository, property_dict)) template_item_list.append((repository, property_dict))
# FIXME: resolve dependencies # FIXME: resolve dependencies
else: else:
...@@ -487,7 +533,8 @@ class TemplateTool (BaseTool): ...@@ -487,7 +533,8 @@ class TemplateTool (BaseTool):
template_list.sort(lambda x,y:cmp(x.getTitle(), y.getTitle())) template_list.sort(lambda x,y:cmp(x.getTitle(), y.getTitle()))
return template_list return template_list
security.declareProtected( Permissions.AccessContentsInformation, 'getUpdatedRepositoryBusinessTemplateList' ) security.declareProtected( Permissions.AccessContentsInformation,
'getUpdatedRepositoryBusinessTemplateList' )
def getUpdatedRepositoryBusinessTemplateList(self, **kw): def getUpdatedRepositoryBusinessTemplateList(self, **kw):
"""Get the list of updated Business Templates in repositories. """Get the list of updated Business Templates in repositories.
""" """
...@@ -495,28 +542,22 @@ class TemplateTool (BaseTool): ...@@ -495,28 +542,22 @@ class TemplateTool (BaseTool):
return self.getRepositoryBusinessTemplateList(update_only=1, **kw) return self.getRepositoryBusinessTemplateList(update_only=1, **kw)
def compareVersions(self, version1, version2): def compareVersions(self, version1, version2):
"""Return negative if version1 < version2, 0 if version1 == version2, positive if version1 > version2. """
Return negative if version1 < version2, 0 if version1 == version2,
positive if version1 > version2.
Here is the algorithm: Here is the algorithm:
- Non-alphanumeric characters are not significant, besides the function
- Non-alphanumeric characters are not significant, besides the function of delimiters. of delimiters.
- If a level of a version number is missing, it is assumed to be zero. - If a level of a version number is missing, it is assumed to be zero.
- An alphabetical character is less than any numerical value. - An alphabetical character is less than any numerical value.
- Numerical values are compared as integers. - Numerical values are compared as integers.
This archives the following predicates: This implements the following predicates:
- 1.0 < 1.0.1 - 1.0 < 1.0.1
- 1.0rc1 < 1.0 - 1.0rc1 < 1.0
- 1.0a < 1.0.1 - 1.0a < 1.0.1
- 1.1 < 2.0 - 1.1 < 2.0
- 1.0.0 = 1.0 - 1.0.0 = 1.0
""" """
r = re.compile('(\d+|[a-zA-Z])') r = re.compile('(\d+|[a-zA-Z])')
......
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