Skip to content
This repository was archived by the owner on Oct 26, 2021. It is now read-only.

add TraversalNames to request and use ApiDispatcherView to call API methods in RestApi utility #2

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 29 additions & 14 deletions src/plone/app/angularjs/api/api.pt
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
<div>
<h1>REST API</h1>
<h2>Root Methods</h2>
<ul tal:repeat="method view/api_methods">
<li>
<a href="" tal:attributes="href method/url">
<span tal:content="method/id" />
</a>:
<span tal:content="method/description" />
</li>
</ul>
<h2>RESTish Methods</h2>
soon...
</div>
<html xm lns="http://www.w3.org/1999/xhtml"
xml:lang="en-US" lang="en-US"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
i18n:domain="plone">
<body>
<h1 class="documentFirstHeading">
REST API
</h1>

<p>
Above you can see all IRestApi providing views.
</p>

<div>
<h2>API Methods</h2>
<ul tal:repeat="method view/api_views">
<li>
<a href="" tal:attributes="href method/url">
<span tal:content="method/id" />
</a>:
<span tal:content="method/description" />
</li>
</ul>
</div>
</body>
</html>

173 changes: 68 additions & 105 deletions src/plone/app/angularjs/api/api.py
Original file line number Diff line number Diff line change
@@ -1,120 +1,83 @@
# -*- coding: utf-8 -*-
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.Five.browser import BrowserView
from zope.component.hooks import getSite
from Products.CMFCore.utils import getToolByName
from zope.interface import implements
from plone.app.angularjs.interfaces import IRestApi

import json

from Products.Five.browser import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from plone.app.angularjs.interfaces import IRestApi
from zope.component import (
getMultiAdapter, getGlobalSiteManager)
from zope.component.interfaces import ComponentLookupError
from zope.component.hooks import getSite

class ApiOverview(BrowserView):
import logging
log = logging.getLogger("plone.app.angularjs.api")

template = ViewPageTemplateFile('api.pt')

def __call__(self):
return self.template()

def api_methods(self):
portal_url = getSite().absolute_url()
return [
{
'id': x,
'description': IRestApi.get(x).getDoc(),
'url': '%s/++api++v1/%s' % (portal_url, x),
} for x in IRestApi.names()
]
def json_api_call(func):
""" decorator to return all values as json
"""
def decorator(*args, **kwargs):
instance = args[0]
request = getattr(instance, 'request', None)
request.response.setHeader(
'Content-Type',
'application/json; charset=utf-8'
)
result = func(*args, **kwargs)
return json.dumps(result, indent=2, sort_keys=True)

return decorator

class RestApi(object):
implements(IRestApi)

def traversal(self, request):
portal = getSite()
path = request.get('path')
if not path:
return
path = '/'.join(portal.getPhysicalPath()) + '/' + path
class ApiDispatcherView(BrowserView):
""" dispatch api calls to api views via lookup on view name and IRestApi
"""
def __call__(self, context=None, request=None):
api_params = self.request.get('api_params')
if api_params:
name = api_params[0]
else:
name = ''
try:
obj = portal.restrictedTraverse(path)
except KeyError:
return json.dumps({'title': 'Object not found.'})
try:
text = obj.getText()
except AttributeError:
text = ''
request.response.setHeader("Content-Type", "application/json")
view = getMultiAdapter(
(self.context, self.request),
IRestApi,
name=name)
return view()
except ComponentLookupError:
log.debug("No API View found with name '%s'" % name)
return json.dumps({
'route': path,
'id': obj.id,
'title': obj.title,
'description': obj.Description(),
'text': text
'code': '404',
'message': "API method '%s' not found." % name,
'data': ''
})

def top_navigation(self, request):
portal = getSite()
catalog = getToolByName(portal, 'portal_catalog')
portal_path = '/'.join(portal.getPhysicalPath())
return json.dumps(
[
{
'id': brain.id,
'title': brain.Title,
'description': brain.description,
'url': brain.getPath().replace(
portal_path, ''
).lstrip('/')
}
for brain in catalog({
'path': {
'query': '/'.join(portal.getPhysicalPath()),
'depth': 1
},
'portal_type': 'Folder',
'sort_on': 'getObjPositionInParent'
}) if brain.exclude_from_nav is not True
]
)

def navigation_tree(self, request):
portal = getSite()
catalog = getToolByName(portal, 'portal_catalog')
portal_path = '/'.join(portal.getPhysicalPath())
class ApiOverview(BrowserView):
""" Api overview, list all api endpoints.
"""
template = ViewPageTemplateFile('api.pt')

def __call__(self):
return self.template()

def _get_children(context):
return [
{
'id': brain.id,
'title': brain.Title,
'description': brain.description,
'url': brain.getPath().replace(
portal_path, ''
).lstrip('/'),
'children': []
} for brain in catalog({
'path': {'query': context.getPath(), 'depth': 1},
'sort_on': 'getObjPositionInParent',
}
) if brain.exclude_from_nav is not True
]
return json.dumps(
[
{
'id': brain.id,
'title': brain.Title,
'description': brain.description,
'url': brain.getPath().replace(
portal_path, ''
).lstrip('/'),
'children': _get_children(brain)
}
for brain in catalog(
{
'path': {'query': portal_path, 'depth': 1},
'sort_on': 'getObjPositionInParent',
}
) if brain.exclude_from_nav is not True
]
)
def api_views(self):
portal_url = getSite().absolute_url()
gsm = getGlobalSiteManager()
api_views = []
for api_adapter in gsm.registeredAdapters():
if not api_adapter.provided == IRestApi:
continue
api_view = {}
api_view['id'] = api_adapter.name
view = getMultiAdapter(
(self.context, self.request),
IRestApi,
name=api_adapter.name)
# XXX this doesn't work because we don't have the original
# class here. How we can get the original class?
api_view['description'] = view.__doc__
api_view['url'] = '%s/++api++v1/%s' % (
portal_url, api_adapter.name)
api_views.append(api_view)
return api_views
29 changes: 25 additions & 4 deletions src/plone/app/angularjs/api/configure.zcml
Original file line number Diff line number Diff line change
@@ -1,17 +1,38 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:i18n="http://namespaces.zope.org/i18n"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="plone.app.angularjs">

<utility
provides="plone.app.angularjs.interfaces.IRestApi"
factory=".api.RestApi" />

<adapter
factory=".traverser.APITraverser"
name="api"
/>

<adapter factory=".traverser.APIInnerTraverser" />

<browser:view
name="traversal"
for="Products.CMFPlone.interfaces.siteroot.IPloneSiteRoot"
class=".traversal.TraversalView"
permission="cmf.SetOwnPassword"
provides="plone.app.angularjs.interfaces.IRestApi"
/>

<browser:view
name="top_navigation"
for="Products.CMFPlone.interfaces.siteroot.IPloneSiteRoot"
class=".navigation.TopNavigation"
permission="cmf.SetOwnPassword"
provides="plone.app.angularjs.interfaces.IRestApi"
/>

<browser:view
name="navigation_tree"
for="Products.CMFPlone.interfaces.siteroot.IPloneSiteRoot"
class=".navigation.NavigationTree"
permission="cmf.SetOwnPassword"
provides="plone.app.angularjs.interfaces.IRestApi"
/>

</configure>
75 changes: 75 additions & 0 deletions src/plone/app/angularjs/api/navigation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from Products.CMFCore.utils import getToolByName
from Products.Five.browser import BrowserView
from zope.component.hooks import getSite

from .api import json_api_call


class TopNavigation(BrowserView):

@json_api_call
def __call__(self):
portal = getSite()
catalog = getToolByName(portal, 'portal_catalog')
portal_path = '/'.join(portal.getPhysicalPath())
return [
{
'id': brain.id,
'title': brain.Title,
'description': brain.description,
'url': brain.getPath().replace(
portal_path, ''
).lstrip('/')
}
for brain in catalog({
'path': {
'query': '/'.join(portal.getPhysicalPath()),
'depth': 1
},
'portal_type': 'Folder',
'sort_on': 'getObjPositionInParent'
}) if brain.exclude_from_nav is not True
]


class NavigationTree(BrowserView):

@json_api_call
def __call__(self):
portal = getSite()
catalog = getToolByName(portal, 'portal_catalog')
portal_path = '/'.join(portal.getPhysicalPath())

def _get_children(context):
return [
{
'id': brain.id,
'title': brain.Title,
'description': brain.description,
'url': brain.getPath().replace(
portal_path, ''
).lstrip('/'),
'children': []
} for brain in catalog({
'path': {'query': context.getPath(), 'depth': 1},
'sort_on': 'getObjPositionInParent',
}
) if brain.exclude_from_nav is not True
]
return [
{
'id': brain.id,
'title': brain.Title,
'description': brain.description,
'url': brain.getPath().replace(
portal_path, ''
).lstrip('/'),
'children': _get_children(brain)
}
for brain in catalog(
{
'path': {'query': portal_path, 'depth': 1},
'sort_on': 'getObjPositionInParent',
}
) if brain.exclude_from_nav is not True
]
31 changes: 31 additions & 0 deletions src/plone/app/angularjs/api/traversal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from Products.Five.browser import BrowserView
from zope.component.hooks import getSite

from .api import json_api_call


class TraversalView(BrowserView):

@json_api_call
def __call__(self):
portal = getSite()
path = self.request.get('path')
if not path:
return
path = '/'.join(portal.getPhysicalPath()) + '/' + path
try:
obj = portal.restrictedTraverse(path)
except KeyError:
return {'title': 'Object not found.'}
try:
text = obj.getText()
except AttributeError:
text = ''
self.request.response.setHeader("Content-Type", "application/json")
return {
'route': path,
'id': obj.id,
'title': obj.title,
'description': obj.Description(),
'text': text
}
Loading