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

Last change on this file since 3491 was 3491, checked in by kroman0, 12 years ago

Added select for mobile menu

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