Back to index

plone3  3.1.7
navtree.py
Go to the documentation of this file.
00001 # This module contains a function to help build navigation-tree-like structures
00002 # from catalog queries. It also contains a standard implementation of the
00003 # strategy/filtering method that uses Plone's navtree_properties to construct
00004 # navtrees.
00005 
00006 from zope.interface import implements
00007 from zope.component import getMultiAdapter, queryUtility
00008 
00009 # Nasty hack to circumvent 'plone' modulealias
00010 import sys
00011 import plone
00012 del sys.modules['Products.CMFPlone.browser.plone']
00013 
00014 from plone.app.layout.navigation.interfaces import INavigationQueryBuilder
00015 from plone.app.layout.navigation.interfaces import INavigationRoot
00016 from plone.app.layout.navigation.interfaces import INavtreeStrategy
00017 
00018 from plone.app.layout.navigation.navtree import buildFolderTree
00019 from plone.app.layout.navigation.navtree import NavtreeStrategyBase
00020 from plone.app.layout.navigation.root import getNavigationRoot
00021 
00022 from plone.i18n.normalizer.interfaces import IIDNormalizer
00023 
00024 import ploneview
00025 sys.modules['Products.CMFPlone.browser.plone'] = ploneview
00026 
00027 from Acquisition import aq_inner
00028 from Products.CMFCore.utils import getToolByName
00029 from Products.CMFPlone import utils
00030 
00031 # Strategy objects for the navtree creation code. You can subclass these
00032 # to expand the default navtree behaviour, and pass instances of your subclasses
00033 # to buildFolderTree().
00034 
00035 class NavtreeQueryBuilder(object):
00036     """Build a navtree query based on the settings in navtree_properties
00037     """
00038     implements(INavigationQueryBuilder)
00039 
00040     def __init__(self, context):
00041         portal_properties = getToolByName(context, 'portal_properties')
00042         portal_url = getToolByName(context, 'portal_url')
00043         navtree_properties = getattr(portal_properties, 'navtree_properties')
00044 
00045         # Acquire a custom nav query if available
00046         customQuery = getattr(context, 'getCustomNavQuery', None)
00047         if customQuery is not None and utils.safe_callable(customQuery):
00048             query = customQuery()
00049         else:
00050             query = {}
00051 
00052         # Construct the path query
00053 
00054         rootPath = getNavigationRoot(context)
00055         currentPath = '/'.join(context.getPhysicalPath())
00056 
00057         # If we are above the navigation root, a navtree query would return
00058         # nothing (since we explicitly start from the root always). Hence,
00059         # use a regular depth-1 query in this case.
00060 
00061         if not currentPath.startswith(rootPath):
00062             query['path'] = {'query' : rootPath, 'depth' : 1}
00063         else:
00064             query['path'] = {'query' : currentPath, 'navtree' : 1}
00065 
00066         topLevel = navtree_properties.getProperty('topLevel', 0)
00067         if topLevel and topLevel > 0:
00068              query['path']['navtree_start'] = topLevel + 1
00069 
00070         # XXX: It'd make sense to use 'depth' for bottomLevel, but it doesn't
00071         # seem to work with EPI.
00072 
00073         # Only list the applicable types
00074         query['portal_type'] = utils.typesToList(context)
00075 
00076         # Apply the desired sort
00077         sortAttribute = navtree_properties.getProperty('sortAttribute', None)
00078         if sortAttribute is not None:
00079             query['sort_on'] = sortAttribute
00080             sortOrder = navtree_properties.getProperty('sortOrder', None)
00081             if sortOrder is not None:
00082                 query['sort_order'] = sortOrder
00083 
00084         # Filter on workflow states, if enabled
00085         if navtree_properties.getProperty('enable_wf_state_filtering', False):
00086             query['review_state'] = navtree_properties.getProperty('wf_states_to_show', ())
00087 
00088         self.query = query
00089 
00090     def __call__(self):
00091         return self.query
00092 
00093 class SitemapQueryBuilder(NavtreeQueryBuilder):
00094     """Build a folder tree query suitable for a sitemap
00095     """
00096 
00097     def __init__(self, context):
00098         NavtreeQueryBuilder.__init__(self, context)
00099         portal_url = getToolByName(context, 'portal_url')
00100         portal_properties = getToolByName(context, 'portal_properties')
00101         navtree_properties = getattr(portal_properties, 'navtree_properties')
00102         sitemapDepth = navtree_properties.getProperty('sitemapDepth', 2)
00103         self.query['path'] = {'query' : portal_url.getPortalPath(),
00104                               'depth' : sitemapDepth}
00105 
00106 class SitemapNavtreeStrategy(NavtreeStrategyBase):
00107     """The navtree building strategy used by the sitemap, based on
00108     navtree_properties
00109     """
00110     implements(INavtreeStrategy)
00111     #adapts(*, ISiteMap)
00112 
00113     def __init__(self, context, view=None):
00114         self.context = context
00115         
00116         portal_url = getToolByName(context, 'portal_url')
00117         self.portal = portal_url.getPortalObject()
00118         portal_properties = getToolByName(context, 'portal_properties')
00119         navtree_properties = getattr(portal_properties, 'navtree_properties')
00120         site_properties = getattr(portal_properties, 'site_properties')
00121         self.excludedIds = {}
00122         for id in navtree_properties.getProperty('idsNotToList', ()):
00123             self.excludedIds[id] = True
00124         self.parentTypesNQ = navtree_properties.getProperty('parentMetaTypesNotToQuery', ())
00125         self.viewActionTypes = site_properties.getProperty('typesUseViewActionInListings', ())
00126 
00127         self.showAllParents = navtree_properties.getProperty('showAllParents', True)
00128         self.rootPath = getNavigationRoot(context)
00129 
00130         membership = getToolByName(context, 'portal_membership')
00131         self.memberId = membership.getAuthenticatedMember().getId()
00132 
00133     def nodeFilter(self, node):
00134         item = node['item']
00135         if getattr(item, 'getId', None) in self.excludedIds:
00136             return False
00137         elif getattr(item, 'exclude_from_nav', False):
00138             return False
00139         else:
00140             return True
00141 
00142     def subtreeFilter(self, node):
00143         portalType = getattr(node['item'], 'portal_type', None)
00144         if portalType is not None and portalType in self.parentTypesNQ:
00145             return False
00146         else:
00147             return True
00148 
00149     def decoratorFactory(self, node):
00150         context = aq_inner(self.context)
00151         request = context.REQUEST
00152         
00153         newNode = node.copy()
00154         item = node['item']
00155 
00156         portalType = getattr(item, 'portal_type', None)
00157         itemUrl = item.getURL()
00158         if portalType is not None and portalType in self.viewActionTypes:
00159             itemUrl += '/view'
00160 
00161         isFolderish = getattr(item, 'is_folderish', None)
00162         showChildren = False
00163         if isFolderish and (portalType is None or portalType not in self.parentTypesNQ):
00164             showChildren = True
00165 
00166         ploneview = getMultiAdapter((context, request), name=u'plone')
00167 
00168         newNode['Title'] = utils.pretty_title_or_id(context, item)
00169         newNode['absolute_url'] = itemUrl
00170         newNode['getURL'] = itemUrl
00171         newNode['path'] = item.getPath()
00172         newNode['icon'] = getattr(item, 'getIcon', None) # Deprecated, use item_icon
00173         newNode['item_icon'] = ploneview.getIcon(item)
00174         newNode['Creator'] = getattr(item, 'Creator', None)
00175         newNode['creation_date'] = getattr(item, 'CreationDate', None)
00176         newNode['portal_type'] = portalType
00177         newNode['review_state'] = getattr(item, 'review_state', None)
00178         newNode['Description'] = getattr(item, 'Description', None)
00179         newNode['getRemoteUrl'] = getattr(item, 'getRemoteUrl', None)
00180         newNode['show_children'] = showChildren
00181         newNode['no_display'] = False # We sort this out with the nodeFilter
00182         newNode['link_remote'] = newNode['getRemoteUrl'] and newNode['Creator'] != self.memberId
00183 
00184         idnormalizer = queryUtility(IIDNormalizer)
00185         newNode['normalized_portal_type'] = idnormalizer.normalize(portalType)
00186         newNode['normalized_review_state'] = idnormalizer.normalize(newNode['review_state'])
00187 
00188         return newNode
00189         
00190     def showChildrenOf(self, object):
00191         getTypeInfo = getattr(object, 'getTypeInfo', None)
00192         if getTypeInfo is not None:
00193             portal_type = getTypeInfo().getId()
00194             if portal_type in self.parentTypesNQ:
00195                 return False
00196         return True
00197 
00198 class DefaultNavtreeStrategy(SitemapNavtreeStrategy):
00199     """The navtree strategy used for the default navigation portlet
00200     """
00201     implements(INavtreeStrategy)
00202     #adapts(*, INavigationTree)
00203 
00204     def __init__(self, context, view=None):
00205         SitemapNavtreeStrategy.__init__(self, context, view)
00206         portal_properties = getToolByName(context, 'portal_properties')
00207         navtree_properties = getattr(portal_properties, 'navtree_properties')
00208         # XXX: We can't do this with a 'depth' query to EPI...
00209         self.bottomLevel = navtree_properties.getProperty('bottomLevel', 0)
00210         if view is not None:
00211             self.rootPath = view.navigationTreeRootPath()
00212         else:
00213             self.rootPath = getNavigationRoot(context)
00214 
00215     def subtreeFilter(self, node):
00216         sitemapDecision = SitemapNavtreeStrategy.subtreeFilter(self, node)
00217         if sitemapDecision == False:
00218             return False
00219         depth = node.get('depth', 0)
00220         if depth > 0 and self.bottomLevel > 0 and depth >= self.bottomLevel:
00221             return False
00222         else:
00223             return True