Change icon size in Plone

For a client we had to customize default Plone icon size. The Zope Component Architecture allowed us to do it very easily.

This is not a howto: it is a "note to self" that might be useful to others. Find good documentation about Plone on

There is a related (minimal) egg here.

Plone is great, but customers always want custom-looking sites.

Icons in the navigation portlet can be customized ttw or in a product, because they come from the plone_images skin layer.

But they will always be 16 pixels x 16 pixels images.

If you look at file browser/ of the CMFPlone package line 407 reads:

icon = getMultiAdapter((context, self.request, item), IContentIcon)
context here is the object we are viewing, while item

is the one we want the icon for.

So this code asks the ZCA

to find  the right adapter that will provide the interface IContentIcon for our context, request and object.

There are a five adapters registered in the configure.zcml

file in the icons directory of the package.

They are (leading dots stand for "here", i.e.

  • .icons.CatalogBrainContentIcon
  • .icons.CMFContentIcon
  • .icons.FTIContentIcon
  • .icons.PloneSiteContentIcon
  • .icons.DefaultContentIcon

We will override DefaultContentIcon and CatalogBrainContentIcon, for two reasons:

  1. It seems to work for our task.
  2. We don't know where and how other adapters come to effect (just guess that FTIContentIcon has something to do with add menus).

We build custom versions of them in browser/

from zope.interface import implements
from Acquisition import aq_inner
from Products.CMFCore.utils  import getToolByName
from import IContentIcon
from import BaseIcon
from import CatalogBrainContentIcon
from import DefaultContentIcon

class wrappedIconMixin:
    width  = 64
    height = 64

class BigContentIcon(wrappedIconMixin,DefaultContentIcon):
    def __init__(self, context, request, obj):
        self.context = context
        self.portal_url = getToolByName(aq_inner(context), 'portal_url')()
        self.obj = obj

class CatalogBrainBigContentIcon(wrappedIconMixin,CatalogBrainContentIcon):
    """ Adapts a brain to IContentIcon
        to set with and height = 64

To make things smarter, we could for example use a @property method and return the real width and height of the image object raising from the skin traversal.

TODO: how do we get that object in this context? Or it would be better to implement icon overriding per-object (as you can just paste icons in Mac os x finder file info window) and store the icon somewhere (annotation? local utility?).
Alternative icon sets for Plone would be very nice. I'll Have a look at oxygen or similar projects.

To register an adapter that will be active only when our product is installed we need plone.browserlayer (as explained on documentation).

This will allow us to define a custom interface that will be applied to all  requests to Plone sites where this product is installed.

we define an empty (marker) interface in browser/

And we register it in the genericsetup profile with the file profiles/default/browserlayer.xml:

  <layer name="gropen.plonebigicons" 
  interface="gropen.plonebigicons.browser.interfaces.bigIconLayer" />

So in the line from

icon = getMultiAdapter((context, self.request, item), IContentIcon)

self.request is marked (if this product is installed) with the

gropen.plonebigicons.browser.interfaces.bigIconLayer marker interface.

We need to register an adapter more specific than the one shipped with the default Plone.

Using our marker interface instead of zope.publisher.interfaces.browser.IBrowserRequest is the right thing to make sure we don't mess up with sites where this product is available but not installed.

So our browser/configure.zcml will look like:

  <adapter for="*
           provides="" />
  <adapter for="*
           provides="" />

 Don't forget Extensions/ and the setuptools dependency as suggested on "customization for developers" on

Here we registered an <adapter /> zcml directive.

If we were to customize a <browser:page /> directive we would have used the layer attribute with our marker interface (see customization for developers):

        permission="some_permission" />


« gennaio 2018 »