From ad5c1783206c3fa6c431ec6750e7f352fd3c2edc Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 29 Jan 2025 02:01:10 +0000 Subject: [PATCH 1/5] See what happens if we just implement IBlocks and add the request objects --- .../restapi/deserializer/controlpanels/__init__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/plone/restapi/deserializer/controlpanels/__init__.py b/src/plone/restapi/deserializer/controlpanels/__init__.py index f27b8fd4b8..0df3420014 100644 --- a/src/plone/restapi/deserializer/controlpanels/__init__.py +++ b/src/plone/restapi/deserializer/controlpanels/__init__.py @@ -15,9 +15,15 @@ from zope.schema import getFields from zope.schema.interfaces import ValidationError +from plone.restapi.behaviors import IBlocks +from zope.globalrequest import getRequest -@implementer(IDexterityContent) + +@implementer(IDexterityContent, IBlocks) class FakeDXContext: + def __init__(self, request): + self.request = request + self.REQUEST = getRequest() """Fake DX content class, so we can re-use the DX field deserializers""" @@ -43,7 +49,7 @@ def __call__(self, mask_validation_errors=True): errors = [] # Make a fake context - fake_context = FakeDXContext() + fake_context = FakeDXContext(self.request) for name, field in getFields(self.schema).items(): field_data = schema_data.setdefault(self.schema, {}) From 9accdba26abce8d42de5e67472c9e6c16f674101 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 29 Jan 2025 02:01:10 +0000 Subject: [PATCH 2/5] AQ wrap the fake DX object to fix request-based object access --- .../restapi/deserializer/controlpanels/__init__.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/plone/restapi/deserializer/controlpanels/__init__.py b/src/plone/restapi/deserializer/controlpanels/__init__.py index 0df3420014..b5a30c7c2a 100644 --- a/src/plone/restapi/deserializer/controlpanels/__init__.py +++ b/src/plone/restapi/deserializer/controlpanels/__init__.py @@ -16,14 +16,11 @@ from zope.schema.interfaces import ValidationError from plone.restapi.behaviors import IBlocks -from zope.globalrequest import getRequest +from Acquisition import ImplicitAcquisitionWrapper @implementer(IDexterityContent, IBlocks) class FakeDXContext: - def __init__(self, request): - self.request = request - self.REQUEST = getRequest() """Fake DX content class, so we can re-use the DX field deserializers""" @@ -49,7 +46,9 @@ def __call__(self, mask_validation_errors=True): errors = [] # Make a fake context - fake_context = FakeDXContext(self.request) + fake_context = FakeDXContext() + wrapped_context = ImplicitAcquisitionWrapper(fake_context, self.context) + for name, field in getFields(self.schema).items(): field_data = schema_data.setdefault(self.schema, {}) @@ -59,7 +58,7 @@ def __call__(self, mask_validation_errors=True): if name in data: deserializer = queryMultiAdapter( - (field, fake_context, self.request), IFieldDeserializer + (field, wrapped_context, self.request), IFieldDeserializer ) try: From 0253fdd3352aa8620be42232e9500257b8305412 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Fri, 31 Jan 2025 09:57:28 +0000 Subject: [PATCH 3/5] format --- src/plone/restapi/deserializer/controlpanels/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plone/restapi/deserializer/controlpanels/__init__.py b/src/plone/restapi/deserializer/controlpanels/__init__.py index b5a30c7c2a..6ffb0d879f 100644 --- a/src/plone/restapi/deserializer/controlpanels/__init__.py +++ b/src/plone/restapi/deserializer/controlpanels/__init__.py @@ -49,7 +49,6 @@ def __call__(self, mask_validation_errors=True): fake_context = FakeDXContext() wrapped_context = ImplicitAcquisitionWrapper(fake_context, self.context) - for name, field in getFields(self.schema).items(): field_data = schema_data.setdefault(self.schema, {}) From df793b806b8a13ffcb395bf880445160ca0f3d3c Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Fri, 31 Jan 2025 11:25:44 +0000 Subject: [PATCH 4/5] changelog --- news/1875.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/1875.bugfix diff --git a/news/1875.bugfix b/news/1875.bugfix new file mode 100644 index 0000000000..5ab79e6e40 --- /dev/null +++ b/news/1875.bugfix @@ -0,0 +1 @@ +Fixed block deserializers not running for control panel fields @JeffersonBledsoe \ No newline at end of file From 23f5a8143e4355f711bf9fba244e0ad9a39b1072 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Thu, 12 Feb 2026 20:30:32 +0000 Subject: [PATCH 5/5] Run DXContent and Block serializers in control panel serialization --- .../serializer/controlpanels/__init__.py | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/plone/restapi/serializer/controlpanels/__init__.py b/src/plone/restapi/serializer/controlpanels/__init__.py index 86d377380b..12fd0c468f 100644 --- a/src/plone/restapi/serializer/controlpanels/__init__.py +++ b/src/plone/restapi/serializer/controlpanels/__init__.py @@ -1,5 +1,7 @@ +from Acquisition import ImplicitAcquisitionWrapper from plone.dexterity.interfaces import IDexterityContent from plone.registry.interfaces import IRegistry +from plone.restapi.behaviors import IBlocks from plone.restapi.controlpanels import IControlpanel from plone.restapi.interfaces import IFieldSerializer from plone.restapi.interfaces import ISerializeToJson @@ -18,6 +20,11 @@ SERVICE_ID = "@controlpanels" +# Same class is in deserializer. Should centralise it. +@implementer(IDexterityContent, IBlocks) +class FakeDXContext: + """Fake DX content class, so we can re-use the DX field deserializers""" + @implementer(ISerializeToJsonSummary) @adapter(IControlpanel) @@ -96,14 +103,22 @@ def __call__(self): proxy = self.registry.forInterface(self.schema, prefix=self.schema_prefix) - # Temporarily provide IDexterityContent, so we can use DX field - # serializers - alsoProvides(proxy, IDexterityContent) + # Make a fake context and copy registry values onto it + fake_context = FakeDXContext() + + + # Copy all field values from the proxy to the fake context + # so that field serializers can properly adapt the context + for name in zope.schema.getFields(self.schema).keys(): + setattr(fake_context, name, getattr(proxy, name, None)) + alsoProvides(fake_context, self.schema) + + wrapped_context = ImplicitAcquisitionWrapper(fake_context, self.controlpanel.context) json_data = {} for name, field in zope.schema.getFields(self.schema).items(): serializer = queryMultiAdapter( - (field, proxy, self.controlpanel.request), IFieldSerializer + (field, wrapped_context, self.controlpanel.request), IFieldSerializer ) if serializer: value = serializer()