diff --git a/CHANGES.rst b/CHANGES.rst index 5b7abd84e1..43dd2c3041 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,10 @@ Changelog New Features: +- Add navigation and breadcrumbs as top-level services. Deprecate navigation + and breadcrumbs as components. + [timo] + - Add support for setting/modifying 'layout' on DX and AT content endpoints. [jaroel] diff --git a/docs/source/breadcrumbs.rst b/docs/source/breadcrumbs.rst new file mode 100644 index 0000000000..f377ff3087 --- /dev/null +++ b/docs/source/breadcrumbs.rst @@ -0,0 +1,12 @@ +Breadcrumbs +=========== + +Getting the breadcrumbs for the current page: + +.. http:example:: curl httpie python-requests + :request: _json/breadcrumbs.req + +Example response: + +.. literalinclude:: _json/breadcrumbs.resp + :language: http diff --git a/docs/source/components.rst b/docs/source/components.rst index f052d35dff..49db73734e 100644 --- a/docs/source/components.rst +++ b/docs/source/components.rst @@ -1,8 +1,13 @@ Components ========== +.. warning:: + The @components endpoint is deprecated and will be removed in plone.restapi + 1.0b1. :doc:`breadcrumbs` and :doc:`navigation` are now top-level endpoints. + How to get pages components (i.e. everything but the main content), like breadcrumbs, navigations, actions, etc. + Breadcrumbs ----------- diff --git a/docs/source/index.rst b/docs/source/index.rst index 66c5b74261..761a46d3d3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -30,6 +30,8 @@ Contents types users components + breadcrumbs + navigation serialization searching vocabularies diff --git a/docs/source/navigation.rst b/docs/source/navigation.rst new file mode 100644 index 0000000000..8027e2d640 --- /dev/null +++ b/docs/source/navigation.rst @@ -0,0 +1,12 @@ +Navigation +========== + +Getting the top navigation items: + +.. http:example:: curl httpie python-requests + :request: _json/navigation.req + +Example response: + +.. literalinclude:: _json/navigation.resp + :language: http diff --git a/src/plone/restapi/services/breadcrumbs/__init__.py b/src/plone/restapi/services/breadcrumbs/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/plone/restapi/services/breadcrumbs/configure.zcml b/src/plone/restapi/services/breadcrumbs/configure.zcml new file mode 100644 index 0000000000..0c39c889ca --- /dev/null +++ b/src/plone/restapi/services/breadcrumbs/configure.zcml @@ -0,0 +1,13 @@ + + + + + diff --git a/src/plone/restapi/services/breadcrumbs/get.py b/src/plone/restapi/services/breadcrumbs/get.py new file mode 100644 index 0000000000..b8d9b97aac --- /dev/null +++ b/src/plone/restapi/services/breadcrumbs/get.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +from plone.restapi.services import Service +from zope.component import getMultiAdapter + + +class BreadcrumbsGet(Service): + + def reply(self): + breadcrumbs_view = getMultiAdapter((self.context, self.request), + name="breadcrumbs_view") + result = [] + for crumb in breadcrumbs_view.breadcrumbs(): + result.append({ + 'title': crumb['Title'], + 'url': crumb['absolute_url'] + }) + return result diff --git a/src/plone/restapi/services/configure.zcml b/src/plone/restapi/services/configure.zcml index a8c77c5cea..4c495bba27 100644 --- a/src/plone/restapi/services/configure.zcml +++ b/src/plone/restapi/services/configure.zcml @@ -2,10 +2,12 @@ xmlns="http://namespaces.zope.org/zope"> + + diff --git a/src/plone/restapi/services/navigation/__init__.py b/src/plone/restapi/services/navigation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/plone/restapi/services/navigation/configure.zcml b/src/plone/restapi/services/navigation/configure.zcml new file mode 100644 index 0000000000..05e200e434 --- /dev/null +++ b/src/plone/restapi/services/navigation/configure.zcml @@ -0,0 +1,13 @@ + + + + + diff --git a/src/plone/restapi/services/navigation/get.py b/src/plone/restapi/services/navigation/get.py new file mode 100644 index 0000000000..a2bf05047e --- /dev/null +++ b/src/plone/restapi/services/navigation/get.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +from plone.restapi.services import Service +from zope.component import getMultiAdapter + + +class NavigationGet(Service): + + def reply(self): + tabs = getMultiAdapter((self.context, self.request), + name="portal_tabs_view") + result = [] + for tab in tabs.topLevelTabs(): + result.append({ + 'title': tab.get('title', tab.get('name')), + 'url': tab['url'] + '' + }) + return result diff --git a/src/plone/restapi/tests/test_services_breadcrumbs.py b/src/plone/restapi/tests/test_services_breadcrumbs.py new file mode 100644 index 0000000000..59dfc00842 --- /dev/null +++ b/src/plone/restapi/tests/test_services_breadcrumbs.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +from plone.app.testing import setRoles +from plone.app.testing import SITE_OWNER_NAME +from plone.app.testing import SITE_OWNER_PASSWORD +from plone.app.testing import TEST_USER_ID +from plone.dexterity.utils import createContentInContainer +from plone.restapi.testing import PLONE_RESTAPI_DX_FUNCTIONAL_TESTING +from plone.restapi.testing import RelativeSession + +import transaction +import unittest + + +class TestServicesBreadcrumbs(unittest.TestCase): + + layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING + + def setUp(self): + self.app = self.layer['app'] + self.portal = self.layer['portal'] + self.portal_url = self.portal.absolute_url() + setRoles(self.portal, TEST_USER_ID, ['Manager']) + + 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.folder = createContentInContainer( + self.portal, u'Folder', + id=u'folder', + title=u'Some Folder') + createContentInContainer( + self.folder, u'Document', + id=u'doc1', + title=u'A document') + transaction.commit() + + def test_breadcrumbs(self): + response = self.api_session.get('/folder/doc1/@breadcrumbs') + + self.assertEqual(response.status_code, 200) + self.assertEqual( + response.json(), + [{ + u'url': u'http://localhost:55001/plone/folder', + u'title': u'Some Folder' + }, { + u'url': u'http://localhost:55001/plone/folder/doc1', + u'title': u'A document' + }] + ) diff --git a/src/plone/restapi/tests/test_services_navigation.py b/src/plone/restapi/tests/test_services_navigation.py new file mode 100644 index 0000000000..ad55a612ed --- /dev/null +++ b/src/plone/restapi/tests/test_services_navigation.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +from plone.app.testing import setRoles +from plone.app.testing import SITE_OWNER_NAME +from plone.app.testing import SITE_OWNER_PASSWORD +from plone.app.testing import TEST_USER_ID +from plone.dexterity.utils import createContentInContainer +from plone.restapi.testing import PLONE_RESTAPI_DX_FUNCTIONAL_TESTING +from plone.restapi.testing import RelativeSession + +import transaction +import unittest + + +class TestServicesNavigation(unittest.TestCase): + + layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING + + def setUp(self): + self.app = self.layer['app'] + self.portal = self.layer['portal'] + self.portal_url = self.portal.absolute_url() + setRoles(self.portal, TEST_USER_ID, ['Manager']) + + 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.folder = createContentInContainer( + self.portal, u'Folder', + id=u'folder', + title=u'Some Folder') + createContentInContainer( + self.folder, u'Document', + id=u'doc1', + title=u'A document') + transaction.commit() + + def test_navigation(self): + response = self.api_session.get('/folder/@navigation') + + self.assertEqual(response.status_code, 200) + self.assertEqual( + response.json(), + [ + { + u'title': u'Home', + u'url': u'http://localhost:55001/plone' + }, + { + u'title': u'Some Folder', + u'url': u'http://localhost:55001/plone/folder' + } + ] + )