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

Last change on this file since 3234 was 3234, checked in by kroman0, 13 years ago

Apply Malthes caching patch

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