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
Pipeline #11622 failed with stage
......@@ -347,7 +347,7 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
site = getSite()
with aq_method_lock:
try:
class_definition = generatePortalTypeClass(site, portal_type)
is_partially_generated, class_definition = generatePortalTypeClass(site, portal_type)
except AttributeError:
LOG("ERP5Type.Dynamic", WARNING,
"Could not access Portal Type Object for type %r"
......@@ -356,6 +356,7 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
portal_type_category_list = []
attribute_dict = dict(_categories=[], constraints=[])
interface_list = []
is_partially_generated = True
else:
base_tuple, portal_type_category_list, \
interface_list, attribute_dict = class_definition
......@@ -376,10 +377,11 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
for interface in interface_list:
classImplements(klass, interface)
# skip this during the early Base Type / Types Tool generation
# because they dont have accessors, and will mess up
# workflow methods. We KNOW that we will re-load this type anyway
if len(base_tuple) > 1:
# Skip this during the early Base Type/Types Tool generation because
# they dont have accessors and will mess up workflow methods (base_tuple
# being either `(klass,)` or `(klass, GetAcquireLocalRolesMixIn)`). We
# KNOW that we will re-load this type anyway.
if not is_partially_generated:
klass.generatePortalTypeAccessors(site, portal_type_category_list)
# need to set %s__roles__ for generated methods
cls.setupSecurity()
......
......@@ -143,6 +143,10 @@ def generatePortalTypeClass(site, portal_type_name):
_categories=[],
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 not core_portal_type_class_dict[portal_type_name]['generating']:
# Loading the (full) outer portal type class
......@@ -158,7 +162,8 @@ def generatePortalTypeClass(site, portal_type_name):
# Don't do anything else, just allow to load fully the outer
# 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
# type provider other than Types Tool.
......@@ -198,13 +203,11 @@ def generatePortalTypeClass(site, portal_type_name):
# Only happen when portal_types is empty (e.g. when creating a
# new ERP5Site)
type_class = core_portal_type_class_dict[portal_type_name]['type_class']
is_partially_generated = True
else:
# Try to figure out a coresponding document class from the
# document side. This can happen when calling newTempAmount for
# instance:
# 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:
# Missing Portal Type. This should not happen (all ERP5 object *must*
# have a Portal Type and newTempXXX being deprecated) but try anyway to
# figure out a coresponding Document class.
type_class = portal_type_name.replace(' ', '')
mixin_list = []
......@@ -277,6 +280,8 @@ def generatePortalTypeClass(site, portal_type_name):
attribute_dict['_categories'] = list(base_category_set)
finally:
property_sheet_generating_portal_type_set.remove(portal_type_name)
else:
is_partially_generated = True
# LOG("ERP5Type.dynamic", INFO,
# "Filled accessor holder list for portal_type %s (%s)" % \
......@@ -344,10 +349,10 @@ def generatePortalTypeClass(site, portal_type_name):
# "Portal type %s loaded with bases %s" \
# % (portal_type_name, repr(base_class_list)))
return (tuple(base_class_list),
portal_type_category_list,
interface_class_list,
attribute_dict)
return is_partially_generated, (tuple(base_class_list),
portal_type_category_list,
interface_class_list,
attribute_dict)
def loadTempPortalTypeClass(portal_type_name):
"""
......
......@@ -377,6 +377,25 @@ class TestPortalTypeClass(ERP5TypeTestCase):
validate_date_list)
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):
"""
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