Commit 425700d0 authored by Arnaud Fontaine's avatar Arnaud Fontaine

Portal Type as Classes: Interaction Workflows methods could be improperly...

Portal Type as Classes: Interaction Workflows methods could be improperly generated because of 58d4ab8e.

1. Add Interaction on _setSourceReference() to `Document Component`.
2. generatePortalTypeClass() is called recursively and an inner `Document
   Component` Portal Type class is created without any accessor holder.
=> As __bases__ is (klass, GetAcquireLocalRolesMixIn) since 58d4ab8e but loadClass()
   was not updated to reflect this, Portal Type Workflow Methods were generated on
   the inner Portal Type Class where _setSourceReference() is not available and
   registerWorkflowMethod() creates a WorkflowMethod calling Base._doNothing.
parent ebfb3675
...@@ -347,7 +347,7 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder): ...@@ -347,7 +347,7 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
site = getSite() site = getSite()
with aq_method_lock: with aq_method_lock:
try: try:
class_definition = generatePortalTypeClass(site, portal_type) is_partially_generated, class_definition = generatePortalTypeClass(site, portal_type)
except AttributeError: except AttributeError:
LOG("ERP5Type.Dynamic", WARNING, LOG("ERP5Type.Dynamic", WARNING,
"Could not access Portal Type Object for type %r" "Could not access Portal Type Object for type %r"
...@@ -356,6 +356,7 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder): ...@@ -356,6 +356,7 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
portal_type_category_list = [] portal_type_category_list = []
attribute_dict = dict(_categories=[], constraints=[]) attribute_dict = dict(_categories=[], constraints=[])
interface_list = [] interface_list = []
is_partially_generated = True
else: else:
base_tuple, portal_type_category_list, \ base_tuple, portal_type_category_list, \
interface_list, attribute_dict = class_definition interface_list, attribute_dict = class_definition
...@@ -376,10 +377,11 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder): ...@@ -376,10 +377,11 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
for interface in interface_list: for interface in interface_list:
classImplements(klass, interface) classImplements(klass, interface)
# skip this during the early Base Type / Types Tool generation # Skip this during the early Base Type/Types Tool generation because
# because they dont have accessors, and will mess up # they dont have accessors and will mess up workflow methods (base_tuple
# workflow methods. We KNOW that we will re-load this type anyway # being either `(klass,)` or `(klass, GetAcquireLocalRolesMixIn)`). We
if len(base_tuple) > 1: # KNOW that we will re-load this type anyway.
if not is_partially_generated:
klass.generatePortalTypeAccessors(site, portal_type_category_list) klass.generatePortalTypeAccessors(site, portal_type_category_list)
# need to set %s__roles__ for generated methods # need to set %s__roles__ for generated methods
cls.setupSecurity() cls.setupSecurity()
......
...@@ -143,6 +143,10 @@ def generatePortalTypeClass(site, portal_type_name): ...@@ -143,6 +143,10 @@ def generatePortalTypeClass(site, portal_type_name):
_categories=[], _categories=[],
constraints=[]) constraints=[])
# When only the inner portal type class is loaded (ERP5Site initial
# installation, recursive loads, bootstrap)
is_partially_generated = False
if portal_type_name in core_portal_type_class_dict: if portal_type_name in core_portal_type_class_dict:
if not core_portal_type_class_dict[portal_type_name]['generating']: if not core_portal_type_class_dict[portal_type_name]['generating']:
# Loading the (full) outer portal type class # Loading the (full) outer portal type class
...@@ -158,7 +162,8 @@ def generatePortalTypeClass(site, portal_type_name): ...@@ -158,7 +162,8 @@ def generatePortalTypeClass(site, portal_type_name):
# Don't do anything else, just allow to load fully the outer # Don't do anything else, just allow to load fully the outer
# portal type class # portal type class
return ((klass,), [], [], attribute_dict) is_partially_generated = True
return is_partially_generated, ((klass,), [], [], attribute_dict)
# Do not use __getitem__ (or _getOb) because portal_type may exist in a # Do not use __getitem__ (or _getOb) because portal_type may exist in a
# type provider other than Types Tool. # type provider other than Types Tool.
...@@ -198,13 +203,11 @@ def generatePortalTypeClass(site, portal_type_name): ...@@ -198,13 +203,11 @@ def generatePortalTypeClass(site, portal_type_name):
# Only happen when portal_types is empty (e.g. when creating a # Only happen when portal_types is empty (e.g. when creating a
# new ERP5Site) # new ERP5Site)
type_class = core_portal_type_class_dict[portal_type_name]['type_class'] type_class = core_portal_type_class_dict[portal_type_name]['type_class']
is_partially_generated = True
else: else:
# Try to figure out a coresponding document class from the # Missing Portal Type. This should not happen (all ERP5 object *must*
# document side. This can happen when calling newTempAmount for # have a Portal Type and newTempXXX being deprecated) but try anyway to
# instance: # figure out a coresponding Document class.
# Amount has no corresponding Base Type and will never have one
# But the semantic of newTempXXX requires us to create an
# object using the Amount Document, so we promptly do it:
type_class = portal_type_name.replace(' ', '') type_class = portal_type_name.replace(' ', '')
mixin_list = [] mixin_list = []
...@@ -277,6 +280,8 @@ def generatePortalTypeClass(site, portal_type_name): ...@@ -277,6 +280,8 @@ def generatePortalTypeClass(site, portal_type_name):
attribute_dict['_categories'] = list(base_category_set) attribute_dict['_categories'] = list(base_category_set)
finally: finally:
property_sheet_generating_portal_type_set.remove(portal_type_name) property_sheet_generating_portal_type_set.remove(portal_type_name)
else:
is_partially_generated = True
# LOG("ERP5Type.dynamic", INFO, # LOG("ERP5Type.dynamic", INFO,
# "Filled accessor holder list for portal_type %s (%s)" % \ # "Filled accessor holder list for portal_type %s (%s)" % \
...@@ -344,7 +349,7 @@ def generatePortalTypeClass(site, portal_type_name): ...@@ -344,7 +349,7 @@ def generatePortalTypeClass(site, portal_type_name):
# "Portal type %s loaded with bases %s" \ # "Portal type %s loaded with bases %s" \
# % (portal_type_name, repr(base_class_list))) # % (portal_type_name, repr(base_class_list)))
return (tuple(base_class_list), return is_partially_generated, (tuple(base_class_list),
portal_type_category_list, portal_type_category_list,
interface_class_list, interface_class_list,
attribute_dict) attribute_dict)
......
...@@ -377,6 +377,25 @@ class TestPortalTypeClass(ERP5TypeTestCase): ...@@ -377,6 +377,25 @@ class TestPortalTypeClass(ERP5TypeTestCase):
validate_date_list) validate_date_list)
self.assertEqual(person.getValidateTransitionDate(), validate_date_list[-1]) self.assertEqual(person.getValidateTransitionDate(), validate_date_list[-1])
def testNoPortalTypeAccessorGeneratedOnPartiallyGeneratedPortalTypeClass(self):
"""
Test for `is_partially_generated` ({lazy_class,portal_type_class}.py):
Portal Type accessors must not be generated on a partially loaded Portal
Type class (eg "inner" class)
"""
from Products.ERP5Type.dynamic.lazy_class import PortalTypeMetaClass
PortalTypeMetaClass_generatePortalTypeAccessors = PortalTypeMetaClass.generatePortalTypeAccessors
def generatePortalTypeAccessors(cls, *args, **kw):
assert len(cls.__bases__) > 2
return PortalTypeMetaClass_generatePortalTypeAccessors(cls, *args, **kw)
try:
PortalTypeMetaClass.generatePortalTypeAccessors = generatePortalTypeAccessors
self.portal.portal_types.resetDynamicDocuments()
import erp5.portal_type
getattr(erp5.portal_type, 'Document Component').loadClass()
finally:
PortalTypeMetaClass.generatePortalTypeAccessors = PortalTypeMetaClass_generatePortalTypeAccessors
class TestZodbPropertySheet(ERP5TypeTestCase): class TestZodbPropertySheet(ERP5TypeTestCase):
""" """
XXX: WORK IN PROGRESS XXX: WORK IN PROGRESS
......
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