def get_data(self): """Get json data to render the fancy tree.""" query = self.get_query() strategy = NavtreeStrategyBase() strategy.rootPath = self.root_path folder_tree = buildFolderTree(self.portal, None, query, strategy) return json.dumps(self.folder_tree_to_fancytree(folder_tree))
def testGetFromRootWithSpecifiedRoot(self): rootPath = '/'.join(self.portal.getPhysicalPath()) strategy = NavtreeStrategyBase() strategy.rootPath = rootPath + '/folder1' tree = buildFolderTree(self.portal, strategy=strategy)['children'] self.assertEqual(len(tree), 3) self.assertEqual(tree[0]['item'].getPath(), rootPath + '/folder1/doc11') self.assertEqual(tree[1]['item'].getPath(), rootPath + '/folder1/doc12') self.assertEqual(tree[2]['item'].getPath(), rootPath + '/folder1/doc13')
def testShowAllParentsWithParentNotInCatalog(self): strategy = NavtreeStrategyBase() strategy.showAllParents = True self.portal.folder1.unindexObject() query = {'portal_type': 'Folder'} context = self.portal.folder1.doc11 tree = buildFolderTree(self.portal, query=query, obj=context, strategy=strategy)['children'] rootPath = '/'.join(self.portal.getPhysicalPath()) # XXX: Ideally, this shouldn't happen, we should get a dummy node, but # there's no way to do that with the catalog self.assertEqual(len(tree), 1) self.assertEqual(tree[0]['item'].getPath(), rootPath + '/folder2')
def testDontShowAllParents(self): strategy = NavtreeStrategyBase() strategy.showAllParents = False query = {'portal_type' : 'Folder'} context = self.portal.folder1.doc11 tree = buildFolderTree(self.portal, query=query, obj=context, strategy=strategy)['children'] rootPath = '/'.join(self.portal.getPhysicalPath()) self.assertEqual(len(tree), 2) self.assertEqual(tree[0]['item'].getPath(), rootPath + '/folder1') self.assertEqual(len(tree[0]['children']), 0) self.assertEqual(tree[1]['item'].getPath(), rootPath + '/folder2') self.assertEqual(len(tree[1]['children']), 1) self.assertEqual(tree[1]['children'][0]['item'].getPath(), rootPath + '/folder2/folder21')
def testShowAllParentsWithParentNotInCatalog(self): strategy = NavtreeStrategyBase() strategy.showAllParents = True wftool = getToolByName(self.portal, 'portal_workflow') self.portal.folder1.unindexObject() query = {'portal_type' : 'Folder'} context = self.portal.folder1.doc11 tree = buildFolderTree(self.portal, query=query, obj=context, strategy=strategy)['children'] rootPath = '/'.join(self.portal.getPhysicalPath()) # XXX: Ideally, this shouldn't happen, we should get a dummy node, but # there's no way to do that with the catalog self.assertEqual(len(tree), 1) self.assertEqual(tree[0]['item'].getPath(), rootPath + '/folder2')
def testShowAllParentsOverridesNonFolderishObjectNotExpanded(self): strategy = NavtreeStrategyBase() strategy.showAllParents = True self.setRoles(['Manager']) f = DummyNonStructuralFolder('ns_folder') self.portal._setObject('ns_folder', f) ns_folder = self.portal.ns_folder self.portal.portal_catalog.indexObject(self.portal.ns_folder) ns_folder.invokeFactory('Document', 'doc') self.setRoles(['Member']) tree = buildFolderTree(self.portal, self.portal.ns_folder.doc, strategy=strategy)['children'] rootPath = '/'.join(self.portal.getPhysicalPath()) self.assertEqual(tree[-1]['item'].getPath(), rootPath + '/ns_folder') self.assertEqual(len(tree[-1]['children']), 1) self.assertEqual(tree[-1]['children'][0]['item'].getPath(), rootPath + '/ns_folder/doc')
def getCourseItemsInOrder(self): """ Create a flattened out list of portal_catalog queried items in their natural depth first navigation order. @param root: Content item which acts as a navigation root @param query: Dictionary of portal_catalog query parameters @return: List of catalog brains """ root = self possible_types = [ 'Kurs', 'Kursabschluss', 'Lerneinheit', 'Document', 'Aufgabe', 'Audiovideo', 'Checkliste', 'Skill' ] query = {'portal_type': possible_types} # Navigation tree base portal_catalog query parameters applied_query = { 'path': '/'.join(root.getPhysicalPath()), 'sort_on': 'getObjPositionInParent' } # Apply caller's filters applied_query.update(query) # Set the navigation tree build strategy # - use navigation portlet strategy as base #strategy = DefaultNavtreeStrategy(root) strategy = NavtreeStrategyBase() strategy.rootPath = '/'.join(root.getPhysicalPath()) strategy.showAllParents = False strategy.bottomLevel = 999 # This will yield out tree of nested dicts of # item brains with retrofitted navigational data tree = buildFolderTree(root, root, query, strategy=strategy) items = [] def flatten(children): """ Recursively flatten the tree """ for c in children: # Copy catalog brain object into the result items.append(c["item"]) children = c.get("children", None) if children: flatten(children) flatten(tree["children"]) return items
def testShowAllParentsWithRestrictedParent(self): strategy = NavtreeStrategyBase() strategy.showAllParents = True wftool = getToolByName(self.portal, 'portal_workflow') wftool.doActionFor(self.portal.folder1, 'hide') self.portal.folder1.reindexObject() query = {'portal_type' : 'Folder'} context = self.portal.folder1.doc11 tree = buildFolderTree(self.portal, query=query, obj=context, strategy=strategy)['children'] rootPath = '/'.join(self.portal.getPhysicalPath()) self.assertEqual(len(tree), 2) self.assertEqual(tree[0]['item'].getPath(), rootPath + '/folder1') self.assertEqual(len(tree[0]['children']), 1) self.assertEqual(tree[0]['children'][0]['item'].getPath(), rootPath + '/folder1/doc11') self.assertEqual(tree[1]['item'].getPath(), rootPath + '/folder2') self.assertEqual(len(tree[1]['children']), 1) self.assertEqual(tree[1]['children'][0]['item'].getPath(), rootPath + '/folder2/folder21')
def _getTemplatesTree(self): '''Create the structure of elements used to display the item templates tree to the item creators. We only want to show folders and items the current creator may use, so we do that in 2 steps : - a first catalog query that will find every items the creator may use, when found, we save the differents paths to these items so we will be able to not take into account folder that could exist in the configuration but that would be empty because no items into it; - when the query is build with list of paths to consider, we use buildFolderTree that will build a 'tree' dict with elements and children.''' # first query the catalog to see wich items the current user may select itemTemplatesPath = '/'.join(self.cfg.itemtemplates.getPhysicalPath()) itemTemplates = self.cfg.getItemTemplates(as_brains=True, onlyActive=True, filtered=True) # we need to keep the itemTemplatesPath folderPathsToKeep = [ itemTemplatesPath, ] # now compute folder paths so only these paths will be passed to buildFolderTree # and this method will not consider any other folders for itemTemplate in itemTemplates: folderPath = '/'.join(itemTemplate.getPath().split('/')[0:-1]) # keep every folders of folderPath in case the itemtemplate is in a sub/sub/sub/...folder while folderPath not in folderPathsToKeep: folderPathsToKeep.append(folderPath) folderPath = '/'.join(folderPath.split('/')[0:-1]) query = self.cfg._itemTemplatesQuery(onlyActive=True, filtered=True) # we want to query every Folders too query['portal_type'] = (query['portal_type'], 'Folder') query['path'] = {'query': folderPathsToKeep, 'depth': 1} # define a strategy so rootPath is managed ourself or folderTree # fails to compute it because query['path']['query'] is a list here... strategy = NavtreeStrategyBase() strategy.rootPath = itemTemplatesPath folderTree = buildFolderTree(self.context, None, query, strategy) # the single left problem is the fact that we could have empty folders # because we look in the itemTemplatesPath and it returns 'directly contained items' and # folders, we can not do anything else using a catalog query... # so check children of the root level, if it is an empty folder, we remove it... childrenToKeep = [] for child in folderTree['children']: if child['item'].portal_type == 'Folder' and not child['children']: continue childrenToKeep.append(child) folderTree['children'] = childrenToKeep return folderTree
def buildFolderTree(context, obj=None, query=None, strategy=NavtreeStrategyBase()): """Create a tree structure representing a navigation tree. By default, it will create a full "sitemap" tree, rooted at the portal, ordered by explicit folder order. If the 'query' parameter contains a 'path' key, this can be used to override this. To create a navtree rooted at the portal root, set query['path'] to: {'query' : '/'.join(context.getPhysicalPath()), 'navtree' : 1} to start this 1 level below the portal root, set query['path'] to: {'query' : '/'.join(obj.getPhysicalPath()), 'navtree' : 1, 'navtree_start' : 1} to create a sitemap with depth limit 3, rooted in the portal: {'query' : '/'.join(obj.getPhysicalPath()), 'depth' : 3} The parameters: - 'context' is the acquisition context, from which tools will be acquired - 'obj' is the current object being displayed. - 'query' is a catalog query to apply to find nodes in the tree. - 'strategy' is an object that can affect how the generation works. It should be derived from NavtreeStrategyBase, if given, and contain: rootPath -- a string property; the physical path to the root node. If not given, it will default to any path set in the query, or the portal root. Note that in a navtree query, the root path will default to the portal only, possibly adjusted for any navtree_start set. If rootPath points to something not returned by the query by the query, a dummy node containing only an empty 'children' list will be returned. showAllParents -- a boolean property; if true and obj is given, ensure that all parents of the object, including any that would normally be filtered out are included in the tree. nodeFilter(node) -- a method returning a boolean; if this returns False, the given node will not be inserted in the tree subtreeFilter(node) -- a method returning a boolean; if this returns False, the given (folderish) node will not be expanded (its children will be pruned off) decoratorFactory(node) -- a method returning a dict; this can inject additional keys in a node being inserted. showChildrenOf(object) -- a method returning True if children of the given object (normally the root) should be returned Returns tree where each node is represented by a dict: item - A catalog brain of this item depth - The depth of this item, relative to the startAt level currentItem - True if this is the current item currentParent - True if this is a direct parent of the current item children - A list of children nodes of this node Note: Any 'decoratorFactory' specified may modify this list, but the 'children' property is guaranteed to be there. Note: If the query does not return the root node itself, the root element of the tree may contain *only* the 'children' list. Note: Folder default-pages are not included in the returned result. If the 'obj' passed in is a default-page, its parent folder will be used for the purposes of selecting the 'currentItem'. """ if not query: query = {} portal_url = getToolByName(context, 'portal_url') portal_catalog = getToolByName(context, 'portal_catalog') rootPath = strategy.rootPath # Find the object's path. Use parent folder if context is a default-page objPath = None objPhysicalPath = None if obj is not None: objPhysicalPath = obj.getPhysicalPath() objPath = '/'.join(objPhysicalPath) portalPath = portal_url.getPortalPath() portalObject = portal_url.getPortalObject() # Calculate rootPath from the path query if not set. if 'path' not in query: if rootPath is None: rootPath = portalPath query['path'] = rootPath elif rootPath is None: pathQuery = query['path'] if isinstance(pathQuery, StringType): rootPath = pathQuery else: # Adjust for the fact that in a 'navtree' query, the actual path # is the path of the current context if pathQuery.get('navtree', False): navtreeLevel = pathQuery.get('navtree_start', 1) if navtreeLevel > 1: navtreeContextPath = pathQuery['query'] navtreeContextPathElements = navtreeContextPath[ len(portalPath) + 1:].split('/') # Short-circuit if we won't be able to find this path if len(navtreeContextPathElements) < (navtreeLevel - 1): return {'children': []} rootPath = portalPath + '/' + '/'.join( navtreeContextPathElements[:navtreeLevel - 1]) else: rootPath = portalPath else: rootPath = pathQuery['query'] rootDepth = len(rootPath.split('/')) # Determine if we need to prune the root (but still force the path to) # the parent if necessary pruneRoot = False if strategy is not None: rootObject = portalObject.unrestrictedTraverse(rootPath, None) if rootObject is not None: pruneRoot = not strategy.showChildrenOf(rootObject) if 'sort_on' not in query: query['sort_on'] = 'getObjPositionInParent' results = portal_catalog.searchResults(query) # We keep track of a dict of item path -> node, so that we can easily # find parents and attach children. If a child appears before its # parent, we stub the parent node. # This is necessary because whilst the sort_on parameter will ensure # that the objects in a folder are returned in the right order relative # to each other, we don't know the relative order of objects from # different folders. So, if /foo comes before /bar, and /foo/a comes # before /foo/b, we may get a list like (/bar/x, /foo/a, /foo/b, /foo, # /bar,). itemPaths = {} # Add an (initially empty) node for the root itemPaths[rootPath] = {'children': []} # If we need to "prune" the parent (but still allow showAllParent to # force some children), do so now if pruneRoot: itemPaths[rootPath]['_pruneSubtree'] = True def insertElement(itemPaths, item, forceInsert=False): """Insert the given 'item' brain into the tree, which is kept in 'itemPaths'. If 'forceInsert' is True, ignore node- and subtree- filters, otherwise any node- or subtree-filter set will be allowed to block the insertion of a node. """ itemPath = item.getPath() itemInserted = (itemPaths.get(itemPath, {}).get('item', None) is not None) # Short-circuit if we already added this item. Don't short-circuit # if we're forcing the insert, because we may have inserted but # later pruned off the node if not forceInsert and itemInserted: return itemPhysicalPath = itemPath.split('/') parentPath = '/'.join(itemPhysicalPath[:-1]) parentPruned = (itemPaths.get(parentPath, {}).get('_pruneSubtree', False)) # Short-circuit if we know we're pruning this item's parent # We could do this recursively, in case of parent of the # parent was being pruned, but this may not be a great trade-off # There is scope for more efficiency improvement here: If we knew we # were going to prune the subtree, # we would short-circuit here each time. # In order to know that, we'd have to make sure we inserted each parent # before its children, by sorting the catalog result set (probably # manually) to get a breadth-first search. if not forceInsert and parentPruned: return isCurrent = isCurrentParent = False if objPath is not None: if objPath == itemPath: isCurrent = True elif objPath.startswith(itemPath + '/') and len( objPhysicalPath) > len(itemPhysicalPath): isCurrentParent = True isDefaultPage = getattr(item, 'is_default_page', False) relativeDepth = len(itemPhysicalPath) - rootDepth newNode = { 'item': item, 'depth': relativeDepth, 'currentItem': isCurrent, 'currentParent': isCurrentParent, } insert = True if not forceInsert and strategy is not None: insert = strategy.nodeFilter(newNode) if insert: if strategy is not None: newNode = strategy.decoratorFactory(newNode) # Tell parent about this item, unless an earlier subtree filter # told us not to. If we're forcing the insert, ignore the # pruning, but avoid inserting the node twice if parentPath in itemPaths: itemParent = itemPaths[parentPath] if forceInsert: nodeAlreadyInserted = False for i in itemParent['children']: if i['item'].getPath() == itemPath: nodeAlreadyInserted = True break if not nodeAlreadyInserted: if isDefaultPage: itemParent['children'].insert(0, newNode) else: itemParent['children'].append(newNode) elif not itemParent.get('_pruneSubtree', False): if isDefaultPage: itemParent['children'].insert(0, newNode) else: itemParent['children'].append(newNode) else: itemPaths[parentPath] = {'children': [newNode]} # Ask the subtree filter (if any), if we should # be expanding this node if strategy.showAllParents and isCurrentParent: # If we will be expanding this later, we # can't prune off children now expand = True else: expand = getattr(item, 'is_folderish', True) if expand and (not forceInsert and strategy is not None): expand = strategy.subtreeFilter(newNode) children = newNode.setdefault('children', []) if expand: # If we had some orphaned children for this node, attach # them if itemPath in itemPaths: children.extend(itemPaths[itemPath]['children']) else: newNode['_pruneSubtree'] = True itemPaths[itemPath] = newNode # Add the results of running the query for r in results: insertElement(itemPaths, r) # If needed, inject additional nodes for the direct parents of the # context. Note that we use an unrestricted query: things we don't normally # have permission to see will be included in the tree. if strategy.showAllParents and objPath is not None: objSubPathElements = objPath[len(rootPath) + 1:].split('/') parentPaths = [] haveNode = (itemPaths.get(rootPath, {}).get('item', None) is None) if not haveNode: parentPaths.append(rootPath) parentPath = rootPath for i in range(len(objSubPathElements)): nodePath = rootPath + '/' + '/'.join(objSubPathElements[:i + 1]) node = itemPaths.get(nodePath, None) # If we don't have this node, we'll have to get it, if we have it # but it wasn't connected, re-connect it if node is None or 'item' not in node: parentPaths.append(nodePath) else: nodeParent = itemPaths.get(parentPath, None) if nodeParent is not None: nodeAlreadyInserted = False for j in nodeParent['children']: if j['item'].getPath() == nodePath: nodeAlreadyInserted = True break if not nodeAlreadyInserted: nodeParent['children'].append(node) parentPath = nodePath # If we were outright missing some nodes, find them again if parentPaths: query = {'path': {'query': parentPaths, 'depth': 0}} results = portal_catalog.unrestrictedSearchResults(query) for r in results: insertElement(itemPaths, r, forceInsert=True) # Return the tree starting at rootPath as the root node. return itemPaths[rootPath]
def customBuildFolderTree(context, obj=None, query={}, strategy=NavtreeStrategyBase()): """ """ from plone import api from genweb.organs import utils as utilsOrgans portal_url = getToolByName(context, 'portal_url') portal_catalog = api.portal.get_tool(name='portal_catalog') showAllParents = strategy.showAllParents rootPath = strategy.rootPath request = getattr(context, 'REQUEST', {}) # Find the object's path. Use parent folder if context is a default-page objPath = None objPhysicalPath = None if obj is not None: objPhysicalPath = obj.getPhysicalPath() if utils.isDefaultPage(obj, request): objPhysicalPath = objPhysicalPath[:-1] objPath = '/'.join(objPhysicalPath) portalPath = portal_url.getPortalPath() portalObject = portal_url.getPortalObject() # Calculate rootPath from the path query if not set. if 'path' not in query: if rootPath is None: rootPath = portalPath query['path'] = rootPath elif rootPath is None: pathQuery = query['path'] if type(pathQuery) == StringType: rootPath = pathQuery else: # Adjust for the fact that in a 'navtree' query, the actual path # is the path of the current context if pathQuery.get('navtree', False): navtreeLevel = pathQuery.get('navtree_start', 1) if navtreeLevel > 1: navtreeContextPath = pathQuery['query'] navtreeContextPathElements = navtreeContextPath[len(portalPath) + 1:].split('/') # Short-circuit if we won't be able to find this path if len(navtreeContextPathElements) < (navtreeLevel - 1): return {'children': []} rootPath = portalPath + '/' + '/'.join(navtreeContextPathElements[:navtreeLevel - 1]) else: rootPath = portalPath else: rootPath = pathQuery['query'] rootDepth = len(rootPath.split('/')) # Determine if we need to prune the root (but still force the path to) # the parent if necessary pruneRoot = False if strategy is not None: rootObject = portalObject.unrestrictedTraverse(rootPath, None) if rootObject is not None: pruneRoot = not strategy.showChildrenOf(rootObject) # Allow the strategy to suppliment the query for keys not already # present in the query such as sorting and omitting default pages for key, value in strategy.supplimentQuery.iteritems(): if key not in query: query[key] = value results2 = portal_catalog.searchResults(query) results = [] from plone import api if api.user.is_anonymous(): username = None else: username = api.user.get_current().id for value in results2: if value.portal_type == 'genweb.organs.organgovern': organ = value.getObject() if username: roles = api.user.get_roles(obj=organ, username=username) else: roles = [] organType = organ.organType if 'Manager' in roles or (organType == 'open_organ'): results.append(value) elif organType == 'restricted_to_members_organ': if utilsOrgans.checkhasRol(['OG1-Secretari', 'OG2-Editor', 'OG3-Membre', 'OG5-Convidat'], roles): results.append(value) elif organType == 'restricted_to_affected_organ': if utilsOrgans.checkhasRol(['OG1-Secretari', 'OG2-Editor', 'OG3-Membre', 'OG4-Afectat', 'OG5-Convidat'], roles): results.append(value) else: # remove element continue else: results.append(value) # We keep track of a dict of item path -> node, so that we can easily # find parents and attach children. If a child appears before its # parent, we stub the parent node. # This is necessary because whilst the sort_on parameter will ensure # that the objects in a folder are returned in the right order relative # to each other, we don't know the relative order of objects from # different folders. So, if /foo comes before /bar, and /foo/a comes # before /foo/b, we may get a list like (/bar/x, /foo/a, /foo/b, /foo, # /bar,). itemPaths = {} # Add an (initially empty) node for the root itemPaths[rootPath] = {'children': []} # If we need to "prune" the parent (but still allow showAllParent to # force some children), do so now if pruneRoot: itemPaths[rootPath]['_pruneSubtree'] = True def insertElement(itemPaths, item, forceInsert=False): """Insert the given 'item' brain into the tree, which is kept in 'itemPaths'. If 'forceInsert' is True, ignore node- and subtree- filters, otherwise any node- or subtree-filter set will be allowed to block the insertion of a node. """ itemPath = item.getPath() itemInserted = (itemPaths.get(itemPath, {}).get('item', None) is not None) # Short-circuit if we already added this item. Don't short-circuit # if we're forcing the insert, because we may have inserted but # later pruned off the node if not forceInsert and itemInserted: return itemPhysicalPath = itemPath.split('/') parentPath = '/'.join(itemPhysicalPath[:-1]) parentPruned = (itemPaths.get(parentPath, {}).get('_pruneSubtree', False)) # Short-circuit if we know we're pruning this item's parent # XXX: We could do this recursively, in case of parent of the # parent was being pruned, but this may not be a great trade-off # There is scope for more efficiency improvement here: If we knew we # were going to prune the subtree, we would short-circuit here each time. # In order to know that, we'd have to make sure we inserted each parent # before its children, by sorting the catalog result set (probably # manually) to get a breadth-first search. if not forceInsert and parentPruned: return isCurrent = isCurrentParent = False if objPath is not None: if objPath == itemPath: isCurrent = True elif objPath.startswith(itemPath + '/') and len(objPhysicalPath) > len(itemPhysicalPath): isCurrentParent = True relativeDepth = len(itemPhysicalPath) - rootDepth newNode = {'item': item, 'depth': relativeDepth, 'currentItem': isCurrent, 'currentParent': isCurrentParent, } insert = True if not forceInsert and strategy is not None: insert = strategy.nodeFilter(newNode) if insert: if strategy is not None: newNode = strategy.decoratorFactory(newNode) # Tell parent about this item, unless an earlier subtree filter # told us not to. If we're forcing the insert, ignore the # pruning, but avoid inserting the node twice if parentPath in itemPaths: itemParent = itemPaths[parentPath] if forceInsert: nodeAlreadyInserted = False for i in itemParent['children']: if i['item'].getPath() == itemPath: nodeAlreadyInserted = True break if not nodeAlreadyInserted: itemParent['children'].append(newNode) elif not itemParent.get('_pruneSubtree', False): itemParent['children'].append(newNode) else: itemPaths[parentPath] = {'children': [newNode]} # Ask the subtree filter (if any), if we should be expanding this node if strategy.showAllParents and isCurrentParent: # If we will be expanding this later, we can't prune off children now expand = True else: expand = getattr(item, 'is_folderish', True) if expand and (not forceInsert and strategy is not None): expand = strategy.subtreeFilter(newNode) children = newNode.setdefault('children', []) if expand: # If we had some orphaned children for this node, attach # them if itemPath in itemPaths: children.extend(itemPaths[itemPath]['children']) else: newNode['_pruneSubtree'] = True itemPaths[itemPath] = newNode # Add the results of running the query for r in results: insertElement(itemPaths, r) # If needed, inject additional nodes for the direct parents of the # context. Note that we use an unrestricted query: things we don't normally # have permission to see will be included in the tree. if strategy.showAllParents and objPath is not None: objSubPathElements = objPath[len(rootPath) + 1:].split('/') parentPaths = [] haveNode = (itemPaths.get(rootPath, {}).get('item', None) is None) if not haveNode: parentPaths.append(rootPath) parentPath = rootPath for i in range(len(objSubPathElements)): nodePath = rootPath + '/' + '/'.join(objSubPathElements[:i + 1]) node = itemPaths.get(nodePath, None) # If we don't have this node, we'll have to get it, if we have it # but it wasn't connected, re-connect it if node is None or 'item' not in node: parentPaths.append(nodePath) else: nodeParent = itemPaths.get(parentPath, None) if nodeParent is not None: nodeAlreadyInserted = False for i in nodeParent['children']: if i['item'].getPath() == nodePath: nodeAlreadyInserted = True break if not nodeAlreadyInserted: nodeParent['children'].append(node) parentPath = nodePath # If we were outright missing some nodes, find them again if len(parentPaths) > 0: query = {'path': {'query': parentPaths, 'depth': 0}} results = portal_catalog.unrestrictedSearchResults(query) for r in results: insertElement(itemPaths, r, forceInsert=True) # Return the tree starting at rootPath as the root node. return itemPaths[rootPath]