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

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

Pep8 fixes

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