Context-dependent navigation folder icons

by Alan Runyan last modified Dec 30, 2008 03:01 PM
Change the icons displayed for folders in the navigation portlets depending on whether or not a folder is visible to anonymous visitors.

In the navigation portlet, I would like to show different folder icons depending on the access rights for Anonymous.

Concretely, if for a given folder, Anonymous does not have the View permission, registered Members should see a red folder icon instead of a yellow one. (of course unregistered visitors will not see the folder at all).

  • Adapt portlet_navigation

In (a custom copy of) portal_skins/plone_portlets/portlet_navigation, replace the line:

    <img tal:replace="structure python:path('here/%s' % sibling.getIcon(1))"

with:

    <img tal:replace="structure python:path(here.portal_url.scripts.folder_icon(sibling))"
  • Customise the icon

Create a custom copy of portal_skins/portal_images/folder_icon.gif, rename it to red_folder_icon.gif, upload a version of the icon with the colours altered.

  • Implement the logic in a Script/External Method pair

In your portal_skins/custom folder in the ZMI, or in another skin layer folder, add a "Script (Python)" named "folder_icon" with the following content, and set as Parameter List: 'obj=None':

    canMaybeView = context.anonymousHasRight(obj, 'View')
        or context.anonymousHasRight(obj, 'Access contents information')
        or context.anonymousHasRight(obj, 'Search ZCatalog')
    if canMaybeView:
        return 'here/' + obj.getIcon(1)
    else:
        return 'here/red_' + obj.getIcon(1)

The actual rights comparison needs to be done using an External Method, to avoid rights limitations of non-Manager users. Create a file CheckRights.py on the file system, in the Extensions directory under Zope's installation directory, with the following content:

    def anonymousHasRight(folder, right='View'):
        """
        For a given Plone folder, determine whether Anonymous has the right
        given by parameter 'right' (String). This recurses to parents if
        'Acquire' is checked.
        Returns 1 if Anonymous has the right, 0 otherwise.
        """
        ps = folder.permission_settings()
        for p in ps:
            if (p['name'] == right):
                acquired = not not p['acquire']
                break
        if acquired:
            # recurse upwardly:
            parent = folder.aq_parent
            return anonymousHasRight(parent, right)
        else:
            for p in folder.rolesOfPermission(right):
                if p['name'] == "Anonymous":
                    selected = not not p['selected']
                    return selected

That's it! Really not elegant, but it seems to work. Suggestions for improvement welcome!