Commit 29dd95fa by Danièle Vanbaelinghem

Add new API of id tool:

- add the id generators generic
- add the zodb continuous increasing id generator
- add the sql non continuous increasing id generator
- change id tool for the id generators and the compatiblity with the old api
- change the business template to not modify the generator dictionaries
  during the export and the install of bt
- change the test of id tool



git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@34543 20353a03-c40f-0410-a6d1-a30d3c3de9de
1 parent 874be360
......@@ -55,7 +55,7 @@ from Products.ERP5Type.Utils import readLocalTest, \
writeLocalTest, \
removeLocalTest
from Products.ERP5Type.Utils import convertToUpperCase
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type.XMLObject import XMLObject
from OFS.Traversable import NotFound
from OFS import SimpleItem, XMLExportImport
......@@ -591,6 +591,10 @@ class BaseTemplateItem(Implicit, Persistent):
obj.deletePdfContent()
elif meta_type == 'Script (Python)':
obj._code = None
elif interfaces.IIdGenerator.providedBy(obj):
for dict_name in ('last_max_id_dict', 'last_id_dict'):
if getattr(obj, dict_name, None) is not None:
setattr(obj, dict_name, None)
return obj
def getTemplateTypeName(self):
......@@ -1064,6 +1068,14 @@ class ObjectTemplateItem(BaseTemplateItem):
obj._setProperty(
'business_template_registered_skin_selections',
skin_selection_list, type='tokens')
# in case the portal ids, we want keep the property dict
elif interfaces.IIdGenerator.providedBy(obj) and \
old_obj is not None:
for dict_name in ('last_max_id_dict', 'last_id_dict'):
# Keep previous last id dict
if getattr(old_obj, dict_name, None) is not None:
old_dict = getattr(old_obj, dict_name, None)
setattr(obj, dict_name, old_dict)
recurse(restoreHook, obj)
# now put original order group
......
##############################################################################
#
# Copyright (c) 2010 Nexedi SARL and Contributors. All Rights Reserved.
# Daniele Vanbaelinghem <daniele@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import zope.interface
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, Constraint, interfaces
from Products.ERP5Type.Cache import caching_instance_method
from Products.ERP5Type.Base import Base
from Products.CMFCore.utils import getToolByName
from zLOG import LOG, INFO
class IdGenerator(Base):
"""
Generator of Ids
"""
zope.interface.implements(interfaces.IIdGenerator)
# CMF Type Definition
meta_type = 'ERP5 Id Generator'
portal_type = 'Id Generator'
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative property
property_sheets = ( PropertySheet.Base,
PropertySheet.Version,
PropertySheet.Reference)
security.declareProtected(Permissions.AccessContentsInformation,
'getLatestVersionValue')
def getLatestVersionValue(self, **kw):
"""
Return the last generator with the reference
"""
id_tool = getToolByName(self, 'portal_ids', None)
last_id = id_tool._getLatestIdGenerator(self.getReference())
last_version = id_tool._getOb(last_id)
return last_version
security.declareProtected(Permissions.AccessContentsInformation,
'generateNewId')
def generateNewId(self, id_group=None, default=None,):
"""
Generate the next id in the sequence of ids of a particular group
Use int to store the last_id, use also a persistant mapping for to be
persistent.
"""
try:
specialise = self.getSpecialiseValue()
except AttributeError:
raise AttributeError, 'specialise is not defined'
if specialise is None:
raise ValueError, "the id generator %s doesn't have specialise value" %\
self.getReference()
return specialise.getLatestVersionValue().generateNewId(id_group=id_group,
default=default)
security.declareProtected(Permissions.AccessContentsInformation,
'generateNewIdList')
def generateNewIdList(self, id_group=None, id_count=1, default=None):
"""
Generate a list of next ids in the sequence of ids of a particular group
Store the last id on a database in the portal_ids table
If stored in zodb is enable, to store the last id use Length inspired
by BTrees.Length to manage conflict in the zodb, use also a persistant
mapping to be persistent
"""
# For compatibilty with sql data, must not use id_group as a list
if not isinstance(id_group, str):
raise AttributeError, 'id_group is not a string'
try:
specialise = self.getSpecialiseValue()
except AttributeError:
raise AttributeError, 'specialise is not defined'
if specialise is None:
raise ValueError, "the id generator %s doesn't have specialise value" %\
self.getReference()
return specialise.getLatestVersionValue().generateNewIdList(id_group=id_group, \
id_count=id_count, default=default)
security.declareProtected(Permissions.AccessContentsInformation,
'initializeGenerator')
def initializeGenerator(self):
"""
Initialize generator. This is mostly used when a new ERP5 site
is created. Some generators will need to do some initialization like
creating SQL Database, prepare some data in ZODB, etc
"""
specialise = self.getSpecialiseValue()
if specialise is None:
raise ValueError, "the id generator %s doesn't have specialise value" %\
self.getReference()
specialise.getLatestVersionValue().initializeGenerator()
security.declareProtected(Permissions.AccessContentsInformation,
'clearGenerator')
def clearGenerator(self):
"""
Clear generators data. This can be usefull when working on a
development instance or in some other rare cases. This will
loose data and must be use with caution
This can be incompatible with some particular generator implementation,
in this case a particular error will be raised (to be determined and
added here)
"""
specialise = self.getSpecialiseValue()
if specialise is None:
raise ValueError, "the id generator %s doesn't have specialise value" %\
self.getReference()
specialise.getLatestVersionValue().clearGenerator()
##############################################################################
#
# Copyright (c) 2010 Nexedi SARL and Contributors. All Rights Reserved.
# Daniele Vanbaelinghem <daniele@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import zope.interface
from Acquisition import aq_base
from AccessControl import ClassSecurityInfo
from Products.ERP5Type.Globals import PersistentMapping
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5.Document.IdGenerator import IdGenerator
from _mysql_exceptions import ProgrammingError
from zLOG import LOG, INFO
import persistent
class LastMaxGeneratedId(persistent.Persistent):
"""
Store the last id generated
The object support application-level conflict resolution
"""
def __init__(self, value=0):
self.value = value
def __getstate__(self):
return self.value
def __setstate__(self, value):
self.value = value
def set(self, value):
self.value = value
def _p_resolveConflict(self, first_id, second_id):
return max(first_id, second_id)
class SQLNonContinuousIncreasingIdGenerator(IdGenerator):
"""
Generate some ids with mysql storage and also zodb is enabled
by the checkbox : StoredInZodb
"""
zope.interface.implements(interfaces.IIdGenerator)
# CMF Type Definition
meta_type = 'ERP5 SQL Non Continous Increasing Id Generator'
portal_type = 'SQL Non Continous Increasing Id Generator'
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative property
property_sheets = (PropertySheet.SQLIdGenerator,
) + IdGenerator.property_sheets
def _generateNewId(self, id_group, id_count=1, default=None):
"""
Return the next_id with the last_id with the sql method
Store the last id on a database in the portal_ids table
If stored in zodb is enable, to store the last id use LastMaxGeneratedId inspired
by BTrees.Length to manage conflict in the zodb, use also a persistant
mapping to be persistent
"""
# Check the arguments
if id_group in (None, 'None'):
raise ValueError, '%s is not a valid group Id.' % (repr(id_group), )
if default is None:
default = 0
# Retrieve the zsql method
portal = self.getPortalObject()
generate_id_method = getattr(portal, 'IdTool_zGenerateId', None)
commit_method = getattr(portal, 'IdTool_zCommit', None)
get_last_id_method = getattr(portal, 'IdTool_zGetLastId', None)
if None in (generate_id_method, commit_method, get_last_id_method):
raise AttributeError, 'Error while generating Id: ' \
'idTool_zGenerateId and/or IdTool_zCommit and/or idTool_zGetLastId' \
'could not be found.'
result_query = generate_id_method(id_group=id_group, id_count=id_count, \
default=default)
try:
# Tries of generate the new_id
new_id = result_query[0]['LAST_INSERT_ID()']
# Commit the changement of new_id
commit_method()
except ProgrammingError:
# If the database not exist, initialise the generator
self.initializeGenerator()
if self.getStoredInZodb():
# Store the new_id on ZODB if the checkbox storedInZodb is enabled
self.last_max_id_dict = getattr(aq_base(self), \
'last_max_id_dict', None)
if self.last_max_id_dict is None:
# If the dictionary not exist, initialize the generator
self.initializeGenerator()
# Store the new value id
if self.last_max_id_dict.get(id_group, None) is None:
self.last_max_id_dict[id_group] = LastMaxGeneratedId(new_id)
self.last_max_id_dict[id_group].set(new_id)
return new_id
security.declareProtected(Permissions.AccessContentsInformation,
'generateNewId')
def generateNewId(self, id_group=None, default=None):
"""
Generate the next id in the sequence of ids of a particular group
"""
new_id = self._generateNewId(id_group=id_group, default=default)
return new_id
security.declareProtected(Permissions.AccessContentsInformation,
'generateNewIdList')
def generateNewIdList(self, id_group=None, id_count=1, default=None):
"""
Generate a list of next ids in the sequence of ids of a particular group
"""
new_id = self._generateNewId(id_group=id_group, id_count=id_count, \
default=default)
return range(new_id - id_count + 1, new_id + 1)
security.declareProtected(Permissions.AccessContentsInformation,
'initializeGenerator')
def initializeGenerator(self):
"""
Initialize generator. This is mostly used when a new ERP5 site
is created. Some generators will need to do some initialization like
prepare some data in ZODB
"""
LOG('initialize SQL Generator', INFO, 'Id Generator: %s' % (self,))
# Check the dictionnary
if getattr(self, 'last_max_id_dict', None) is None:
self.last_max_id_dict = PersistentMapping()
# Create table portal_ids if not exists
portal = self.getPortalObject()
get_value_list = getattr(portal, 'IdTool_zGetValueList', None)
if get_value_list is None:
raise AttributeError, 'Error while initialize generator:' \
'idTool_zGetValueList could not be found.'
try:
get_value_list()
except ProgrammingError:
drop_method = getattr(portal, 'IdTool_zDropTable', None)
create_method = getattr(portal, 'IdTool_zCreateEmptyTable', None)
if None in (drop_method, create_method):
raise AttributeError, 'Error while initialize generator: ' \
'idTool_zDropTable and/or idTool_zCreateTable could not be found.'
drop_method()
create_method()
# XXX compatiblity code below, dump the old dictionnaries
# Retrieve the zsql_method
portal_ids = getattr(self, 'portal_ids', None)
get_last_id_method = getattr(portal, 'IdTool_zGetLastId', None)
set_last_id_method = getattr(portal, 'IdTool_zSetLastId', None)
if None in (get_last_id_method, set_last_id_method):
raise AttributeError, 'Error while generating Id: ' \
'idTool_zGetLastId and/or idTool_zSetLastId could not be found.'
storage = self.getStoredInZodb()
# Recovery last_max_id_dict datas in zodb if enabled and is in mysql
if len(self.last_max_id_dict) != 0:
dump_dict = self.last_max_id_dict
elif getattr(portal_ids, 'dict_length_ids', None) is not None:
dump_dict = portal_ids.dict_length_ids
for id_group, last_id in dump_dict.items():
last_insert_id = get_last_id_method(id_group=id_group)
if len(last_insert_id) != 0:
last_insert_id = last_insert_id[0]['LAST_INSERT_ID()']
if last_insert_id > last_id.value:
# Check value in dict
if storage and (not self.last_max_id_dict.has_key(id_group) or \
self.last_max_id_dict.has_key[id_group] != last_insert_id):
self.last_max_id_dict[id_group] = LastMaxGeneratedId(last_insert_id)
self.last_max_id_dict[id_group].set(last_insert_id)
continue
last_id = int(last_id.value)
set_last_id_method(id_group=id_group, last_id=last_id)
if storage:
self.last_max_id_dict[id_group] = LastMaxGeneratedId(last_id)
self.last_max_id_dict[id_group].set(last_id)
security.declareProtected(Permissions.AccessContentsInformation,
'clearGenerator')
def clearGenerator(self):
"""
Clear generators data. This can be usefull when working on a
development instance or in some other rare cases. This will
loose data and must be use with caution
This can be incompatible with some particular generator implementation,
in this case a particular error will be raised (to be determined and
added here)
"""
# Remove dictionary
self.last_max_id_dict = PersistentMapping()
# Remove and recreate portal_ids table
portal = self.getPortalObject()
drop_method = getattr(portal, 'IdTool_zDropTable', None)
create_method = getattr(portal, 'IdTool_zCreateEmptyTable', None)
if None in (drop_method, create_method):
raise AttributeError, 'Error while clear generator: ' \
'idTool_zDropTable and/or idTool_zCreateTable could not be found.'
drop_method()
create_method()
##############################################################################
#
# Copyright (c) 2010 Nexedi SARL and Contributors. All Rights Reserved.
# Daniele Vanbaelinghem <daniele@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import zope.interface
from AccessControl import ClassSecurityInfo
from Products.ERP5Type.Globals import PersistentMapping
from Products.ERP5Type import Permissions, interfaces
from Products.ERP5.Document.IdGenerator import IdGenerator
from zLOG import LOG, INFO
class ZODBContinuousIncreasingIdGenerator(IdGenerator):
"""
Create some Ids with the zodb storage
"""
zope.interface.implements(interfaces.IIdGenerator)
# CMF Type Definition
meta_type = 'ERP5 ZODB Continous Increasing Id Generator'
portal_type = 'ZODB Continous Increasing Id Generator'
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
def _generateNewId(self, id_group, id_count=1, default=None):
"""
Return the new_id from the last_id of the zodb
Use int to store the last_id, use also a persistant mapping for to be
persistent.
"""
if id_group in (None, 'None'):
raise ValueError, '%s is not a valid group Id.' % (repr(id_group), )
if default is None:
default = 0
self.last_id_dict = getattr(self, 'last_id_dict', None)
if self.last_id_dict is None:
# If the dictionary not exist initialize generator
self.initializeGenerator()
marker = []
# Retrieve the last id
last_id = self.last_id_dict.get(id_group, marker)
if last_id is marker:
new_id = default
if id_count > 1:
# If create a list use the default and increment
new_id = new_id + id_count - 1
else:
# Increment the last_id
new_id = last_id + id_count
# Store the new_id in the dictionary
self.last_id_dict[id_group] = new_id
return new_id
security.declareProtected(Permissions.AccessContentsInformation,
'generateNewId')
def generateNewId(self, id_group=None, default=None):
"""
Generate the next id in the sequence of ids of a particular group
"""
new_id = self._generateNewId(id_group=id_group, default=default)
return new_id
security.declareProtected(Permissions.AccessContentsInformation,
'generateNewIdList')
def generateNewIdList(self, id_group=None, id_count=1, default=None):
"""
Generate a list of next ids in the sequence of ids of a particular group
"""
new_id = self._generateNewId(id_group=id_group, id_count=id_count, \
default=default)
return range(new_id - id_count + 1, new_id + 1)
security.declareProtected(Permissions.AccessContentsInformation,
'initializeGenerator')
def initializeGenerator(self):
"""
Initialize generator. This is mostly used when a new ERP5 site
is created. Some generators will need to do some initialization like
prepare some data in ZODB
"""
LOG('initialize ZODB Generator', INFO, 'Id Generator: %s' % (self,))
if getattr(self, 'last_id_dict', None) is None:
self.last_id_dict = PersistentMapping()
# XXX compatiblity code below, dump the old dictionnaries
portal_ids = getattr(self, 'portal_ids', None)
# Dump the dict_ids dictionary
if getattr(portal_ids, 'dict_ids', None) is not None:
for id_group, last_id in portal_ids.dict_ids.items():
if self.last_id_dict.has_key(id_group) and \
self.last_id_dict[id_group] > last_id:
continue
self.last_id_dict[id_group] = last_id
security.declareProtected(Permissions.AccessContentsInformation,
'clearGenerator')
def clearGenerator(self):
"""
Clear generators data. This can be usefull when working on a
development instance or in some other rare cases. This will
loose data and must be use with caution
This can be incompatible with some particular generator implementation,
in this case a particular error will be raised (to be determined and
added here)
"""
# Remove dictionary
self.last_id_dict = PersistentMapping()
......@@ -1848,15 +1848,11 @@ class ERP5Generator(PortalGenerator):
# we don't want to make it crash
if p.erp5_sql_connection_type is not None:
setattr(p, 'isIndexable', ConstantGetter('isIndexable', value=True))
portal_catalog = p.portal_catalog
# Clear portal ids sql table, like this we do not take
# ids for a previously created web site
# XXX It's temporary, a New API will be implemented soon
# the code will be change
p.IdTool_zDropTable()
p.IdTool_zCreateTable()
p.portal_ids.initializeGenerator(all=True)
# Then clear the catalog and reindex it
portal_catalog.manage_catalogClear()
p.portal_catalog.manage_catalogClear()
# Calling ERP5Site_reindexAll is useless.
def setupUserFolder(self, p):
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
# Daniele Vanbaelinghem <daniele@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from Products.CMFCore.Expression import Expression
class SQLIdGenerator:
"""
Id Generator provides properties to check the storage on ZODB
"""
_properties = (
{'id' :'stored_in_zodb',
'label' : 'A flag indicating if the last_id is stored in the ZODB',
'type' :'boolean',
'mode' :'w'
},
)
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
# Daniele Vanbaelinghem <daniele@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from Products.CMFCore.Expression import Expression
class SQLIdGenerator:
"""
Id Generator provides properties to check the storage on ZODB
"""
_properties = (
{'id' :'stored_in_zodb',
'label' : 'A flag indicating if the last_id is stored in the ZODB',
'type' :'boolean',
'mode' :'w'
},
)
......@@ -129,6 +129,14 @@ class TemplateTool (BaseTool):
installed_bts.append(bt)
return installed_bts
def getInstalledBusinessTemplateRevision(self, title, **kw):
"""
Return the revision of business template installed with the title
given
"""
bt = self.getInstalledBusinessTemplate(title)
return bt.getRevision()
def getBuiltBusinessTemplatesList(self):
"""Deprecated.
"""
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!