Commit d02ba206 authored by Nicolas Dumazet's avatar Nicolas Dumazet

Last chunk of portal type classes / zodb property sheets.

After this, all ERP5 objects become instances of portal type classes

Preferences:
* all the trickery for preferences is gone and is handled by a specific
  accessor holder holding all preference methods

Property holders
* our Base.aq_portal_type property holders are not used anymore:
  the "property holder" becomes the portal type class itself and the
  set of accessor_holder classes in the mro of the portal type class:
  portal-type-specific methods are on the portal type class, while
  portal-type-independant method are put on the accessor holder ancestors
* the portal type meta class now also inherits from "PropertyHolder" to
  provide the same introspection interface and methods.
  (In the future this class / interface will need to be refined)

Bootstrap/migration:
* bootstrapping/migration from older instances: provide with code able to
  import XML from ERP5/bootstrap/ to load necessary tools from almost any
  instance state
* migrate in BusinessTemplate installation code all non-portal type classes
  objects to portal type classes
* Change the way Tools are installed when creating a site, so that we create
  directly portal type classes objects instead of Documents

Accessors:
* add a generatePortalTypeAccessors method on the portal type class to generate
  portal-type-specific accessors
* associate BaseAccessorHolder to all portal type classes to contain
  all common category related accessors
* change the way workflow methods are generated to bind them directly on
  the portal type class
* disable Base._aq_dynamic (while still keeping its code for debugging and
  reference, this can be cleanup up later)


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@42902 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent b49d48ed
......@@ -502,6 +502,8 @@ class ActivityTool (Folder, UniqueObject):
allowed_types = ( 'CMF Active Process', )
security = ClassSecurityInfo()
isIndexable = False
manage_options = tuple(
[ { 'label' : 'Overview', 'action' : 'manage_overview' }
, { 'label' : 'Activities', 'action' : 'manageActivities' }
......
......@@ -816,11 +816,18 @@ class ObjectTemplateItem(BaseTemplateItem):
if context.getTemplateFormatVersion() == 1:
upgrade_list = []
type_name = self.__class__.__name__.split('TemplateItem')[-2]
for path in self._objects:
for path, obj in self._objects.iteritems():
if installed_item._objects.has_key(path):
upgrade_list.append((path, installed_item._objects[path]))
else: # new object
modified_object_list[path] = 'New', type_name
# if that's an old style class, use a portal type class instead
migrateme = getattr(obj, '_migrateToPortalTypeClass', None)
if migrateme is not None:
migrateme()
self._objects[path] = obj
# update _p_jar property of objects cleaned by removeProperties
transaction.savepoint(optimistic=True)
for path, old_object in upgrade_list:
......@@ -1006,7 +1013,7 @@ class ObjectTemplateItem(BaseTemplateItem):
workflow_history = None
old_obj = container._getOb(object_id, None)
object_existed = old_obj is not None
if old_obj is not None:
if object_existed:
# Object already exists
recurse(saveHook, old_obj)
if getattr(aq_base(old_obj), 'groups', None) is not None:
......@@ -1920,10 +1927,24 @@ class PortalTypeTemplateItem(ObjectTemplateItem):
PersistentMigrationMixin._no_migration -= 1
return object_key_list
# XXX : this method is kept temporarily, but can be removed once all bt5 are
# re-exported with separated workflow-chain information
def install(self, context, trashbin, **kw):
if context.getTemplateFormatVersion() == 1:
object_list = self._objects
else:
object_list = self._archive
for path, obj in object_list.iteritems():
# if that's an old style class, use a portal type class instead
# XXX PortalTypeTemplateItem-specific
migrateme = getattr(obj, '_migrateToPortalTypeClass', None)
if migrateme is not None:
migrateme()
object_list[path] = obj
ObjectTemplateItem.install(self, context, trashbin, **kw)
# XXX : following be removed once all bt5 are
# re-exported with separated workflow-chain information
update_dict = kw.get('object_to_update')
force = kw.get('force')
# We now need to setup the list of workflows corresponding to
......@@ -1933,10 +1954,6 @@ class PortalTypeTemplateItem(ObjectTemplateItem):
# best solution, by default it is 'default_workflow', which is
# not very usefull
default_chain = ''
if context.getTemplateFormatVersion() == 1:
object_list = self._objects
else:
object_list = self._archive
for path in object_list.keys():
if update_dict.has_key(path) or force:
if not force:
......@@ -3478,7 +3495,7 @@ class PropertySheetTemplateItem(DocumentTemplateItem,
# If set to False, then the migration of Property Sheets will never
# be performed, required until the code of ZODB Property Sheets is
# stable and completely documented
_perform_migration = False
_perform_migration = True
# Only meaningful for filesystem Property Sheets
local_file_reader_name = staticmethod(readLocalPropertySheet)
......@@ -4336,6 +4353,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
, 'icon' : 'file_icon.gif'
, 'product' : 'ERP5Type'
, 'factory' : 'addBusinessTemplate'
, 'type_class' : 'BusinessTemplate'
, 'immediate_view' : 'BusinessTemplate_view'
, 'allow_discussion' : 1
, 'allowed_content_types': (
......
......@@ -139,6 +139,14 @@ def getCatalogStorageList(*args, **kw):
result.append((item, title))
return result
def addERP5Tool(portal, id, portal_type):
if portal.hasObject(id):
return
import erp5.portal_type
klass = getattr(erp5.portal_type, portal_type)
obj = klass()
portal._setObject(id, obj)
class ReferCheckerBeforeTraverseHook:
"""This before traverse hook checks the HTTP_REFERER argument in the request
and refuses access to anything else that portal_url.
......@@ -313,6 +321,7 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin):
return CMFSite.manage_renameObject(self, id=id, new_id=new_id,
REQUEST=REQUEST)
def _getAcquireLocalRoles(self):
"""
Prevent local roles from being acquired outside of Portal object.
......@@ -1287,7 +1296,7 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin):
module_id = expected_module_id
# then look for module where the type is allowed
else:
for expected_module_id in portal_object.objectIds(spec=('ERP5 Folder',)):
for expected_module_id in portal_object.objectIds(('ERP5 Folder',)):
module = portal_object._getOb(expected_module_id, None)
if module is not None:
if portal_type in self.portal_types[module.getPortalType()].\
......@@ -1465,7 +1474,6 @@ class PortalGenerator:
addCMFCoreTool('CMF Catalog', None)
addCMFCoreTool('CMF Member Data Tool', None)
addCMFCoreTool('CMF Skins Tool', None)
addCMFCoreTool('CMF Types Tool', None)
addCMFCoreTool('CMF Undo Tool', None)
addCMFCoreTool('CMF URL Tool', None)
addCMFCoreTool('CMF Workflow Tool', None)
......@@ -1610,23 +1618,17 @@ class ERP5Generator(PortalGenerator):
# Add several other tools, only at the end in order
# to make sure that they will be reindexed
addTool = p.manage_addProduct['ERP5'].manage_addTool
if not p.hasObject('portal_rules'):
addTool('ERP5 Rule Tool', None)
if not p.hasObject('portal_simulation'):
addTool('ERP5 Simulation Tool', None)
if not p.hasObject('portal_deliveries'):
addTool('ERP5 Delivery Tool', None)
if not p.hasObject('portal_orders'):
addTool('ERP5 Order Tool', None)
addERP5Tool(p, 'portal_rules', 'Rule Tool')
addERP5Tool(p, 'portal_simulation', 'Simulation Tool')
addERP5Tool(p, 'portal_deliveries', 'Delivery Tool')
addERP5Tool(p, 'portal_orders', 'Order Tool')
def setupTemplateTool(self, p, **kw):
"""
Setup the Template Tool. Security must be set strictly.
"""
addTool = p.manage_addProduct['ERP5'].manage_addTool
addTool('ERP5 Template Tool', None)
addERP5Tool(p, 'portal_templates', 'Template Tool')
context = p.portal_templates
permission_list = context.possible_permissions()
for permission in permission_list:
......@@ -1638,7 +1640,6 @@ class ERP5Generator(PortalGenerator):
"""
if not 'portal_actions' in p.objectIds():
PortalGenerator.setupTools(self, p)
p._delObject('portal_types')
# It is better to remove portal_catalog
# which is ZCatalog as soon as possible,
......@@ -1660,51 +1661,31 @@ class ERP5Generator(PortalGenerator):
pass
# Add ERP5 Tools
addTool = p.manage_addProduct['ERP5'].manage_addTool
if not p.hasObject('portal_categories'):
addTool('ERP5 Categories', None)
if not p.hasObject('portal_ids'):
addTool('ERP5 Id Tool', None)
addERP5Tool(p, 'portal_categories', 'Category Tool')
addERP5Tool(p, 'portal_ids', 'Id Tool')
if not p.hasObject('portal_templates'):
self.setupTemplateTool(p)
if not p.hasObject('portal_trash'):
addTool('ERP5 Trash Tool', None)
if not p.hasObject('portal_alarms'):
addTool('ERP5 Alarm Tool', None)
if not p.hasObject('portal_domains'):
addTool('ERP5 Domain Tool', None)
if not p.hasObject('portal_tests'):
addTool('ERP5 Test Tool', None)
if not p.hasObject('portal_password'):
addTool('ERP5 Password Tool', None)
if not p.hasObject('portal_acknowledgements'):
addTool('ERP5 Acknowledgement Tool', None)
addERP5Tool(p, 'portal_trash', 'Trash Tool')
addERP5Tool(p, 'portal_alarms', 'Alarm Tool')
addERP5Tool(p, 'portal_domains', 'Domain Tool')
addERP5Tool(p, 'portal_tests', 'Test Tool')
addERP5Tool(p, 'portal_password', 'Password Tool')
addERP5Tool(p, 'portal_acknowledgements', 'Acknowledgement Tool')
# Add ERP5Type Tool
addTool = p.manage_addProduct['ERP5Type'].manage_addTool
if not p.hasObject('portal_caches'):
addTool('ERP5 Cache Tool', None)
if not p.hasObject('portal_memcached'):
addTool('ERP5 Memcached Tool', None)
if not p.hasObject('portal_types'):
addTool('ERP5 Types Tool', None)
if not p.hasObject('portal_property_sheets'):
addTool('ERP5 Property Sheet Tool', None)
addERP5Tool(p, 'portal_caches', 'Cache Tool')
addERP5Tool(p, 'portal_memcached', 'Memcached Tool')
try:
addTool = p.manage_addProduct['ERP5Subversion'].manage_addTool
if not p.hasObject('portal_subversion'):
addTool('ERP5 Subversion Tool', None)
addERP5Tool(p, 'portal_subversion', 'Subversion Tool')
except AttributeError:
pass
# Add ERP5Type Tools
addTool = p.manage_addProduct['ERP5Type'].manage_addTool
if not p.hasObject('portal_classes'):
if allowClassTool():
addTool('ERP5 Class Tool', None)
else:
addTool('ERP5 Dummy Class Tool', None)
if allowClassTool():
addERP5Tool(p, 'portal_classes', 'Class Tool')
else:
addERP5Tool(p, 'portal_classes', 'Dummy Class Tool')
# Add ERP5 SQL Catalog Tool
addTool = p.manage_addProduct['ERP5Catalog'].manage_addTool
......@@ -1758,17 +1739,12 @@ class ERP5Generator(PortalGenerator):
pass
# Add ERP5Form Tools
addTool = p.manage_addProduct['ERP5Form'].manage_addTool
if not p.hasObject('portal_selections'):
addTool('ERP5 Selections', None)
if not p.hasObject('portal_preferences'):
addTool('ERP5 Preference Tool', None)
addERP5Tool(p, 'portal_selections', 'Selection Tool')
addERP5Tool(p, 'portal_preferences', 'Preference Tool')
try:
# Add ERP5SyncML Tools
addTool = p.manage_addProduct['ERP5SyncML'].manage_addTool
if not p.hasObject('portal_synchronizations'):
addTool('ERP5 Synchronizations', None)
addERP5Tool(p, 'portal_synchronizations', 'Synchronization Tool')
except AttributeError:
pass
......@@ -2032,10 +2008,6 @@ class ERP5Generator(PortalGenerator):
self.setupPermissions(p)
self.setupDefaultSkins(p)
# ERP5 Design Choice is that all content should be user defined
# Content is disseminated through business templates
self.setupPortalTypes(p)
if not p.hasObject('content_type_registry'):
self.setupMimetypes(p)
if not update:
......@@ -2054,18 +2026,6 @@ class ERP5Generator(PortalGenerator):
if not update:
self.setupIndex(p, **kw)
def setupPortalTypes(self, p):
"""
Install the portal_type of Business Template
"""
tool = getToolByName(p, 'portal_types', None)
if tool is None:
return
for t in (BusinessTemplate, ):
t = t.factory_type_information
if not tool.hasObject(t['id']):
tool._setObject(t['id'], ERP5TypeInformation(uid=None, **t))
def setupERP5Core(self,p,**kw):
"""
Install the core part of ERP5
......
......@@ -93,11 +93,3 @@ class Preference( Folder ):
def disable(self):
"""Workflow method"""
self._clearCache()
def _aq_dynamic(self, id):
""" force _aq_dynamic on preference tool, because list of property sheet of
preferences depends on the code of PreferenceTool._aq_dynamic"""
if not PreferenceTool.aq_preference_generated:
portal = self.getPortalObject()
portal.portal_preferences._aq_dynamic('dummy')
return Preference.inheritedAttribute('_aq_dynamic')(self, id)
......@@ -30,17 +30,15 @@
from AccessControl import ClassSecurityInfo
from AccessControl.SecurityManagement import getSecurityManager,\
setSecurityManager, newSecurityManager
from AccessControl.PermissionRole import PermissionRole
from MethodObject import Method
from Products.ERP5Type.Globals import InitializeClass, DTMLFile
from zLOG import LOG, PROBLEM
from Products.CMFCore.utils import getToolByName
from Products.ERP5Type.Tool.BaseTool import BaseTool
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type import Permissions
from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Type.Utils import convertToUpperCase
from Products.ERP5Type.Accessor.TypeDefinition import list_types
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from Products.ERP5Form import _dtmldir
......@@ -52,76 +50,6 @@ class Priority:
GROUP = 2
USER = 3
def updatePreferenceClassPropertySheetList():
# XXX obsolete, and now handled in dynamic.portal_type_class
from Products.ERP5Form.Document.Preference import Preference
# 'Static' property sheets defined on the class
class_property_sheet_list = Preference.property_sheets
# Time to lookup for preferences defined on other modules
property_sheets = list(class_property_sheet_list)
for id in dir(PropertySheet):
if id.endswith('Preference'):
ps = getattr(PropertySheet, id)
if not isinstance(ps, basestring) and ps not in property_sheets:
property_sheets.append(ps)
class_property_sheet_list = tuple(property_sheets)
Preference.property_sheets = class_property_sheet_list
def createPreferenceToolAccessorList(portal) :
"""
Initialize all Preference methods on the preference tool.
This method must be called on startup.
This tool is capable of updating the list of Preference
property sheets by looking at all registered property sheets
and considering those which name ends with 'Preference'
"""
property_list = []
# 'Static' property sheets defined on the class
# The Preference class should be imported from the common location
# in ERP5Type since it could be overloaded in another product
from Products.ERP5Type.Document.Preference import Preference
for property_sheet in Preference.property_sheets:
if not isinstance(property_sheet, basestring):
property_list += property_sheet._properties
if not len(property_list):
return
# 'Dynamic' property sheets added by portal_type
pref_portal_type = portal.portal_types.getTypeInfo('Preference')
if pref_portal_type is None:
LOG('ERP5Form.PreferenceTool', PROBLEM,
'Preference type information is not installed.')
else:
pref_portal_type.updatePropertySheetDefinitionDict(
{'_properties': property_list})
# Generate common method names
for prop in property_list:
if prop.get('preference'):
# XXX read_permission and write_permissions defined at
# property sheet are not respected by this.
# only properties marked as preference are used
attribute = prop['id']
attr_list = [ 'get%s' % convertToUpperCase(attribute)]
if prop['type'] == 'boolean':
attr_list.append('is%s' % convertToUpperCase(attribute))
if prop['type'] in list_types :
attr_list.append('get%sList' % convertToUpperCase(attribute))
for attribute_name in attr_list:
method = PreferenceMethod(attribute_name, prop.get('default'))
setattr(PreferenceTool, attribute_name, method)
read_permission = prop.get('read_permission')
if read_permission:
setattr(PreferenceTool, attribute_name + '__roles__',
PermissionRole(read_permission))
class func_code: pass
class PreferenceMethod(Method):
......@@ -207,23 +135,6 @@ class PreferenceTool(BaseTool):
return method(default)
return default
def _aq_dynamic(self, id):
# XXX as soon as zodb property sheets are put everywhere, this can
# be safely deleted
base_value = PreferenceTool.inheritedAttribute('_aq_dynamic')(self, id)
if not PreferenceTool.aq_preference_generated:
updatePreferenceClassPropertySheetList()
portal = self.getPortalObject()
while portal.portal_type != 'ERP5 Site':
portal = portal.aq_parent.aq_inner.getPortalObject()
createPreferenceToolAccessorList(portal)
PreferenceTool.aq_preference_generated = True
if base_value is None:
return getattr(self, id)
return base_value
security.declareProtected(Permissions.ModifyPortalContent, "setPreference")
def setPreference(self, pref_name, value) :
""" set the preference on the active Preference object"""
......
This diff is collapsed.
......@@ -396,9 +396,8 @@ class ERP5TypeInformation(XMLObject,
return ob
def _getPropertyHolder(self):
ob = self.constructTempInstance(self, self.getId())
ob._aq_dynamic('id')
return ob.aq_portal_type[ob._aq_key()]
import erp5.portal_type as module
return getattr(module, self.getId())
security.declarePrivate('updatePropertySheetDefinitionDict')
def updatePropertySheetDefinitionDict(self, definition_dict):
......@@ -499,7 +498,7 @@ class ERP5TypeInformation(XMLObject,
"""
Returns the list of properties which are specific to the portal type.
"""
return self.constructTempInstance(self, self.getId()).propertyMap()
return self.__class__.propertyMap()
security.declareProtected(Permissions.AccessContentsInformation,
'PrincipiaSearchSource')
......
......@@ -62,7 +62,7 @@ class TranslationProviderBase(object):
"""
property_domain_dict = {}
for prop in self._getPropertyHolder()._properties:
for prop in self._getPropertyHolder().getAccessorHolderPropertyList():
prop_id = prop['id']
if prop.get('translatable') and prop_id not in property_domain_dict:
domain_name = prop.get('translation_domain')
......
......@@ -1301,7 +1301,8 @@ def getExistingBaseCategoryList(portal, base_cat_list):
category_tool = getattr(portal, 'portal_categories', None)
if category_tool is None:
# most likely, accessor generation when bootstrapping a site
warnings.warn("Category Tool is missing. Accessors can not be generated.")
if not getattr(portal, '_v_bootstrapping', False):
warnings.warn("Category Tool is missing. Accessors can not be generated.")
return ()
new_base_cat_list = []
......@@ -1577,13 +1578,6 @@ def setDefaultProperties(property_holder, object=None, portal=None):
portal=portal)
# Create Category Accessors
createAllCategoryAccessors(portal, property_holder, cat_list, econtext)
if object is not None and property_holder.__name__ == "Base":
# XXX use if possible is and real class
if portal is not None:
portal_categories = getattr(portal, 'portal_categories', None)
else:
portal_categories = None
createRelatedAccessors(portal_categories, property_holder, econtext)
property_holder.constraints = []
for constraint in constraint_list:
......@@ -1642,29 +1636,6 @@ def setDefaultProperties(property_holder, object=None, portal=None):
# # setattr(property_holder, prop['id'], defaults[prop['type']])
# pass
# Create for every portal type group an accessor (like isPortalDeliveryType)
# In the future, this should probably use categories
if portal is not None and object is not None: # we can not do anything without portal
# import lately in order to avoid circular dependency
from Products.ERP5Type.ERP5Type import ERP5TypeInformation
portal_type = object.portal_type
for group in ERP5TypeInformation.defined_group_list:
value = portal_type in portal._getPortalGroupedTypeSet(group)
prop = {
'id' : group,
'description' : "accessor to know the membership of portal group %s" \
% group,
'type' : 'group_type',
'default' : value,
'group_type' : group,
}
createDefaultAccessors(
property_holder,
prop['id'],
prop=prop,
read_permission=Permissions.AccessContentsInformation,
portal=portal)
#####################################################
# Accessor initialization
#####################################################
......@@ -2767,6 +2738,7 @@ def createGroupTypeAccessors(property_holder, prop,
Generate accessors that allows to know if we belongs to a particular
group of portal types
"""
raise ValueError("This method is not used. Remove it?")
# Getter
group = prop['group_type']
accessor_name = 'is' + UpperCase(group) + 'Type'
......@@ -2933,7 +2905,8 @@ def createTranslationLanguageAccessors(property_holder, property,
localizer = getattr(portal, 'Localizer', None)
if localizer is None:
warnings.warn("Localizer is missing. Accessors can not be generated.")
if not getattr(portal, '_v_bootstrapping', False):
warnings.warn("Localizer is missing. Accessors can not be generated.")
return
for language in localizer.get_languages():
......
......@@ -2,12 +2,17 @@
import sys
from Products.ERP5Type import Permissions
from Products.ERP5Type.Accessor.Constant import Getter as ConstantGetter
from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Base import Base as ERP5Base
from Products.ERP5Type.Base import PropertyHolder, initializePortalTypeDynamicWorkflowMethods
from Products.ERP5Type.Utils import createAllCategoryAccessors, createExpressionContext, UpperCase
from ExtensionClass import ExtensionClass, pmc_init_of
from zope.interface import classImplements
from ZODB.broken import Broken, PersistentBroken
from AccessControl import ClassSecurityInfo
from zLOG import LOG, WARNING, BLATHER
from portal_type_class import generatePortalTypeClass
......@@ -87,7 +92,7 @@ class GhostBaseMetaClass(ExtensionClass, AccessorHolderType):
InitGhostBase = GhostBaseMetaClass('InitGhostBase', (ERP5Base,), {})
class PortalTypeMetaClass(GhostBaseMetaClass):
class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
"""
Meta class that is used by portal type classes
......@@ -114,6 +119,9 @@ class PortalTypeMetaClass(GhostBaseMetaClass):
if issubclass(type(parent), PortalTypeMetaClass):
PortalTypeMetaClass.subclass_register.setdefault(parent, []).append(cls)
cls.security = ClassSecurityInfo()
cls.workflow_method_registry = {}
cls.__isghost__ = True
super(GhostBaseMetaClass, cls).__init__(name, bases, dictionary)
......@@ -166,11 +174,14 @@ class PortalTypeMetaClass(GhostBaseMetaClass):
for attr in cls.__dict__.keys():
if attr not in ('__module__',
'__doc__',
'security',
'workflow_method_registry',
'__isghost__',
'portal_type'):
delattr(cls, attr)
# generate a ghostbase that derives from all previous bases
ghostbase = GhostBaseMetaClass('GhostBase', cls.__bases__, {})
cls.workflow_method_registry.clear()
cls.__bases__ = (ghostbase,)
cls.__isghost__ = True
cls.resetAcquisitionAndSecurity()
......@@ -191,6 +202,69 @@ class PortalTypeMetaClass(GhostBaseMetaClass):
raise AttributeError
def generatePortalTypeAccessors(cls, site):
createAllCategoryAccessors(site,
cls,
cls._categories,
createExpressionContext(site, site))
# make sure that category accessors from the portal type definition
# are generated, no matter what
# XXX this code is duplicated here, in PropertySheetTool, and in Base
# and anyway is ugly, as tuple-like registration does not help
for id, fake_accessor in cls._getPropertyHolderItemList():
if not isinstance(fake_accessor, tuple):
continue
if fake_accessor is PropertyHolder.WORKFLOW_METHOD_MARKER:
# Case 1 : a workflow method only
accessor = ERP5Base._doNothing
else:
# Case 2 : a workflow method over an accessor
(accessor_class, accessor_args, key) = fake_accessor
accessor = accessor_class(id, key, *accessor_args)
# Add the accessor to the accessor holder
setattr(cls, id, accessor)
portal_workflow = getattr(site, 'portal_workflow', None)
if portal_workflow is None:
if not getattr(site, '_v_bootstrapping', False):
LOG("ERP5Type.Dynamic", WARNING,
"Could not generate workflow methods for %s"
% cls.__name__)
else:
initializePortalTypeDynamicWorkflowMethods(cls, portal_workflow)
# portal type group methods, isNodeType, isResourceType...
from Products.ERP5Type.ERP5Type import ERP5TypeInformation
# XXX possible optimization:
# generate all methods on Base accessor holder, with all methods
# returning False, and redefine on portal types only those returning True,
# aka only those for the group they belong to
for group in ERP5TypeInformation.defined_group_list:
value = cls.__name__ in site._getPortalGroupedTypeSet(group)
accessor_name = 'is' + UpperCase(group) + 'Type'
setattr(cls, accessor_name, ConstantGetter(accessor_name, group, value))
cls.declareProtected(Permissions.AccessContentsInformation,
accessor_name)
from Products.ERP5Type.Cache import initializePortalCachingProperties
initializePortalCachingProperties(site)
# TODO in reality much optimization can be done for all
# PropertyHolder methods:
# - workflow methods are only on the MetaType erp5.portal_type method
# Iterating over the complete MRO is nonsense and inefficient
def _getPropertyHolderItemList(cls):
cls.loadClass()
result = PropertyHolder._getPropertyHolderItemList(cls)
for parent in cls.mro():
if parent.__module__ == 'erp5.accessor_holder':
for x in parent.__dict__.items():
if x[0] not in PropertyHolder.RESERVED_PROPERTY_SET:
result.append(x)
return result
def loadClass(cls):
"""
- mro before load:
......@@ -218,6 +292,7 @@ class PortalTypeMetaClass(GhostBaseMetaClass):
site = getSite()
try:
try:
class_definition = generatePortalTypeClass(site, portal_type)
except AttributeError:
LOG("ERP5Type.Dynamic", WARNING,
......@@ -238,11 +313,14 @@ class PortalTypeMetaClass(GhostBaseMetaClass):
for key, value in attribute_dict.iteritems():
setattr(klass, key, value)
# XXX disabled
#klass._categories = base_category_list
klass._categories = base_category_list
for interface in interface_list:
classImplements(klass, interface)
klass.generatePortalTypeAccessors(site)
except:
import traceback; traceback.print_exc()
finally:
ERP5Base.aq_method_lock.release()
......
......@@ -28,6 +28,7 @@
##############################################################################
import sys
import os
import inspect
from types import ModuleType
......@@ -291,6 +292,16 @@ def generatePortalTypeClass(site, portal_type_name):
erp5.accessor_holder,
property_sheet_tool)
if "Base" in property_sheet_set:
accessor_holder = None
# useless if Base Category is not yet here
if hasattr(erp5.accessor_holder, "Base Category"):
accessor_holder = _generateBaseAccessorHolder(
site,
erp5.accessor_holder)
if accessor_holder is not None:
accessor_holder_list.append(accessor_holder)
# XXX a hook to add per-portal type accessor holders maybe?
if portal_type_name == "Preference Tool":
accessor_holder = _generatePreferenceToolAccessorHolder(
......@@ -397,6 +408,8 @@ def initializeDynamicModules():
erp5.temp_portal_type = registerDynamicModule('erp5.temp_portal_type',
loadTempPortalTypeClass)
required_tool_list = [('portal_types', 'Base Type'),
('portal_property_sheets', 'BaseType')]
last_sync = -1
def synchronizeDynamicModules(context, force=False):
"""
......@@ -423,12 +436,63 @@ def synchronizeDynamicModules(context, force=False):
return
last_sync = cookie
LOG("ERP5Type.dynamic", 0, "Resetting dynamic classes")
import erp5
Base.aq_method_lock.acquire()
try:
migrated = False
for tool_id, line_id in required_tool_list:
# if the instance has no property sheet tool, or incomplete
# property sheets, we need to import some data to bootstrap
# (only likely to happen on the first run ever)
tool = getattr(portal, tool_id, None)
if tool is not None:
if getattr(tool, line_id, None) is None:
# tool exists, but is incomplete
portal._delObject(tool_id)
else:
# tool exists, Base Type is represented; probably OK
continue
if not migrated:
# XXX: if some portal types are missing, for instance
# if some Tools have no portal types, this is likely to fail with an
# error. On the other hand, we can't proceed without this change,
# and if we dont import the xml, the instance wont start.
portal.migrateToPortalTypeClass()
migrated = True
LOG('ERP5Site', INFO, 'importing transitional %s tool'
' from Products.ERP5.bootstrap to be able to load'
' core items...' % tool_id)
from Products.ERP5.ERP5Site import getBootstrapDirectory
bundle_path = os.path.join(getBootstrapDirectory(),
'%s.xml' % tool_id)
assert os.path.exists(bundle_path), 'Please update ERP5 product'
try:
tool = portal._importObjectFromFile(
bundle_path,
id=tool_id,
verify=False,
set_owner=False,
suppress_events=True)
from Products.ERP5.Document.BusinessTemplate import _recursiveRemoveUid
_recursiveRemoveUid(tool)
portal._setOb(tool_id, tool)
except:
import traceback; traceback.print_exc()
raise
if not getattr(portal, '_v_bootstrapping', False):
LOG('ERP5Site', INFO, 'Transition successful, please update your'
' business templates')
LOG("ERP5Type.dynamic", 0, "Resetting dynamic classes")
for class_name, klass in inspect.getmembers(erp5.portal_type,
inspect.isclass):
klass.restoreGhostState()
......
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