diff --git a/news/1639.bugfix b/news/1639.bugfix new file mode 100644 index 000000000..4923c63d3 --- /dev/null +++ b/news/1639.bugfix @@ -0,0 +1,2 @@ +Remove the hard code dependency by plone.app.multilingual, use it conditionaly instead +[@folix-01] diff --git a/news/1642.feature b/news/1642.feature new file mode 100644 index 000000000..bf5f1a7dc --- /dev/null +++ b/news/1642.feature @@ -0,0 +1,2 @@ +When serializing blocks, `image_scales` is now added to blocks that contain a resolveuid-based `url`. +When deserializing blocks, `image_scales` is removed. @davisagli diff --git a/src/plone/restapi/__init__.py b/src/plone/restapi/__init__.py index 303836933..ba8cb1604 100644 --- a/src/plone/restapi/__init__.py +++ b/src/plone/restapi/__init__.py @@ -4,6 +4,13 @@ from Products.PluggableAuthService.PluggableAuthService import registerMultiPlugin from zope.i18nmessageid import MessageFactory +import pkg_resources + +try: + pkg_resources.get_distribution("plone.app.multilingual") + HAS_MULTILINGUAL = True +except pkg_resources.DistributionNotFound: + HAS_MULTILINGUAL = False _ = MessageFactory("plone.restapi") PROJECT_NAME = "plone.restapi" diff --git a/src/plone/restapi/deserializer/blocks.py b/src/plone/restapi/deserializer/blocks.py index 99199f78a..a4746eb4b 100644 --- a/src/plone/restapi/deserializer/blocks.py +++ b/src/plone/restapi/deserializer/blocks.py @@ -75,14 +75,9 @@ def _process_data(self, data, field=None): if data.get("@type", None) == "URL" and data.get("value", None): data["value"] = path2uid(context=self.context, link=data["value"]) elif data.get("@id", None): - item_clone = deepcopy(data) - item_clone["@id"] = path2uid( - context=self.context, link=item_clone["@id"] - ) - return { - field: self._process_data(data=value, field=field) - for field, value in item_clone.items() - } + data = deepcopy(data) + data["@id"] = path2uid(context=self.context, link=data["@id"]) + data.pop("image_scales", None) return { field: self._process_data(data=value, field=field) for field, value in data.items() diff --git a/src/plone/restapi/serializer/blocks.py b/src/plone/restapi/serializer/blocks.py index e42e6baf3..5a8733580 100644 --- a/src/plone/restapi/serializer/blocks.py +++ b/src/plone/restapi/serializer/blocks.py @@ -1,4 +1,3 @@ -from copy import deepcopy from plone.restapi.bbb import IPloneSiteRoot from plone.restapi.behaviors import IBlocks from plone.restapi.blocks import visit_blocks, iter_block_transform_handlers @@ -9,7 +8,7 @@ from plone.restapi.interfaces import IFieldSerializer from plone.restapi.serializer.converters import json_compatible from plone.restapi.serializer.dxfields import DefaultFieldSerializer -from plone.restapi.serializer.utils import uid_to_url +from plone.restapi.serializer.utils import resolve_uid, uid_to_url from plone.schema import IJSONField from zope.component import adapter from zope.interface import implementer @@ -57,19 +56,27 @@ def _process_data(self, data, field=None): if isinstance(data, list): return [self._process_data(data=value, field=field) for value in data] if isinstance(data, dict): - if data.get("@type", None) == "URL" and data.get("value", None): - data["value"] = uid_to_url(data["value"]) - elif data.get("@id", None): - item_clone = deepcopy(data) - item_clone["@id"] = uid_to_url(item_clone["@id"]) - return { - field: self._process_data(data=value, field=field) - for field, value in item_clone.items() - } - return { - field: self._process_data(data=value, field=field) + fields = ["value"] if data.get("@type") == "URL" else [] + fields.append("@id") + fields.extend(self.fields) + newdata = {} + for field in fields: + if field not in data or not isinstance(data[field], str): + continue + newdata[field], brain = resolve_uid(data[field]) + if brain is not None and "image_scales" not in newdata: + newdata["image_scales"] = getattr(brain, "image_scales", None) + result = { + field: ( + newdata[field] + if field in newdata + else self._process_data(data=newdata.get(field, value), field=field) + ) for field, value in data.items() } + if newdata.get("image_scales"): + result["image_scales"] = newdata["image_scales"] + return result return data diff --git a/src/plone/restapi/serializer/utils.py b/src/plone/restapi/serializer/utils.py index f47541663..c2f89e4e2 100644 --- a/src/plone/restapi/serializer/utils.py +++ b/src/plone/restapi/serializer/utils.py @@ -11,26 +11,25 @@ RESOLVEUID_RE = re.compile("^[./]*resolve[Uu]id/([^/]*)/?(.*)$") -def uid_to_url(path): - """turns a resolveuid url into a real url. +def resolve_uid(path): + """Resolves a resolveuid URL into a tuple of absolute URL and catalog brain. - This uses the catalog first, but wake up the object to check if there is - an IObjectPrimaryFieldTarget on this object. If so, it will return the - target url instead of the object url. + If the original path is not found (including external URLs), + it will be returned unchanged and the brain will be None. """ if not path: - return "" + return "", None match = RESOLVEUID_RE.match(path) if match is None: - return path + return path, None uid, suffix = match.groups() brain = uuidToCatalogBrain(uid) if brain is None: - return path + return path, None href = brain.getURL() if suffix: - return href + "/" + suffix + return href + "/" + suffix, brain target_object = brain._unrestrictedGetObject() adapter = queryMultiAdapter( (target_object, target_object.REQUEST), @@ -39,8 +38,13 @@ def uid_to_url(path): if adapter: a_href = adapter() if a_href: - return a_href - return href + return a_href, None + return href, brain + + +def uid_to_url(path): + path, brain = resolve_uid(path) + return path def get_portal_type_title(portal_type): diff --git a/src/plone/restapi/services/configure.zcml b/src/plone/restapi/services/configure.zcml index f22f4a207..2096fd818 100644 --- a/src/plone/restapi/services/configure.zcml +++ b/src/plone/restapi/services/configure.zcml @@ -48,10 +48,9 @@ package=".workingcopy" zcml:condition="installed plone.app.iterate" /> - + + +