From d67535e8279adba3a0db87db832b365d40e1961a Mon Sep 17 00:00:00 2001 From: Timo Stollenwerk Date: Mon, 25 Aug 2014 10:42:53 +0200 Subject: [PATCH 1/3] Add angular-bootstrap-nav-tree as a dependency and replace the navigation portlet with a dummy navtree. --- bower.json | 3 +- src/plone/app/angularjs/app/index.html | 4 ++ .../angularjs/app/navigation-portlet.tpl.html | 10 ++- src/plone/app/angularjs/app/scripts/app.js | 1 + .../app/angularjs/app/scripts/directives.js | 20 ------ .../app/scripts/navigation-portlet.js | 72 +++++++++++++++++++ 6 files changed, 87 insertions(+), 23 deletions(-) create mode 100644 src/plone/app/angularjs/app/scripts/navigation-portlet.js diff --git a/bower.json b/bower.json index 8c3f434..ee11c2e 100644 --- a/bower.json +++ b/bower.json @@ -23,6 +23,7 @@ "angular-animate": "1.2.14", "angular-loading-bar": "0.4.2", "angular-sanitize": "1.2.14", - "angular-bootstrap": "0.10.0" + "angular-bootstrap": "0.10.0", + "angular-bootstrap-nav-tree": "*" } } diff --git a/src/plone/app/angularjs/app/index.html b/src/plone/app/angularjs/app/index.html index 07020c8..37d63f1 100644 --- a/src/plone/app/angularjs/app/index.html +++ b/src/plone/app/angularjs/app/index.html @@ -6,7 +6,9 @@ + + @@ -16,9 +18,11 @@ + +
diff --git a/src/plone/app/angularjs/app/navigation-portlet.tpl.html b/src/plone/app/angularjs/app/navigation-portlet.tpl.html index 2c78c06..8a38602 100644 --- a/src/plone/app/angularjs/app/navigation-portlet.tpl.html +++ b/src/plone/app/angularjs/app/navigation-portlet.tpl.html @@ -1,4 +1,4 @@ ---> diff --git a/src/plone/app/angularjs/app/scripts/navigation-portlet.js b/src/plone/app/angularjs/app/scripts/navigation-portlet.js index 7eaf586..9f2faca 100644 --- a/src/plone/app/angularjs/app/scripts/navigation-portlet.js +++ b/src/plone/app/angularjs/app/scripts/navigation-portlet.js @@ -1,15 +1,66 @@ var ploneModule; -/* +ploneModule.factory('reportTreeService', function($q, $http) { + + var getTreeData = function() { + var deferred = $q.defer(); + $http.get("++api++v1/navigation_tree").then(function (response) { + deferred.resolve(response.data); + }); + return deferred.promise; + }; + + return { + getTreeData: getTreeData + }; + +}); + + ploneModule.controller('NavigationPortletController', - function($scope, $http) { + function($scope, reportTreeService) { 'use strict'; - var url = '++api++v1/navigation_tree'; - $http.get(url).success(function(data) { - $scope.items = data; + $scope.folders = []; + + reportTreeService.getTreeData().then(function(data) { + $scope.folders = data; + + $scope.my_tree_handler = function(branch) { + $scope.output = "You selected: " + branch.label; + if (branch.label == 'Nachrichten') { + branch.children = [ + { + label: 'Canada', + children: ['Toronto', 'Vancouver'] + }, { + label: 'USA', + children: ['New York', 'Los Angeles'] + }, { + label: 'Mexico', + children: ['Mexico City', 'Guadalajara'] + } + ]; + } + if (branch.label == 'South America') { + branch.children = [ + { + label: 'Venezuela', + children: ['Caracas', 'Maracaibo'] + }, { + label: 'Brazil', + children: ['Sao Paulo', 'Rio de Janeiro'] + }, { + label: 'Argentina', + children: ['Buenos Aires', 'Cordoba'] + } + ]; + } + }; }); + } -);*/ +); + ploneModule.directive('navigationPortletDirective', function() { @@ -20,53 +71,3 @@ ploneModule.directive('navigationPortletDirective', }; } ); - -ploneModule.controller('NavigationPortletController', function($scope, $timeout) { - - $scope.my_data = [ - { - label: 'North America', - children: [] - }, { - label: 'South America', - children: [] - } - ]; - - $scope.my_tree_handler = function(branch) { - var _ref; - $scope.output = "You selected: " + branch.label; - if (branch.label == 'North America') { - branch.children = [ - { - label: 'Canada', - children: ['Toronto', 'Vancouver'] - }, { - label: 'USA', - children: ['New York', 'Los Angeles'] - }, { - label: 'Mexico', - children: ['Mexico City', 'Guadalajara'] - } - ]; - } - if (branch.label == 'South America') { - branch.children = [ - { - label: 'Venezuela', - children: ['Caracas', 'Maracaibo'] - }, { - label: 'Brazil', - children: ['Sao Paulo', 'Rio de Janeiro'] - }, { - label: 'Argentina', - children: ['Buenos Aires', 'Cordoba'] - } - ]; - } - if ((_ref = branch.data) !== null ? _ref.description : void 0) { - return $scope.output += '(' + branch.data.description + ')'; - } - }; -}); - From f56c5fb264bfdd082791050aea82c28bd085c7bd Mon Sep 17 00:00:00 2001 From: Timo Stollenwerk Date: Mon, 25 Aug 2014 14:28:08 +0200 Subject: [PATCH 3/3] Implement TreeService that returns news/events (static). --- src/plone/app/angularjs/api/api.py | 65 +++++++++++--- .../app/scripts/navigation-portlet.js | 52 +++++------ .../tests/test_api_folder_children.py | 89 +++++++++++++++++++ 3 files changed, 164 insertions(+), 42 deletions(-) create mode 100644 src/plone/app/angularjs/tests/test_api_folder_children.py diff --git a/src/plone/app/angularjs/api/api.py b/src/plone/app/angularjs/api/api.py index 08231e8..edb9896 100644 --- a/src/plone/app/angularjs/api/api.py +++ b/src/plone/app/angularjs/api/api.py @@ -1,4 +1,7 @@ # -*- coding: utf-8 -*- +from Acquisition import aq_chain +from Products.CMFCore.interfaces import IContentish +from Products.CMFCore.interfaces import IFolderish from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile from Products.Five.browser import BrowserView from zope.component.hooks import getSite @@ -83,27 +86,32 @@ def navigation_tree(self, request): catalog = getToolByName(portal, 'portal_catalog') portal_path = '/'.join(portal.getPhysicalPath()) -# return json.dumps([ -# { -# 'label': 'North America', -# 'children': [] -# }, { -# 'label': 'South America', -# 'children': [] -# } -# ]) - + 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, - 'label': brain.Title, 'description': brain.description, 'url': brain.getPath().replace( portal_path, '' ).lstrip('/'), - 'children': [] + 'children': _get_children(brain) } for brain in catalog( { @@ -113,3 +121,36 @@ def navigation_tree(self, request): ) if brain.exclude_from_nav is not True ] ) + + def folder_children(self, request): + portal = getSite() + portal_path = '/'.join(portal.getPhysicalPath()) + path = request.get('path') + if not path: + path = portal_path + else: + path = portal_path + path + try: + folder = portal.restrictedTraverse(path) + except KeyError: + return json.dumps([]) + if not IFolderish.providedBy(folder): + for item in aq_chain(folder): + if IFolderish.providedBy(item): + folder = item + break + return json.dumps( + [ + { + 'id': obj[1].id, + 'title': obj[1].title, + 'label': obj[1].title, + 'description': obj[1].description, + 'url': '/'.join(obj[1].getPhysicalPath()), + 'children': [] + } + for obj in folder.objectItems() + if IContentish.providedBy(obj[1]) + and obj[1].exclude_from_nav is not True + ] + ) diff --git a/src/plone/app/angularjs/app/scripts/navigation-portlet.js b/src/plone/app/angularjs/app/scripts/navigation-portlet.js index 9f2faca..1d0bbee 100644 --- a/src/plone/app/angularjs/app/scripts/navigation-portlet.js +++ b/src/plone/app/angularjs/app/scripts/navigation-portlet.js @@ -2,9 +2,16 @@ var ploneModule; ploneModule.factory('reportTreeService', function($q, $http) { - var getTreeData = function() { + var getTreeData = function(path) { + console.log('getTreeData(' + path + ')'); var deferred = $q.defer(); - $http.get("++api++v1/navigation_tree").then(function (response) { + $http({ + method: 'GET', + url: '++api++v1/folder_children', + params: { + path: path + } + }).then(function (response) { deferred.resolve(response.data); }); return deferred.promise; @@ -18,42 +25,27 @@ ploneModule.factory('reportTreeService', function($q, $http) { ploneModule.controller('NavigationPortletController', - function($scope, reportTreeService) { + function($scope, $location, reportTreeService) { 'use strict'; + $scope.location = $location; $scope.folders = []; - - reportTreeService.getTreeData().then(function(data) { + var path = $scope.location.path(); + console.log("PATH: " + path); + reportTreeService.getTreeData(path).then(function(data) { $scope.folders = data; $scope.my_tree_handler = function(branch) { + console.log('my_tree_handler(' + branch + ')'); $scope.output = "You selected: " + branch.label; if (branch.label == 'Nachrichten') { - branch.children = [ - { - label: 'Canada', - children: ['Toronto', 'Vancouver'] - }, { - label: 'USA', - children: ['New York', 'Los Angeles'] - }, { - label: 'Mexico', - children: ['Mexico City', 'Guadalajara'] - } - ]; + reportTreeService.getTreeData('/news').then(function(data) { + branch.children = data; + }); } - if (branch.label == 'South America') { - branch.children = [ - { - label: 'Venezuela', - children: ['Caracas', 'Maracaibo'] - }, { - label: 'Brazil', - children: ['Sao Paulo', 'Rio de Janeiro'] - }, { - label: 'Argentina', - children: ['Buenos Aires', 'Cordoba'] - } - ]; + if (branch.label == 'Termine') { + reportTreeService.getTreeData('/events').then(function(data) { + branch.children = data; + }); } }; }); diff --git a/src/plone/app/angularjs/tests/test_api_folder_children.py b/src/plone/app/angularjs/tests/test_api_folder_children.py new file mode 100644 index 0000000..921fa73 --- /dev/null +++ b/src/plone/app/angularjs/tests/test_api_folder_children.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +from plone.app.angularjs.interfaces import IRestApi +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID +from plone.app.angularjs.testing import\ + PLONE_APP_ANGULARJS_INTEGRATION_TESTING +from zope.component import getUtility + +import json +import unittest2 as unittest + + +class TestAngularJsFolderChildren(unittest.TestCase): + + layer = PLONE_APP_ANGULARJS_INTEGRATION_TESTING + + def setUp(self): + self.app = self.layer['app'] + self.portal = self.layer['portal'] + self.request = self.layer['request'] + setRoles(self.portal, TEST_USER_ID, ['Manager']) + self.api = getUtility(IRestApi) + + def test_empty_navigation(self): + self.assertEqual( + json.loads(self.api.folder_children(self.request)), + [] + ) + + def test_folder_in_navigation(self): + self.portal.invokeFactory('Folder', 'folder1', title='Folder 1') + + self.assertTrue(self.api.folder_children(self.request)) + self.assertEqual( + json.loads(self.api.folder_children(self.request)), + [{ + u'id': u'folder1', + u'title': u'Folder 1', + u'label': u'Folder 1', + u'description': u'', + u'url': '/plone/folder1', + u'children': [] + }] + ) + + def test_do_not_show_excluded_from_nav_documents(self): + self.portal.invokeFactory('Folder', 'folder1', title='Folder 1') + self.portal.folder1.exclude_from_nav = True + self.portal.folder1.reindexObject(idxs=['exclude_from_nav']) + + self.assertEqual( + len(json.loads(self.api.folder_children(self.request))), + 0 + ) + + def test_folder_in_navigation_with_path(self): + self.portal.invokeFactory('Folder', 'folder1', title='Folder 1') + self.portal.folder1.invokeFactory( + 'Folder', 'folder2', title='Folder 2') + self.request.set('path', '/folder1') + self.assertTrue( + self.api.folder_children(self.request)) + self.assertEqual( + json.loads( + self.api.folder_children(self.request) + ), + [{ + u'id': u'folder2', + u'title': u'Folder 2', + u'label': u'Folder 2', + u'description': u'', + u'url': '/plone/folder1/folder2', + u'children': [] + }] + ) + + def test_document_returns_parent_folder(self): + self.portal.invokeFactory('Folder', 'folder1', title='Folder 1') + self.portal.folder1.invokeFactory( + 'Document', 'doc1', title='Document 1') + self.request.set('path', '/folder1/doc1') + self.assertTrue( + self.api.folder_children(self.request)) + self.assertEqual( + json.loads( + self.api.folder_children(self.request) + )[0]['id'], + u'doc1' + )