From f1ee39214d538ce0f16efc0f4bbed8247f7fd807 Mon Sep 17 00:00:00 2001 From: Petri Savolainen Date: Sat, 6 Jun 2020 00:55:04 +0300 Subject: [PATCH 1/7] rewrite based on asking what do we need override for & let Zope do the rest --- src/plone/rest/traverse.py | 68 +++++++------------------------------- 1 file changed, 12 insertions(+), 56 deletions(-) diff --git a/src/plone/rest/traverse.py b/src/plone/rest/traverse.py index 1cacba9..61a0250 100644 --- a/src/plone/rest/traverse.py +++ b/src/plone/rest/traverse.py @@ -13,36 +13,23 @@ @adapter(ISiteRoot, IAPIRequest) class RESTTraverse(DefaultPublishTraverse): + def publishTraverse(self, request, name): - try: - obj = super(RESTTraverse, self).publishTraverse(request, name) - if not IContentish.providedBy(obj) and not IService.providedBy(obj): - if isinstance(obj, VirtualHostMonster): - return obj - else: - raise KeyError - except KeyError: - # No object, maybe a named rest service - service = queryMultiAdapter( - (self.context, request), name=request._rest_service_id + name - ) - if service is None: - # No service, fallback to regular view - view = queryMultiAdapter((self.context, request), name=name) - if view is not None: - return view - raise + + service = queryMultiAdapter( + (self.context, request), name=request._rest_service_id + name + ) + if service: return service - if name.startswith(request._rest_service_id): - return obj + obj = super(RESTTraverse, self).publishTraverse(request, name) - # Do not handle view namespace - if "@@" in request["PATH_INFO"] or "++view++" in request["PATH_INFO"]: + # Wrap object to ensure we handle further traversal + if IContentish.providedBy(obj) and not ("@@" in request["PATH_INFO"] or "++view++" in request["PATH_INFO"]): + return RESTWrapper(obj, request) + else: return obj - # Wrap object to ensure we handle further traversal - return RESTWrapper(obj, request) def browserDefault(self, request): # Called when we have reached the end of the path @@ -74,35 +61,4 @@ def __before_publishing_traverse__(self, arg1, arg2=None): if bpth: if not self._bpth_called: self._bpth_called = True - bpth(arg1, arg2) - - def publishTraverse(self, request, name): - # Try to get an object using default traversal - adapter = DefaultPublishTraverse(self.context, request) - try: - obj = adapter.publishTraverse(request, name) - if not IContentish.providedBy(obj) and not IService.providedBy(obj): - raise KeyError - - # If there's no object with the given name, we get a KeyError. - # In a non-folderish context a key lookup results in an AttributeError. - except (KeyError, AttributeError): - # No object, maybe a named rest service - service = queryMultiAdapter( - (self.context, request), name=request._rest_service_id + name - ) - if service is None: - # No service, fallback to regular view - view = queryMultiAdapter((self.context, request), name=name) - if view is not None: - return view - raise - return service - else: - # Wrap object to ensure we handle further traversal - return RESTWrapper(obj, request) - - def browserDefault(self, request): - # Called when we have reached the end of the path - # In our case this means an unamed service - return self.context, (request._rest_service_id,) + bpth(arg1, arg2) \ No newline at end of file From 599cfbc73608ab0e6023afa4e4310206d0aa2d85 Mon Sep 17 00:00:00 2001 From: Petri Savolainen Date: Sat, 6 Jun 2020 11:47:01 +0300 Subject: [PATCH 2/7] some blacked polish ... flake8 don't worry be happy now --- src/plone/rest/traverse.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/plone/rest/traverse.py b/src/plone/rest/traverse.py index 61a0250..024c8ee 100644 --- a/src/plone/rest/traverse.py +++ b/src/plone/rest/traverse.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- from Products.CMFCore.interfaces import ISiteRoot -from Products.SiteAccess.VirtualHostMonster import VirtualHostMonster from ZPublisher.BaseRequest import DefaultPublishTraverse from plone.rest.interfaces import IAPIRequest -from plone.rest.interfaces import IService from zope.component import adapter from zope.component import queryMultiAdapter from zope.interface import implementer @@ -13,7 +11,6 @@ @adapter(ISiteRoot, IAPIRequest) class RESTTraverse(DefaultPublishTraverse): - def publishTraverse(self, request, name): service = queryMultiAdapter( @@ -25,12 +22,13 @@ def publishTraverse(self, request, name): obj = super(RESTTraverse, self).publishTraverse(request, name) # Wrap object to ensure we handle further traversal - if IContentish.providedBy(obj) and not ("@@" in request["PATH_INFO"] or "++view++" in request["PATH_INFO"]): + if IContentish.providedBy(obj) and not ( + "@@" in request["PATH_INFO"] or "++view++" in request["PATH_INFO"] + ): return RESTWrapper(obj, request) else: return obj - def browserDefault(self, request): # Called when we have reached the end of the path # In our case this means an unamed service @@ -61,4 +59,4 @@ def __before_publishing_traverse__(self, arg1, arg2=None): if bpth: if not self._bpth_called: self._bpth_called = True - bpth(arg1, arg2) \ No newline at end of file + bpth(arg1, arg2) From 0c51b838d77f9d44f36d849dc426be77e07abd20 Mon Sep 17 00:00:00 2001 From: Petri Savolainen Date: Mon, 2 Nov 2020 19:58:27 +0200 Subject: [PATCH 3/7] add towncrier entry for #105 --- news/105.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/105.bugfix diff --git a/news/105.bugfix b/news/105.bugfix new file mode 100644 index 0000000..326c332 --- /dev/null +++ b/news/105.bugfix @@ -0,0 +1 @@ +Simplify traversal, fixing e.g. plone.app.theming #165 [petri] \ No newline at end of file From 477521c13bd1f2535e8875d81f4c4ac85d408663 Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Mon, 6 Sep 2021 17:38:25 +0200 Subject: [PATCH 4/7] refactor traversal adapter and wrapper to work with lineage marked virtual hosted objects --- news/105.bugfix | 2 +- src/plone/rest/tests/test_traversal.py | 38 ++++++++++++++++++-------- src/plone/rest/traverse.py | 31 ++++++++++++--------- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/news/105.bugfix b/news/105.bugfix index 326c332..7447cd0 100644 --- a/news/105.bugfix +++ b/news/105.bugfix @@ -1 +1 @@ -Simplify traversal, fixing e.g. plone.app.theming #165 [petri] \ No newline at end of file +Simplify traversal, fixing e.g. plone.app.theming #165 [petri, petschki] diff --git a/src/plone/rest/tests/test_traversal.py b/src/plone/rest/tests/test_traversal.py index dc67591..8701c2c 100644 --- a/src/plone/rest/tests/test_traversal.py +++ b/src/plone/rest/tests/test_traversal.py @@ -106,17 +106,6 @@ def test_html_request_on_existing_view_returns_view(self): obj = self.traverse(path="/plone/folder1/search", accept="text/html") self.assertFalse(isinstance(obj, Service), "Got a service") - def test_virtual_hosting(self): - app = self.layer["app"] - vhm = VirtualHostMonster() - vhm.id = "virtual_hosting" - vhm.addToContainer(app) - obj = self.traverse( - path="/VirtualHostBase/http/localhost:8080/plone/VirtualHostRoot/" - ) # noqa - self.assertTrue(isinstance(obj, Service), "Not a service") - del app["virtual_hosting"] - def test_json_request_to_regular_view_returns_view(self): obj = self.traverse("/plone/folder_contents") self.assertTrue(IBrowserView.providedBy(obj), "IBrowserView expected") @@ -132,3 +121,30 @@ def test_json_request_to_view_namespace_returns_view(self): self.portal[self.portal.invokeFactory("Folder", id="folder1")] obj = self.traverse("/plone/folder1/@@folder_contents") self.assertTrue(IBrowserView.providedBy(obj), "IBrowserView expected") + + def setup_vhm(self): + app = self.layer["app"] + vhm = VirtualHostMonster() + vhm.id = "virtual_hosting" + vhm.addToContainer(app) + + def teardown_vhm(self): + del self.layer["app"]["virtual_hosting"] + + def test_virtual_hosting(self): + self.setup_vhm() + obj = self.traverse( + path="/VirtualHostBase/http/localhost:8080/plone/VirtualHostRoot/" + ) # noqa + self.assertTrue(isinstance(obj, Service), "Not a service") + self.teardown_vhm() + + def test_virtual_hosting_on_navroot_folder(self): + self.setup_vhm() + folder = self.portal[self.portal.invokeFactory("Folder", id="folder1")] + alsoProvides(folder, INavigationRoot) + obj = self.traverse( + path="/VirtualHostBase/http/localhost:8080/plone/folder1/VirtualHostRoot/" + ) # noqa + self.assertTrue(isinstance(obj, Service), "Not a service") + self.teardown_vhm() diff --git a/src/plone/rest/traverse.py b/src/plone/rest/traverse.py index 024c8ee..0170b89 100644 --- a/src/plone/rest/traverse.py +++ b/src/plone/rest/traverse.py @@ -1,33 +1,33 @@ # -*- coding: utf-8 -*- -from Products.CMFCore.interfaces import ISiteRoot -from ZPublisher.BaseRequest import DefaultPublishTraverse from plone.rest.interfaces import IAPIRequest +from Products.CMFCore.interfaces import IContentish +from Products.CMFCore.interfaces import ISiteRoot from zope.component import adapter from zope.component import queryMultiAdapter from zope.interface import implementer from zope.publisher.interfaces.browser import IBrowserPublisher -from Products.CMFCore.interfaces import IContentish +from ZPublisher.BaseRequest import DefaultPublishTraverse -@adapter(ISiteRoot, IAPIRequest) -class RESTTraverse(DefaultPublishTraverse): - def publishTraverse(self, request, name): +class RESTPublishTraverse(object): + def publishTraverse(self, request, name): service = queryMultiAdapter( (self.context, request), name=request._rest_service_id + name ) - if service: + if service is not None: return service - obj = super(RESTTraverse, self).publishTraverse(request, name) + adapter = DefaultPublishTraverse(self.context, request) + obj = adapter.publishTraverse(request, name) - # Wrap object to ensure we handle further traversal if IContentish.providedBy(obj) and not ( - "@@" in request["PATH_INFO"] or "++view++" in request["PATH_INFO"] + "@@" in request["PATH_INFO"] + or "++view++" in request["PATH_INFO"] ): return RESTWrapper(obj, request) - else: - return obj + + return obj def browserDefault(self, request): # Called when we have reached the end of the path @@ -35,8 +35,13 @@ def browserDefault(self, request): return self.context, (request._rest_service_id,) +@adapter(ISiteRoot, IAPIRequest) +class RESTTraverse(RESTPublishTraverse, DefaultPublishTraverse): + """ traversal object during REST requests. """ + + @implementer(IBrowserPublisher) -class RESTWrapper(object): +class RESTWrapper(RESTPublishTraverse): """A wrapper for objects traversed during a REST request.""" def __init__(self, context, request): From afba67c8dd9170a8269bcd09000363cf44b0fe87 Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Mon, 6 Sep 2021 17:51:30 +0200 Subject: [PATCH 5/7] version pins --- plone-5.2.x.cfg | 2 -- 1 file changed, 2 deletions(-) diff --git a/plone-5.2.x.cfg b/plone-5.2.x.cfg index b810a87..f4d814f 100644 --- a/plone-5.2.x.cfg +++ b/plone-5.2.x.cfg @@ -14,8 +14,6 @@ virtualenv = 20.0.35 # Error: The requirement ('pep517>=0.9') is not allowed by your [versions] constraint (0.8.2) pep517 = 0.9.1 -# Error: The requirement ('importlib-metadata>=1') is not allowed by your [versions] constraint (0.23) -importlib-metadata = 2.0.0 # cryptography 3.4 requires a rust compiler installed on the system: # https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst#34---2021-02-07 From 467c0fc13133ae0435ba1f93684b814a983e7f68 Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Mon, 6 Sep 2021 17:57:45 +0200 Subject: [PATCH 6/7] black --- src/plone/rest/traverse.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/plone/rest/traverse.py b/src/plone/rest/traverse.py index 0170b89..1d167de 100644 --- a/src/plone/rest/traverse.py +++ b/src/plone/rest/traverse.py @@ -10,7 +10,6 @@ class RESTPublishTraverse(object): - def publishTraverse(self, request, name): service = queryMultiAdapter( (self.context, request), name=request._rest_service_id + name @@ -22,8 +21,7 @@ def publishTraverse(self, request, name): obj = adapter.publishTraverse(request, name) if IContentish.providedBy(obj) and not ( - "@@" in request["PATH_INFO"] - or "++view++" in request["PATH_INFO"] + "@@" in request["PATH_INFO"] or "++view++" in request["PATH_INFO"] ): return RESTWrapper(obj, request) @@ -37,7 +35,7 @@ def browserDefault(self, request): @adapter(ISiteRoot, IAPIRequest) class RESTTraverse(RESTPublishTraverse, DefaultPublishTraverse): - """ traversal object during REST requests. """ + """traversal object during REST requests.""" @implementer(IBrowserPublisher) From 0282fa5b84bb1f99b2610ac03a3b2eb623245ff0 Mon Sep 17 00:00:00 2001 From: Petri Savolainen Date: Wed, 6 Oct 2021 20:55:26 +0300 Subject: [PATCH 7/7] remove unused import --- src/plone/rest/traverse.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plone/rest/traverse.py b/src/plone/rest/traverse.py index 7304e31..9957389 100644 --- a/src/plone/rest/traverse.py +++ b/src/plone/rest/traverse.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- from plone.rest.interfaces import IAPIRequest -from plone.rest.interfaces import IService from plone.rest.events import mark_as_api_request from zope.component import adapter from zope.component import queryMultiAdapter