############################################################################## # # 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 six import zope.interface from AccessControl import ClassSecurityInfo from Products.ERP5Type import Permissions, interfaces from Products.ERP5.Document.IdGenerator import IdGenerator from BTrees.OOBTree import OOBTree from zLOG import LOG, INFO @zope.interface.implementer(interfaces.IIdGenerator) class ZODBContinuousIncreasingIdGenerator(IdGenerator): """ Create some Ids with the zodb storage """ # 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, poison=False): """ 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('%r is not a valid group Id.' % id_group) if default is None: default = 0 last_id_dict = getattr(self, 'last_id_dict', None) if last_id_dict is None: # If the dictionary not exist initialize generator self.initializeGenerator() last_id_dict = self.last_id_dict # Retrieve the last id and increment new_id = last_id_dict.get(id_group, default - 1) + id_count # Store the new_id in the dictionary last_id_dict[id_group] = None if poison else new_id return new_id security.declareProtected(Permissions.AccessContentsInformation, 'generateNewId') def generateNewId(self, id_group=None, default=None, poison=False): """ Generate the next id in the sequence of ids of a particular group """ return self._generateNewId(id_group=id_group, default=default, poison=poison) security.declareProtected(Permissions.AccessContentsInformation, 'generateNewIdList') def generateNewIdList(self, id_group=None, id_count=1, default=None, poison=False): """ Generate a list of next ids in the sequence of ids of a particular group """ new_id = 1 + self._generateNewId(id_group=id_group, id_count=id_count, default=default, poison=poison) if six.PY2: return range(new_id - id_count, new_id) else: return list(range(new_id - id_count, new_id)) security.declareProtected(Permissions.ModifyPortalContent, '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 = OOBTree() # 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 not isinstance(id_group, str): id_group = repr(id_group) if id_group in self.last_id_dict and \ self.last_id_dict[id_group] > last_id: continue self.last_id_dict[id_group] = last_id security.declareProtected(Permissions.ModifyPortalContent, '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 = OOBTree() security.declareProtected(Permissions.ModifyPortalContent, 'exportGeneratorIdDict') def exportGeneratorIdDict(self): """ Export last id values in a dictionnary in the form { group_id : last_id } """ return dict(self.last_id_dict) security.declareProtected(Permissions.ModifyPortalContent, 'importGeneratorIdDict') def importGeneratorIdDict(self, id_dict, clear=False): """ Import data, this is usefull if we want to replace a generator by another one. """ if clear: self.clearGenerator() if not isinstance(id_dict, dict): raise TypeError('the argument given is not a dictionary') for value in id_dict.values(): if not isinstance(value, six.integer_types): raise TypeError('the value given in dictionary is not a integer') self.last_id_dict.update(id_dict) security.declareProtected(Permissions.ModifyPortalContent, 'rebuildGeneratorIdDict') def rebuildGeneratorIdDict(self): """ Rebuild generator id dict. In fact, export it, clear it and import it into new dict. This is mostly intendted to use when we are migrating the id dict structure. """ id_dict = self.exportGeneratorIdDict() self.importGeneratorIdDict(id_dict=id_dict, clear=True)