diff --git a/plone-4.3.x.cfg b/plone-4.3.x.cfg index 348b0ceee4..e413c52583 100644 --- a/plone-4.3.x.cfg +++ b/plone-4.3.x.cfg @@ -11,3 +11,7 @@ zope.interface = 4.1.0 # Pillow = 5.4.1 future = 0.17.1 six = 1.11.0 +# fixes: SyntaxError: invalid syntax (more.py, line 340) +zipp = 0.5.2 +# more-itertools >= 6.0.0 dropped python2.7 support +more-itertools = 5.0.0 \ No newline at end of file diff --git a/src/plone/restapi/serializer/blocks.py b/src/plone/restapi/serializer/blocks.py index 7bf39b9659..58c948b8e7 100644 --- a/src/plone/restapi/serializer/blocks.py +++ b/src/plone/restapi/serializer/blocks.py @@ -8,6 +8,8 @@ from zope.component import adapter from zope.interface import implementer from zope.interface import Interface + +import copy import re @@ -18,7 +20,7 @@ @implementer(IFieldSerializer) class BlocksJSONFieldSerializer(DefaultFieldSerializer): def __call__(self): - value = self.get_value() + value = copy.deepcopy(self.get_value()) # Resolve UID links if self.field.getName() == "blocks": @@ -41,4 +43,4 @@ def __call__(self): entity["data"]["href"] = href entity["data"]["url"] = href print("SERIALIZE " + before + " -> " + href) # noqa - return json_compatible(self.get_value()) + return json_compatible(value) diff --git a/src/plone/restapi/tests/test_resolveuid.py b/src/plone/restapi/tests/test_resolveuid.py index 05978a422c..a467bf7528 100644 --- a/src/plone/restapi/tests/test_resolveuid.py +++ b/src/plone/restapi/tests/test_resolveuid.py @@ -160,6 +160,56 @@ def test_keeps_resolveuid_link_if_unknown_uid(self): "../resolveuid/{}".format(uid), ) + def test_blocks_field_serialization_doesnt_update_stored_values(self): + uid = IUUID(self.doc2) + blocks = { + "07c273fc-8bfc-4e7d-a327-d513e5a945bb": {"@type": "title"}, + "effbdcdc-253c-41a7-841e-5edb3b56ce32": { + "@type": "text", + "text": { + "blocks": [ + { + "data": {}, + "depth": 0, + "entityRanges": [{"key": 0, "length": 5, "offset": 0}], + "inlineStyleRanges": [], + "key": "68rve", + "text": "Volto also supports other APIs.", + "type": "unstyled", + } + ], + "entityMap": { + "0": { + "data": { + "href": "../resolveuid/{}".format(uid), + "rel": "nofollow", + "url": "../resolveuid/{}".format(uid), + }, + "mutability": "MUTABLE", + "type": "LINK", + } + }, + }, + }, + } + value = self.serialize("blocks", blocks) + self.assertNotEqual( + value["effbdcdc-253c-41a7-841e-5edb3b56ce32"]["text"]["entityMap"]["0"][ + "data" + ]["href"], + blocks["effbdcdc-253c-41a7-841e-5edb3b56ce32"]["text"]["entityMap"]["0"][ + "data" + ]["href"], + ) + self.assertNotEqual( + value["effbdcdc-253c-41a7-841e-5edb3b56ce32"]["text"]["entityMap"]["0"][ + "data" + ]["url"], + blocks["effbdcdc-253c-41a7-841e-5edb3b56ce32"]["text"]["entityMap"]["0"][ + "data" + ]["url"], + ) + def test_blocks_field_deserialization_resolves_paths_to_uids(self): uid = IUUID(self.doc2) blocks = { diff --git a/src/plone/restapi/tests/test_resolveuid_functional.py b/src/plone/restapi/tests/test_resolveuid_functional.py new file mode 100644 index 0000000000..3d35418112 --- /dev/null +++ b/src/plone/restapi/tests/test_resolveuid_functional.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +from plone.app.testing import login +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID +from plone.app.testing import TEST_USER_NAME +from plone.app.testing import SITE_OWNER_NAME +from plone.app.testing import SITE_OWNER_PASSWORD +from plone.dexterity.fti import DexterityFTI +from plone.restapi.behaviors import IBlocks +from plone.uuid.interfaces import IUUID + +from plone.restapi.testing import PLONE_RESTAPI_DX_FUNCTIONAL_TESTING +from plone.restapi.testing import RelativeSession +from zope.interface import alsoProvides + +import transaction +import unittest + + +class TestResolveUIDFunctional(unittest.TestCase): + + layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING + + def setUp(self): + self.portal = self.layer["portal"] + self.request = self.layer["request"] + login(self.portal, TEST_USER_NAME) + setRoles(self.portal, TEST_USER_ID, ["Manager"]) + fti = DexterityFTI("blockspage") + self.portal.portal_types._setObject("blockspage", fti) + fti.klass = "plone.dexterity.content.Container" + fti.behaviors = ("volto.blocks",) + self.fti = fti + alsoProvides(self.request, IBlocks) + self.portal_url = self.portal.absolute_url() + + self.api_session = RelativeSession(self.portal_url) + self.api_session.headers.update({"Accept": "application/json"}) + self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD) + + self.portal.invokeFactory("Document", id="target", title="Target") + self.target = self.portal.target + self.target_uuid = IUUID(self.target) + + transaction.commit() + + def tearDown(self): + self.api_session.close() + + def test_resolveuid(self): + self.api_session.post( + "/", + json={ + "title": "Document", + "@type": "blockspage", + "blocks": { + "ca5908a5-3f58-4cd5-beb7-9bd1539d6744": {"@type": "title"}, + "791bf004-7c88-4278-8490-13b85c3fa4b4": { + "@type": "text", + "text": { + "blocks": [ + { + "key": "3bnq6", + "text": "Link", + "type": "unstyled", + "depth": 0, + "inlineStyleRanges": [], + "entityRanges": [ + {"offset": 0, "length": 4, "key": 0} + ], + "data": {}, + } + ], + "entityMap": { + "0": { + "type": "LINK", + "mutability": "MUTABLE", + "data": { + "url": "http://localhost:55001/plone/target" + }, + } + }, + }, + }, + }, + "blocks_layout": { + "items": [ + "ca5908a5-3f58-4cd5-beb7-9bd1539d6744", + "791bf004-7c88-4278-8490-13b85c3fa4b4", + ] + }, + }, + ) + transaction.commit() + self.assertEqual( + "../resolveuid/{}".format(self.target_uuid), + self.portal.document.blocks + .get("791bf004-7c88-4278-8490-13b85c3fa4b4") + .get("text") + .get("entityMap") + .get("0") + .get("data") + .get("url"), + )