Portlet

« Return to page index

Plone Theme Reference

1. Anatomy of a Portlet

The bits that go to make up a portlet renderer (which is the bit of a portlet you'll want to customize).

Customizing a portlet is similar to overriding a viewlet, but rather more straightforward. There is a specific ZCML directive for customization.

Directive in ZCML

<plone:portletRenderer />

Attributes in ZCML

layer
a marker interface for your particular theme
portlet
the interface of the portlet you wish to customize
template
location of the template you wish to override
class
your custom class (use this if you don't specify a template) for rendering the portlet

2. Moving, Removing or Hiding a Portlet

Some tips on moving or hiding portlets.

Whether or not portlets appear on your site is highly customizable through the web, you can use the manage portlets link in most contexts. For more information:

It's assumed that you wouldn't want to fix portlets on a page (otherwise they'd probably be viewlets). However, if you wish to set up an initial assignment of portlets on installation of your theme product, use

  • [your theme package]/profiles/default/portlets.xml.

Here's an extract from the Plone Default portlets.xml, setting up the login and navigation portlet for the left-hand column, and the review and news portlets for the right-hand column.

<?xml version="1.0"?>
<portlets
    xmlns:i18n="http://xml.zope.org/namespaces/i18n"
    i18n:domain="plone">

 
 <!-- Assign the standard portlets -->
 
 <assignment
     manager="plone.leftcolumn"
     category="context"
     key="/"
     type="portlets.Navigation"
     name="navigation"
     />
 
 <assignment
     manager="plone.leftcolumn"
     category="context"
     key="/"
     type="portlets.Login"
     name="login"
     />
     
  <assignment
     manager="plone.rightcolumn"
     category="context"
     key="/"
     type="portlets.Review"
     name="review"
     />

 <assignment
     manager="plone.rightcolumn"
     category="context"
     key="/"
     type="portlets.News"
     name="news"
     />
     
 
</portlets>

 The attributes for the assignment directive are described in full here: http://plone.org/products/plone/roadmap/203/.  In brief:

manager and type
The names for these can be looked up in plone.app.portlets.portlets.configure.zcml (for type of portlet) or in the Plone Default profiles/default/portlets.xml file.
category
This can be one of four values "context", "content_type", "group" or "user" - depending on where you wish to assign your portlets.
key
This will depend on the value given in category above. In the case of "context", the location in the site is indicated (the examples above specify the site root).

If you wish to configure the portlet in more detail, you can nest property directives within the assignment directive. Here's a tweak to ensure the navigation portlet appears at the root of the site:

<assignment name="navigation" category="context" key="/"
    manager="plone.leftcolumn" type="portlets.Navigation">
     <property name="topLevel">0</property>
 </assignment>

3. Overriding a Portlet

A quick cheat sheet of how to override or customize a portlet.

Through the Web

  • Use Site Setup > Zope Management Interfaces > portal_view_customizations to customize the template of an existing Plone Default portlet.

In your own product

There is a detailed tutorial available here:

You can also look up the details of the portlet you want to override in the Elements section of this manual.

Quick Cheat Sheet

You will need to know the name of

Your theme-specific interface
This is optional but ensures that your portlet is only available for your theme. If you used the plone3_theme paster template, then the name will probably be IThemeSpecific.

You will need to create the following (you should be able to locate the originals to copy by looking them up in the Elements section):

plone portlet renderer directive
[your theme package]/browser/configure.zcml
page template
[your theme package]/browser/[your template name].pt
Python class *
[your theme package]/browser/[your module name].py

* in most cases you won't need a Python class

Sample configuration.zcml directive

<configure 
    xmlns:plone="http://namespaces.plone.org/plone">
    <include package="plone.app.portlets"  />
    <plone:portletRenderer
       portlet="[element interface]"
       template="[your template name].pt"
      (or class=".[your module].[your class name]")
      layer="[your theme specific interface]"
     />
</configure>

Sample Python class for navigation portlet override

If you want to customize the navigation portlet, you may need to supply the class as well as the template. Two templates are involved: the first is the usual display template; the second handles the recursion through the navigation tree. If you need to make your own version of the second, then you'll need to assign it to the recurse method in the class.

from plone.app.portlets.portlets.navigation import Renderer
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile

class [your class name](Renderer):
    _template = ViewPageTemplateFile([your template name].pt)  
    recurse = ViewPageTemplateFile([your recurse template name])

4. Override the portlets in Plone 3.0

Customizing the portlets is a regular task, working with Plone theme. In this how-to we will find out how-to do this in Plone 3.0 with it's new mechanism for managing portlets (contributed by Denys Mishunov)

Purpose

It was pretty easy to customize one of the standard portlet in times of Plone versions prior 3.0. You just had to copy a page template for appropriate portlet to your theme product and do whatever you want, changing it's XHTML. You could also create a new portlet the same easy way - just create a template for the new portlet and register this portlet with your site's Properties.

In Plone 3.0 portlets became slightly more complex. But don't be afraid. They became much more powerful at the same time! The advantage becomes obvious now, right? ;) They are served from separate python package and have really flexible management possibilities. So, it's worth to try this new mechanism to realize how powerful it is.

Prerequisities

First thing we should mention - this is not about TTW (Through The Web) customization. If you need a fast and dirty trick, have a look at portal_view_customizations tool. This how-to assumes you want to have your changes in a repeatable way, so that you could have the same changes on any site where you install your product.

Another thing you might consider is worth a mentioning - you don't need this technique in any case you want to customize a portlet in Plone 3.0. If you have hardly customized portlets used in previous versions of Plone or you want to continue using portlets in "pre-3.0-era-way" (that we strongly don't recommend) you might need to have a look at ClassicPortlet that is shipped with Plone 3.0. It deals with the regular page templates the same way you have worked with portlets before Plone 3.0.

And the last before we move on. If you want to customize any of the standard portlets removing all back-end logic from it (making a static portlet), don't do this. We mean that - don't do this. Wise people thought about you. Better install plone.portlet.static and play with it, creating the static portlets when you need it.

So, for all those who are still with us... we move on finally.

We assume you have created MyTheme Plone 3 theme with either DIYPloneStyle or ZopeSkel generators. We do not cover explanations of all aspects of creating a theme for Plone 3.0. To find out more about core ideas of making a theme, managing the viewlets in Plone 3.0 and many more, check an excellent tutorial by David Convent - Customizing the viewlets in main_template.

The key concept in working with portlets in Plone 3.0 is the use of Zope 3 skin layer - the same as we have when overriding a viewlet. We assume you have at least the following minimum set of files in MyTheme/browser folder:

- browser/
- __init__.py
- configure.zcml
- interfaces.py

In common, portlets' overriding process looks like this:

  • choose the portlet you want to override;
  • register a skin layer if you don't have one yet in interfaces.py;
  • add the special <plone:portletRenderer/> directive to MyTheme/browser/configure.zcml;
  • define portlet attribute for <plone:portletRenderer /> directive. This is a portlet data provider type for which this override is used. This can be either class or interface. For example plone.app.portlets.portlets.navigation.INewsPortlet;
  • define a new template attribute for <plone:portletRenderer /> directive. When you add this the default renderer for portlet you are overriding will be used, but with your template;
  • in case you need to customize the default behavior for the portlet, you should use class attribute instead of simple template. This new class will be acting as the renderer for the portlet instead of the default one;
  • define layer attribute for <plone:portletRenderer /> directive with MyTheme browser layer. The layer attribute of the portletRenderer attribute associates a particular IPortletRenderer with a particular browser layer (MyTheme layer in our case). When our layer is in effect (i.e. MyTheme is installed) the new renderer will be used instead of the default one;
  • add a new template to MyTheme/browser that will implement the renderer;
  • restart Zope and enjoy.

Step by step

1. Choose the portlet

First of all we should decide what portlet we would like to customize. Let's choose the News portlet. If you will have a look at the standard news portlet, you will see those news_icon images in-front of the titles. Let's get rid of them in the XHTML just for the test.

Plone default portlets are declared in the plone.app.portlets.portlets package. The core Plone 3.0 portlets can be found in $INSTANCE_HOME/lib/python/plone/app/portlets/portlets/. It might be located somewhere else in the $PYTHONPATH though. Depending on the zope installation (win32 or unix like operating system, installation from source, installer, eggs or else…), you may need to use the search tools available in your operating system to locate the package.

plone.app.portlets.portlets package contains python modules, page templates and ZCML configuration file - configure.zcml. This file contains a set of <plone:portlet /> directives that define the standard portlets like this:

<plone:portlet
name="portlets.News"
interface=".news.INewsPortlet"
assignment=".news.Assignment"
renderer=".news.Renderer"
addview=".news.AddForm"
editview=".news.EditForm"
/>

Attributes in the above code are pretty self-explanatory. But if they are not clear to you or you want to know more about additional attributes for <plone:portlet />, have a look at IPortletDirective interface in metadirectives module inside the plone.app.portlets package.

2. Register a skin layer if you don't have one yet

We can register an override for a portlet only for one theme (one skin selection) thanks to the plone.theme package. Thanks to plone.theme, we can set a Zope 3 skin layer that corresponds to a particular skin selection in portal_skins (a theme).

Add the following code to MyTheme/browser/interfaces.py if you don't have it yet:

from plone.theme.interfaces import IDefaultPloneLayer

class IThemeSpecific(IDefaultPloneLayer):
"""Marker interface that defines a Zope 3 skin layer bound to a Skin
Selection in portal_skins.
"""

3. Add the directive to configure.zcml with appropriate attributes

Along with <plone:portlet /> directive, plone.app.portlets package defines another one - <plone:portletRenderer />. The last one is used to override the portlets, defined in your site. It has quite a few possible attributes that can be found in metadirectives module inside the plone.app.portlets package. We will not list them all here, so just spend 5 minutes and have a look at those attributes, so that you could understand the following code...

... 5 minutes later...

Ok, let's get back to work. So, to override the standard News portlet for MyTheme product we should add <plone:portletRenderer /> directive to MyTheme/browser/configure.zcml. Let's have a look how this should look like (be sure you have xmlns:plone="http://namespaces.plone.org/plone" namespace defined in your <configure> top node.):

<include package="plone.app.portlets" />

<interface
interface=".interfaces.IThemeSpecific"
type="zope.publisher.interfaces.browser.IBrowserSkinType"
name="My Theme"
/>

<plone:portletRenderer
portlet="plone.app.portlets.portlets.news.INewsPortlet"
template="mytheme_news.pt"
layer=".interfaces.IThemeSpecific"
/>

First of all we include plone.app.portlets package to be sure that default portlets are enabled before we override anything.

Then we make browser layer interface for MyTheme, defined in MyTheme/browser/interfaces.py, available. If you have customized any viewlet you should already have this in configure.zcml so no need to add it twice in the same theme.

Next, let's sort out what attributes we use here:

  • portlet - define the portlet that we are going to override. In our case we define the full dotted path to INewsPortlet interface, that is implemented by news portlet;
  • template - the name of a template that implements the renderer. The default renderer for this news portlet will be used, but with "mytheme_news.pt template instead of the default one.
  • layer - our browser layer for which this renderer is used.
  • one more attribute you might need to remember here is class. You will need to use it in case you want to change the default behavior of the portlet. This attribute will define the class that will be used as a renderer for this portlet instead of the default one.

That's it with configure.zcml. Let's move on.

4. Add a new template for portlet's renderer

So, in previous part we have defined mytheme_news.pt as a value for template attribute. But we don't have that template on file-system. Let's add it to MyTheme/browser/. Just copy news.pt template for news portlet from plone.app.portlets.portlets to MyTheme/browser/ and rename it to mytheme_news.pt. Open this template in your favorite editor and let's play with it a little bit.

As you remember we should get rid of standard news_icon.gif icons we get for news items by default. Find the following line in your template:

<img tal:replace="structure item_icon/html_tag" />

and comment it out so that we do not un-recoverable steps and could revert our changes later. So, we get:

<!-- <img tal:replace="structure item_icon/html_tag" /> -->

That's all folks!

So, that's it. Restart your Zope and have a look at your news items portlet - no images! Cool! Yeah! Actually not that cool just to remove the images, that might be useful for community portals :)

What's next?

This example is really simple and not pretty useful for sure. But you definitely can do much better customizations now. When using class attribute in <plone:portletRenderer/> directive you can do portlets that will really differ from default one. And that's where the beauty of portlets in Plone 3.0 goes - you will not need to put a load of python to your page templates as you had to do before. All python will be exactly where it should be - in python class. And template will just get the results from different python methods within that class.

Enjoy!