diff --git a/product/CMFActivity/ActivityTool.py b/product/CMFActivity/ActivityTool.py
index 9f38e0e0f85098ea79efe1ebb0b23bdb0bb64769..6962db64f0e0db2a80eef65e30882761a0679c8e 100644
--- a/product/CMFActivity/ActivityTool.py
+++ b/product/CMFActivity/ActivityTool.py
@@ -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' }
diff --git a/product/ERP5/Document/BusinessTemplate.py b/product/ERP5/Document/BusinessTemplate.py
index 8de63306461d78b091d965a4c0974bb560c31887..34586ce97c22a4dc7c9f9a9e9507940b7c74fa6a 100644
--- a/product/ERP5/Document/BusinessTemplate.py
+++ b/product/ERP5/Document/BusinessTemplate.py
@@ -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': (
diff --git a/product/ERP5/ERP5Site.py b/product/ERP5/ERP5Site.py
index 3764ed233a705e2dd6691ee3dd5d2f937eae63a6..b3f6abaa737f49e8e56dc9914029aceefc13ec1a 100644
--- a/product/ERP5/ERP5Site.py
+++ b/product/ERP5/ERP5Site.py
@@ -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
diff --git a/product/ERP5Form/Document/Preference.py b/product/ERP5Form/Document/Preference.py
index b5e2fe17fcfddb30e66538a8a6cd42ddc31be107..5733f668828f29ff28335dbe861a5de9d74a6187 100644
--- a/product/ERP5Form/Document/Preference.py
+++ b/product/ERP5Form/Document/Preference.py
@@ -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)
diff --git a/product/ERP5Form/PreferenceTool.py b/product/ERP5Form/PreferenceTool.py
index 68716a05ffdfe2acd752c8e4328835bd10c5b98f..7d0b6f608e0fa5f6ddc06d72db939f18883990bb 100644
--- a/product/ERP5Form/PreferenceTool.py
+++ b/product/ERP5Form/PreferenceTool.py
@@ -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"""
diff --git a/product/ERP5Type/Base.py b/product/ERP5Type/Base.py
index 1d5564d0f00b01b2bea631b8eeb7ddeeffead0c8..0c0e33e2d133d46a69d6a53fdb72045cb4f546a6 100644
--- a/product/ERP5Type/Base.py
+++ b/product/ERP5Type/Base.py
@@ -339,7 +339,9 @@ class PropertyHolder(object):
     Invokes appropriate factory and create an accessor
     """
     fake_accessor = getattr(self, id)
-    ptype = self._portal_type
+    ptype = getattr(self, '_portal_type', None)
+    if ptype is None:
+      ptype = self.portal_type
     if fake_accessor is PropertyHolder.WORKFLOW_METHOD_MARKER:
       # Case 1 : a workflow method only
       accessor = Base._doNothing
@@ -416,6 +418,7 @@ class PropertyHolder(object):
       self.workflow_method_registry[id] = signature_list + (signature,)
     if getattr(self, id, None) is None:
       setattr(self, id, PropertyHolder.WORKFLOW_METHOD_MARKER)
+      self.createAccessor(id)
 
   def declareProtected(self, permission, accessor_name):
     """
@@ -476,10 +479,8 @@ class PropertyHolder(object):
     """
     result = {}
     if inherited:
-      base_list = list(klass.__bases__)
-      base_list.reverse()
-      for klass in base_list:
-        result.update(self._getClassDict(klass, inherited=1, local=1))
+      for parent in reversed(klass.mro()):
+        result.update(parent.__dict__)
     if local:
       result.update(klass.__dict__)
     return result
@@ -495,7 +496,7 @@ class PropertyHolder(object):
     Return a list of tuple (id, method, module) for every class method
     """
     return [x for x in self._getClassItemList(klass, inherited=inherited,
-      local=local) if callable(x[1]) and not isinstance(x[1], Method)]
+      local=local) if callable(x[1])]
 
   def getClassMethodIdList(self, klass, inherited=1, local=1):
     """
@@ -542,6 +543,8 @@ def initializeClassDynamicProperties(self, klass):
       Base.aq_method_generated.add(klass)
 
 def initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, portal):
+  raise ValueError("No reason to go through this no more with portal type classes")
+
   ## Init CachingMethod which implements caching for ERP5
   from Products.ERP5Type.Cache import initializePortalCachingProperties
   initializePortalCachingProperties(portal)
@@ -615,8 +618,7 @@ def initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, portal):
     #klass.__ac_permissions__ = prop_holder.__ac_permissions__
     Base.aq_portal_type[aq_key] = prop_holder
 
-def initializePortalTypeDynamicWorkflowMethods(self, klass, ptype, prop_holder,
-                                               portal):
+def initializePortalTypeDynamicWorkflowMethods(ptype_klass, portal_workflow):
   """We should now make sure workflow methods are defined
   and also make sure simulation state is defined."""
   # aq_inner is required to prevent extra name lookups from happening
@@ -624,12 +626,13 @@ def initializePortalTypeDynamicWorkflowMethods(self, klass, ptype, prop_holder,
   # wrapper contains an object with _aq_dynamic defined, the workflow id
   # is looked up with _aq_dynamic, thus causes infinite recursions.
 
-  portal_workflow = aq_inner(getToolByName(portal, 'portal_workflow'))
+  portal_workflow = aq_inner(portal_workflow)
+  portal_type = ptype_klass.__name__
 
   dc_workflow_dict = dict()
   interaction_workflow_dict = dict()
 
-  for wf in portal_workflow.getWorkflowsFor(self):
+  for wf in portal_workflow.getWorkflowsFor(portal_type):
     wf_id = wf.id
     wf_type = wf.__class__.__name__
     if wf_type == "DCWorkflowDefinition":
@@ -643,11 +646,11 @@ def initializePortalTypeDynamicWorkflowMethods(self, klass, ptype, prop_holder,
                                      WorkflowState.TranslatedGetter),
           ('getTranslated%sTitle' % UpperCase(state_var),
                                      WorkflowState.TranslatedTitleGetter)):
-        if not hasattr(prop_holder, method_id):
+        if not hasattr(ptype_klass, method_id):
           method = getter(method_id, wf_id)
           # Attach to portal_type
-          setattr(prop_holder, method_id, method)
-          prop_holder.security.declareProtected(
+          setattr(ptype_klass, method_id, method)
+          ptype_klass.security.declareProtected(
                                  Permissions.AccessContentsInformation,
                                  method_id )
 
@@ -673,40 +676,38 @@ def initializePortalTypeDynamicWorkflowMethods(self, klass, ptype, prop_holder,
     transition_id_set, trigger_dict = v
     for tr_id, tdef in trigger_dict.iteritems():
       method_id = convertToMixedCase(tr_id)
-      method = getattr(klass, method_id, _MARKER)
+      method = getattr(ptype_klass, method_id, _MARKER)
       if method is _MARKER:
-        prop_holder.security.declareProtected(Permissions.AccessContentsInformation,
+        ptype_klass.security.declareProtected(Permissions.AccessContentsInformation,
                                               method_id)
-        prop_holder.registerWorkflowMethod(method_id, wf_id, tr_id)
+        ptype_klass.registerWorkflowMethod(method_id, wf_id, tr_id)
         continue
 
       # Wrap method
       if not callable(method):
         LOG('initializePortalTypeDynamicWorkflowMethods', 100,
             'WARNING! Can not initialize %s on %s' % \
-              (method_id, str(klass)))
+              (method_id, portal_type))
         continue
 
       if not isinstance(method, WorkflowMethod):
         method = WorkflowMethod(method)
-        setattr(klass, method_id, method)
+        setattr(ptype_klass, method_id, method)
       else:
         # We must be sure that we
         # are going to register class defined
         # workflow methods to the appropriate transition
         transition_id = method.getTransitionId()
         if transition_id in transition_id_set:
-          method.registerTransitionAlways(ptype, wf_id, transition_id)
-      method.registerTransitionAlways(ptype, wf_id, tr_id)
+          method.registerTransitionAlways(portal_type, wf_id, transition_id)
+      method.registerTransitionAlways(portal_type, wf_id, tr_id)
 
   if not interaction_workflow_dict:
     return
 
-  class_method_list = prop_holder.getClassMethodIdList(klass)
-  # only compute once this (somehow) costly list
-  all_method_id_list = prop_holder.getAccessorMethodIdList() + \
-                       prop_holder.getWorkflowMethodIdList() + \
-                       class_method_list
+  # all methods in mro of portal type class: that contains all
+  # workflow methods and accessors you could possibly ever need
+  class_method_id_list = ptype_klass.getClassMethodIdList(ptype_klass)
 
   interaction_queue = []
   # XXX This part is (more or less...) a copy and paste
@@ -714,7 +715,7 @@ def initializePortalTypeDynamicWorkflowMethods(self, klass, ptype, prop_holder,
     transition_id_set, trigger_dict = v
     for tr_id, tdef in trigger_dict.iteritems():
       if (tdef.portal_type_filter is not None and \
-          ptype not in tdef.portal_type_filter):
+          portal_type not in tdef.portal_type_filter):
         continue
       for imethod_id in tdef.method_id:
         if wildcard_interaction_method_id_match(imethod_id):
@@ -730,21 +731,21 @@ def initializePortalTypeDynamicWorkflowMethods(self, klass, ptype, prop_holder,
                                     method_id_matcher))
 
           # XXX - class stuff is missing here
-          method_id_list = filter(method_id_matcher, all_method_id_list)
+          method_id_list = filter(method_id_matcher, class_method_id_list)
         else:
           # Single method
           # XXX What if the method does not exist ?
           #     It's not consistent with regexp based filters.
           method_id_list = [imethod_id]
         for method_id in method_id_list:
-          method = getattr(klass, method_id, _MARKER)
+          method = getattr(ptype_klass, method_id, _MARKER)
           if method is _MARKER:
             # set a default security, if this method is not already
             # protected.
-            if method_id not in prop_holder.security.names:
-              prop_holder.security.declareProtected(
+            if method_id not in ptype_klass.security.names:
+              ptype_klass.security.declareProtected(
                   Permissions.AccessContentsInformation, method_id)
-            prop_holder.registerWorkflowMethod(method_id, wf_id, tr_id,
+            ptype_klass.registerWorkflowMethod(method_id, wf_id, tr_id,
                                                tdef.once_per_transaction)
             continue
 
@@ -752,42 +753,46 @@ def initializePortalTypeDynamicWorkflowMethods(self, klass, ptype, prop_holder,
           if not callable(method):
             LOG('initializePortalTypeDynamicWorkflowMethods', 100,
                 'WARNING! Can not initialize %s on %s' % \
-                  (method_id, str(klass)))
+                  (method_id, portal_type))
             continue
           if not isinstance(method, WorkflowMethod):
             method = WorkflowMethod(method)
-            setattr(klass, method_id, method)
+            setattr(ptype_klass, method_id, method)
           else:
             # We must be sure that we
             # are going to register class defined
             # workflow methods to the appropriate transition
             transition_id = method.getTransitionId()
             if transition_id in transition_id_set:
-              method.registerTransitionAlways(ptype, wf_id, transition_id)
+              method.registerTransitionAlways(portal_type, wf_id, transition_id)
           if tdef.once_per_transaction:
-            method.registerTransitionOncePerTransaction(ptype, wf_id, tr_id)
+            method.registerTransitionOncePerTransaction(portal_type, wf_id, tr_id)
           else:
-            method.registerTransitionAlways(ptype, wf_id, tr_id)
+            method.registerTransitionAlways(portal_type, wf_id, tr_id)
 
   if not interaction_queue:
     return
 
-  new_method_set = set(prop_holder.getClassMethodIdList(klass))
-  added_method_set = new_method_set.difference(class_method_list)
+  # the only methods that could have appeared since last check are
+  # workflow methods
+  # TODO we could just queue the ids of methods that are attached to the
+  # portal type class in the previous loop, to improve performance
+  new_method_set = set(ptype_klass.getWorkflowMethodIdList())
+  added_method_set = new_method_set.difference(class_method_id_list)
   # We need to run this part twice in order to handle interactions of interactions
   # ex. an interaction workflow creates a workflow method which matches
   # the regexp of another interaction workflow
   for wf_id, tr_id, transition_id_set, once, method_id_matcher in interaction_queue:
     for method_id in filter(method_id_matcher, added_method_set):
       # method must already exist and be a workflow method
-      method = getattr(klass, method_id)
+      method = getattr(ptype_klass, method_id)
       transition_id = method.getTransitionId()
       if transition_id in transition_id_set:
-        method.registerTransitionAlways(ptype, wf_id, transition_id)
+        method.registerTransitionAlways(portal_type, wf_id, transition_id)
       if once:
-        method.registerTransitionOncePerTransaction(ptype, wf_id, tr_id)
+        method.registerTransitionOncePerTransaction(portal_type, wf_id, tr_id)
       else:
-        method.registerTransitionAlways(ptype, wf_id, tr_id)
+        method.registerTransitionAlways(portal_type, wf_id, tr_id)
 
 class Base( CopyContainer,
             PortalContent,
@@ -906,23 +911,15 @@ class Base( CopyContainer,
 
   def _propertyMap(self):
     """ Method overload - properties are now defined on the ptype """
-    # Get all the accessor holders for ZODB Property Sheets
-    if hasattr(self.__class__, 'getAccessorHolderPropertyList'):
-      accessor_holder_property_list = \
-          tuple(self.__class__.getAccessorHolderPropertyList())
-    # Temporary portal type (such as 'TempBase' meaningful to display
-    # the objects being created/updated/removed on SVN update) does
-    # not inherit from any class of erp5.portal_type
-    else:
-      accessor_holder_property_list = ()
+    klass = self.__class__
+    property_list = []
+    # Get all the accessor holders for this portal type
+    if hasattr(klass, 'getAccessorHolderPropertyList'):
+      property_list += \
+          self.__class__.getAccessorHolderPropertyList()
 
-    self._aq_dynamic('id') # Make sure aq_dynamic has been called once
-    property_holder = Base.aq_portal_type.get(self._aq_key())
-    if property_holder is None:
-      return ERP5PropertyManager._propertyMap(self)
-    return (tuple(getattr(property_holder, '_properties', ())) +
-            tuple(getattr(self, '_local_properties', ())) +
-            accessor_holder_property_list)
+    property_list += getattr(self, '_local_properties', [])
+    return tuple(property_list)
 
   def manage_historyCompare(self, rev1, rev2, REQUEST,
                             historyComparisonResults=''):
@@ -945,6 +942,9 @@ class Base( CopyContainer,
     createPreferenceToolAccessorList(self.getPortalObject())
 
   def _aq_dynamic(self, id):
+    # ahah! disabled, thanks to portal type classes
+    return None
+
     # _aq_dynamic has been created so that callable objects
     # and default properties can be associated per portal type
     # and per class. Other uses are possible (ex. WebSection).
@@ -1432,28 +1432,12 @@ class Base( CopyContainer,
         except TypeError:
           pass
       return method(**kw)
-    # Try to get a portal_type property (Implementation Dependent)
-    aq_key = self._aq_key()
-    aq_portal_type = Base.aq_portal_type
-    if aq_key not in aq_portal_type:
-      try:
-        self._aq_dynamic(accessor_name)
-      except AttributeError:
-        pass
-    if hasattr(aq_portal_type[aq_key], accessor_name):
-      method = getattr(self, accessor_name)
-      if d is not _MARKER:
-        try:
-          return method(d, **kw)
-        except TypeError:
-          pass
-      return method(**kw)
     # Try a mono valued accessor if it is available
     # and return it as a list
     if accessor_name.endswith('List'):
       mono_valued_accessor_name = accessor_name[:-4]
-      if hasattr(aq_portal_type[aq_key], mono_valued_accessor_name):
-        method = getattr(self, mono_valued_accessor_name)
+      method = getattr(self, mono_valued_accessor_name, None)
+      if method is not None:
         # We have a monovalued property
         if d is _MARKER:
           result = method(**kw)
@@ -1519,27 +1503,15 @@ class Base( CopyContainer,
     if getattr(aq_self, public_accessor_name, None) is not None:
       method = getattr(self, public_accessor_name)
       return method(value, **kw)
-    # Try to get a portal_type property (Implementation Dependent)
-    aq_key = self._aq_key()
-    aq_portal_type = Base.aq_portal_type
-    if not aq_portal_type.has_key(aq_key):
-      self._aq_dynamic('id') # Make sure _aq_dynamic has been called once
-    if getattr(aq_portal_type[aq_key], accessor_name, None) is not None:
-      method = getattr(self, accessor_name)
-      # LOG("Base.py", 0, "method = %s, name = %s" %(method, accessor_name))
-      return method(value, **kw)
-    if getattr(aq_portal_type[aq_key], public_accessor_name, None) is not None:
-      method = getattr(self, public_accessor_name)
-      return method(value, **kw)
     # Try a mono valued setter if it is available
     # and call it
     if accessor_name.endswith('List'):
       mono_valued_accessor_name = accessor_name[:-4]
       mono_valued_public_accessor_name = public_accessor_name[:-4]
       method = None
-      if hasattr(aq_portal_type[aq_key], mono_valued_accessor_name):
+      if hasattr(self, mono_valued_accessor_name):
         method = getattr(self, mono_valued_accessor_name)
-      elif hasattr(aq_portal_type[aq_key], mono_valued_public_accessor_name):
+      elif hasattr(self, mono_valued_public_accessor_name):
         method = getattr(self, mono_valued_public_accessor_name)
       if method is not None:
         if isinstance(value, (list, tuple)):
@@ -1584,18 +1556,6 @@ class Base( CopyContainer,
       method = getattr(self, public_accessor_name)
       method(value, **kw)
       return
-    # Try to get a portal_type property (Implementation Dependent)
-    aq_key = self._aq_key()
-    if not Base.aq_portal_type.has_key(aq_key):
-      self._aq_dynamic('id') # Make sure _aq_dynamic has been called once
-    if hasattr(Base.aq_portal_type[aq_key], accessor_name):
-      method = getattr(self, accessor_name)
-      method(value, **kw)
-      return
-    if hasattr(Base.aq_portal_type[aq_key], public_accessor_name):
-      method = getattr(self, public_accessor_name)
-      method(value, **kw)
-      return
     # Finaly use standard PropertyManager
     #LOG("Changing attr: ",0, key)
     #try:
@@ -1692,20 +1652,16 @@ class Base( CopyContainer,
     unordered_key_list = [k for k in key_list if k not in edit_order]
     ordered_key_list = [k for k in edit_order if k in key_list]
     restricted_method_set = set()
+    default_permission_set = set(('Access contents information',
+                              'Modify portal content'))
     if restricted:
       # retrieve list of accessors which doesn't use default permissions
-      aq_key = self._aq_key()
-      aq_portal_type = Base.aq_portal_type
-      if aq_key not in aq_portal_type:
-        try:
-          self._aq_dynamic("")
-        except AttributeError:
-          pass
-      prop_holder = aq_portal_type[aq_key]
-      for permissions in prop_holder.__ac_permissions__:
-        if permissions[0] not in ('Access contents information', 'Modify portal content'):
-          for method in permissions[1]:
-            restricted_method_set.add(method)
+      for ancestor in self.__class__.mro():
+        for permissions in getattr(ancestor, '__ac_permissions__', ()):
+          if permissions[0] not in default_permission_set:
+            for method in permissions[1]:
+              if method.startswith('set'):
+                restricted_method_set.add(method)
 
     getProperty = self.getProperty
     hasProperty = self.hasProperty
diff --git a/product/ERP5Type/ERP5Type.py b/product/ERP5Type/ERP5Type.py
index c0107880feacc3a0e1aff49a0dc7db0c186a643b..7e7d1c42cc7cdc564207600f81d4af9629497d6e 100644
--- a/product/ERP5Type/ERP5Type.py
+++ b/product/ERP5Type/ERP5Type.py
@@ -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')
diff --git a/product/ERP5Type/TranslationProviderBase.py b/product/ERP5Type/TranslationProviderBase.py
index 71361fdcde282033f67b17bd3fd8394ac2c9bd7b..baae16869226050a8904d70b731b3dd6a33860c8 100644
--- a/product/ERP5Type/TranslationProviderBase.py
+++ b/product/ERP5Type/TranslationProviderBase.py
@@ -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')
diff --git a/product/ERP5Type/Utils.py b/product/ERP5Type/Utils.py
index e3fff27b3e59f2ed1fa35ede26b679ffb6b23d6e..8eff16a5e2d40523a99823da7ea1c0869daf5e34 100644
--- a/product/ERP5Type/Utils.py
+++ b/product/ERP5Type/Utils.py
@@ -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():
diff --git a/product/ERP5Type/dynamic/lazy_class.py b/product/ERP5Type/dynamic/lazy_class.py
index 24cde15756f164d66bad7c5a95d0d2bc76a22362..e80dd93b3965a391234388fde416300c691bf51f 100644
--- a/product/ERP5Type/dynamic/lazy_class.py
+++ b/product/ERP5Type/dynamic/lazy_class.py
@@ -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()
 
diff --git a/product/ERP5Type/dynamic/portal_type_class.py b/product/ERP5Type/dynamic/portal_type_class.py
index d2eb0c2bdbb9d580ef70feb84ec3850199cf75ba..468ae162829113c1c700da2e3043d458414a8a22 100644
--- a/product/ERP5Type/dynamic/portal_type_class.py
+++ b/product/ERP5Type/dynamic/portal_type_class.py
@@ -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()