############################################################################## # # Copyright (c) 2005 CMFonFive Contributors. All rights reserved. # # This software is distributed under the terms of the Zope Public # License (ZPL) v2.1. See COPYING.txt for more information. # ########################################################################### """ Five actions tool. $Id$ """ from AccessControl import ClassSecurityInfo from Acquisition import aq_base, aq_inner, aq_parent from Globals import InitializeClass from OFS.SimpleItem import SimpleItem from Products.CMFCore.ActionInformation import ActionInformation from Products.CMFCore.ActionProviderBase import ActionProviderBase from Products.CMFCore.Expression import createExprContext, Expression from Products.CMFCore.utils import UniqueObject, getToolByName from zope.interface import providedBy, Interface from zope.app import zapi from zope.app.pagetemplate import engine from zope.app.publisher.interfaces.browser import IBrowserMenu from zope.app.publisher.interfaces.browser import IBrowserSubMenuItem from zope.security.proxy import removeSecurityProxy def _listMenuIds(): return [id for id, utility in zapi.getUtilitiesFor(IBrowserMenu)] def _getItemsWithCMFFilter(portal, object, request, menu): result = [] folder = aq_parent(aq_inner(object)) econtext = createExprContext(folder, portal, object) # Originally the code simply tested item.available() which is a standard # Zope 3 technique. The problem was that this in turn evaluated # the filter TAL expression with the Zope 3 TALES engine. This needed # to be changed to instead using the CMF expression mechanism so that # property security (among other things) was taken into account. - Rocky for name, item in zapi.getAdapters((object, request), menu.getMenuItemType()): filter_ = item.filter if filter_ is None: result.append(item) else: exprtext = None if hasattr(filter_, 'text'): # python TAL expr exprtext = 'python:' + filter_.text elif hasattr(filter_, '_s'): # standard path TAL expr exprtext = filter_._s if exprtext is not None: expr = Expression(text=exprtext) if expr(econtext): result.append(item) elif item.available(): result.append(item) return result def getMenu(id, object, request): """Return menu item entries in a TAL-friendly form.""" menu = zapi.getUtility(IBrowserMenu, id) try: portal = getToolByName(object, 'portal_url').getPortalObject() except AttributeError: # there is no accessible portal in the aq chain, fall back to # previous behaviour result = [] for name, item in zapi.getAdapters((object, request), menu.getMenuItemType()): if item.available(): result.append(item) else: # use CMF / Zope 2 TAL expression filtering result = _getItemsWithCMFFilter(portal, object, request, menu) # Now order the result. This is not as easy as it seems. # # (1) Look at the interfaces and put the more specific menu entries # to the front. # (2) Sort unabigious entries by order and then by title. ifaces = list(providedBy(removeSecurityProxy(object)).__iro__) result = [(ifaces.index(item._for or Interface), item.order, item.title, item) for item in result] result.sort() res = [] for index, order, title, item in result: if item._for: for_ = item._for else: for_ = Interface identifier = '%s_%s' % (for_.__identifier__.split('.')[-1], item.action.split('/')[-1]) identifier = identifier.replace(' ', '_').lower() res.append({'title': item.title, 'description': item.description, 'action': item.action, 'selected': (item.selected() and u'selected') or u'', 'icon': item.icon, 'extra': item.extra, 'submenu': (IBrowserSubMenuItem.providedBy(item) and getMenu(item.submenuId, object, request)) or None, 'identifier': identifier, }) return res from Products.Five import security import zope.thread class FiveActionsTool( UniqueObject, SimpleItem, ActionProviderBase ): """ Links content to discussions. """ __implements__ = (ActionProviderBase.__implements__) id = 'portal_fiveactions' meta_type = 'Five Actions Tool' security = ClassSecurityInfo() def getReqestURL(self): return self.REQUEST.URL security.declarePrivate('listActions') def listActions(self, info=None, object=None): """ List all the actions defined by a provider. """ if object is None: if info is None: return () object = info.content actions = [] for menu_id in _listMenuIds(): for entry in getMenu(menu_id, object, self.REQUEST): # The action needs a unique name, so I'll build one # from the object_id and the action url. That is sure # to be unique. action = str(entry['action']) meta_type = getattr(object, 'meta_type', '') act_id = entry['identifier'] if entry.get('filter') is None: filter = None else: filter = Expression(text=str(entry['filter'])) title = entry['title'] # Having bits of unicode here can make rendering very confused, # so we convert it to plain strings, but NOT if it is a # messageID. In Zope 3.2 there are two types of messages, # Message and MessageID, where MessageID is depracated. We can # type-check for both but that provokes a deprecation warning, # so we check for the "domain" attribute instead. if not hasattr(title, 'domain'): title = str(title) act = ActionInformation(id=act_id, title=title, action=Expression(text='string:%s' % action), condition=filter, category=str(menu_id), visible=1) actions.append(act) return tuple(actions) InitializeClass( FiveActionsTool )