Skip to content

Instantly share code, notes, and snippets.

@fredvd
Last active October 8, 2022 14:47
Show Gist options
  • Save fredvd/9b2a99d544d8bd95664414a0ff2e7667 to your computer and use it in GitHub Desktop.
Save fredvd/9b2a99d544d8bd95664414a0ff2e7667 to your computer and use it in GitHub Desktop.
TPN10: Change portlet rendering order and restrict editing portlets to a PortletManager Role
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
xmlns:i18n="http://namespaces.zope.org/i18n"
xmlns:plone="http://namespaces.plone.org/plone"
i18n_domain="test.site">
<i18n:registerTranslations directory="locales" />
<!--
Be careful if you use general includeDependencies, it can have side effects!
Better import explicit packages or configurations ;)
-->
<!--<includeDependencies package="." />-->
<include package=".browser" />
<include file="permissions.zcml" />
<genericsetup:registerProfile
name="default"
title="test.site"
directory="profiles/default"
description="Installs the test.site add-on."
provides="Products.GenericSetup.interfaces.EXTENSION"
post_handler=".setuphandlers.post_install"
/>
<genericsetup:registerProfile
name="uninstall"
title="test.site (uninstall)"
directory="profiles/uninstall"
description="Uninstalls the test.site add-on."
provides="Products.GenericSetup.interfaces.EXTENSION"
post_handler=".setuphandlers.uninstall"
/>
<utility
factory=".setuphandlers.HiddenProfiles"
name="test.site-hiddenprofiles"
/>
<!-- -*- extra stuff goes here -*- -->
<adapter factory=".portletretriever.ReversePortletRetriever" />
<!-- Extra local roles managed in the @@sharing view -->
<utility
name="PortletManager"
factory=".roles.PortletManagerRole"
/>
</configure>
from zope.component import adapter
from zope.interface import implementer
from plone.portlets.retriever import PortletRetriever
from Products.CMFCore.interfaces._content import IContentish
from plone.portlets.interfaces import IPortletManager
from plone.portlets.interfaces import IPortletRetriever
from itertools import groupby
@adapter(IContentish, IPortletManager)
@implementer(IPortletRetriever)
class ReversePortletRetriever(PortletRetriever):
"""Custom retriever to reorder context portlets
default portlet retriever."""
def getPortlets(self):
""" Reshuffle the assignments so that the inherited context portlets
are displayed in reverse order, but keep portlet-ordering on each
context level intact
"""
# the assigments are one flat listing, but have a category and a key
# the key contains the path on which the assignments are made.
# we have to pick out the context category, there reverse all
# assignments after we have grouped them first by key, then
# reassemble the flat assignment list and lastly add possible group,
# user, or contenttype assignments with order unchanged.
assignments = super(ReversePortletRetriever,self).getPortlets()
if not assignments:
return []
# only reverse right column, return otherwise
elif self.storage.__name__ != 'plone.rightcolumn':
return assignments
elif assignments[0]['category'] != 'context':
# shortcut if we have no context portlets as first in the list
return assignments
# get all context portlets
on_category = [(cat, list(ass)) for cat, ass in
groupby(assignments, lambda a: a['category'])]
context_portlets = on_category[0][1]
# sort on path_key and reverse each path_key group
on_key = [(key, list(ass)) for key, ass in
groupby(context_portlets, lambda a: a['key'])]
on_key.reverse()
new_assignments = []
for key,ass_list in on_key:
new_assignments.extend(ass_list)
# append other portlet categories (group, user, contenttype) if we had
# them at all
if len(on_category) > 1:
for cat, ass_list in on_category[1:]:
new_assignments.extend(ass_list)
return new_assignments
<?xml version="1.0"?>
<rolemap>
<roles>
<role name="PortletManager"/>
</roles>
<permissions>
<!-- Portlets -->
<permission name="Portlets: Manage portlets"
acquire="True">
<role name="Manager"/>
<role name="PortletManager"/>
<role name="Site Administrator"/>
</permission>
<permission name="Portlets: Manage own portlets"
acquire="True">
<role name="Manager"/>
<role name="Member"/>
<role name="PortletManager"/>
<role name="Site Administrator"/>
</permission>
<permission name="plone.portlet.collection: Add collection portlet"
acquire="True">
<role name="Manager"/>
<role name="PortletManager"/>
<role name="Site Administrator"/>
</permission>
<permission name="plone.portlet.static: Add static portlet"
acquire="True">
<role name="Manager"/>
<role name="PortletManager"/>
<role name="Site Administrator"/>
</permission>
</permissions>
</rolemap>
"""Defines and tests a new PortletManager role.
A new PortletManager role is added to plone's default list::
>>> 'PortletManager' in list(self.portal.__ac_roles__)
True
"""
from zope.interface import implementer
from plone.app.workflow.interfaces import ISharingPageRole
@implementer(ISharingPageRole)
class PortletManagerRole(object):
title = "Can manage portlets"
required_permission = "Manage portal"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment