source: products/quintagroup.dropdownmenu/trunk/quintagroup/dropdownmenu/browser/viewlets.py @ 3461

Last change on this file since 3461 was 3461, checked in by chervol, 12 years ago

minor fixes code clean up

  • Property svn:eol-style set to native
File size: 10.6 KB
RevLine 
[1194]1# -*- coding: utf-8 -*-
2from Acquisition import aq_inner
[1182]3
[3153]4from zope.component import getMultiAdapter
[1194]5
[1182]6from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
[1194]7from Products.CMFCore.utils import getToolByName
8from Products.CMFCore.interfaces import IAction, IActionCategory
9from Products.CMFCore.ActionInformation import ActionInfo
[2657]10from Products.CMFPlone.utils import versionTupleFromString
[1182]11
[1194]12from plone.memoize.instance import memoize
[3234]13from plone.memoize.compress import xhtmlslimmer
[1182]14from plone.app.layout.viewlets import common
[1197]15from plone.app.layout.navigation.navtree import buildFolderTree
16from plone.app.layout.navigation.interfaces import INavtreeStrategy
[1182]17
[1197]18from quintagroup.dropdownmenu.browser.menu import DropDownMenuQueryBuilder
[1319]19from quintagroup.dropdownmenu.util import getDropDownMenuSettings
[1182]20
[2775]21from time import time
22from plone.memoize import ram
23import copy
[1194]24
[3348]25
[3339]26def menu_cache_key(f, view):
27    # menu cache key conssits of:
[3461]28    # - path to selected item
[3347]29    # - site can be accessed on different domains
[3339]30    # - language is important for multilingua sites
[3235]31
[3239]32    portal_state = getMultiAdapter((view.context, view.request),
33                                   name=u'plone_portal_state')
34    site_len = len(portal_state.navigation_root_path().split('/'))
35    content_path = view.context.getPhysicalPath()[site_len:]
36    if view.conf.content_tabs_level > 0:
37        content_path = content_path[:view.conf.content_tabs_level]
[3339]38    path_key = view.request.physicalPathToURL(content_path)
39
40    language = portal_state.locale().getLocaleID()
41
42    # Cache for five minutes. Note that the HTTP RAM-cache
43    # typically purges entries after 60 minutes.
[3234]44    return view.__name__ + \
[3239]45           path_key + \
[3339]46           language + \
[3347]47           str(time() // (60 * 5))
[2775]48
[3347]49
50# we are caching the menu structure built out of portal_actions tool
[3339]51# this cache key does not take in account expressions and roles settings
[3234]52def tabs_cache_key(f, view, site_url):
53    return site_url + str(time() // (60 * 60))
54
[3347]55
[3239]56def dropdowncache(f):
57    def func(view):
[3339]58        portal_state = getMultiAdapter((view.context, view.request),
59                                        name=u'plone_portal_state')
[3347]60        # it is impossible to reliably cache entire rendered menu generated
[3339]61        # with potral actions strategy.
62        if not view.conf.enable_caching or view.conf.show_actions_tabs or \
63           (not portal_state.anonymous() and \
64                view.conf.caching_strategy == 'anonymous'):
[3239]65            return f(view)
66        return ram.cache(menu_cache_key)(f)(view)
67    return func
[3234]68
[3239]69
[3347]70def tabscache(f):
71    def func(view, site_url):
72        if not view.conf.enable_caching:
73            return f(view, site_url)
74        return ram.cache(tabs_cache_key)(f)(view, site_url)
75    return func
76
77
[1182]78class GlobalSectionsViewlet(common.GlobalSectionsViewlet):
79    index = ViewPageTemplateFile('templates/sections.pt')
[1194]80    recurse = ViewPageTemplateFile('templates/sections_recurse.pt')
[1182]81
82    def update(self):
[1194]83        # we may need some previously defined variables
84        #super(GlobalSectionsViewlet, self).update()
85
86        # prepare to gather portal tabs
87        context = aq_inner(self.context)
[3236]88        self.conf = self._settings()
[1194]89        self.tool = getToolByName(context, 'portal_actions')
[2700]90        self.site_url = getToolByName(context, 'portal_url')()
[3151]91        self.context_state = getMultiAdapter((self.context, self.request),
[2701]92                                              name="plone_context_state")
[3151]93        self.context_url = self.context_state.is_default_page() and \
[2768]94            '/'.join(self.context.absolute_url().split('/')[:-1]) or \
95            self.context.absolute_url()
[3151]96
97        self.cat_sufix = self.conf.nested_category_sufix or ''
[2772]98        self.cat_prefix = self.conf.nested_category_prefix or ''
[3234]99
100    def portal_tabs(self):
101        tabs = []
102
[1194]103        # fetch actions-based tabs?
[3234]104        if self.conf.show_actions_tabs:
[1194]105            tabs.extend(self._actions_tabs())
106
107        # fetch content structure-based tabs?
[3234]108        if self.conf.show_content_tabs:
[1194]109            # put content-based actions before content structure-based ones?
[3234]110            if self.conf.content_before_actions_tabs:
[1194]111                tabs = self._content_tabs() + tabs
112            else:
113                tabs.extend(self._content_tabs())
114
[3234]115        return tabs
[1194]116
117    def _actions_tabs(self):
118        """Return tree of tabs based on portal_actions tool configuration"""
119        conf = self.conf
120        tool = self.tool
121
122        # check if we have required root actions category inside tool
123        if conf.actions_category not in tool.objectIds():
124            return []
[2775]125        listtabs = []
126        res, listtabs = self.prepare_tabs(self.site_url)
127        res = copy.deepcopy(res)
128        self.tabs = listtabs
[3151]129
[2775]130        # if there is no custom menu in portal tabs return
131        if not listtabs:
132            return []
[3151]133
[2774]134        current_item = -1
[2772]135        delta = 1000
[2775]136        for info in listtabs:
[2772]137            if  self.context_url.startswith(info['url']) and \
[3151]138                len(self.context_url) - len(info['url']) < delta:
139                delta = len(self.context_url) - len(info['url'])
140                current_item = listtabs.index(info)
141        self.id_chain = []
142
[2843]143        if current_item > -1 and current_item < len(listtabs) and \
144            (listtabs[current_item]['url'] != self.site_url or \
[3151]145            listtabs[current_item]['url'] == self.site_url and \
146            self.context_url == self.site_url):
[2774]147
[3151]148            self.mark_active(listtabs[current_item]['id'],
149                             listtabs[current_item]['url'])
150
[2775]151        self._activate(res)
152        return res
[3151]153
[3347]154    @tabscache
[2775]155    def prepare_tabs(self, site_url):
[3151]156        def normalize_actions(category, object, level, parent_url=None):
[2775]157            """walk through the tabs dictionary and build list of all tabs"""
158            tabs = []
159            for info in self._actionInfos(category, object):
160                icon = info['icon'] and '<img src="%s" />' % info['icon'] or ''
161                children = []
162                bottomLevel = self.conf.actions_tabs_level
163                if bottomLevel < 1 or level < bottomLevel:
164                    # try to find out appropriate subcategory
165                    subcat_id = self.cat_prefix + info['id'] + self.cat_sufix
166                    if subcat_id != info['id'] and \
167                       subcat_id in category.objectIds():
168                        subcat = category._getOb(subcat_id)
169                        if IActionCategory.providedBy(subcat):
[3151]170                            children = normalize_actions(subcat, object,
171                                                         level + 1,
172                                                         info['url'])
[2775]173
[3461]174                parent_id = category.getId().replace(self.cat_prefix,
[3151]175                                '').replace(self.cat_sufix, '')
176                tab = {'id': info['id'],
[2775]177                   'title': info['title'],
178                   'url': info['url'],
179                   'parent': (parent_id, parent_url)}
180                tabslist.append(tab)
[3151]181
182                tab = {'id': info['id'],
[2775]183                       'Title': info['title'],
184                       'Description': info['description'],
185                       'getURL': info['url'],
186                       'show_children': len(children) > 0,
187                       'children': children,
[3151]188                       'currentItem': False,
[2775]189                       'currentParent': False,
190                       'item_icon': {'html_tag': icon},
191                       'normalized_review_state': 'visible'}
192                tabs.append(tab)
[3151]193            return tabs
[2775]194        tabslist = []
[3151]195        tabs = normalize_actions(self.tool._getOb(self.conf.actions_category),
196                                 aq_inner(self.context), 0)
[2775]197        return tabs, tabslist
198
199    def _activate(self, res):
200        """Mark selected chain in the tabs dictionary"""
201        for info in res:
202            if info['getURL'] in self.id_chain:
203                info['currentItem'] = True
204                info['currentParent'] = True
205                if info['children']:
206                    self._activate(info['children'])
207
208    def mark_active(self, current_id, url):
[2772]209        for info in self.tabs:
[2775]210            if info['id'] == current_id and info['url'] == url:
[2773]211                self.mark_active(info['parent'][0], info['parent'][1])
[2775]212                self.id_chain.append(info['url'])
[1194]213
214    def _actionInfos(self, category, object, check_visibility=1,
215                     check_permissions=1, check_condition=1, max=-1):
216        """Return action infos for a given category"""
217        ec = self.tool._getExprContext(object)
218        actions = [ActionInfo(action, ec) for action in category.objectValues()
219                    if IAction.providedBy(action)]
220
221        action_infos = []
222        for ai in actions:
223            if check_visibility and not ai['visible']:
224                continue
225            if check_permissions and not ai['allowed']:
226                continue
227            if check_condition and not ai['available']:
228                continue
229            action_infos.append(ai)
230            if max + 1 and len(action_infos) >= max:
231                break
232        return action_infos
233
234    def _content_tabs(self):
235        """Return tree of tabs based on content structure"""
[1197]236        context = aq_inner(self.context)
[1194]237
[1197]238        queryBuilder = DropDownMenuQueryBuilder(context)
239        strategy = getMultiAdapter((context, None), INavtreeStrategy)
240        # XXX This works around a bug in plone.app.portlets which was
241        # fixed in http://dev.plone.org/svn/plone/changeset/18836
242        # When a release with that fix is made this workaround can be
243        # removed and the plone.app.portlets requirement in setup.py
244        # be updated.
245        if strategy.rootPath is not None and strategy.rootPath.endswith("/"):
246            strategy.rootPath = strategy.rootPath[:-1]
247
248        return buildFolderTree(context, obj=context, query=queryBuilder(),
249                               strategy=strategy).get('children', [])
250
[1194]251    @memoize
252    def _settings(self):
253        """Fetch dropdown menu settings registry"""
[1319]254        return getDropDownMenuSettings(self.context)
[1194]255
[3239]256    @dropdowncache
[1194]257    def createMenu(self):
[3461]258        html = self.recurse(children=self.portal_tabs(), level=1)
[3458]259        return xhtmlslimmer.compress(html).strip('\n')
[1194]260
[2658]261    @memoize
[2657]262    def is_plone_four(self):
263        """Indicates if we are in plone 4.
[3151]264
[2657]265        """
[3151]266        pm = getToolByName(aq_inner(self.context), 'portal_migration')
[2658]267        try:
268            version = versionTupleFromString(pm.getSoftwareVersion())
269        except AttributeError:
270            version = versionTupleFromString(pm.getFileSystemVersion())
271
[2657]272        if version:
273            return version[0] == 4
Note: See TracBrowser for help on using the repository browser.