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 Plone.org.
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/ploneview.py 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 ZCAto 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.zcmlfile in the icons directory of the plone.app.layout package.
They are (leading dots stand for "here", i.e. plone.app.layout.icons):
- .icons.CatalogBrainContentIcon
- .icons.CMFContentIcon
- .icons.FTIContentIcon
- .icons.PloneSiteContentIcon
- .icons.DefaultContentIcon
We will override DefaultContentIcon and CatalogBrainContentIcon, for two reasons:
- It seems to work for our task.
- 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/icon.py:
from zope.interface import implements from Acquisition import aq_inner from Products.CMFCore.utils import getToolByName from plone.app.layout.icons.interfaces import IContentIcon from plone.app.layout.icons.icons import BaseIcon from plone.app.layout.icons.icons import CatalogBrainContentIcon from plone.app.layout.icons.icons import DefaultContentIcon class wrappedIconMixin: width = 64 height = 64 class BigContentIcon(wrappedIconMixin,DefaultContentIcon): """ """ implements(IContentIcon) 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 Overrides plone.app.layout.icons.icons.CatalogBrainContentIcon 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 Plone.org 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/interfaces.py
And we register it in the genericsetup profile with the file profiles/default/browserlayer.xml:
<layers> <layer name="gropen.plonebigicons" interface="gropen.plonebigicons.browser.interfaces.bigIconLayer" /> </layers>
So in the line from ploneview.py:
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:
<configure xmlns="http://namespaces.zope.org/zope" xmlns:browser="http://namespaces.zope.org/browser" xmlns:plone="http://namespaces.plone.org/plone" i18n_domain="gropen.plonebigicons"> <adapter for="* .interfaces.bigIconLayer *" factory=".icon.BigContentIcon" provides="plone.app.layout.icons.interfaces.IContentIcon" /> <adapter for="* .interfaces.bigIconLayer Products.ZCatalog.CatalogBrains.AbstractCatalogBrain" factory=".icon.CatalogBrainBigContentIcon" provides="plone.app.layout.icons.interfaces.IContentIcon" /> </configure>
Don't forget Extensions/Install.py and the setuptools dependency as suggested on "customization for developers" on Plone.org
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 Plone.org customization for developers):
<browser:page
for="*"
class="some_class"
name="a_name"
template="a_template.pt"
layer=".interfaces.bigIconLayer"
permission="some_permission" />