Back to index

plone3  3.1.7
ploneview.py
Go to the documentation of this file.
00001 from urllib import unquote
00002 
00003 from Acquisition import aq_inner
00004 from Products.Five import BrowserView
00005 from Products.CMFCore.permissions import AddPortalContent
00006 from Products.CMFCore.permissions import DeleteObjects
00007 from Products.CMFCore.permissions import ListFolderContents
00008 from Products.CMFCore.permissions import ModifyPortalContent
00009 from Products.CMFCore.permissions import ReviewPortalContent
00010 from Products.CMFCore.utils import _checkPermission
00011 from Products.CMFCore.utils import getToolByName
00012 from Products.CMFPlone import utils
00013 from Products.CMFPlone.browser.interfaces import IPlone
00014 
00015 from zope.deprecation import deprecate, deprecated
00016 from zope.interface import implements, alsoProvides
00017 from zope.component import getMultiAdapter, queryMultiAdapter, getUtility
00018 
00019 import ZTUtils
00020 import sys
00021 
00022 from plone.memoize.view import memoize
00023 from plone.portlets.interfaces import IPortletManager, IPortletManagerRenderer
00024 
00025 from plone.app.layout.globals.interfaces import IViewView
00026 from plone.app.layout.icons.interfaces import IContentIcon
00027 
00028 from plone.app.content.browser.folderfactories import _allowedTypes
00029 
00030 deprecated(
00031     ('IndexIterator'),
00032     "This reference to IndexIterator will be removed in Plone 4.0. Please "
00033     "import it from Products.CMFPlone.utils instead.")
00034 
00035 IndexIterator = utils.IndexIterator
00036 
00037 _marker = []
00038 
00039 import zope.deferredimport
00040 zope.deferredimport.deprecated(
00041     "It has been replaced by plone.memoize.instance.memoize. This alias will " 
00042     "be removed in Plone 4.0.",
00043     cache_decorator = 'plone.memoize.instance:memoize',
00044     )
00045 
00046 class Plone(BrowserView):
00047     implements(IPlone)
00048 
00049     def globalize(self):
00050         """
00051         Pure optimization hack, globalizes entire view for speed. Yes
00052         it's evil, but this hack will eventually be removed after
00053         globals are officially deprecated.
00054 
00055         YOU CAN ONLY CALL THIS METHOD FROM A PAGE TEMPLATE AND EVEN
00056         THEN IT MIGHT DESTROY YOU!
00057         """
00058         context = sys._getframe(2).f_locals['econtext']
00059         # Some of the original global_defines used 'options' to get parameters
00060         # passed in through the template call, so we need this to support
00061         # products which may have used this little hack
00062         options = context.vars.get('options',{})
00063         view = context.vars.get('view', None)
00064         template = context.vars.get('template', None)
00065 
00066         state = {}
00067         self._initializeData(options=options, view=view, template=template)
00068         for name, v in self._data.items():
00069             state[name] = v
00070             context.setGlobal(name, v)
00071 
00072     def __init__(self, context, request):
00073         super(Plone, self).__init__(context, request)
00074         self._data = {}
00075 
00076     def _initializeData(self, options=None, view=None, template=None):
00077         # We don't want to do this in __init__ because the view provides
00078         # methods which are useful outside of globals.  Also, performing
00079         # actions during __init__ is dangerous because instances are usually
00080         # created during traversal, which means authentication hasn't yet
00081         # happened.
00082         context = aq_inner(self.context)
00083         if options is None:
00084             options = {}
00085         if view is None:
00086             view = self
00087 
00088         show_portlets = not options.get('no_portlets', False)
00089         def_actions = options.get('actions', None)
00090 
00091         # XXX: Can't store data as attributes directly because it will
00092         # insert the view into the acquisition chain. Someone should
00093         # come up with a way to prevent this or get rid of the globals
00094         # view altogether
00095 
00096         tools = getMultiAdapter((context, self.request), name=u'plone_tools')
00097         portal_state = getMultiAdapter((context, self.request), name=u'plone_portal_state')
00098         context_state = getMultiAdapter((context, self.request), name=u'plone_context_state')
00099 
00100         self._data['utool'] = utool = tools.url()
00101         self._data['portal'] = portal = portal_state.portal()
00102         self._data['portal_url'] =  portal_state.portal_url()
00103         self._data['mtool'] = mtool = tools.membership()
00104         self._data['atool'] = atool = tools.actions()
00105         self._data['putils'] = putils = getToolByName(context, "plone_utils")
00106         self._data['acl_users'] = getToolByName(context, 'acl_users')
00107         self._data['wtool'] = wtool = tools.workflow()
00108         self._data['ifacetool'] = tools.interface()
00109         self._data['syntool'] = tools.syndication()
00110         self._data['portal_title'] = portal_state.portal_title()
00111         self._data['object_title'] = context_state.object_title()
00112         self._data['checkPermission'] = checkPermission = mtool.checkPermission
00113         self._data['member'] = portal_state.member()
00114         self._data['membersfolder'] =  mtool.getMembersFolder()
00115         self._data['isAnon'] =  portal_state.anonymous()
00116         self._data['actions'] = actions = def_actions or context_state.actions()
00117         self._data['keyed_actions'] =  def_actions or context_state.keyed_actions()
00118         self._data['user_actions'] =  actions['user']
00119         self._data['workflow_actions'] =  actions['workflow']
00120         self._data['folder_actions'] =  actions['folder']
00121         self._data['global_actions'] =  actions['global']
00122 
00123         portal_tabs_view = getMultiAdapter((context, context.REQUEST), name='portal_tabs_view')
00124         self._data['portal_tabs'] =  portal_tabs_view.topLevelTabs(actions=actions)
00125 
00126         self._data['wf_state'] =  context_state.workflow_state()
00127         self._data['portal_properties'] = props = tools.properties()
00128         self._data['site_properties'] = site_props = props.site_properties
00129         self._data['ztu'] =  ZTUtils
00130         self._data['isFolderish'] =  context_state.is_folderish()
00131         
00132         self._data['sl'] = have_left_portlets = show_portlets and self.have_portlets('plone.leftcolumn', view)
00133         self._data['sr'] = have_right_portlets = show_portlets and self.have_portlets('plone.rightcolumn', view)
00134         self._data['hidecolumns'] =  self.hide_columns(have_left_portlets, have_right_portlets)
00135         
00136         self._data['here_url'] =  context_state.object_url()
00137         self._data['default_language'] = portal_state.default_language()
00138         self._data['language'] =  portal_state.language()
00139         self._data['is_editable'] = context_state.is_editable()
00140         self._data['isLocked'] = context_state.is_locked()
00141         self._data['isRTL'] =  portal_state.is_rtl()
00142         self._data['visible_ids'] =  self.visibleIdsEnabled() or None
00143         self._data['current_page_url'] =  context_state.current_page_url()
00144         self._data['normalizeString'] = putils.normalizeString
00145         self._data['toLocalizedTime'] = self.toLocalizedTime
00146         self._data['isStructuralFolder'] = context_state.is_structural_folder()
00147         self._data['isContextDefaultPage'] = context_state.is_default_page()
00148 
00149         self._data['navigation_root_url'] = portal_state.navigation_root_url()
00150         self._data['Iterator'] = utils.IndexIterator
00151         self._data['tabindex'] = utils.IndexIterator(pos=30000, mainSlot=False)
00152         self._data['uniqueItemIndex'] = utils.RealIndexIterator(pos=0)
00153 
00154         template_id = options.get('template_id', None)
00155         if template_id is None and template is not None:
00156             template_id = template.getId()
00157         self._data['template_id'] = template_id
00158         
00159         isViewTemplate = context_state.is_view_template()
00160         if isViewTemplate and not IViewView.providedBy(view):
00161             # Mark the view as being "the" view
00162             alsoProvides(view, IViewView)
00163             
00164         self._data['isViewTemplate'] = isViewTemplate
00165 
00166     # XXX: This is lame
00167     def hide_columns(self, column_left, column_right):
00168         if not column_right and not column_left:
00169             return "visualColumnHideOneTwo"
00170         if column_right and not column_left:
00171             return "visualColumnHideOne"
00172         if not column_right and column_left:
00173             return "visualColumnHideTwo"
00174         return "visualColumnHideNone"
00175 
00176     # Utility methods
00177     
00178     def toLocalizedTime(self, time, long_format=None):
00179         """Convert time to localized time
00180         """
00181         context = aq_inner(self.context)
00182         util = getToolByName(context, 'translation_service')
00183         return util.ulocalized_time(time, long_format, context=context,
00184                                     domain='plonelocales', request=self.request)
00185     
00186     @memoize
00187     def visibleIdsEnabled(self):
00188         """Determine if visible ids are enabled
00189         """
00190         context = aq_inner(self.context)
00191         props = getToolByName(context, "portal_properties").site_properties
00192         if not props.getProperty('visible_ids', False):
00193             return False
00194 
00195         pm = getToolByName(context, "portal_membership")
00196         if pm.isAnonymousUser():
00197             return False
00198 
00199         user = pm.getAuthenticatedMember()
00200         if user is not None:
00201             return user.getProperty('visible_ids', False)
00202         return False
00203     
00204     @memoize
00205     def prepareObjectTabs(self, default_tab='view', sort_first=['folderContents']):
00206         """Prepare the object tabs by determining their order and working
00207         out which tab is selected. Used in global_contentviews.pt
00208         """
00209         context = aq_inner(self.context)
00210         context_url = context.absolute_url()
00211         context_fti = context.getTypeInfo()
00212         
00213         site_properties = getToolByName(context, "portal_properties").site_properties
00214 
00215         context_state = getMultiAdapter((context, self.request), name=u'plone_context_state')
00216         actions = context_state.actions()
00217 
00218         action_list = []
00219         if context_state.is_structural_folder():
00220             action_list = actions['folder'] + actions['object']
00221         else:
00222             action_list = actions['object']
00223 
00224         tabs = []
00225         
00226         found_selected = False
00227         fallback_action = None
00228 
00229         request_url = self.request['ACTUAL_URL']
00230         request_url_path = request_url[len(context_url):]
00231         
00232         if request_url_path.startswith('/'):
00233             request_url_path = request_url_path[1:]
00234 
00235         for action in action_list:
00236             
00237             item = {'title'    : action['title'],
00238                     'id'       : action['id'],
00239                     'url'      : '',
00240                     'selected' : False}
00241 
00242             action_url = action['url'].strip()
00243             if action_url.startswith('http') or action_url.startswith('javascript'):
00244                 item['url'] = action_url
00245             else:
00246                 item['url'] = '%s/%s'%(context_url, action_url)
00247 
00248             action_method = item['url'].split('/')[-1]
00249 
00250             # Action method may be a method alias: Attempt to resolve to a template.
00251             action_method = context_fti.queryMethodID(action_method, default=action_method)
00252             if action_method:
00253                 request_action = unquote(request_url_path)
00254                 request_action = context_fti.queryMethodID(request_action, default=request_action)
00255     
00256                 if action_method == request_action:
00257                     item['selected'] = True
00258                     found_selected = True
00259 
00260             current_id = item['id']
00261             if current_id == default_tab:
00262                 fallback_action = item
00263 
00264             tabs.append(item)
00265 
00266         if not found_selected and fallback_action is not None:
00267             fallback_action['selected'] = True
00268 
00269         def sortOrder(tab):
00270             try:
00271                 return sort_first.index(tab['id'])
00272             except ValueError:
00273                 return 255
00274 
00275         tabs.sort(key=sortOrder)
00276         return tabs
00277 
00278     # XXX: This can't be request-memoized, because it won't necessarily remain
00279     # valid across traversals. For example, you may get tabs on an error 
00280     # message. :)
00281     # 
00282     # @memoize
00283     def showEditableBorder(self):
00284         """Determine if the editable border should be shown
00285         """
00286         request = self.request
00287         
00288         if request.has_key('disable_border'): #short circuit
00289             return False
00290         if request.has_key('enable_border'): #short circuit
00291             return True
00292         
00293         context = aq_inner(self.context)
00294         
00295         portal_membership = getToolByName(context, 'portal_membership')
00296         checkPerm = portal_membership.checkPermission
00297 
00298         if checkPerm('Modify portal content', context) or \
00299                checkPerm('Add portal content', context)  or \
00300                checkPerm('Review portal content', context):
00301             return True
00302 
00303         if portal_membership.isAnonymousUser():
00304             return False
00305 
00306         context_state = getMultiAdapter((context, request), name="plone_context_state")
00307         actions = context_state.actions()
00308             
00309         if actions.get('workflow', ()):
00310             return True
00311 
00312         if actions.get('batch', []):
00313             return True
00314             
00315         for action in actions.get('object', []):
00316             if action.get('id', '') != 'view':
00317                 return True
00318 
00319         template_id = self._data.get('template_id', None)
00320         if template_id is None and 'PUBLISHED' in request:
00321             if hasattr(request['PUBLISHED'], 'getId'):
00322                 template_id=request['PUBLISHED'].getId()
00323 
00324         idActions = {}
00325         for obj in actions.get('object', ()) + actions.get('folder', ()):
00326             idActions[obj.get('id', '')] = 1
00327 
00328         if idActions.has_key('edit'):
00329             if (idActions.has_key(template_id) or \
00330                 template_id in ['synPropertiesForm', 'folder_contents', 'folder_listing']) :
00331                 return True
00332 
00333         # Check to see if the user is able to add content or change workflow state
00334         if _allowedTypes(request, context):
00335             return True
00336 
00337         return False
00338     
00339     @memoize
00340     def displayContentsTab(self):
00341         """Whether or not the contents tabs should be displayed
00342         """
00343         context = aq_inner(self.context)
00344         modification_permissions = (ModifyPortalContent,
00345                                     AddPortalContent,
00346                                     DeleteObjects,
00347                                     ReviewPortalContent)
00348 
00349         contents_object = context
00350         # If this object is the parent folder's default page, then the
00351         # folder_contents action is for the parent, we check permissions
00352         # there. Otherwise, if the object is not folderish, we don not display
00353         # the tab.
00354         if self.isDefaultPageInFolder():
00355             contents_object = self.getCurrentFolder()
00356         elif not self.isStructuralFolder():
00357             return 0
00358 
00359         # If this is not a structural folder, stop.
00360         plone_view = getMultiAdapter((contents_object, self.request),
00361                                      name='plone')
00362         if not plone_view.isStructuralFolder():
00363             return 0
00364 
00365         show = 0
00366         # We only want to show the 'contents' action under the following
00367         # conditions:
00368         # - If you have permission to list the contents of the relavant
00369         #   object, and you can DO SOMETHING in a folder_contents view. i.e.
00370         #   Copy or Move, or Modify portal content, Add portal content,
00371         #   or Delete objects.
00372 
00373         # Require 'List folder contents' on the current object
00374         if _checkPermission(ListFolderContents, contents_object):
00375             # If any modifications are allowed on object show the tab.
00376             for permission in modification_permissions:
00377                 if _checkPermission(permission, contents_object):
00378                     show = 1
00379                     break
00380 
00381         return show
00382 
00383     @memoize
00384     def icons_visible(self):
00385         context = aq_inner(self.context)
00386         membership = getToolByName(context, "portal_membership")
00387         properties = getToolByName(context, "portal_properties")
00388 
00389         site_properties = getattr(properties, 'site_properties')
00390         icon_visibility = site_properties.getProperty('icon_visibility', 'enabled')
00391 
00392         if icon_visibility == 'enabled':
00393             return True
00394         elif icon_visibility == 'authenticated' and not membership.isAnonymousUser():
00395             return True
00396         else:
00397             return False
00398 
00399     def getIcon(self, item):
00400         """Returns an object which implements the IContentIcon interface and
00401            provides the informations necessary to render an icon.
00402            The item parameter needs to be adaptable to IContentIcon.
00403            Icons can be disabled globally or just for anonymous users with
00404            the icon_visibility property in site_properties."""
00405         context = aq_inner(self.context)
00406         if not self.icons_visible():
00407             icon = getMultiAdapter((context, self.request, None), IContentIcon)
00408         else:
00409             icon = getMultiAdapter((context, self.request, item), IContentIcon)
00410         return icon
00411 
00412     def normalizeString(self, text, relaxed=False):
00413         """Normalizes a title to an id.
00414         """
00415         return utils.normalizeString(text, context=self, relaxed=relaxed)
00416 
00417     def cropText(self, text, length, ellipsis='...'):
00418         """Crop text on a word boundary
00419         """
00420         converted = False
00421         if not isinstance(text, unicode):
00422             encoding = utils.getSiteEncoding(aq_inner(self.context))
00423             text = unicode(text, encoding)
00424             converted = True
00425         if len(text)>length:
00426             text = text[:length]
00427             l = text.rfind(' ')
00428             if l > length/2:
00429                 text = text[:l+1]
00430             text += ellipsis
00431         if converted:
00432             # encode back from unicode
00433             text = text.encode(encoding)
00434         return text
00435 
00436     # Deprecated in favour of the @@plone_context_state and @@plone_portal_state views
00437 
00438     @deprecate("The keyFilteredActions method of the Plone view has been "
00439                "deprecated and will be removed in Plone 4.0. Use the "
00440                "keyed_actions method of the plone_context_state adapter "
00441                "instead.")
00442     def keyFilteredActions(self, actions=None):
00443         context_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_context_state')
00444         return context_state.keyed_actions()
00445 
00446     def getCurrentUrl(self):
00447         context_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_context_state')
00448         return context_state.current_page_url()
00449 
00450     @deprecate("The isRightToLeft method of the Plone view has been "
00451                "deprecated and will be removed in Plone 4.0. Use the "
00452                "is_rtl method of the plone_portal_state adapter instead.")
00453     def isRightToLeft(self, domain='plone'):
00454         portal_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_portal_state')
00455         return portal_state.is_rtl()
00456 
00457     def isDefaultPageInFolder(self):
00458         context_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_context_state')
00459         return context_state.is_default_page()
00460 
00461     def isStructuralFolder(self):
00462         context_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_context_state')
00463         return context_state.is_structural_folder()
00464 
00465     def navigationRootPath(self):
00466         portal_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_portal_state')
00467         return portal_state.navigation_root_path()
00468 
00469     def navigationRootUrl(self):
00470         portal_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_portal_state')
00471         return portal_state.navigation_root_url()
00472 
00473     def getParentObject(self):
00474         context_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_context_state')
00475         return context_state.parent()
00476 
00477     def getCurrentFolder(self):
00478         context_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_context_state')
00479         return context_state.folder()
00480 
00481     def getCurrentFolderUrl(self):
00482         context_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_context_state')
00483         return context_state.folder().absolute_url()
00484 
00485     @memoize
00486     def getCurrentObjectUrl(self):
00487         context_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_context_state')
00488         return context_state.canonical_object_url()
00489 
00490     @memoize
00491     def isFolderOrFolderDefaultPage(self):
00492         context_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_context_state')
00493         return context_state.is_structural_folder() or context_state.is_default_page()
00494 
00495     @memoize
00496     def isPortalOrPortalDefaultPage(self):
00497         context_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_context_state')
00498         return context_state.is_portal_root()
00499         
00500     @memoize
00501     def getViewTemplateId(self):
00502         context_state = getMultiAdapter((aq_inner(self.context), self.request), name=u'plone_context_state')
00503         return context_state.view_template_id()
00504 
00505     # Helper methods
00506     def have_portlets(self, manager_name, view=None):
00507         """Determine whether a column should be shown.
00508         The left column is called plone.leftcolumn; the right column is called
00509         plone.rightcolumn. Custom skins may have more portlet managers defined
00510         (see portlets.xml).
00511         """
00512         
00513         context = aq_inner(self.context)
00514         if view is None:
00515             view = self
00516 
00517         manager = getUtility(IPortletManager, name=manager_name)
00518         renderer = queryMultiAdapter((context, self.request, view, manager), IPortletManagerRenderer)
00519         if renderer is None:
00520             renderer = getMultiAdapter((context, self.request, self, manager), IPortletManagerRenderer)
00521 
00522         return renderer.visible