[Erp5-report] r11853 - /erp5/trunk/products/ERP5Type/Core/
nobody at svn.erp5.org
nobody at svn.erp5.org
Wed Jan 3 02:53:23 CET 2007
Author: jp
Date: Wed Jan 3 02:53:19 2007
New Revision: 11853
URL: http://svn.erp5.org?rev=11853&view=rev
Log:
Moved from Document to prevent unexpected overriding
Added:
erp5/trunk/products/ERP5Type/Core/
erp5/trunk/products/ERP5Type/Core/CacheFactory.py
erp5/trunk/products/ERP5Type/Core/DistributedRamCache.py
erp5/trunk/products/ERP5Type/Core/Folder.py
erp5/trunk/products/ERP5Type/Core/RamCache.py
erp5/trunk/products/ERP5Type/Core/SQLCache.py
erp5/trunk/products/ERP5Type/Core/__init__.py
Added: erp5/trunk/products/ERP5Type/Core/CacheFactory.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Core/CacheFactory.py?rev=11853&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/Core/CacheFactory.py (added)
+++ erp5/trunk/products/ERP5Type/Core/CacheFactory.py Wed Jan 3 02:53:19 2007
@@ -1,0 +1,88 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+# Ivan Tyagov <ivan at 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.
+#
+##############################################################################
+
+
+from AccessControl import ClassSecurityInfo
+from Products.CMFCore import CMFCorePermissions
+from Products.ERP5Type import Permissions
+from Products.ERP5Type import PropertySheet
+from Products.ERP5Type.PropertySheet.CacheFactory import CacheFactory
+from Products.ERP5Type.XMLObject import XMLObject
+from Products.ERP5Type.Cache import CachingMethod
+
+class CacheFactory(XMLObject):
+ """
+ CacheFactory is a collection of cache plugins. CacheFactory is an object which lives in ZODB.
+ """
+
+ meta_type = 'ERP5 Cache Factory'
+ portal_type = 'Cache Factory'
+ isPortalContent = 1
+ isRADContent = 1
+
+ allowed_types = ('ERP5 Ram Cache',
+ 'ERP5 Distributed Ram Cache',
+ 'ERP5 SQL Cache',
+ )
+
+ security = ClassSecurityInfo()
+ security.declareProtected(CMFCorePermissions.ManagePortal,
+ 'manage_editProperties',
+ 'manage_changeProperties',
+ 'manage_propertiesForm',
+ )
+
+ property_sheets = ( PropertySheet.Base
+ , PropertySheet.SimpleItem
+ , PropertySheet.Folder
+ , CacheFactory
+ )
+
+
+ def getCachePluginList(self):
+ """ get ordered list of installed cache plugins in ZODB """
+ cache_plugins = self.objectValues(self.allowed_types)
+ cache_plugins = map(None, cache_plugins)
+ cache_plugins.sort(lambda x,y: cmp(x.int_index, y.int_index))
+ return cache_plugins
+
+ security.declareProtected(Permissions.AccessContentsInformation, 'getRamCacheFactory')
+ def getRamCacheFactory(self):
+ """ Return RAM based cache factory """
+ erp5_site_id = self.getPortalObject().getId()
+ return CachingMethod.factories[erp5_site_id][self.cache_scope]
+
+ security.declareProtected(Permissions.AccessContentsInformation, 'getRamCacheFactoryPluginList')
+ def getRamCacheFactoryPluginList(self):
+ """ Return RAM based list of cache plugins for this factory """
+ return self.getRamCacheFactory().getCachePluginList()
+
+ def clearCache(self):
+ """ clear cache for this cache factory """
+ for cp in self.getRamCacheFactory().getCachePluginList():
+ cp.clearCache()
Added: erp5/trunk/products/ERP5Type/Core/DistributedRamCache.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Core/DistributedRamCache.py?rev=11853&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/Core/DistributedRamCache.py (added)
+++ erp5/trunk/products/ERP5Type/Core/DistributedRamCache.py Wed Jan 3 02:53:19 2007
@@ -1,0 +1,63 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+# Ivan Tyagov <ivan at 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.
+#
+##############################################################################
+
+from AccessControl import ClassSecurityInfo
+from Products.CMFCore import CMFCorePermissions
+from Products.ERP5Type.XMLObject import XMLObject
+from Products.ERP5Type import PropertySheet
+from Products.ERP5.PropertySheet.SortIndex import SortIndex
+from Products.ERP5Type.PropertySheet.BaseCache import BaseCache
+from Products.ERP5Type.PropertySheet.DistributedRamCache import DistributedRamCache
+
+class DistributedRamCache(XMLObject):
+ """
+ DistributedRamCache is a Zope (persistent) representation of
+ the Distributed RAM Cache real cache plugin object.
+ """
+
+ meta_type='ERP5 Distributed Ram Cache'
+ portal_type='Distributed Ram Cache'
+ isPortalContent = 1
+ isRADContent = 1
+
+ allowed_types = ()
+
+ security = ClassSecurityInfo()
+ security.declareProtected(CMFCorePermissions.ManagePortal,
+ 'manage_editProperties',
+ 'manage_changeProperties',
+ 'manage_propertiesForm',
+ )
+
+ property_sheets = ( PropertySheet.Base
+ , PropertySheet.SimpleItem
+ , PropertySheet.Folder
+ , BaseCache
+ , SortIndex
+ , DistributedRamCache
+ )
Added: erp5/trunk/products/ERP5Type/Core/Folder.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Core/Folder.py?rev=11853&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/Core/Folder.py (added)
+++ erp5/trunk/products/ERP5Type/Core/Folder.py Wed Jan 3 02:53:19 2007
@@ -1,0 +1,875 @@
+##############################################################################
+#
+# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
+# Jean-Paul Smets-Solanes <jp at 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.
+#
+##############################################################################
+
+from AccessControl import ClassSecurityInfo
+from Acquisition import aq_base, aq_self
+from OFS.History import Historical
+import ExtensionClass
+
+from Products.CMFCore.utils import _getAuthenticatedUser
+from Products.CMFCore.CMFCatalogAware import CMFCatalogAware
+
+from Products.ERP5Type.Base import Base
+from Products.ERP5Type.CopySupport import CopyContainer
+from Products.ERP5Type import PropertySheet, Permissions
+from Products.ERP5Type.XMLExportImport import Folder_asXML
+from Products.ERP5Type.Cache import CachingMethod
+from Products.ERP5Type.Utils import sortValueList
+
+try:
+ from Products.CMFCore.CMFBTreeFolder import CMFBTreeFolder
+except ImportError:
+ from Products.BTreeFolder2.CMFBTreeFolder import CMFBTreeFolder
+from AccessControl import getSecurityManager
+from Products.ERP5Type import Permissions
+from random import randint
+
+import os
+
+from zLOG import LOG, PROBLEM
+
+# Dummy Functions for update / upgrade
+def dummyFilter(object,REQUEST=None):
+ return 1
+
+def dummyTestAfter(object,REQUEST=None):
+ return []
+
+class FolderMixIn(ExtensionClass.Base):
+ """A mixin class for folder operations, add content, delete content etc.
+ """
+
+ # Declarative security
+ security = ClassSecurityInfo()
+ security.declareObjectProtected(Permissions.AccessContentsInformation)
+
+ security.declarePublic('newContent')
+ def newContent(self, id=None, portal_type=None, id_group=None,
+ default=None, method=None, immediate_reindex=0,
+ container=None, created_by_builder=0, activate_kw=None,
+ is_indexable=None, temp_object=0, **kw):
+ """Creates a new content.
+ This method is public, since TypeInformation.constructInstance will perform
+ the security check.
+ """
+ if container is None:
+ container = self
+ if id is None:
+ new_id = str(container.generateNewId(id_group = id_group,
+ default=default, method=method))
+ else:
+ new_id = str(id)
+ if portal_type is None:
+ # XXX This feature is very confusing
+ # And made the code more difficult to update
+ portal_type = container.allowedContentTypes()[0].id
+
+ if temp_object:
+ from Products.ERP5Type import Document
+ # we get an object from factory only for first temp container object
+ # otherwise we get an id so we can use the classic way
+ if not hasattr(container, 'isTempObject') or \
+ (hasattr(container, 'isTempObject') and not container.isTempObject()):
+ factory_name = 'newTemp%s' %(portal_type.replace(' ', ''))
+ m = getattr(Document, factory_name)
+ return m(container, new_id)
+
+ self.portal_types.constructContent(type_name=portal_type,
+ container=container,
+ id=new_id,
+ created_by_builder=created_by_builder,
+ activate_kw=activate_kw,
+ is_indexable=is_indexable
+ ) # **kw) removed due to CMF bug
+ # TODO :the **kw makes it impossible to create content not based on
+ # ERP5TypeInformation, because factory method often do not support
+ # keywords arguments.
+
+ new_instance = container[new_id]
+ if kw != {} : new_instance._edit(force_update=1, **kw)
+ if immediate_reindex: new_instance.immediateReindexObject()
+ return new_instance
+
+ security.declareProtected(
+ Permissions.DeletePortalContent, 'deleteContent')
+ def deleteContent(self, id):
+ """ delete items in this folder.
+ `id` can be a list or a string.
+ """
+ if isinstance(id, str):
+ self._delObject(id)
+ elif isinstance(id, list) or isinstance(id, tuple):
+ for my_id in id:
+ self._delObject(my_id)
+ else:
+ raise TypeError, 'deleteContent only accepts string or list, '\
+ 'not %s' % type(id)
+
+ def _generateRandomId(self):
+ """
+ Generate a random Id.
+ 10000 factor makes the odd to generate an already existing Id of 1 out
+ of 10000, not depending on the number of objects present in this folder.
+ len(self)+1 to make sure generation works on an empty Folder.
+ """
+ return '%X' % (randint(1, 10000 * (len(self) + 1)), )
+
+ def _generateNextId(self):
+ """
+ Get the last generated Id, increment it until no object with generated
+ Id exist, then save the Id.
+ """
+ try:
+ my_id = int(self.getLastId())
+ except TypeError:
+ my_id = 1
+ while self.hasContent(str(my_id)):
+ my_id = my_id + 1
+ my_id = str(my_id)
+ self._setLastId(my_id) # Make sure no reindexing happens
+ return my_id
+
+ # Automatic ID Generation method
+ security.declareProtected(Permissions.View, 'generateNewId')
+ def generateNewId(self,id_group=None,default=None,method=None):
+ """
+ Generate a new Id which has not been taken yet in this folder.
+ Eventually increment the id number until an available id
+ can be found
+
+ Permission is view because we may want to add content to a folder
+ without changing the folder content itself.
+ XXX
+ """
+ my_id = None
+ if id_group is None:
+ id_group = self.getIdGroup()
+ if id_group in (None, 'None'):
+ id_generator = self.getIdGenerator()
+ if not isinstance(id_generator, str):
+ LOG('Folder.generateNewId', 0, '%s.id_generator is not a string. Falling back on default behaviour.' % (self.absolute_url(), ))
+ id_generator = ''
+ if id_generator != '': # Custom aq_dynamic function (like the one defined on WebSite objects) can find an object which has no name. So we must recognise the default value of id_generator and force safe fallback in this case.
+ idGenerator = getattr(self, id_generator, None)
+ if idGenerator is None:
+ idGenerator = self._generateNextId
+ else:
+ idGenerator = self._generateNextId
+ my_id = idGenerator()
+ while self.hasContent(my_id):
+ my_id = _generateNextId()
+ else:
+ my_id = str(self.portal_ids.generateNewId(id_group=id_group,default=default,method=method))
+ return my_id
+
+ security.declareProtected(Permissions.View, 'hasContent')
+ def hasContent(self,id):
+ return self.hasObject(id)
+
+ # Get the content
+ security.declareProtected(Permissions.View, 'searchFolder')
+ def searchFolder(self, **kw):
+ """
+ Search the content of a folder by calling
+ the portal_catalog.
+ """
+ if not kw.has_key('parent_uid'): #WHY ????
+ kw['parent_uid'] = self.getUid()
+
+ # Make sure that if we use parent base category
+ # We do not have conflicting parent uid values
+ delete_parent_uid = 0
+ if kw.has_key('selection_domain'):
+ if kw['selection_domain'].asDomainDict().has_key('parent'):
+ delete_parent_uid = 1
+ if kw.has_key('selection_report'):
+ if kw['selection_report'].asDomainDict().has_key('parent'):
+ delete_parent_uid = 1
+ if delete_parent_uid:
+ del kw['parent_uid']
+
+ kw2 = {}
+ # Remove useless matter before calling the
+ # catalog. In particular, consider empty
+ # strings as None values
+ for cname in kw.keys():
+ if kw[cname] != '' and kw[cname] != None:
+ kw2[cname] = kw[cname]
+ # The method to call to search the folder
+ # content has to be called z_search_folder
+ method = self.portal_catalog.portal_catalog
+ return method(**kw2)
+
+ security.declareProtected(Permissions.View, 'countFolder')
+ def countFolder(self, **kw):
+ """
+ Search the content of a folder by calling
+ the portal_catalog.
+ """
+ if not kw.has_key('parent_uid'): #WHY ????
+ kw['parent_uid'] = self.getUid()
+
+ # Make sure that if we use parent base category
+ # We do not have conflicting parent uid values
+ delete_parent_uid = 0
+ if kw.has_key('selection_domain'):
+ if kw['selection_domain'].asDomainDict().has_key('parent'):
+ delete_parent_uid = 1
+ if kw.has_key('selection_report'):
+ if kw['selection_report'].asDomainDict().has_key('parent'):
+ delete_parent_uid = 1
+ if delete_parent_uid:
+ del kw['parent_uid']
+
+ kw2 = {}
+ # Remove useless matter before calling the
+ # catalog. In particular, consider empty
+ # strings as None values
+ for cname in kw.keys():
+ if kw[cname] != '' and kw[cname]!=None:
+ kw2[cname] = kw[cname]
+ # The method to call to search the folder
+ # content has to be called z_search_folder
+ method = self.portal_catalog.countResults
+ return method(**kw2)
+
+ # Count objects in the folder
+ security.declarePrivate('_count')
+ def _count(self, **kw):
+ """
+ Returns the number of items in the folder.
+ """
+ return self.countFolder(**kw)[0][0]
+
+class Folder( CopyContainer, CMFBTreeFolder, Base, FolderMixIn):
+ """
+ A Folder is a subclass of Base but not of XMLObject.
+ Folders are not considered as documents and are therefore
+ not synchronisable.
+
+ ERP5 folders are implemented as CMFBTreeFolder objects
+ and can store up to a million documents on a standard
+ computer.
+ ERP5 folders will eventually use in the near future the
+ AdaptableStorage implementation in order to reach performances
+ of 10 or 100 millions of documents in a single folder.
+
+ ERP5 folders include an automatic id generation feature
+ which allows user not to define an id when they create
+ a new document in a folder.
+
+ ERP5 folders use the ZSQLCatalog to search for objects
+ or display content. This requires a method called
+ *z_search_folder* to be put inside the ZSQLCatalog object
+ of the ERP5 portal.
+
+ An ERP5 Binder document class will eventually be defined
+ in order to implement a binder of documents which can itself
+ be categorized.
+ """
+
+ meta_type = 'ERP5 Folder'
+ portal_type = 'Folder'
+ add_permission = Permissions.AddPortalContent
+ isPortalContent = 1
+ isRADContent = 1
+
+ # Declarative security
+ security = ClassSecurityInfo()
+ security.declareObjectProtected(Permissions.AccessContentsInformation)
+
+ manage_options = ( CMFBTreeFolder.manage_options +
+ Historical.manage_options +
+ CMFCatalogAware.manage_options
+ )
+ # Declarative properties
+ property_sheets = ( PropertySheet.Base
+ , PropertySheet.XMLObject
+ , PropertySheet.SimpleItem
+ , PropertySheet.Folder
+ )
+
+ # Class inheritance fixes
+ security.declareProtected( Permissions.ModifyPortalContent, 'edit' )
+ edit = Base.edit
+ security.declareProtected( Permissions.ModifyPortalContent, '_edit' )
+ _edit = Base._edit
+ _setPropValue = Base._setPropValue
+ _propertyMap = Base._propertyMap # are there any others XXX ?
+
+ # Override Zope default by folder id generation
+ def _get_id(self, id):
+ if self._getOb(id, None) is None :
+ return id
+ return self.generateNewId()
+
+ #security.declareProtected( Permissions.DeletePortalContent, 'manage_delObjects' )
+ #manage_delObjects = CopyContainer.manage_delObjects
+
+ # Implementation
+# security.declarePrivate('_setObject')
+# def _setObject(self, id, object, roles=None, user=None, set_owner=1):
+# """
+# This method is here in order to dynamically update old
+# folders into the new BTree folder type.
+# This method is destructive in the sens that objects
+# of the old folder will be lost during the update
+# """
+# # First make sur the folder has been initialized
+# if not hasattr(self, '_tree'):
+# CMFBTreeFolder.__init__(self, self.id)
+# if not self._tree:
+# CMFBTreeFolder.__init__(self, self.id)
+# # Then insert the object
+# CMFBTreeFolder._setObject(self, id, object, roles=roles, user=user, set_owner=set_owner)
+# This method destroys the title when we create new object in empty folder
+
+ security.declareProtected(Permissions.View, 'hasContent')
+ def hasContent(self,id):
+ return self.hasObject(id)
+
+ security.declareProtected( Permissions.ModifyPortalContent, 'exportAll' )
+ def exportAll(self,dir=None):
+ """
+ Allows to export all object inside a particular folder, one by one
+ """
+ folder_id = self.getId()
+ if dir != None:
+ for id in self.objectIds():
+ f = os.path.join(dir, '%s___%s.zexp' % (folder_id,id))
+ ob = self._getOb(id)
+ ob._p_jar.exportFile(ob._p_oid,f)
+ get_transaction().commit()
+
+ security.declareProtected( Permissions.ModifyPortalContent, 'recursiveApply')
+ def recursiveApply(self, filter=dummyFilter, method=None,
+ test_after=dummyTestAfter, include=1, REQUEST=None, **kw):
+ """
+ Apply a method to self and to all children
+
+ filter -- only instances which return 1 when applied filter
+ are considered
+
+ method -- the method to apply to acceptable instances
+
+ test_after -- test to apply after calling method in order to search
+ for inconsistencies
+
+ include -- if set to 1 (default), apply method to self
+
+
+ REQUEST -- the http REQUEST (if needed)
+
+ **kw -- optional parameters passed to method
+ """
+ update_list = []
+ #LOG('Folder, recursiveApply ',0,"first one self.path: %s" % self.getPath())
+
+ # Only apply method to self if filter is to 1 and filter returns 1
+ if include==1 and filter(object=self.getObject(),REQUEST=REQUEST):
+ method_message = method(object=self.getObject(),REQUEST=REQUEST, **kw)
+ if type(method_message) is type([]):
+ update_list += method_message
+ update_list += test_after(object=self.getObject(),REQUEST=REQUEST)
+
+ for o in self.objectValues(): # contentValues sometimes fail in BTreeFolder
+ # Test on each sub object if method should be applied
+ if filter(object=o,REQUEST=REQUEST):
+ method_message = method(object=o,REQUEST=REQUEST, **kw)
+ if type(method_message) is type([]):
+ update_list += method_message
+ update_list += test_after(o,REQUEST=REQUEST)
+ # And commit subtransaction
+ #get_transaction().commit(1)
+ get_transaction().commit() # we may use commit(1) some day XXX
+ # Recursively call recursiveApply if o has a recursiveApply method (not acquired)
+ obase = aq_base(o)
+ if hasattr(obase, 'recursiveApply'):
+ #LOG('Found recursiveApply', 0, o.absolute_url())
+ update_list += o.recursiveApply(filter=filter, \
+ method=method, test_after=test_after,REQUEST=REQUEST,include=0,**kw)
+
+ return update_list
+
+ security.declareProtected( Permissions.ModifyPortalContent, 'updateAll' )
+ def updateAll(self, filter=None, method=None, test_after=None, request=None, include=1,**kw):
+ """
+ update all objects inside this particular folder wich
+ returns not None to the test.
+
+ filter have to be a method with one parameter (the object)
+ wich returns None if we must not update the object
+
+ test_after have to be a method with one parameter (the object)
+ wich returns a string
+
+ method is the update method with also one parameter
+
+ """
+ update_list = []
+ #LOG('Folder, updateAll ',0,"first one self.path: %s" % self.getPath())
+
+ if include==1 and filter(object=self.getObject(),request=request):
+ method_message = method(object=self.getObject(),request=request)
+ if type(method_message) is type([]):
+ update_list += method_message
+ update_list += test_after(object=self.getObject(),request=request)
+
+ for o in self.objectValues():
+ # Test if we must apply the upgrade
+ if filter(object=o,request=request):
+ method_message = method(object=o,request=request)
+ if type(method_message) is type([]):
+ update_list += method_message
+ update_list += test_after(object=o,request=request)
+ #for object in o.objectValues():
+ #LOG('Folder, updateAll ',0,"object.id: %s" % object.id)
+ obase = aq_base(o)
+ get_transaction().commit()
+ if hasattr(obase, 'updateAll'):
+ update_list += o.updateAll(filter=filter, \
+ method=method, test_after=test_after,request=request,include=0,**kw)
+
+ return update_list
+
+ security.declareProtected( Permissions.ModifyPortalContent, 'upgradeObjectClass' )
+ def upgradeObjectClass(self, test_before=None, from_class=None,\
+ to_class=None, test_after=None):
+ """
+ upgrade the class of all objects inside this
+ particular folder
+ test have to be a method with one parameter
+ migrations is a dictionnary of class, { from_class : to_class }
+ """
+ #LOG("upradeObjectClass: folder ",0,self.id)
+ test_list = []
+ folder = self.getObject()
+ for o in self.listFolderContents():
+ # Make sure this sub object is not the same as object
+ if o.getPhysicalPath() != self.getPhysicalPath():
+ id = o.getId()
+ obase = aq_base(o)
+ # Check if the subobject have to also be upgraded
+ if hasattr(obase,'upgradeObjectClass'):
+ test_list += o.upgradeObjectClass(test_before=test_before, \
+ from_class=from_class, to_class=to_class,
+ test_after=test_after)
+ # Test if we must apply the upgrade
+ if test_before(o) is not None:
+ LOG("upradeObjectClass: id ",0,id)
+ klass = obase.__class__
+ LOG("upradeObjectClass: klass ",0,str(klass))
+ LOG("upradeObjectClass: from_class ",0,str(from_class))
+ if klass == from_class:
+ try:
+ newob = to_class(obase.id)
+ newob.id = obase.id # This line activates obase.
+ except AttributeError:
+ newob = to_class(id)
+ newob.id = id
+ keys = obase.__dict__.keys()
+ for k in keys:
+ if k not in ('id', 'meta_type', '__class__'):
+ setattr(newob,k,obase.__dict__[k])
+
+ self.manage_delObjects(id)
+ LOG("upradeObjectClass: ",0,"add new object: %s" % str(newob.id))
+ get_transaction().commit() # XXX this commit should be after _setObject
+ LOG("upradeObjectClass: ",0,"newob.__class__: %s" % str(newob.__class__))
+ self._setObject(id, newob)
+ object_to_test = self._getOb(id)
+ test_list += test_after(object_to_test)
+
+ return test_list
+
+
+ # Catalog related
+ security.declarePublic( 'reindexObject' )
+ def reindexObject(self, *args, **kw):
+ """
+ Fixes the hierarchy structure (use of Base class)
+ XXXXXXXXXXXXXXXXXXXXXXXX
+ BUG here : when creating a new base category
+ """
+ return Base.reindexObject(self, *args, **kw)
+
+ security.declareProtected(Permissions.ModifyPortalContent, 'reindexObjectSecurity')
+ def reindexObjectSecurity(self):
+ """
+ Reindex security-related indexes on the object
+ (and its descendants).
+ """
+ # In ERP5, simply reindex all objects.
+ self.recursiveReindexObject()
+
+ security.declarePublic( 'recursiveReindexObject' )
+ def recursiveReindexObject(self, *args, **kw):
+ """
+ Fixes the hierarchy structure (use of Base class)
+ XXXXXXXXXXXXXXXXXXXXXXXX
+ BUG here : when creating a new base category
+ """
+ if self.isIndexable:
+ self.activate(group_method_id='portal_catalog/catalogObjectList', expand_method_id='getIndexableChildValueList', alternate_method_id='alternateReindexObject', **kw).recursiveImmediateReindexObject(*args, **kw)
+
+ security.declareProtected( Permissions.AccessContentsInformation, 'getIndexableChildValueList' )
+ def getIndexableChildValueList(self):
+ """
+ Get indexable childen recursively.
+ """
+ value_list = []
+ if self.isIndexable:
+ value_list.append(self)
+ for c in self.objectValues():
+ if hasattr(aq_base(c), 'getIndexableChildValueList'):
+ value_list.extend(c.getIndexableChildValueList())
+ return value_list
+
+ security.declarePublic( 'recursiveImmediateReindexObject' )
+ def recursiveImmediateReindexObject(self, *args, **kw):
+ """
+ Applies immediateReindexObject recursively
+ """
+ # Reindex self
+ root_indexable = int(getattr(self.getPortalObject(),'isIndexable',1))
+ if self.isIndexable and root_indexable:
+ self.flushActivity(invoke = 0, method_id='immediateReindexObject') # This might create a recursive lock
+ self.flushActivity(invoke = 0, method_id='recursiveImmediateReindexObject') # This might create a recursive lock
+ self.immediateReindexObject(*args, **kw)
+ # Reindex contents
+ #LOG('recursiveImmediateReindexObject', 0, 'self = %r, self.objectValues = %r' % (self, self.objectValues()))
+ for c in self.objectValues():
+ if hasattr(aq_base(c), 'recursiveImmediateReindexObject'):
+ c.recursiveImmediateReindexObject(*args, **kw)
+
+ security.declareProtected( Permissions.ModifyPortalContent, 'recursiveMoveObject' )
+ def recursiveMoveObject(self):
+ """
+ Called when the base of a hierarchy is renamed
+ """
+ # Reindex self
+ if self.isIndexable:
+ self.moveObject()
+ # Reindex contents
+ for c in self.objectValues():
+ if hasattr(aq_base(c), 'recursiveMoveObject'):
+ c.recursiveMoveObject()
+
+ # Special Relation keyword : 'content' and 'container'
+ security.declareProtected( Permissions.AccessContentsInformation, '_getCategoryMembershipList' )
+ def _getCategoryMembershipList(self, category,
+ spec=(), filter=None, portal_type=(), base=0 ):
+ if category == 'content':
+ content_list = self.searchFolder(portal_type=spec)
+ return map(lambda x: x.relative_url, content_list)
+ else:
+ return Base.getCategoryMembershipList(self, category,
+ spec=spec, filter=filter, portal_type=portal_type, base=base)
+
+ # Alias - class inheritance resolution
+ security.declareProtected( Permissions.View, 'Title' )
+ Title = Base.Title
+
+ security.declareProtected(Permissions.AccessContentsInformation,
+ 'checkConsistency')
+ def checkConsistency(self, fixit=0):
+ """
+ Check the consistency of this object, then
+ check recursively the consistency of every sub object.
+ """
+ error_list = []
+ # Fix BTree
+ if fixit:
+ btree_ok = self._cleanup()
+ if not btree_ok:
+ # We must commit if we want to keep on recursing
+ get_transaction().commit(1)
+ error_list += [(self.getRelativeUrl(), 'BTree Inconsistency',
+ 199, '(fixed)')]
+ # Call superclass
+ error_list += Base.checkConsistency(self, fixit=fixit)
+ # We must commit before listing folder contents
+ # in case we erased some data
+ if fixit:
+ get_transaction().commit(1)
+ # Then check the consistency on all sub objects
+ for obj in self.contentValues():
+ if fixit:
+ extra_errors = obj.fixConsistency()
+ else:
+ extra_errors = obj.checkConsistency()
+ if len(extra_errors) > 0:
+ error_list += extra_errors
+ # We should also return an error if any
+ return error_list
+
+ security.declareProtected( Permissions.AccessContentsInformation, 'asXML' )
+ def asXML(self, ident=0):
+ """
+ Generate an xml text corresponding to the content of this object
+ """
+ return Folder_asXML(self,ident=ident)
+
+ # Optimized Menu System
+ security.declarePublic('getVisibleAllowedContentTypeList')
+ def getVisibleAllowedContentTypeList(self):
+ """
+ List portal_types' names wich can be added in this folder / object.
+ Cache results.
+
+ This function is *much* similar to allowedContentTypes, except it does
+ not returns portal types but their ids and filter out those listed as
+ hidden content types. It allows to be much faster when only the type id
+ is needed.
+ """
+ if not getSecurityManager().checkPermission(
+ Permissions.AddPortalContent, self):
+ return []
+
+ portal = self.getPortalObject()
+
+ def _getVisibleAllowedContentTypeList():
+ hidden_type_list = portal.portal_types.getTypeInfo(self).getHiddenContentTypeList()
+ return [type.id for type in CMFBTreeFolder.allowedContentTypes(self) if type.id not in hidden_type_list]
+
+ user = str(_getAuthenticatedUser(self))
+ portal_type = self.getPortalType()
+ portal_path = portal.getPhysicalPath()
+
+ _getVisibleAllowedContentTypeList = CachingMethod(_getVisibleAllowedContentTypeList,
+ id=("_getAllowedContentTypeTitleList",
+ user, portal_path, portal_type),
+ cache_duration=None)
+ return _getVisibleAllowedContentTypeList()
+
+ security.declarePublic('allowedContentTypes')
+ def allowedContentTypes( self ):
+ """ List portal_types which can be added in this folder / object.
+ Cache results.
+ Only paths are cached, because we must not cache objects.
+ This makes the result, even if based on cache, O(n) so it becomes quite
+ costly with many allowed content types.
+ Example:
+ on Person (12 allowed content types): 1000 calls take 3s.
+ on Person Module (1 allowed content type): 1000 calls take 0.3s.
+ """
+ # if we don't have add portal content permission, return directly.
+ # this prevents returning cached allowed types when the user no longer have
+ # the permission to any content type. (security definitions in workflows
+ # usually remove some permission once an object is "Valid")
+ # This also prevents filling the cache with an empty list, when the user
+ # does not have the permission to add any content yet.
+
+ # XXX this works just fine, unless some objects can be added with another
+ # permission that "Add portal content". For now, this is only the case for
+ # Role Definition objects, but this shows that generally speaking, this is
+ # not the right approach.
+ if not getSecurityManager().checkPermission(
+ Permissions.AddPortalContent, self):
+ return []
+
+ def _allowedContentTypes( portal_type=None, user=None, portal_path=None ):
+ # Sort the list for convenience -yo
+ # XXX This is not the best solution, because this does not take
+ # account i18n into consideration.
+ # XXX So sorting should be done in skins, after translation is performed.
+ def compareTypes(a, b): return cmp(a.title or a.id, b.title or b.id)
+ type_list = CMFBTreeFolder.allowedContentTypes(self)
+ type_list.sort(compareTypes)
+ return ['/'.join(x.getPhysicalPath()) for x in type_list]
+
+ _allowedContentTypes = CachingMethod( _allowedContentTypes,
+ id = 'allowedContentTypes',
+ cache_duration = None)
+ user = str(_getAuthenticatedUser(self))
+ portal_type = self.getPortalType()
+ portal = self.getPortalObject()
+ portal_path = portal.getPhysicalPath()
+ return [portal.restrictedTraverse(path) for path in
+ _allowedContentTypes( portal_type = portal_type,
+ user = user,
+ portal_path = portal_path )]
+
+ # Multiple Inheritance Priority Resolution
+ _setProperty = Base._setProperty
+ setProperty = Base.setProperty
+ getProperty = Base.getProperty
+ hasProperty = Base.hasProperty
+ view = Base.view
+
+ # Aliases
+ getObjectIds = CMFBTreeFolder.objectIds
+
+ # Overloading
+ security.declareProtected( Permissions.AccessContentsInformation, 'getParentSqlExpression' )
+ def getParentSqlExpression(self, table = 'catalog', strict_membership = 0):
+ """
+ Builds an SQL expression to search children and subclidren
+ """
+ if strict_membership:
+ return Base.getParentSqlExpression(self, table=table, strict_membership=strict_membership)
+ result = "%s.parent_uid = %s" % (table, self.getUid())
+ for o in self.objectValues():
+ if hasattr(aq_base(o), 'objectValues'):
+ # Do not consider non folder objects
+ result = "%s OR %s" % (result, o.getParentSqlExpression(table=table, strict_membership=strict_membership))
+ return "( %s )" % result
+
+
+ def mergeContent(self,from_object=None,to_object=None, delete=1,**kw):
+ """
+ This method will merge two objects.
+
+ When we have to different objects wich represent the same content, we
+ may want to merge them. In this case, we want to be sure to report
+
+ """
+ if from_object is None or to_object is None:
+ return
+
+ from_object_related_object_list = self.portal_categories.getRelatedValueList(from_object)
+ to_object_url = to_object.getRelativeUrl()
+ from_object_url = from_object.getRelativeUrl()
+ corrected_list = []
+ for object in from_object_related_object_list:
+ #LOG('Folder.mergeContent, working on object:',0,object)
+ object_url = object.getRelativeUrl()
+ new_category_list = []
+ found = 0
+ for category in object.getCategoryList(): # so ('destination/person/1',...)
+ #LOG('Folder.mergeContent, working on category:',0,category)
+ linked_object_url = '/'.join(category.split('/')[1:])
+ if linked_object_url == from_object_url:
+ base_category = category.split('/')[0]
+ found = 1
+ new_category_list.append(base_category + '/' + to_object_url)
+ else:
+ new_category_list.append(category)
+ if found:
+ corrected_list.append(object)
+ object.setCategoryList(new_category_list)
+ object.immediateReindexObject()
+ if delete:
+ if len(from_object.portal_categories.getRelatedValueList(from_object))==0:
+ parent = from_object.getParentValue()
+ parent.manage_delObjects(from_object.getId())
+ return corrected_list
+
+ security.declareProtected( Permissions.AccessContentsInformation,
+ 'objectValues' )
+ def objectValues(self, spec=None, meta_type=None, portal_type=None,
+ sort_on=None, sort_order=None, **kw):
+ """
+ Returns a list containing object contained in this folder.
+ """
+ if meta_type is not None:
+ spec = meta_type
+ # when an object inherits from Folder after it was instanciated, it lacks
+ # its BTreeFolder properties.
+ if getattr(self, '_tree', None) is None:
+ try:
+ self._initBTrees()
+ except AttributeError:
+ from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2Base
+ BTreeFolder2Base.__init__(self, self.getId())
+ object_list = CMFBTreeFolder.objectValues(self, spec=spec)
+ if portal_type is not None:
+ if type(portal_type) == type(''):
+ portal_type = (portal_type,)
+ object_list = filter(lambda x: x.getPortalType() in portal_type, object_list)
+ object_list = sortValueList(object_list, sort_on, sort_order, **kw)
+ return object_list
+
+ security.declareProtected( Permissions.AccessContentsInformation,
+ 'contentValues' )
+ def contentValues(self, spec=None, meta_type=None, portal_type=None,
+ sort_on=None, sort_order=None, **kw):
+ """
+ Returns a list containing object contained in this folder.
+ Filter objects with appropriate permissions (as in contentValues)
+ """
+ if meta_type is not None:
+ spec = meta_type
+ if portal_type is not None:
+ kw['portal_type'] = portal_type
+ filter = kw.pop('filter', {}) or {}
+ kw.update(filter)
+ object_list = CMFBTreeFolder.contentValues(self, spec=spec, filter=kw)
+ object_list = sortValueList(object_list, sort_on, sort_order, **kw)
+ return object_list
+
+ # Override security declaration of CMFCore/PortalFolder (used by CMFBTreeFolder)
+ security.declareProtected(Permissions.ModifyPortalContent,'setDescription')
+ security.declareProtected( Permissions.ModifyPortalContent, 'setTitle' )
+
+ security.declareProtected( Permissions.AccessContentsInformation, 'manage_copyObjects' ) # XXX Why this one doesn't work in CopySupport ?
+ security.declareProtected( Permissions.AddPortalContent, 'manage_pasteObjects' ) # XXX Why this one doesn't work in CopySupport ?
+
+ # Template Management
+ security.declareProtected(Permissions.View, 'getDocumentTemplateList')
+ def getDocumentTemplateList(self) :
+ """
+ Returns the list of allowed templates for this folder
+ by calling the preference tool
+ """
+ return self.getPortalObject().portal_preferences.getDocumentTemplateList(self)
+
+ security.declareProtected(Permissions.ModifyPortalContent,'makeTemplate')
+ def makeTemplate(self):
+ """
+ Make document behave as a template.
+ A template is no longer indexable
+
+ TODO:
+ - prevent from changing templates or invoking workflows
+ """
+ Base.makeTemplate(self)
+ for o in self.objectValues():
+ if hasattr(aq_base(o), 'makeTemplate'): o.makeTemplate()
+
+ security.declareProtected(Permissions.ModifyPortalContent,'makeTemplateInstance')
+ def makeTemplateInstance(self):
+ """
+ Make document behave as standard document (indexable)
+ """
+ Base.makeTemplateInstance(self)
+ for o in self.objectValues():
+ if hasattr(aq_base(o), 'makeTemplateInstance'): o.makeTemplateInstance()
+
+ def _delObject(self, id, dp=1):
+ """
+ _delObject is redefined here in order to make sure
+ we do not do silent except while we remove objects
+ from catalog
+ """
+ object = self._getOb(id)
+ object.manage_beforeDelete(object, self)
+ self._delOb(id)
+
+# Overwrite Zope setTitle()
+Folder.setTitle = Base.setTitle
Added: erp5/trunk/products/ERP5Type/Core/RamCache.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Core/RamCache.py?rev=11853&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/Core/RamCache.py (added)
+++ erp5/trunk/products/ERP5Type/Core/RamCache.py Wed Jan 3 02:53:19 2007
@@ -1,0 +1,60 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+# Ivan Tyagov <ivan at 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.
+#
+##############################################################################
+
+
+from AccessControl import ClassSecurityInfo
+from Products.CMFCore import CMFCorePermissions
+from Products.ERP5Type.XMLObject import XMLObject
+from Products.ERP5Type import PropertySheet
+from Products.ERP5.PropertySheet.SortIndex import SortIndex
+from Products.ERP5Type.PropertySheet.BaseCache import BaseCache
+
+class RamCache(XMLObject):
+ """
+ RamCache is a Zope (persistent) representation of
+ the RAM based real cache plugin object.
+ """
+ meta_type = 'ERP5 Ram Cache'
+ portal_type = 'Ram Cache'
+ isPortalContent = 1
+ isRADContent = 1
+ allowed_types = ()
+
+ security = ClassSecurityInfo()
+ security.declareProtected(CMFCorePermissions.ManagePortal,
+ 'manage_editProperties',
+ 'manage_changeProperties',
+ 'manage_propertiesForm',
+ )
+
+ property_sheets = ( PropertySheet.Base
+ , PropertySheet.SimpleItem
+ , PropertySheet.Folder
+ , SortIndex
+ , BaseCache
+ )
Added: erp5/trunk/products/ERP5Type/Core/SQLCache.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Core/SQLCache.py?rev=11853&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/Core/SQLCache.py (added)
+++ erp5/trunk/products/ERP5Type/Core/SQLCache.py Wed Jan 3 02:53:19 2007
@@ -1,0 +1,64 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+# Ivan Tyagov <ivan at 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.
+#
+##############################################################################
+
+from AccessControl import ClassSecurityInfo
+from Products.CMFCore import CMFCorePermissions
+from Products.ERP5Type.Base import Base
+from Products.ERP5Type.XMLObject import XMLObject
+from Products.ERP5Type import PropertySheet
+from Products.ERP5.PropertySheet.SortIndex import SortIndex
+from Products.ERP5Type.PropertySheet.BaseCache import BaseCache
+from Products.ERP5Type.PropertySheet.SQLCache import SQLCache
+
+class SQLCache(XMLObject):
+ """
+ SQLCache is a Zope (persistent) representation of
+ the RAM based real SQL cache plugin object.
+ """
+
+ meta_type = 'ERP5 SQL Cache'
+ portal_type = 'SQL Cache'
+ isPortalContent = 1
+ isRADContent = 1
+
+ allowed_types = ()
+
+ security = ClassSecurityInfo()
+ security.declareProtected(CMFCorePermissions.ManagePortal,
+ 'manage_editProperties',
+ 'manage_changeProperties',
+ 'manage_propertiesForm',
+ )
+
+ property_sheets = ( PropertySheet.Base
+ , PropertySheet.SimpleItem
+ , PropertySheet.Folder
+ , BaseCache
+ , SortIndex
+ , SQLCache
+ )
Added: erp5/trunk/products/ERP5Type/Core/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Core/__init__.py?rev=11853&view=auto
==============================================================================
(empty)
More information about the Erp5-report
mailing list