Back to index

plone3  3.1.7
menu.py
Go to the documentation of this file.
00001 from urllib import quote_plus
00002 
00003 from zope.interface import implements
00004 from zope.component import getMultiAdapter, queryMultiAdapter
00005 from zope.app.component.hooks import getSite
00006 
00007 from zope.i18n import translate
00008 from zope.i18nmessageid.message import Message
00009 
00010 from zope.app.publisher.browser.menu import BrowserMenu
00011 from zope.app.publisher.browser.menu import BrowserSubMenuItem
00012 
00013 from plone.memoize.instance import memoize
00014 
00015 from Acquisition import aq_inner, aq_base
00016 
00017 from Products.CMFCore.utils import getToolByName
00018 from Products.CMFDynamicViewFTI.interface import ISelectableBrowserDefault
00019 
00020 from Products.CMFPlone.interfaces.structure import INonStructuralFolder
00021 from Products.CMFPlone.interfaces.constrains import IConstrainTypes
00022 from Products.CMFPlone.interfaces.constrains import ISelectableConstrainTypes
00023 
00024 from interfaces import IActionsSubMenuItem
00025 from interfaces import IDisplaySubMenuItem
00026 from interfaces import IFactoriesSubMenuItem
00027 from interfaces import IWorkflowSubMenuItem
00028 
00029 from interfaces import IActionsMenu
00030 from interfaces import IDisplayMenu
00031 from interfaces import IFactoriesMenu
00032 from interfaces import IWorkflowMenu
00033 
00034 from Products.CMFPlone import utils
00035 from Products.CMFPlone import PloneMessageFactory as _
00036 
00037 from plone.app.content.browser.folderfactories import _allowedTypes
00038 
00039 def _safe_unicode(text):
00040     if not isinstance(text, unicode):
00041         text = unicode(text, 'utf-8', 'ignore')
00042     return text
00043 
00044 
00045 class ActionsSubMenuItem(BrowserSubMenuItem):
00046     implements(IActionsSubMenuItem)
00047 
00048     title = _(u'label_actions_menu', default=u'Actions')
00049     description = _(u'title_actions_menu', default=u'Actions for the current content item')
00050     submenuId = 'plone_contentmenu_actions'
00051 
00052     order = 10
00053     extra = {'id': 'plone-contentmenu-actions'}
00054 
00055     def __init__(self, context, request):
00056         BrowserSubMenuItem.__init__(self, context, request)
00057         self.context_state = getMultiAdapter((context, request), name='plone_context_state')
00058 
00059     def getToolByName(self, tool):
00060         return getToolByName(getSite(), tool)
00061 
00062     @property
00063     def action(self):
00064         folder = self.context
00065         if not self.context_state.is_structural_folder():
00066             folder = utils.parent(self.context)
00067         return folder.absolute_url() + '/folder_contents'
00068 
00069     @memoize
00070     def available(self):
00071         actions_tool = self.getToolByName('portal_actions')
00072         editActions = actions_tool.listActionInfos(object=aq_inner(self.context), categories=('object_buttons',), max=1)
00073         return len(editActions) > 0
00074 
00075     def selected(self):
00076         return False
00077 
00078 
00079 class ActionsMenu(BrowserMenu):
00080     implements(IActionsMenu)
00081 
00082     def getMenuItems(self, context, request):
00083         """Return menu item entries in a TAL-friendly form."""
00084         results = []
00085 
00086         portal_state = getMultiAdapter((context, request), name='plone_portal_state')
00087 
00088         actions_tool = getToolByName(aq_inner(context), 'portal_actions')
00089         editActions = actions_tool.listActionInfos(object=aq_inner(context), categories=('object_buttons',))
00090 
00091         if not editActions:
00092             return []
00093 
00094         plone_utils = getToolByName(context, 'plone_utils')
00095         portal_url = portal_state.portal_url()
00096 
00097         for action in editActions:
00098             if action['allowed']:
00099                 cssClass = 'actionicon-object_buttons-%s' % action['id']
00100                 icon = plone_utils.getIconFor('object_buttons', action['id'], None)
00101                 if icon:
00102                     icon = '%s/%s' % (portal_url, icon)
00103 
00104                 results.append({ 'title'       : action['title'],
00105                                  'description' : '',
00106                                  'action'      : action['url'],
00107                                  'selected'    : False,
00108                                  'icon'        : icon,
00109                                  'extra'       : {'id': action['id'], 'separator': None, 'class': cssClass},
00110                                  'submenu'     : None,
00111                                  })
00112 
00113         return results
00114 
00115 
00116 class DisplaySubMenuItem(BrowserSubMenuItem):
00117     implements(IDisplaySubMenuItem)
00118 
00119     title = _(u'label_choose_template', default=u'Display')
00120     submenuId = 'plone_contentmenu_display'
00121 
00122     order = 20
00123 
00124     def __init__(self, context, request):
00125         BrowserSubMenuItem.__init__(self, context, request)
00126         self.context_state = getMultiAdapter((context, request), name='plone_context_state')
00127 
00128     @property
00129     def extra(self):
00130         return {'id': 'plone-contentmenu-display', 'disabled': self.disabled()}
00131 
00132     @property
00133     def description(self):
00134         if self.disabled():
00135             return _(u'title_remove_index_html_for_display_control', default=u'Delete or rename the index_html item to gain full control over how this folder is displayed.')
00136         else:
00137             return _(u'title_choose_default_view', default=u'Select the view mode for this folder, or set a content item as its default view.')
00138 
00139     @property
00140     def action(self):
00141         if self.disabled():
00142             return ''
00143         else:
00144             if self.context_state.is_default_page():
00145                 return self.context_state.parent().absolute_url() + '/select_default_view'
00146             else:
00147                 return self.context.absolute_url() + '/select_default_view'
00148 
00149     @memoize
00150     def available(self):
00151         if self.disabled():
00152             return False
00153 
00154         isDefaultPage = self.context_state.is_default_page()
00155 
00156         folder = None
00157         context = None
00158 
00159         folderLayouts = []
00160         contextLayouts = []
00161 
00162         # If this is a default page, also get menu items relative to the parent
00163         if isDefaultPage:
00164             folder = ISelectableBrowserDefault(utils.parent(self.context), None)
00165 
00166         context = ISelectableBrowserDefault(self.context, None)
00167 
00168         folderLayouts = []
00169         folderCanSetLayout = False
00170         folderCanSetDefaultPage = False
00171 
00172         if folder is not None:
00173             folderLayouts = folder.getAvailableLayouts()
00174             folderCanSetLayout = folder.canSetLayout()
00175             folderCanSetDefaultPage = folder.canSetDefaultPage()
00176 
00177         contextLayouts = []
00178         contextCanSetLayout = False
00179         contextCanSetDefaultPage = False
00180 
00181         if context is not None:
00182             contextLayouts = context.getAvailableLayouts()
00183             contextCanSetLayout = context.canSetLayout()
00184             contextCanSetDefaultPage = context.canSetDefaultPage()
00185 
00186         # Show the menu if we either can set a default-page, or we have more
00187         # than one layout to choose from.
00188         if (folderCanSetDefaultPage) or \
00189            (folderCanSetLayout and len(folderLayouts) > 1) or \
00190            (folder is None and contextCanSetDefaultPage) or \
00191            (contextCanSetLayout and len(contextLayouts) > 1):
00192             return True
00193         else:
00194             return False
00195 
00196     def selected(self):
00197         return False
00198 
00199     @memoize
00200     def disabled(self):
00201         context = self.context
00202         if self.context_state.is_default_page():
00203             context = utils.parent(context)
00204         if not getattr(context, 'isPrincipiaFolderish', False):
00205             return False
00206         elif 'index_html' not in context.objectIds():
00207             return False
00208         else:
00209             return True
00210 
00211 
00212 class DisplayMenu(BrowserMenu):
00213     implements(IDisplayMenu)
00214 
00215     def getMenuItems(self, obj, request):
00216         """Return menu item entries in a TAL-friendly form."""
00217         results = []
00218 
00219         context_state = getMultiAdapter((obj, request), name='plone_context_state')
00220         isDefaultPage = context_state.is_default_page()
00221 
00222         parent = None
00223 
00224         folder = None
00225         context = None
00226 
00227         folderLayouts = []
00228         contextLayouts = []
00229 
00230         # If this is a default page, also get menu items relative to the parent
00231         if isDefaultPage:
00232             parent = utils.parent(obj)
00233             folder = ISelectableBrowserDefault(parent, None)
00234 
00235         context = ISelectableBrowserDefault(obj, None)
00236 
00237         folderLayouts = []
00238         folderCanSetLayout = False
00239         folderCanSetDefaultPage = False
00240 
00241         if folder is not None:
00242             folderLayouts = folder.getAvailableLayouts()
00243             folderCanSetLayout = folder.canSetLayout()
00244             folderCanSetDefaultPage = folder.canSetDefaultPage()
00245 
00246         contextLayouts = []
00247         contextCanSetLayout = False
00248         contextCanSetDefaultPage = False
00249 
00250         if context is not None:
00251             contextLayouts = context.getAvailableLayouts()
00252             contextCanSetLayout = context.canSetLayout()
00253             contextCanSetDefaultPage = context.canSetDefaultPage()
00254 
00255         # Short circuit if neither folder nor object will provide us with
00256         # items
00257         if not (folderCanSetLayout or folderCanSetDefaultPage or \
00258                 contextCanSetLayout or contextCanSetDefaultPage):
00259             return []
00260 
00261         # Only show the block "Folder display" and "Item display" separators if
00262         # they are necessars
00263         useSeparators = False
00264         if folderCanSetLayout or folderCanSetDefaultPage:
00265             if (contextCanSetLayout and len(contextLayouts) > 1) or \
00266                 contextCanSetDefaultPage:
00267                 useSeparators = True
00268 
00269         # 1. If this is a default-page, first render folder options
00270         if folder is not None:
00271             folderUrl = parent.absolute_url()
00272 
00273             if useSeparators:
00274                 results.append({ 'title'       : _(u'label_current_folder_views', default=u'Folder display'),
00275                                  'description' : '',
00276                                  'action'      : None,
00277                                  'selected'    : False,
00278                                  'icon'        : None,
00279                                  'extra'       : {'id': 'folderHeader', 'separator': 'actionSeparator', 'class': ''},
00280                                  'submenu'     : None,
00281                                  })
00282 
00283             if folderCanSetLayout:
00284                 for id, title in folderLayouts:
00285                     results.append({ 'title'       : title,
00286                                      'description' : '',
00287                                      'action'      : '%s/selectViewTemplate?templateId=%s' % (folderUrl, id,),
00288                                      'selected'    : False,
00289                                      'icon'        : None,
00290                                      'extra'       : {'id': id, 'separator': None, 'class': ''},
00291                                      'submenu'     : None,
00292                                      })
00293             # Display the selected item (i.e. the context)
00294             results.append({ 'title'       : _(u'label_item_selected', default=u'Item: ${contentitem}', mapping={'contentitem' : _safe_unicode(obj.Title())}),
00295                              'description' : '',
00296                              'action'      : None,
00297                              'selected'    : True,
00298                              'icon'        : None,
00299                              'extra'       : {'id': 'folderDefaultPageDisplay', 'separator': 'actionSeparator', 'class': 'actionMenuSelected'},
00300                              'submenu'     : None,
00301                              })
00302             # Let the user change the selection
00303             if folderCanSetDefaultPage:
00304                 results.append({ 'title'       : _(u'label_change_default_item', default=u'Change content item as default view...'),
00305                                  'description' : _(u'title_change_default_view_item', default=u'Change the item used as default view in this folder'),
00306                                  'action'      : '%s/select_default_page' % (folderUrl,),
00307                                  'selected'    : False,
00308                                  'icon'        : None,
00309                                  'extra'       : {'id': 'folderChangeDefaultPage', 'separator': 'actionSeparator', 'class': ''},
00310                                  'submenu'     : None,
00311                                  })
00312 
00313         # 2. Render context options
00314         if context is not None:
00315             contextUrl = obj.absolute_url()
00316             selected = context.getLayout()
00317             defaultPage = context.getDefaultPage()
00318             layouts = context.getAvailableLayouts()
00319 
00320             if useSeparators:
00321                 results.append({ 'title'       : _(u'label_current_item_views', default=u'Item display'),
00322                                  'description' : '',
00323                                  'action'      : None,
00324                                  'selected'    : False,
00325                                  'icon'        : None,
00326                                  'extra'       : {'id': 'contextHeader', 'separator': 'actionSeparator', 'class': ''},
00327                                  'submenu'     : None,
00328                                  })
00329 
00330             # If context is a default-page in a folder, that folder's views will
00331             # be shown. Only show context views if there are any to show.
00332 
00333             showLayouts = False
00334             if not isDefaultPage:
00335                 showLayouts = True
00336             elif len(layouts) > 1:
00337                 showLayouts = True
00338 
00339             if showLayouts and contextCanSetLayout:
00340                 for id, title in contextLayouts:
00341                     results.append({ 'title'       : title,
00342                                      'description' : '',
00343                                      'action'      : '%s/selectViewTemplate?templateId=%s' % (contextUrl, id,),
00344                                      'selected'    : (defaultPage is None and id == selected),
00345                                      'icon'        : None,
00346                                      'extra'       : {'id': id, 'separator': None, 'class': ''},
00347                                      'submenu'     : None,
00348                                      })
00349 
00350             # Allow setting / changing the default-page, unless this is a
00351             # default-page in a parent folder.
00352             if not INonStructuralFolder.providedBy(obj):
00353                 if defaultPage is None:
00354                     if contextCanSetDefaultPage:
00355                         results.append({ 'title'       : _(u'label_choose_item', default=u'Select a content item\nas default view...'),
00356                                          'description' : _(u'title_select_default_view_item', default=u'Select an item to be used as default view in this folder...'),
00357                                          'action'      : '%s/select_default_page' % (contextUrl,),
00358                                          'selected'    : False,
00359                                          'icon'        : None,
00360                                          'extra'       : {'id': 'contextSetDefaultPage', 'separator': 'actionSeparator', 'class': ''},
00361                                          'submenu'     : None,
00362                                          })
00363                 else:
00364                     defaultPageObj = getattr(obj, defaultPage, None)
00365                     defaultPageTitle = u""
00366                     if defaultPageObj is not None:
00367                         if hasattr(aq_base(defaultPageObj), 'Title'):
00368                             defaultPageTitle = defaultPageObj.Title()
00369                         else:
00370                             defaultPageTitle = getattr(aq_base(defaultPageObj), 'title', u'')
00371 
00372                     results.append({ 'title'       : _(u'label_item_selected', default=u'Item: ${contentitem}', mapping={'contentitem' : _safe_unicode(defaultPageTitle)}),
00373                                      'description' : '',
00374                                      'action'      : None,
00375                                      'selected'    : True,
00376                                      'icon'        : None,
00377                                      'extra'       : {'id': 'contextDefaultPageDisplay', 'separator': 'actionSeparator', 'class': ''},
00378                                      'submenu'     : None,
00379                                      })
00380                     if contextCanSetDefaultPage:
00381                         results.append({ 'title'       : _(u'label_change_item', default=u'Change content item\nas default view...'),
00382                                          'description' : _(u'title_change_default_view_item', default=u'Change the item used as default view in this folder'),
00383                                          'action'      : '%s/select_default_page' % (contextUrl,),
00384                                          'selected'    : False,
00385                                          'icon'        : None,
00386                                          'extra'       : {'id': 'contextChangeDefaultPage', 'separator': 'actionSeparator', 'class': ''},
00387                                          'submenu'     : None,
00388                                          })
00389 
00390         return results
00391 
00392 
00393 class FactoriesSubMenuItem(BrowserSubMenuItem):
00394     implements(IFactoriesSubMenuItem)
00395 
00396     submenuId = 'plone_contentmenu_factory'
00397     order = 30
00398 
00399     def __init__(self, context, request):
00400         BrowserSubMenuItem.__init__(self, context, request)
00401         self.context_state = getMultiAdapter((context, request), name='plone_context_state')
00402 
00403     @property
00404     def extra(self):
00405         return {'id': 'plone-contentmenu-factories',
00406                 'hideChildren': self._hideChildren()}
00407 
00408     @property
00409     def title(self):
00410         itemsToAdd = self._itemsToAdd()
00411         showConstrainOptions = self._showConstrainOptions()
00412         if showConstrainOptions or len(itemsToAdd) > 1:
00413             return _(u'label_add_new_item', default=u'Add new\u2026')
00414         elif len(itemsToAdd) == 1:
00415             fti=itemsToAdd[0][1]
00416             title = fti.Title()
00417             if isinstance(title, Message):
00418                 title = translate(title, context=self.request)
00419             else:
00420                 title = translate(_safe_unicode(title),
00421                                   domain='plone',
00422                                   context=self.request)
00423             return _(u'label_add_type', default='Add ${type}',
00424                      mapping={'type' : title})
00425         else:
00426             return _(u'label_add_new_item', default=u'Add new\u2026')
00427 
00428     @property
00429     def description(self):
00430         itemsToAdd = self._itemsToAdd()
00431         showConstrainOptions = self._showConstrainOptions()
00432         if showConstrainOptions or len(itemsToAdd) > 1:
00433             return _(u'title_add_new_items_inside_item', default=u'Add new items inside this item')
00434         elif len(itemsToAdd) == 1:
00435             return itemsToAdd[0][1].Description()
00436         else:
00437             return _(u'title_add_new_items_inside_item', default=u'Add new items inside this item')
00438 
00439     @property
00440     def action(self):
00441         addContext = self._addContext()
00442         if self._hideChildren():
00443             (addContext, fti) = self._itemsToAdd()[0]
00444             baseUrl = addContext.absolute_url()
00445             addingview = queryMultiAdapter((addContext, self.request), name='+')
00446             if addingview is not None:
00447                 addview = queryMultiAdapter((addingview, self.request), name=fti.factory)
00448                 if addview is not None:
00449                     return '%s/+/%s' % (baseUrl, fti.factory,)
00450             return '%s/createObject?type_name=%s' % (baseUrl, quote_plus(fti.getId()),)
00451         else:
00452             return '%s/folder_factories' % self.context_state.folder().absolute_url()
00453 
00454     @property
00455     def icon(self):
00456         if self._hideChildren():
00457             fti = self._itemsToAdd()[0][1]
00458             return fti.getIcon()
00459         else:
00460             return None
00461 
00462     def available(self):
00463         itemsToAdd = self._itemsToAdd()
00464         showConstrainOptions = self._showConstrainOptions()
00465         if self._addingToParent() and not self.context_state.is_default_page():
00466             return False
00467         return (len(itemsToAdd) > 0 or showConstrainOptions)
00468 
00469     def selected(self):
00470         return False
00471 
00472     @memoize
00473     def _addContext(self):
00474         return self.context_state.folder()
00475 
00476     @memoize
00477     def _itemsToAdd(self):
00478         context=self.context_state.folder()
00479         return [(context, fti) for fti in self._addableTypesInContext(context)]
00480 
00481     def _addableTypesInContext(self, addContext):
00482         allowed_types = _allowedTypes(self.request, addContext)
00483         constrain = IConstrainTypes(addContext, None)
00484         if constrain is None:
00485             return allowed_types
00486         else:
00487             locallyAllowed = constrain.getLocallyAllowedTypes()
00488             return [fti for fti in allowed_types if fti.getId() in locallyAllowed]
00489 
00490     @memoize
00491     def _addingToParent(self):
00492         return (self._addContext().absolute_url() != self.context.absolute_url())
00493 
00494     @memoize
00495     def _showConstrainOptions(self):
00496         addContext = self._addContext()
00497         constrain = ISelectableConstrainTypes(addContext, None)
00498         if constrain is None:
00499             return False
00500         elif constrain.canSetConstrainTypes() and constrain.getDefaultAddableTypes():
00501             return True
00502         elif len(constrain.getLocallyAllowedTypes()) < len(constrain.getImmediatelyAddableTypes()):
00503             return True
00504 
00505     @memoize
00506     def _hideChildren(self):
00507         itemsToAdd = self._itemsToAdd()
00508         showConstrainOptions = self._showConstrainOptions()
00509         return (len(itemsToAdd) == 1 and not showConstrainOptions)
00510 
00511 
00512 class FactoriesMenu(BrowserMenu):
00513     implements(IFactoriesMenu)
00514 
00515     def getMenuItems(self, context, request):
00516         """Return menu item entries in a TAL-friendly form."""
00517         factories_view = getMultiAdapter((context, request), name='folder_factories')
00518 
00519         haveMore = False
00520         include = None
00521 
00522         addContext = factories_view.add_context()
00523         allowedTypes = _allowedTypes(request, addContext)
00524 
00525         constraints = IConstrainTypes(addContext, None)
00526         if constraints is not None:
00527             include = constraints.getImmediatelyAddableTypes()
00528             if len(include) < len(allowedTypes):
00529                 haveMore = True
00530 
00531         results = factories_view.addable_types(include=include)
00532 
00533         if haveMore:
00534             url = '%s/folder_factories' % (addContext.absolute_url(),)
00535             results.append({ 'title'       : _(u'folder_add_more', default=u'More\u2026'),
00536                              'description' : _(u'Show all available content types'),
00537                              'action'      : url,
00538                              'selected'    : False,
00539                              'icon'        : None,
00540                              'extra'       : {'id': 'more', 'separator': None, 'class': ''},
00541                              'submenu'     : None,
00542                             })
00543 
00544         constraints = ISelectableConstrainTypes(addContext, None)
00545         if constraints is not None:
00546             if constraints.canSetConstrainTypes() and constraints.getDefaultAddableTypes():
00547                 url = '%s/folder_constraintypes_form' % (addContext.absolute_url(),)
00548                 results.append({'title'       : _(u'folder_add_settings', default=u'Restrictions\u2026'),
00549                                 'description' : _(u'title_configure_addable_content_types', default=u'Configure which content types can be added here'),
00550                                 'action'      : url,
00551                                 'selected'    : False,
00552                                 'icon'        : None,
00553                                 'extra'       : {'id': 'settings', 'separator': None, 'class': ''},
00554                                 'submenu'     : None,
00555                                 })
00556 
00557         return results
00558 
00559 
00560 class WorkflowSubMenuItem(BrowserSubMenuItem):
00561     implements(IWorkflowSubMenuItem)
00562 
00563     MANAGE_SETTINGS_PERMISSION = 'Manage portal'
00564 
00565     title = _(u'label_state', default=u'State:')
00566     submenuId = 'plone_contentmenu_workflow'
00567     order = 40
00568 
00569     def __init__(self, context, request):
00570         BrowserSubMenuItem.__init__(self, context, request)
00571         self.tools = getMultiAdapter((context, request), name='plone_tools')
00572         self.context = context
00573         self.context_state = getMultiAdapter((context, request), name='plone_context_state')
00574 
00575     @property
00576     def extra(self):
00577         state = self.context_state.workflow_state()
00578         stateTitle = self._currentStateTitle()
00579         return {'id'         : 'plone-contentmenu-workflow',
00580                 'class'      : 'state-%s' % state,
00581                 'state'      : state,
00582                 'stateTitle' : stateTitle,}
00583 
00584     @property
00585     def description(self):
00586         if self._manageSettings() or len(self._transitions()) > 0:
00587             return _(u'title_change_state_of_item', default=u'Change the state of this item')
00588         else:
00589             return u''
00590 
00591     @property
00592     def action(self):
00593         if self._manageSettings() or len(self._transitions()) > 0:
00594             return self.context.absolute_url() + '/content_status_history'
00595         else:
00596             return ''
00597 
00598     @memoize
00599     def available(self):
00600         return (self.context_state.workflow_state() is not None)
00601 
00602     def selected(self):
00603         return False
00604 
00605     @memoize
00606     def _manageSettings(self):
00607         return self.tools.membership().checkPermission(WorkflowSubMenuItem.MANAGE_SETTINGS_PERMISSION, self.context)
00608 
00609     @memoize
00610     def _transitions(self):
00611         wf_tool = getToolByName(aq_inner(self.context), 'portal_workflow')
00612         return wf_tool.listActionInfos(object=aq_inner(self.context), max=1)
00613 
00614     @memoize
00615     def _currentStateTitle(self):
00616         state = self.context_state.workflow_state()
00617         workflows = self.tools.workflow().getWorkflowsFor(self.context)
00618         if workflows:
00619             for w in workflows:
00620                 if w.states.has_key(state):
00621                     return w.states[state].title or state
00622 
00623 
00624 class WorkflowMenu(BrowserMenu):
00625     implements(IWorkflowMenu)
00626 
00627     # BBB: These actions (url's) existed in old workflow definitions
00628     # but were never used. The scripts they reference don't exist in
00629     # a standard installation. We allow the menu to fail gracefully
00630     # if these are encountered.
00631 
00632     BOGUS_WORKFLOW_ACTIONS = (
00633         'content_hide_form',
00634         'content_publish_form',
00635         'content_reject_form',
00636         'content_retract_form',
00637         'content_show_form',
00638         'content_submit_form',
00639     )
00640 
00641     def getMenuItems(self, context, request):
00642         """Return menu item entries in a TAL-friendly form."""
00643         results = []
00644         context = aq_inner(context)
00645 
00646         wf_tool = getToolByName(context, 'portal_workflow')
00647         workflowActions = wf_tool.listActionInfos(object=context)
00648 
00649         locking_info = queryMultiAdapter((context, request), name='plone_lock_info')
00650         if locking_info and locking_info.is_locked_for_current_user():
00651             return []
00652 
00653         for action in workflowActions:
00654             if action['category'] != 'workflow':
00655                 continue
00656 
00657             cssClass = 'kssIgnore'
00658             actionUrl = action['url']
00659             if actionUrl == "":
00660                 actionUrl = '%s/content_status_modify?workflow_action=%s' % (context.absolute_url(), action['id'])
00661                 cssClass = ''
00662 
00663             description = ''
00664 
00665             transition = action.get('transition', None)
00666             if transition is not None:
00667                 description = transition.description
00668 
00669             for bogus in self.BOGUS_WORKFLOW_ACTIONS:
00670                 if actionUrl.endswith(bogus):
00671                     if getattr(context, bogus, None) is None:
00672                         actionUrl = '%s/content_status_modify?workflow_action=%s' % (context.absolute_url(), action['id'],)
00673                         cssClass =''
00674                     break
00675 
00676             if action['allowed']:
00677                 results.append({ 'title'       : action['title'],
00678                                  'description' : description,
00679                                  'action'      : actionUrl,
00680                                  'selected'    : False,
00681                                  'icon'        : None,
00682                                  'extra'       : {'id': 'workflow-transition-%s' % action['id'], 'separator': None, 'class': cssClass},
00683                                  'submenu'     : None,
00684                                  })
00685 
00686         url = context.absolute_url()
00687 
00688         if len(results) > 0:
00689             results.append({ 'title'        : _(u'label_advanced', default=u'Advanced...'),
00690                              'description'  : '',
00691                              'action'       : url + '/content_status_history',
00692                              'selected'     : False,
00693                              'icon'         : None,
00694                              'extra'        : {'id': 'advanced', 'separator': 'actionSeparator', 'class': 'kssIgnore'},
00695                              'submenu'      : None,
00696                             })
00697 
00698         if getToolByName(context, 'portal_placeful_workflow', None) is not None:
00699             results.append({ 'title'       : _(u'workflow_policy', default=u'Policy...'),
00700                              'description' : '',
00701                              'action'      : url + '/placeful_workflow_configuration',
00702                              'selected'    : False,
00703                              'icon'        : None,
00704                              'extra'       : {'id': 'policy', 'separator': None, 'class': 'kssIgnore'},
00705                              'submenu'     : None,
00706                             })
00707 
00708         return results