diff --git a/CHANGES.rst b/CHANGES.rst index 4d9079f7..dde2e5a2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ Changelog ========= -2.6.6 (unreleased) +2.7.0 (unreleased) ------------------ Breaking changes: @@ -13,12 +13,21 @@ New features: - Do not render social metadata if you are a logged user. [bsuttor] +- Add method ``is_toolbar_visible`` to context state. + It uses a whitelist and defaults to authenticated users. + Use new method it in toolbar viewlet manager and layout body classes. + [jensens] + Bug fixes: - Deprecating getIcon() in @@plone_layout see https://github.com/plone/Products.CMFPlone/issues/1734 [fgrcon] +- Factor toolbar classes out to own method. + Includes minor cleanup. + [jensens] + 2.6.5 (2017-03-29) ------------------ diff --git a/plone/app/layout/globals/context.py b/plone/app/layout/globals/context.py index cd865de0..6b45b97d 100644 --- a/plone/app/layout/globals/context.py +++ b/plone/app/layout/globals/context.py @@ -22,6 +22,7 @@ BLACKLISTED_PROVIDERS = ('portal_workflow', ) BLACKLISTED_CATEGORIES = ('folder_buttons', 'object_buttons', ) +WHITELISTED_TOOLBAR_ROLES = set(('Authenticated', )) @implementer(IContextState) @@ -201,7 +202,7 @@ def is_portal_root(self): @memoize def is_editable(self): - tool = getToolByName(self.context, "portal_membership") + tool = getToolByName(self.context, 'portal_membership') return bool(tool.checkPermission( 'Modify portal content', aq_inner(self.context) @@ -221,6 +222,13 @@ def is_locked(self): context.aq_explicit, 'wl_isLocked', None) is not None return lockable and context.wl_isLocked() + @memoize + def is_toolbar_visible(self): + portal_membership = getToolByName(self.context, 'portal_membership') + user = portal_membership.getAuthenticatedMember() + roles = set(user.getRolesInContext(self.context)) + return bool(roles & WHITELISTED_TOOLBAR_ROLES) + @memoize def actions(self, category=None, max=-1): context = aq_inner(self.context) diff --git a/plone/app/layout/globals/interfaces.py b/plone/app/layout/globals/interfaces.py index bd72e535..1c7f6e98 100644 --- a/plone/app/layout/globals/interfaces.py +++ b/plone/app/layout/globals/interfaces.py @@ -68,7 +68,8 @@ def icons_visible(): def getIcon(item): """ - deprecated for Plone > 5.0 see https://github.com/plone/Products.CMFPlone/issues/1151 + deprecated for Plone > 5.0 see + https://github.com/plone/Products.CMFPlone/issues/1151 Returns an object which implements the IContentIcon interface and provides the informations necessary to render an icon. The item parameter needs to be adaptable to IContentIcon. Icons can be disabled @@ -260,6 +261,10 @@ def is_locked(): """Whether or not the current object is locked """ + def is_toolbar_visible(): + """Wether toolbar is visible or not in the actual context + """ + def actions(category): """The filtered actions in the context. You can restrict the actions to just one category. @@ -283,6 +288,7 @@ class IPatternsSettingsRenderer(Interface): DEPRECATED """ + IPatternsSettingsRenderer = deprecated( IPatternsSettingsRenderer, 'This interface was deprecated because it was pointless.' diff --git a/plone/app/layout/globals/layout.py b/plone/app/layout/globals/layout.py index 25e13c1a..88cc15a8 100644 --- a/plone/app/layout/globals/layout.py +++ b/plone/app/layout/globals/layout.py @@ -25,10 +25,24 @@ import json +TEMPLATE_CLASSES = ( + ViewPageTemplateFile, + ZopeViewPageTemplateFile, + ViewMixinForTemplates +) + + @implementer(ILayoutPolicy) class LayoutPolicy(BrowserView): """A view that gives access to various layout related functions. """ + @property + @memoize + def _context_state(self): + return getMultiAdapter( + (self.context, self.request), + name='plone_context_state' + ) def mark_view(self, view): """Adds a marker interface to the view if it is "the" view for the @@ -36,11 +50,10 @@ def mark_view(self, view): """ if not view: return - - context_state = getMultiAdapter( - (self.context, self.request), name=u'plone_context_state') - - if context_state.is_view_template() and not IViewView.providedBy(view): + if ( + self._context_state.is_view_template() and + not IViewView.providedBy(view) + ): alsoProvides(view, IViewView) def hide_columns(self, column_left, column_right): @@ -116,9 +129,12 @@ def thumb_visible(self): return True else: return False - @deprecate('deprecated since Plone 4, ContentIcons are rendered \ - as Fonts now see \ - https://docs.plone.org/develop/addons/index.html#upgrading-to-plone-5-1.') + + @deprecate( + 'deprecated since Plone 4, ContentIcons are rendered as Fonts now see' + 'https://docs.plone.org/develop/addons/index.html' + '#upgrading-to-plone-5-1.' + ) def getIcon(self, item): """Returns an object which implements the IContentIcon interface and provides the informations necessary to render an icon. The item @@ -133,6 +149,47 @@ def getIcon(self, item): icon = getMultiAdapter((context, self.request, item), IContentIcon) return icon + def _toolbar_classes(self): + """current toolbar controlling classes + """ + if not self._context_state.is_toolbar_visible(): + return [] + + toolbar_classes = [] + registry = getUtility(IRegistry) + site_settings = registry.forInterface( + ISiteSchema, + prefix='plone', + check=False + ) + try: + left = site_settings.toolbar_position == 'side' + except KeyError: + left = True + if left: + toolbar_classes.append('plone-toolbar-left') + else: + toolbar_classes.append('plone-toolbar-top') + try: + toolbar_state = {} + toolbar_state_cookie = self.request.cookies.get('plone-toolbar') + if toolbar_state_cookie: + toolbar_state = json.loads(toolbar_state_cookie) + if toolbar_state.get('expanded', True): + toolbar_classes.append('plone-toolbar-expanded') + if left: + toolbar_classes.append('plone-toolbar-left-expanded') + else: + toolbar_classes.append('plone-toolbar-top-expanded') + else: + if left: + toolbar_classes.append('plone-toolbar-left-default') + else: + toolbar_classes.append('plone-toolbar-top-default') + except Exception: + pass + return toolbar_classes + def bodyClass(self, template, view): """ Returns the CSS class to be used on the body tag. @@ -160,21 +217,18 @@ def bodyClass(self, template, view): - plone-toolbar-top-default: top toolbar is not expanded - pat-markspeciallinks: mark special links is set """ - context = self.context portal_state = getMultiAdapter( - (context, self.request), + (self.context, self.request), name=u'plone_portal_state' ) normalizer = queryUtility(IIDNormalizer) registry = getUtility(IRegistry) - body_classes = [] + body_classes = self._toolbar_classes() # template class (required) template_name = '' - if isinstance(template, ViewPageTemplateFile) or \ - isinstance(template, ZopeViewPageTemplateFile) or \ - isinstance(template, ViewMixinForTemplates): + if isinstance(template, TEMPLATE_CLASSES): # Browser view template_name = view.__name__ elif template is not None: @@ -187,7 +241,7 @@ def bodyClass(self, template, view): body_classes.append('template-%s' % template_name) # portal type class (optional) - portal_type = normalizer.normalize(context.portal_type) + portal_type = normalizer.normalize(self.context.portal_type) if portal_type: body_classes.append("portaltype-%s" % portal_type) @@ -195,14 +249,16 @@ def bodyClass(self, template, view): navroot = portal_state.navigation_root() body_classes.append("site-%s" % navroot.getId()) - contentPath = context.getPhysicalPath()[ + contentPath = self.context.getPhysicalPath()[ len(navroot.getPhysicalPath()):] if contentPath: body_classes.append("section-%s" % contentPath[0]) # skip first section since we already have that... if len(contentPath) > 1: depth = registry.get( - 'plone.app.layout.globals.bodyClass.depth', 4) + 'plone.app.layout.globals.bodyClass.depth', + 4 + ) if depth > 1: classes = ['subsection-%s' % contentPath[1]] for section in contentPath[2:depth]: @@ -234,54 +290,25 @@ def bodyClass(self, template, view): body_classes.append('viewpermission-' + permission) # class for user roles - membership = getToolByName(context, "portal_membership") + membership = getToolByName(self.context, "portal_membership") if membership.isAnonymousUser(): body_classes.append('userrole-anonymous') else: user = membership.getAuthenticatedMember() for role in user.getRolesInContext(self.context): body_classes.append( - 'userrole-' + role.lower().replace(' ', '-')) - - # toolbar classes - site_settings = registry.forInterface( - ISiteSchema, prefix='plone', check=False - ) - try: - left = site_settings.toolbar_position == 'side' - except KeyError: - left = True - if left: - body_classes.append('plone-toolbar-left') - else: - body_classes.append('plone-toolbar-top') - try: - toolbar_state = self.request.cookies.get('plone-toolbar') - if toolbar_state: - toolbar_state = json.loads(toolbar_state) - else: - toolbar_state = {'expanded': True} - if toolbar_state.get('expanded', True): - body_classes.append('plone-toolbar-expanded') - if left: - body_classes.append('plone-toolbar-left-expanded') - else: - body_classes.append('plone-toolbar-top-expanded') - else: - if left: - body_classes.append('plone-toolbar-left-default') - else: - body_classes.append('plone-toolbar-top-default') - except: - pass + 'userrole-' + role.lower().replace(' ', '-') + ) # class for markspeciallinks pattern link_settings = registry.forInterface( - ILinkSchema, prefix="plone", check=False + ILinkSchema, + prefix="plone", + check=False ) msl = link_settings.mark_special_links elonw = link_settings.external_links_open_new_window if msl or elonw: body_classes.append('pat-markspeciallinks') - return ' '.join(body_classes) + return ' '.join(sorted(body_classes)) diff --git a/plone/app/layout/viewlets/toolbar.pt b/plone/app/layout/viewlets/toolbar.pt index 7dd8a68c..5e6a6c52 100644 --- a/plone/app/layout/viewlets/toolbar.pt +++ b/plone/app/layout/viewlets/toolbar.pt @@ -1,7 +1,7 @@