Plone Theme Reference

« Return to page index

Plone Theme Reference

1. Introduction

Aims, prerequisites, an overview of the manual and a quick definition of what a Plone theme is.

1.1. Aims and Prerequisites

The aim of this manual is to give you an overview of the theory, tools, and techniques involved in customizing Plone's look and feel or creating your own theme.

Aims

Don't read this manual from beginning to end. Think of it as a guide or phrase book to help you get your bearings in the rather complicated world of Plone themes.

We'll introduce you to the theory, but there's no substitute for practice; so we'll point you to the many excellent tutorials, books, and resources on this site and elsewhere, which will walk you through various aspects of themes for Plone 3. We aim to complement those resources by filling in gaps, providing brief overviews of theory, setting things in context, and giving you a quick reference for bits you find confusing or can't remember clearly from the last time you tried them out.

Prerequisites

This manual is written for integrators and customizers, and we're not assuming any development experience. We do however imagine that you are experienced with XHTML and CSS, know a little about XML, and have some knowledge of scripting languages. We're working from the premise that you are entirely new to Plone, though if you are familiar with Plone 2, you'll find one or two new things.

It will help if you have installed Plone and have looked briefly at the directories that ended up on your file system when you did this. It is also useful to have investigated the Site Setup link on your Plone website and to have clicked through to the Zope Management Interface for a brief look behind the scenes.

1.2. What's a Plone Theme?

A brief description of what we're talking about.

A theme is a collection of page templates, style sheets, components, and configuration settings that go to make up the individual look and feel of a Plone site.

Plone gives you the option of embedding your theme changes and additions into a single site by working through the web. Or, alternatively, by packaging your theme into your own product, you can then install and uninstall it at will and apply it to a number of sites. There are pros and cons to both approaches, and this manual runs through them in later sections.

Plone 3 comes with with two themes:

  • an inbuilt, ready-made theme - Plone Default
  • and an additional optional replacement - NuPlone. 

Things are somewhat different in Plone 4:

  • Two themes are available -  Plone Classic and Sunburst (with Sunburst being the active theme when you first install Plone)
  • Plone Default still exists as the core on which both Plone Classic and Sunburst are built and should prove a useful basis for any theme product
  • NuPlone has been removed but is still available for download if required

If you're at all skeptical about what can be achieved, have a look at the wealth of different sites showcased on plone.net or at the downloadable themes available from the Products section of this site.

If you already have a Plone 3 theme and want to know how to upgrade it to work with Plone 4 then the upgrade guide has further information and guidance.

 

1.3. Overview

Here's a quick overview of what this manual covers.

Mindmap of the Reference Manual

A mind map of the manual, click to enlarge

Section 1: Introduction
A theme is a distinct look and feel for Plone, which is often based structurally on the out-of-the box Default Plone theme.
Section 2: Approaches
What's the best way to go about it - what are the pros and cons of working through the web or on the file system?
Section 3: Tools
What tools are required and what's available to help you build your theme?
Section 4: Building Blocks
There are three main building-blocks in a Plone 3 theme. While there are a few overlaps between them, in general, it helps to see them as discrete entities.
  • skin
  • components
  • configuration
This section will give you an overview of
  • the terminology involved in each of these building blocks
  • the languages you'll need to work with each of them
  • the techniques / approaches required to customize these building blocks or create new ones
  • how you can locate the files you need
Section 5: Putting a Page Together
How is everything pulled together to create a page? We'll look at
  • how a page is constructed
  • how content reaches the page
  • how style sheets and JavaScript reach the page
  • how you can get hold of other information about your site
Sections 6: Elements Reference
There's a quick reference to page elements and a brief summary of how to tackle customization and creation of components.
Section 7: Where is What?
It's often difficult to identify the location of the files you need. This section gives you a quick reference to the file layout of a theme product. There are also pointers to other diagrams on the web which should help you to map the visual page elements to components, templates and styles.

2. Quick Start

Getting started, and some techniques to familiarise yourself with the basics

2.1. Overview

Once you have your new shiny Plone site installed and running, the first thing we recommend you do is try out a few through the web customizations - changing the font colours and replacing the Plone logo with your own.

You probably have greater ambitions than this for the look and feel of your site, but editing the CSS and replacing the logo is a good way of making a start in learning theming techniques.

Prerequisites

We're assuming that you are familiar with HTML and CSS - although the very basic customizations outlined here, don't require much knowledge of that. It will help if, as a manager, you've had a chance to investigate the Site Setup section of Plone.

Outline

First Sharpen Your Pencils
There are a number of tools out there that will make the process of theming Plone a great deal easier, so we run through them here. To see your customizations, you'll also need to be sure that you are running your site in debug/development mode
Next Try Some CSS Customizations
We'll walk you through the process of overriding the page title style, by customizing and editing the ploneCustom.css style sheet. All of this is done through-the-web via the Zope Management Interface.
Finally Replace the Logo
We'll revisit the techniques of customization and CSS editing and expand on them to replace the Plone logo image with your own logo.

 

 


2.2. Sharpen your Pencils

An overview of the tools you'll find useful

2.2.1. Firefox/mozilla UI development tools

Firefox and mozilla have a number of extensions that can greatly help your UI development work. A basic set is listed here.

Using trusty old "view source" used to be the way to debug bad-looking html. Now there are mozilla/firefox extensions that make html development much more productive. A basic set is listed here to get you up to speed.

Web developer
Web developer adds a web development toolbar to your firefox with almost everything you'd want to do or know. CSS info, validation, resizing to test other screen resolutions, converting POSTs to GETs. An essential.
Aardvark
Aardvark, when enabled for a page, shows you the class/id information when you hover over an element. Pressing v for instance, gives you the source for the item you hover over. Start the demo on their site and experiment with the keystrokes. It is a lightweight and elegant tool.
ColorZilla
ColorZilla is surprisingly handy. It does what the name suggest: providing a color picker that displays the hex code of the pixel you hover over in the status bar. There's more: showing the box size of the current box element; showing the element, class and ID of the current element; distance between two points. All in the status bar.
FireBug
FireBug constantly displays the number of errors it finds in your page. Handy during development for finding that mis-spelled css class or the faulty javascript statement. Also includes some css and source examination, but aardvark tends to be a bit handier for that.
X-ray
The x-ray firefox extension is pretty handy in figuring out the layout of a plone site. It displays the tags, the IDs and the classes inline, giving you a surprisingly good idea of what's happening behind the scenes.
View formatted source
View formatted source gives you a well-rendered view of the page's source. More important, when you hover over an opening tag, it shows you the css that gets used for that tag. And with multiple css files (plone anyone?) it shows them in the order in which they're used (and overwritten).
View source with
View source with allows you to right-click on every textarea or source view and select a program to edit/view it with. A bit like ExternalEditor, but then for every textarea. Not 100% developer oriented, but handy for small changes to test css files in the custom skin folder and so anyway.

Another type of useful gadgets are bookmarklets.

Two sources of these are http://squarefree.com and http://slayeroffice.com See for example:

Web development bookmarklets
The Web development bookmarklets provide the same kind of functionality as the web developer toolbar. The JavaScript Shell and JavaScript Development Environment deserve mention.
Mouse-over DOM Inspector
The Mouseover DOM Inspector , or MODI for short, is a favelet (also known as a bookmarklet) that allows you to view and manipulate the DOM of a web page simply by mousing around the document.
Javascript Object Tree Favelet
The Javascript Object Tree Favelet will overlay your current document with a DIV element containing a collapsed list of all the javascript object types currently referenced by the page, from functions to strings to booleans and all else that falls between.
Favelet Suite
This is a favelet that combines most of [the slayeroffice] development favelets . When invoked, a div element will appear in the top left corner of your browser window with a list of all the favelets I've included. Simply click the link you want to invoke the favelet.

2.2.2. How to Make Your CSS Changes Take Effect Instantly

How to ensure that any CSS changes you make can be seen instantly. This is the most common problem themers run into when they are new to Plone.

Enabling development mode

Before beginning any CSS customization you should turn on debug-/development mode. This will guarantee that caching and compression of CSS is disabled. This will also ensure that you can see your changes in real time, after refreshing your browser.

Here is how you enable debug-/development mode:

  1. log on to your Plone site as the 'admin' user
  2. add '/manage' to the URL to access the ZMI
  3. navigate to ZMI > portal_css

In Plone 3:

  1. click the Debug/development mode checkbox
  2. click the "Save" button

In Plone 4:

  1. make sure the Development mode checkbox is clicked - if you've started your Zope instance in development mode then this will automatically be checked
  2. click the "Save" button

When you are finished making your CSS modifications you should disable debug-/development mode as it does impact the performance of your Plone site.

2.3. Change the Font Colours

How to change the font colours - a through-the-web approach.

You'll be introduced to some very simple techniques here for through-the-web customizations of Plone's CSS.

  • How to locate the styles you want to change
  • How to override these styles using the ploneCustom.css style sheet

In this case we'll change the colour of page titles from black to turquoise.

Before you start

For convenience, Plone themes often comprise a number of separate style sheets, but for speed and efficiency, in production mode, Plone has a mechanism (portal_css) for packaging these up into just one or two files.

You'll need to disable this when making changes to your site or customizing CSS. So make sure you've followed the instructions on how to put your site into development/debug mode.

Locating the styles you want to change

  • If you don't already have a page in your Plone site, add one, save it and inspect it in view mode.
  • Use Firebug, or a similar tool, to locate the class name of the page title - in this case its h1.documentFirstHeading.

Locating the ploneCustom.css style sheet

As a matter of course, the last style sheet to load on every Plone page is ploneCustom.css. You'll see this if you inspect the HTML head tag of your page using Firebug. If you dig further, you'll probably find that this style sheet is completely empty. By the rules of precedence in the CSS Cascade, any styles in this sheet will override styles specified in the preceding sheets. So you have a "blank sheet" here for your own customizations.

The trick now is to locate this file, so that you can make it available for editing.

  • To make life easier for yourself, you might like to open a second tab or browser window at this point - you can then quickly switch back to the first tab to see your changes.
  • Go to Site Setup > Zope Management Interface and click portal_skins
  • Use the Find option in the tabs across the top to locate ploneCustom.css:
    • Type ploneCustom.css in the "with ids:" box and click Find
    • You may get more than one result, it doesn't matter which you choose to click on, however best practice is to choose the one flagged with the red asterisk.

Customizing and Editing ploneCustom.css

When you click on ploneCustom.css you'll find that you can't edit it. The next stage is to put the ploneCustom.css in a place where it can be edited. You'll see a Customize option just above the grey text area, click the Customize button and you'll find that the style sheet has been automatically copied to portal_skins/custom.

You're now free to edit the file as you like. To change the colour of our page titles, add:

h1.documentFirstHeading {
    color: #0AAE95;
}

and save.

If you've installed Plone 4 with the Sunburst theme, the ploneCustom.css comes with a number of commented out pre-packaged styles that you might like to experiment with. You can override the layout styles to a fixed width and alter the colours of the links.

Rolling back your changes

You've got a couple of options for reverting back to the original CSS:

  • comment out your styles in the ploneCustom.css - the usual CSS commenting syntax applies
  • delete (or, if you want to keep a note of what you did, rename) your version of ploneCustom.css, you'll find it here:
    • Site Setup > Zope Management Interface > portal_skins > custom
    • you can choose the delete or rename options - try renaming to ploneCustom.css.old
    • you can then go back to the beginning of the process of locating and customizing ploneCustom.css

Further Information

You've actually encountered two types of customization here.

  1. The first is a standard method of using order of precedence - the Cascade - to overwrite CSS styles as they reach the browser.
  2. The second is a Plone/Zope specific method of overriding the style sheets themselves by dropping them into the custom folder of portal_skins. This method can also be used for templates and other resources and is explained in more depth in the section on Skin Layers in this manual.

More advanced techniques, including incorporating your own style sheets into a theme product, are covered later in this manual.

You can find out more about how the CSS Registry (portal_css) packages up the style sheets to deliver them to the page in the Templates and Components to Page section of this manual.

2.4. Change the Logo

How to substitute the standard Plone logo with your own logo - a through-the-web approach.

The Basics

In Plone 3 and 4, the logo is simply an image with a link to the home page of your site wrapped around it (there's just one small difference between versions, the name is logo.jpg in Plone 3, logo.png in Plone 4).

<a id="portal-logo" href="http://[your site]" accesskey="1">
    <img width="252" height="57" title="Plone" alt="" src="http://[your site]/logo.jpg"/>
</a>

If you're happy with this approach, then you won't need to touch the HTML as all the attributes in this snippet are generated automatically. Follow the instructions in Section 1: Changing the Image and its Title.

If you just want to tweak the styles a little bit, then go to Section 2: Changing the portal_logo style.

If you would rather deliver your logo in a different fashion and need to rewrite the HTML, then you can do this by customizing the logo template, follow the instructions in Section 3: Changing the HTML.

1. Changing the Image and its Title

The logo image - logo.jpg (Plone 3) logo.png (Plone 4) - can be found in the plone_images folder in portal_skins. The quickest way to replace this is simply to upload your own image and give it the same name:

  • Go to the Zope Management Interface (Site Setup > Zope Management Interface)
  • Go to portal_skins > plone_images
  • Click logo.jpg (Plone 3) or logo.png (Plone 4) and then click the customize button
  • Now replace the image by clicking the browse button and choosing your own image from your file system
  • Edit the Title field (this will ensure that the title attribute changes in the HTML)
  • Save your changes and refresh your browser to see the changes on your site

Alternative Approach (Plone 3 only)

The name (ID) of the logo image is specified in base_properties - a list of useful values that, in Plone 3, are picked up and used across the Plone Default theme style sheets. This gives you the ability to upload your own logo image, give it any name you like, and then customize the base_properties with that name.

  • Go to the Zope Management Interface (Site Setup > Zope Management Interface)
  • Make sure that you've switched your css registry to debug mode (portal_css)
  • Go to portal_skins > custom and choose Image from the drop down list on the right
  • Upload your own image giving it an ID and a Title e.g.:
    ID = MyLogo.jpg
    Title = My Logo
  • Go to portal_skins > plone_styles, click base_properties and then click the customize button
  • You'll now have a customized version of base_properties in the custom folder of portal_skins which you can change as you like. Find the logoName field and replace the value logo.jpg with the ID you gave your image (if you gave your ID a .jpg or .gif suffix, make sure you include that, and remember that it is case-sensitive) e.g.:
    logoName = MyLogo.jpg
  • Save your changes and refresh your browser

In Plone 4 base_properties still exists but has only a very limited use.

Note that when you go back to your customized base_properties in portal_skins > custom, it will look like an empty folder. Click the properties tab to get back to the list of properties.

2. Changing the portal_logo style

There are no styles set for #portal-logo, but there are some for #portal-logo img in basic.css. Use the Firebug extension for Firefox to investigate these. The simplest approach is to override these with your own styles in ploneCustom.css.

  • Go to the Zope Management Interface (Site Setup > Zope Management Interface)
  • Make sure you have debug mode / development mode turned on in the CSS Registry (portal_css)
  • Click portal_skins > plone_styles > ploneCustom.css and then click the Customize button
  • You will now have an editable version of ploneCustom.css in the custom folder of portal_skins
  • Add your own styles here, click Save, and refresh your browser to see the changes

3. Changing the HTML

The HTML for the logo is generated by logo.pt - a page template which is part of a viewlet called plone.logo. To customize this through the web, you'll need to use portal_view_customizations.

  • Go to the portal_view_customizations in the Zope Management Interface (Site Setup > Zope Management Interface)
  • Click plone.logo and then click the Customize button
  • You'll now have a template you can rewrite - we've highlighted the significant bits in the theory section below and given you a couple of examples to get you started.
  • Save your changes and refresh your browser to see them

Note: if you want to go back and make further changes later, you'll see that plone.logo is highlighted in the portal_view_customizations list, click on it to edit it. If you want to remove your customizations completely, use the contents tab of portal_view_customizations, tick the box next to your template and click Delete.

The Theory

Here's the logo.pt template. It is written in the templating language used by Plone - TAL (or ZPT). It helps to know this (and it doesn't take long to learn), but we'll talk you through this example:

<a metal:define-macro="portal_logo"
   id="portal-logo"
   accesskey="1"
   tal:attributes="href view/navigation_root_url"
   i18n:domain="plone">
    <img src="logo.jpg" alt=""
         tal:replace="structure view/logo_tag" />
</a>

First we have the link tag:

You can disregard metal:define-macro="portal_logo" this is just wrapping the code into something that can be re-used again if necessary.

The important bit is tal:attributes="href view/navigation_root_url", this is the code that supplies your site URL to the href attribute.

There is a magic variable here, view/navigation_root_url, that seems to have appeared from nowhere. In fact, view is a collection of properties computed by the plone.logo viewlet and seamlessly passed to the logo.pt template. Here are the available properties:

navigation_root_url
supplies the URL of your site (it could potentially be something different if you've set up a different navigation root)
logo_tag
looks up the name of the logo image from base_properties, finds the image, collects its dimensions and title and turns all of this into an HTML image tag with the appropriate attributes Check back to the alternative approach in Section 1 of this How To for more information about base_properties.
portal_title
looks up and supplies the title of your site

Now look at the image tag in the template.

The key here is tal:replace="structure view/logo_tag". This means that the template won't deliver the image tag actually written out here, instead, it will replace the whole thing with the tag generated by the plone.logo viewlet. If you don't want this to happen, then you should delete this line.

Note: structure means treat the value as HTML rather than a text string.

Example 1: A plain text title

Here's a customized version of the template, using view/portal_title rather than view/logo_tag, to give you a text header instead (if you've used Plone 2, then you might recognize this):

<h1 metal:define-macro="portal_logo"
   id="portal-logo">
   <a accesskey="1"
   tal:attributes="href view/navigation_root_url"
   i18n:domain="plone" tal:content="view/portal_title">
    </a>
</h1>

Of course you'll want to supply your own styles, look back at Section 2 of this How To for information on defining these in ploneCustom.css. You could adjust this example to use an accessible image replacement technique in your CSS.

Example 2: Supplying your own image tag

You don't have to use logo_tag if you don't want to:

<a metal:define-macro="portal_logo"
   id="portal-logo"
   accesskey="1"
   tal:attributes="href view/navigation_root_url"
   i18n:domain="plone">
    <img src="[My logo ID]" alt="[My Logo]"
         width="[My Width]" height="[My Height]" 
         tal:attributes="title view/portal_title" />
</a>

You will, of course, need to upload your own logo to the custom folder in portal_skins, see the instructions in Section 1 of this How To.

Further Information

  • There are further How Tos in the Logo section of the Plone documentation dealing with more advanced customization methods.
  • More guidance on TAL and ZPT can be found in the ZPT tutorial.
  • If you want to transfer your changes to the file system in your own theme product, then the further sections in this reference manual will give you an outline of the files and templates you'll need (Logo viewlet section).

 

3. Approaches

There are different ways of tackling Plone themes. Here is information to help you make a decision on your approach - whether to base your theme on existing structures, whether to work through the web or on the filesystem, whether you even need this manual.

3.1. From Scratch or Based on Plone Default?

It is perfectly possible to build your own Plone theme completely from scratch, but you probably won't want to do this.

Based on Plone Default?

In particular, the bells and whistles of the Plone editing interface are wrapped up as part of the out-of-the box Plone Default, and you'll probably want to keep these.

The good news is that you can base your own theme on Plone Default and interweave your bits of templates, styles, scripts and components with what's already there. There are three ways of doing this:

  • with the Skin building block you customize the Plone Default bits (there's a neat way of doing this which ensures you leave the Plone Default theme completely intact)
  • with the Components building block you build your own, but you can reuse bits of the Plone Default components in the process
  • with the Configuration you simply add new directives

There's more good news - the elements of a Plone theme are broken up into very small parts. Each one can be dealt with independently of the others, so you can home in on just the bits you want to change.The price of all this flexibility is that it is sometimes difficult to track down exactly which bit you want, and things can start to seem complicated. This manual should help with that.

You can change a great deal of the look and feel just by overwriting existing CSS styles, or by rewriting some of the existing style sheets. However, if you want to start moving page elements around or rewriting some of the XHTML, then you'll need to delve into the templates, components, and configuration in more detail.

In the end, you're likely to come up with a theme based on Plone Default (that is, based structurally, not necessarily visually). This will probably contain

  • your own style sheet; or rewrites of some of the Plone CSS
  • some rearrangement of page elements
  • a few rewrites of some page elements
  • a few 'new' page elements

 

3.2. Through the Web or on the File System?

How to decide whether to build your theme through the web or on the file system.

Sooner or later with Plone you'll be faced with a decision. Plone is sufficiently flexible that there is often more that one way of doing things, and the conundrum is, usually, not how to do it, but which way.

You can customize Plone Default through the web very easily - particularly the skin and the configuration building blocks; further sections of this manual will point you in the direction of the relevant places in the Zope Management Interface to do this. However, if you want to move these customizations to a new site, undertake quite extensive customizations, or build a completely new theme, then it is advisable to move your work to the file system.

In this case you will need to create an installable module (also known as a theme product or  egg). This can be a daunting prospect, but there are tools available to simplify this process, providing you with a ready-made package into which to drop all the elements of your theme building blocks. We explain these tools on the next few pages.

If you are just starting out, then it is a good idea to get familiar with the building blocks and techniques by working through the web. It isn't difficult to move what you've done to the file system later. Once you start rewiring or moving components around you'll find the file system a more convenient way to work.

Through the Web

Pros Cons
Quick and easy Difficult to replicate or move from one site to another
Results immediately visible Large customizations can get complicated

Some customizations of components aren't possible (e.g. can't move viewlets between viewlet managers)

On the File System

Pros Cons
Portable and reusable Steeper learning curve when you first start out
Complete flexibility, can write your own viewlets and portlets Need access to the file system
Bundles your changes up into your own theme / skin Will sometimes need to restart to see changes

3.3. Future Directions

This reference manual outlines the current approach to Plone themes. But you may as well know now that there are other, perhaps simpler, approaches on the horizon.

Plone theming is getting a bit complicated. So the Plone community, in its inimitable and energetic way, is already exploring different solutions to the “theme problem”.

Things move fast. At the time of writing, some of the solutions listed below are probably not mature enough to use in earnest, particularly if you are just starting out. However, you might want to investigate them to see how they’re progressing:

Out of the Box Themes

A sprint and ongoing project to generate themes that will ship with out-of-the box Plone, and to brainstorm on other ways to improve the Plone theming story

Deliverance

Deliverance is a lightweight program that applies a theme to content according to a set of rules.

 

 

4. Tools

Time to sharpen your pencils. Authoring tools, putting your Plone site into debug mode, how to create a theme product (for working on the filesystem).

4.1. Authoring Tools

If you're working through the file system, you can use any text editor to write templates, configuration files (xml, zcml) and the small amounts of Python code you'll need.

You might find the following useful:

Zope/Plone TextMate support

You will have to check this out of the collective svn - instructions on how to do this can be found on http://svn.plone.org. There is also a Windows version of Textmate (http://www.e-texteditor.com/).

Checking template syntax

A quick and dirty route to finding out what's wrong with a template you've written yourself is to customize it through the Zope Management Interface. However you can also set up your own checking, to run before you install a template on your site:

this is a little complex if you're not comfortable with Python, but it's worth the effort in the long run.

Python code editors

Something a little more advanced than Notepad will give you code highlighting for Python. You'll find a comprehensive listing here

Integrated Development Environments

If you fancy using an IDE, then there are plenty of options, though these are directed towards Python development rather than writing or customizing templates:

Other IDEs include Wing (http://www.wingware.com/), BoaConstructor and Komodo (http://www.activestate.com/Products/komodo_ide/index.mhtml).

 

4.2. Debug Mode

Inevitably you won't get things right first time, so you need to make sure your site is running in debug mode.

CSS Debug Mode

Plone bundles all its CSS files into one or two for efficiency by using a resource registry (there is more information on how this works in the CSS and JavaScript to Page section). It is a lot easier to see what you're doing if you turn this function off when you're designing. You can do the same for JavaScript.

  • Go to Site Setup > Zope Management Interface > portal_css or portal_javascripts
  • Tick the debug check box

Zope Debug Mode

If you're making your own theme product, you'll also find it helpful to run Zope itself in debug mode. This is set in the zope.conf file which you'll find in /etc in your Zope instance. Just remove the # from this line:

#debug-mode = on

If you're using buildout, you can set this in buildout.cfg:

[instance]
debug-mode = on

You'll still need to restart from time to time, but changes to the Skin part of your theme made on the file system will refresh straightaway.

Can't see your changes?

Changes to.... Through the Web On the File System
Components Should see these immediately Restart Zope
Skins Should see these immediately Run Zope in debug mode
Style sheets and JavaScript Switch portal_css and portal_javascripts to debug Switch portal_css and portal_javascripts to debug
Configuration Should see these immediately Reinstall product with quick installer

Error messages

Plone comes with an error reporting module - PloneErrorReporting. When you've created a Plone site, you'll find this ready to install

  • Site Setup > Add/Remove Products

Make sure you uninstall it before you put a site into production mode.

Avoid restarting all the time

If you are doing a lot of work on components on the file system, you'll soon get tired of restarting Zope. plone.reload will save you time. Add it to your buildout configuration file like any other egg, rerun buildout and you'll find that you can reload your code via your browser.

4.3. On the File System: Creating a Theme Product

If you want to work on the file system, here's the magic required to get yourself set up with a skeleton set of files and code.

4.3.1. Overview

If you want to work on the file system, here's the magic required to get yourself set up with a skeleton set of files and code.

This section will talk you through the processes required to create your own theme on the file system and to install this in your own Plone site. 

The good news is that you don't have to write substantial amounts of code yourself to create the framework for your own file system theme, you can use a generator (Paster from ZopeSkel) to do this for you. This will give you a directory containing a number of pre-prepared directories and files, which you can augment or re-write with your own customizations.

 

 

4.3.2. Practical 1: How to Create a Plone 3 Theme Product on the File System

description goes here

4.3.2.1. Jumpstart Your Theme Development Using Paster

The quickest and most efficient way to get started is not to create your theme product folders and associated files from scratch, but to take advantage of a product generator which will automatically create the framework for a theme product based on your responses to a few interactive questions.

Using Paster on Your Local Machine

For users more comfortable using the command line, you are more likely to use a tool called ZopeSkel and the paster templates it contains. ZopeSkel is a collection of PasteScript templates which can be used to quickly generate Zope and Plone projects like buildouts, archetypes products and, most pertinently for us, Plone themes.

Please refer to this manual page for up-to-date instructions how to include Paster with ZopeSkel templates in your Plone configuration. Plone Unified Installer should ship with a working Paster command.

Create your Theme Product

If you have paster and ZopeSkel installed, navigate to the directory where you'd like to create your product (we'd recommend [your buildout]/[zinstance|zeocluster/src]) and run from the command line:

$ bin/paster create -t plone3_theme plonetheme.mytheme

or, if you have paster in your Plone installation:

$ [path to your buildout]/python-[version]/paster create -t plone3_theme plonetheme.mytheme

This will initiate a series of questions by the paster script. The defaults are largely appropriate for your first theme, so in many cases you can simply hit return. Here is example output from the interactive session.

Selected and implied templates:
ZopeSkel#basic_namespace A project with a namespace package
ZopeSkel#plone A Plone project
ZopeSkel#plone3_theme A Theme for Plone 3.0

Variables:
egg: plonetheme.mytheme
package: plonethememytheme
project: plonetheme.mytheme
Enter namespace_package (Namespace package (like plonetheme)) ['plonetheme']:
Enter package (The package contained namespace package (like example)) ['example']:mytheme
Enter skinname (The skin selection to be added to 'portal_skins' (like 'My Theme')) ['']:My Theme
Enter skinbase (Name of the skin selection from which the new one will be copied) ['Plone Default']:
Enter empty_styles (Override default public stylesheets with empty ones?) [True]: False
Enter include_doc (Include in-line documentation in generated code?) [False]:True
Enter zope2product (Are you creating a Zope 2 Product?) [True]:
Enter version (Version) ['1.0']:
Enter description (One-line description of the package) ['An installable theme for Plone 3.0']:
Enter long_description (Multi-line description (in reST)) ['']:
Enter author (Author name) ['Plone Collective']:
Enter author_email (Author email) ['product-developers@lists.plone.org']:
Enter keywords (Space-separated keywords/tags) ['web zope plone theme']:
Enter url (URL of homepage) ['http://svn.plone.org/svn/collective/']:
Enter license_name (License name) ['GPL']:
Enter zip_safe (True/False: if the package can be distributed as a .zip file) [False]:

You cannot use 'delete' to correct a typo during the interactive session. If you make a mistake, ctrl-c to stop the script and start over.

Paster Options

Some of these questions warrant further explanation:

Enter namespace_package
It is good practice to use the 'plonetheme' namespace for your theme. You can use other namespaces, of course ('products' might be another), but unless you have a compelling reason to do otherwise, use 'plonetheme'.
Enter package
The 'package' is simply the lowercase name of your theme product, sans spaces or underscores.
Enter skinname
The 'skinname' is the human-readable name for your theme. Spaces and capitalization are appropriate.
Enter skinbase
In nearly all cases, you'll want to leave this as 'Plone Default'.
Enter empty_styles
Answering 'True' will cause empty stylesheets to be added to your product which will override the default base.css, public.css, and portlets.css included in any Plone site using the 'Plone Default' skin. 'False' will not add empty stylesheets. For the purposes of this practical, we recommend entering 'False'.
Enter include_doc
Answering 'True' will cause inline documentation to be added to the files created by ZopeSkel. It is worth doing this at least once, as some of the documentation is quite useful.
Enter zope2product
Answering 'True' means that the package will be useable as an egg, it will be listed in the ZMI, skin folders will be registered as layers with the Skins Tool ('portal_skins'), and the Generic Setup profile for the product can be loaded via the Setup Tool ('portal_setup'). We'll explore some of this further; for now, suffice to say that you'll always want to enter 'True' here when generating a Plone theme.
Enter zip_safe
Stick with the default here.
Creating new eggs and packages quickly with paster
How to use the paster command to create new packages with proper setuptools- and egg-compliant filesystem layout quickly and easily.

4.3.2.2. Python Eggs, Generic Setup and Zope 3

Background notes on changes between Plone 2.5 and Plone 3.

Products, in Plone's parlance, are analogous to modules or extensions for other applications. In the move from Plone 2.5 to Plone 3, several important changes were made in the way Plone handles products. First, some products began to be packaged as Python eggs, which made them easier to manage, distribute and install. Second, products began to use GenericSetup as a means for installation. And third, products increasingly incorporated Zope 3 (Z3) technologies like browser views.

Python Eggs

A python egg is simply a bundle of files and directories which constitute a python package. Eggs can either be compressed, in which case they appear as a single *.egg file, or uncompressed. Eggs are similar in concept and function to Java's JAR files.

Eggs are installed via the setuptools framework, a side project of the Python Enterprise Application Kit (PEAK), which provides for package (and dependendency) management and distribution.

If you're using version control, you'll want to add *.egg-info and *.pyc to the ignore patterns for your setup so that the egg metadata and compiled python files aren't added to your repository.

A Quick Guide to Python Eggs
A good overview of eggs and setuptools from the folks at PEAK.
Hatch Python Eggs with SetupTools
David Metz takes a look at the setuptools framework.

GenericSetup

GenericSetup (GS) is a tool for managing site configuration in Plone using xml files. GS makes it possible to export customizations from one Plone site and import them into another. And to some extent, GS replaces the Portal QuickInstaller (QI) post-Plone 2.5 in that GS can be used to install products. In products which rely on GS, we find xml configuration files; in products which use the older, venerable QI for installation, by comparison, we find install methods written in python.

Keep in mind that GenericSetup does not currently allow you to undo the profile applied during installation. You can uninstall your theme using the QuickInstaller, however, assuming that an uninstall method is present.

Because our skeleton theme product utilizes GenericSetup to install itself, we will shortly be configuring several xml files needed by GS.

Understanding and Using GenericSetup in Plone
Now a bit dated, Rob Miller's tutorial on GS remains a useful resource for background on GS.
GenericSetup Improvements
More information about GS from Rob Miller.
Benefit NOW from Using GenericSetup and Z3 Technologies
Impress your colleagues by using GenericSetup and Zope 3 views efficiently and with minimal effort! This tutorial shows you how to add a new view, how to use it, how to add a new content type and how to hook it all up.

Zope 3 Technology

Despite any version number-induced miasma, remember that Plone 3 runs on Zope 2. Zope 3 is a dramatic rewrite of Zope 2 and some Zope 3 functionality has been backported to work under Zope 2. (And yes, Plone 3.) For a full explanation of the Zope 3 technologies involved, consult this tutorial:

Customization for developers
An overview of Plone 3 customization by Martin Aspeli.

4.3.2.3. Anatomy of a Plone Theme Product

The directory structure and an explanation of what all these files do.

 

Assuming that you've created your theme product successfully, you should have a directory structure that looks roughly like this:

    plonetheme.mytheme
docs
HISTORY.txt
INSTALL.txt
LICENSE.GPL
LICENSE.txt
MANIFEST.in
plonetheme
__init__.py
mytheme
__init__.py
browser
__init__.py
configure.zcml
images
README.txt
interfaces.py
stylesheets
main.css
README.txt
viewlet.pt
viewlets.py
configure.zcml
profiles
default
cssregistry.xml
import_steps.xml
jsregistry.xml
metadata.xml
plonetheme.mytheme_various.txt
skins.xml
viewlets.xml
profiles.zcml
setuphandlers.py
skins
plonetheme_mytheme_custom_images
CONTENT.txt
plonetheme_mytheme_custom_templates
CONTENT.txt
plonetheme_mytheme_styles
base.css.dtml
base_properties.props
CONTENT.txt
portlets.css.dtml
public.css.dtml
skins.zcml
tests.py
version.txt
plonetheme.mytheme-configure.zcml
plonetheme.mytheme.egg-info
dependency_links.txt
entry_points.txt
namespace_packages.txt
not-zip-safe
paster_plugins.txt
PKG-INFO
requires.txt
SOURCES.txt
top_level.txt
README.txt
setup.cfg
setup.py
zopeskel.txt

Things may seem a little complicated at this point, but not to worry. Let's take closer look at the main files and directories according to their respective functions.

Documentation

docs/
The docs directory contains installation instructions (INSTALL.txt), license files, and the development log (HISTORY.txt).
README.txt
The top-level text file contains the one-line description of the product you entered during the interactive session with ZopeSkel. Other README files exist throughout the product.

Python Package

plonetheme/
This is a namespace package, which serves to group other packages.
mytheme/
This is the actual name of your theme, usually the name of the client or project you are working on.
tests.py
Python tests for our package go here. Typically themes don't have much python code, and so don't have much in the way of testing.
version.txt
The version of our product. This information is also contained in /profiles/default/metadata.xml.

Python Egg

plonetheme.mytheme.egg-info/
The egg metadata is stored here.
setup.cfg
This configuration file contains information used to create egg-info files.
setup.py
If we wanted setuptools to handle the installation of the package and dependencies we could install via "python setup.py install" (for now, we don't).

GenericSetup

profiles.zcml
Register appropriate GenericSetup profiles.
profiles/
"Default" is the current configuration profile (only one profile is automatically created, but more could be added). Within our configuration profile we have XML files which tell GS how to configure CSS files (cssregistry.xml), Javascript files (jsregistry.xml), skin layers (skins.xml), and viewlets (viewlets.xml). Metadata.xml tracks the product version number and other metadata, import_steps.xml _____ and the presence of plonetheme.mytheme-various.txt tells GS to look to setuphandlers.py for additional methods.

Zope 3

plonetheme.mytheme-configure.zcml
This is the ZCML slug which must be placed in the etc/package-includes if the theme is installed as a python package (ours won't be).
configure.zcml

skins.zcml
Register skin layers (images, styles, templates) as filesystem directory views.
browser/

Stylesheets, Templates and More

Once you've got your theme product in place, the next step is to modify the pieces that Plone gives us, specifically templates, stylesheets, and viewlets.

Templates/
Plone templates, specifically the main_template that controls the layout of a Plone site, can be grabbed from the parts/plone/CMFPlone/skins/plone_templates directory. Most of the templates that were contained here in 2.5 have been moved to eggs and are controlled by viewlets. To modify a template from this directory, copy it to your theme product, into your theme's skins/templates folder and make your modifications there.
Stylesheets/
Plone's default stylesheets can be found in your buildout/parts/plone/CMFPlone/skins/plone_styles directory. It's usually advisable to create a stylesheet specific to your theme product, e.g. "mytheme.css" (where "mytheme" is the name of your theme product), and then take any relevant styles from CMFPlone's stylesheets and customize them in your own theme product, rather than overriding entire CMFPlone stylesheets. The one exception here may be IEFixes.css, which you likely want to keep intact as a single file, since it is called in explicitly from the main_template.
Viewlets/
It is a great oversimplification to state that most often you will be overriding viewlets from eggs commonly known as plone.app.layout, plone.app.portlets, and plone.app.content. Those viewlets, can be found in your buildout/eggs/ in packages named "plone.app.layout[xx]," "plone.app.portlets[xx]," and "plone.app.content[xx]," where [xx] is a version number. When modified, these viewlets and their related code belong in your theme product's browser/ directory. For more information on how to work with viewlets, read this tutorial.

If modifying page templates, you won't need to restart Zope in order to see your changes take effect. Changes to python, XML or ZCML, however, will require a restart.

Customization for developers
An overview of Plone 3 customization by Martin Aspeli.

4.3.3. Practical 2: How to Install your Plone 3 Theme Using Buildout

description goes here

4.3.3.1. Installing your Egg-Based Theme Product

In this section, we will look at how to install egg-based themes using buildout. As of Plone 3.1.2, all of the Plone installers create a buildout that contains your Plone instance. When installing or developing themes, buildout is highly recommended.

 To install the theme product you created in Practical 1:

  • First, if it isn't already there, copy your theme product to [your buildout]/[zinstance|zeocluster]/src (if you find that this directory doesn't exist, you can create it yourself).
  • Then, using a text editor, edit your buildout.cfg (you'll find it in [your buildout]/[zinstance|zeocluster]) and add the following information into the buildout, instance, and zcml sections. The actual buildout.cfg file will be much longer than the snippets below:
[buildout]
 ...
 develop =
    src/plonetheme.mytheme

[instance]
 eggs =
 ...
 plonetheme.mytheme

zcml =
 ...
 plonetheme.mytheme

The last line tells buildout to generate a ZCML snippet (slug) that tells Zope to recognize your theme product. The dots [...] indicate that you may have additional lines of ZCML code here.

  • After updating the configuration, stop your site and run the ''bin/buildout'' command, which will refresh your buildout.
  • Then, restart your site and go to the 'Site Setup' page in the Plone interface and click on the 'Add-on Products' link. The 'Site Setup' area is also known as plone_control_panel, as this is the URL used to get to 'Site Setup'.
  • Choose the product (My Theme 1.0) by selecting the checkbox next to it and click the 'Install' button.

Note: You may have to empty your browser cache to see the effects of the product installation.

Uninstalling a Theme Product

Uninstalling can be done from the 'Site Setup' / 'Add/Remove Products' page, but only if you installed it from the 'Add/Remove Products' screen. Not all themes uninstall cleanly, but reinstalling the Default Plone product generally cures any issues here.

4.3.3.2. Background: Third Party Theme Products

In this section, we will look at how to install a Plone theme that you have downloaded from Plone.org/products, PyPi, etc. We will also show how you can distinguish between an old-style 2.5 product and a new egg-based one.

There are two kinds of theme products: newer egg-based products, and older theme products that are in the "magical Products namespace". The type of theme product you are working with determines the steps you must take to install your theme. We'll now see how to tell the difference between the two.

Is the Theme Product Egg-Based or in the Product's Namespace?

First, we need to understand what egg-based means. If the theme, when unzipped, is named plonetheme.whatever, or you generate a new theme using the Paster recipe and answer "yes" to the "is this a Zope2 product", then your theme product is egg-based. On an even simpler note, if the root folder contains setup.py, it's an egg. In a typical egg-based theme product, setup.py would look something like this, where the highlighted text is the name of the egg.

from setuptools import setup, find_packages

version = '1.1'

setup(name='webcouturier.icompany.theme',

[...]

If the product looks like it was created using DIYPloneStyle 3.x (now outdated), it lives in the Products namespace. You can also tell that you are working with a theme in the Products namespace because there is no setup.py file in the root.

Installing an Egg-based Product

We recommend using buildout to install an egg-based product. You can decide whether you want to download the package yourself or leave buildout to do it for you. If the former, then follow the instructions in the previous section. If you want to leave the download up to buildout, then buildout configuration is simpler:

 

[configuration here]

Dependencies

If another package depends on the theme egg or includes its ZCML directly, you do not need to specify anything in the buildout configuration; buildout will detect this automatically. This is considered a more advanced topic. Similarly, if the theme egg depends on another product, then buildout will take care of this too.

Installing a Product if it is in the 2.x Products namespace

Assuming the theme product is an older 3.x theme and that it is in the Products namespace, all you have to do is place the theme product in your buildout's "products/" directory and restart your Zope instance. There is no need to rerun your buildout, because we have not changed any ZCML code.

Then, after your Zope has restarted, go to the 'Site Setup' page in the Plone interface and click on the 'Add/Remove Products' link. The 'Site Setup' area is also known as plone_control_panel, as this is the URL used to get to 'Site Setup'.

Choose the product by selecting the checkbox next to it and click the 'Install' button.

Older themes in the Products namespace may show up twice in the portal_quickinstaller, but this is a bug that is fixed in a more recent version of ZopeSkel. You can either ignore the bug or resolve it by removing this line from your theme product's configure.zcml file and restarting your Zope instance:

<five:registerPackage package="." initialize=".initialize" />

Note: You may have to empty your browser cache to see the effects of the product installation.

5. Building Blocks

Skin, Components, Configuration. The three main building blocks of a theme; interconnected, but each with a distinctive way of behaving.

5.1. Overview

An overview of the building blocks and how they fit together to create a theme.

There are really three main building blocks in a theme. The diagram below shows you how these slot together:

diagram of the building blocks used to create a theme

Skin

  • deals with the overall construction of a page and the delivery of content
  • comprises page templates, macros, and Python scripts, and is also the place to put style sheets and JavaScript
  • to help you understand these we'll point you in the direction of tutorials on the templating language TAL and introduce you to skin layers and order of precedence
  • to find skin elements, look in
    • portal_skins in the Zope Management Interface
    • the skins directory in a file system product

Components

  • the Components part deals (mostly) with page furniture - the page elements which have some level of consistency from page to page along with page elements involving a level of processing - such as the navigation tree, RSS feeds
  • deploys a mixture of Python classes and page templates to create viewlets, portlets and browser views
  • to help you understand these, we'll give you an overview of how they are wired together with ZCML, and we'll give you the briefest of introductions to the bits you really need to know about Python classes
  • to find the pieces that go together to make a component, look in
    • portal_view_customizations in the Zope Management Interface
    • the browser directory in a file system product

Configuration

  • the Configuration part deals with setting the order of some page elements (or individual items) on the page and with automatically setting some of the configuration you would otherwise make manually through the Site Setup interface
  • to help you understand the configuration, we'll point you in the direction of the main tools for manual configuration, give you a quick overview of the Generic Setup Tool and the XML used for automatic configuration
  • configuration tools are found in several places in the site, but the files required to run a configuration automatically are found in the profiles directory of a file system product

5.2. Skin

Templates, style sheets, Javascript files, how to customize them, where to find them.

5.2.1. Templates and the Templating Language

Templates and the Templating Language

5.2.1.1. Templates and the Templating Language

The main elements of a skin are page templates, images, Python scripts, CSS files, and JavaScript files.

(Zope) Page Templates

Page templates (.pt files or ZPT) are an essential part of a Plone theme and are probably the easiest aspect of Plone to get to grips with. They are written in an elegant XML-based templating language called TAL, sometimes make use of macros (METAL), and sometimes incorporate Python expressions (small one-line calculations) or Python scripts.

There are several excellent introductions to ZPT, and it doesn't take long to learn TAL. Try these:

TAL is the one language that we really recommend you learn properly. The rest you can pick your way through or familiarise yourself with as you go along.

A Plone web page is delivered via an aggregation of templates, rather than just one, and there a couple of aspects of Zope Page Template that you'll need to be aware of.

1. Slot

A slot is a predefined section of a template. This can be left empty, or given some default content, but it is available to be filled on the fly. A slot is defined in a template in code like this:

<metal:bodytext metal:define-slot="main" tal:content="nothing">
    .....
</metal:bodytext>

And filled via another template like this:

<metal:main fill-slot="main">
 <h1 class="documentFirstHeading">
    ......
 </h1>
</metal:main>

The ZPT tutorial on plone.org talks you through this in more detail, and the Templates and Components to Page section of this manual gives you an example.

2. Content view templates (_view)

Note: the term "view" also has a more technical application, so in the context of Components (discussed later in this manual) it will mean something different.

From the user's, contributor's, or visitor's perspective, a view is the way in which a content item is presented on the page. There's a useful introduction to this in the Plone User Manual.

Templates that are used to render a content item for a view have _view appended to their name (e.g., document_view.pt) and may have a title such as "Standard View." These templates are, in fact, sets of information ready to drop into slots.

Scripts

These are small stand-alone functions for times when you need a few lines of code to perform your calculation. On the file system, they have a .py extension; you'll find them in the Zope Management Interface as Script (Python).

Here's a snippet from the event_view template (the content view for the event content type) which uses a Python script to format the a time field according to the default format for the site. If you look in CMFPlone/skins/plone_scripts, you'll find toLocalizedTime.py - just a few lines of code.

<span metal:define-slot="inside" 
            class="explain"
            tal:attributes="title python:here.end()"
            tal:content="python:here.toLocalizedTime(here.end(),
                         long_format=1)">End Date Time</span>

 

5.2.1.2. Getting started

Page Templates are a web page generation tool. In this part, we'll go through their basics and show how to use them in your web site to create dynamic web pages easily.

The goal of Page Templates is natural workflow. A designer will use a WYSIWYG HTML editor to create a template, then a programmer will edit it to make it part of an application. If required, the designer can load the template back into his editor and make further changes to its structure and appearance. By taking reasonable steps to preserve the changes made by the programmer, he will not disrupt the application.

Page Templates aim at this goal by adopting three principles:

  1. Play nicely with editing tools.

  2. What you see is very similar to what you get.

  3. Keep code out of templates, except for structural logic.

A Page Template is like a model of the pages that it will generate. In particular, it is a valid HTML/XHTML page. Since HTML is highly structured, and WYSIWYG editors carefully preserve this structure, there are strict limits on the ways in which the programmer can change a page and still respect the first principle.

Although Page Templates are suited for programmers and designers who need to work together to create dynamic web pages, they form the basis for most of Plone's pages, so you should learn them a bit at least, if you need to customize the Plone look or layout. Moreover, they can be simpler to use and understand than the alternative, DTML.

Why Yet Another Template Language?

There are plenty of template systems out there, some of them quite popular, such as ASP, JSP, and PHP. Since the beginning, Zope has come with a template language called DTML. Why invent another?

First, none of these template systems are aimed at HTML designers. Once a page has been converted into a template, it is invalid HTML, making it difficult to work with outside of the application. Each of them violates the first or second principle of Zope Page Templates to one degree or another. Programmers should not "hijack" the work of the designers and turn HTML into software. XMLC, part of the Enhydra project, shares our goal, but requires the programmer to write substantial amounts of Java support code for each template.

Second, all of these systems suffer from failure to separate presentation, logic, and content (data). Their violations of the third principle decrease the scalability of content management and website development efforts that use these systems.

Applying The Principles

Page Templates use the Template Attribute Language (TAL). TAL consists of special tag attributes. For example, a dynamic page title might look like this:

      <title tal:content="context/title">Page Title</title>

The tal:content attribute is a TAL statement. Since it has an XML namespace (the tal: part) most editing tools will not complain that they don't understand it, and will not remove it. It will not change the structure or appearance of the template when loaded into a WYSIWYG editor or a web browser. The name content indicates that it will set the content of the title tag, and the value "context/title" is an expression providing the text to insert into the tag.

To the HTML designer using a WYSIWYG tool, this is perfectly valid HTML, and shows up in the editor looking the way a title should look. The designer, not caring about the application details of TAL, only sees a mockup of the dynamic template, complete with dummy values like "Page Title" for the title of the document.

When this template is saved in Zope and viewed by a user, Zope turns this static content into dynamic content and replaces "Page Title" with whatever "context/title" resolves to. In this case, "context/title" resolves to the title of the object to which to the template is applied. This substitution is done dynamically, when the template is viewed.

This example also demonstrates the second principle. When you view the template in an editor, the title text will act as a placeholder for the dynamic title text. The template provides an example of how generated documents will look.

There are template commands for replacing entire tags, their contents, or just some of their attributes. You can repeat a tag several times or omit it entirely. You can join parts of several templates together, and specify simple error handling. All of these capabilities are used to generate document structures. You can't create subroutines or classes, write loops or multi-way tests, or easily express complex algorithms. For these tasks, you should use Python.

The template language is deliberately not as powerful and general-purpose as it could be. It is meant to be used inside of a framework (such as Zope) in which other objects handle business logic and tasks unrelated to page layout.

For instance, template language would be useful for rendering an invoice page, generating one row for each line item, and inserting the description, quantity, price, and so on into the text for each row. It would not be used to create the invoice record in a database or to interact with a credit card processing facility.

Creating a Page Template

If you design pages, you will probably use FTP or WebDAV instead of the Zope Management Interface (ZMI) to create and edit Page Templates, or you will be developing templates on the filesystem for later installation. If you're not the Zope site owner, ask your Zope administrator for instructions. For the very small examples in this article, it is much easier to use the ZMI. For more information on using FTP or WebDAV with Zope, see The Zope Book or Jeffrey Shell's WebDAV article.

You may also use Emacs, cadaver, or some other client, but if you are a Zope administrator or a programmer, you will probably use the ZMI anyway at least occasionally. See the Zope Book for instructions on setting up Zope to to work with various clients.

Use your web browser to log into the Zope management interface as you normally would with Zope. Choose a Folder (the root is fine) and pick "Page Template" from the drop-down add list. Type "simple_page" in the add form's Id field, then push the "Add and Edit" button.

You should now see the main editing page for the new Page Template. The title is blank, the content-type is text/html, and the default template text is in the editing area.

Now you will create a very simple dynamic page. Type the words "a Simple Page" in the Title field. Then, edit the template's body text to look like this:

      This is <b tal:replace="template/title">the Title</b>.

Now push the "Save Changes" button. The edit page should show a message confirming that your changes have been saved. If  an error message appears above the code area, or some text starting with <-- Page Template Diagnostics is added to the template, then check to make sure you typed the example correctly and save it again. You don't need to erase the error comment: once the error is corrected it will go away.

Click on the Test tab. You should see a mostly blank page with "This is a Simple Page." at the top.

Back up, then click on the "Browse HTML source" link under the content-type field. This will show you the unrendered source of the template. You should see "This is the Title." Back up again, so that you are ready to edit the example further.

Simple Expressions

The text "template/title" in your simple Page Template is a path expression. This the most commonly used of the expression types defined by the TAL Expression Syntax (TALES). It fetches the title property of the template. Here are some other common path expressions:

  • request/URL: The URL of the current web request.

  • user/getUserName: The authenticated user's login name.

  • container/objectIds: A list of Ids of the objects in the same Folder as the template.

Every path starts with a variable name. If the variable contains the value you want, you stop there. Otherwise, you add a slash (/) and the name of a sub-object or property. You may need to work your way through several sub-objects to get to the value you're looking for.

There is a small built in set of variables, such as request and user, that will be listed and described later. You will also learn how to define your own variables.

Inserting Text

In your "simple_page" template, you used the tal:replace statement on a bold tag. When you tested it, it replaced the entire tag with the title of the template. When you browsed the source, instead, you saw the template text in bold. We used a bold tag in order to highlight the difference.

In order to place dynamic text inside of other text, you typically use tal:replace on a span tag. Add the following lines to your example:

      <br>
      The URL is <span tal:replace="request/URL">URL</span>.

The span tag is structural, not visual, so this looks like "The URL is URL." when you view the source in an editor or browser. When you view the rendered version, it may look something like:

      <br>
      The URL is http://localhost:8080/simple_page.

Remember to take care when editing not to destroy the span or place formatting tags such as b or font inside of it, since they would also be replaced.

If you want to insert text into a tag but leave the tag itself alone, you use tal:content. To set the title of your example page to the template's title property, add the following lines above the other text:

      <head>
        <title tal:content="template/title">The Title</title>
      </head>

If you open the "Test" tab in a new window, the window's title will be "a Simple Page".

Repeating Structures

Now you will add some context to your page, in the form of a list of the objects that are in the same Folder. You will make a table that has a numbered row for each object, and columns for the id, meta-type, and title. Add these lines to the bottom of your example template:

      <table border="1" width="100%">
        <tr>
          <th>#</th><th>Id</th><th>Meta-Type</th><th>Title</th>
        </tr>
        <tr tal:repeat="item container/objectValues">
          <td tal:content="repeat/item/number">#</td>
          <td tal:content="item/id">Id</td>
          <td tal:content="item/meta_type">Meta-Type</td>
          <td tal:content="item/title">Title</td>
        </tr>
      </table>

The tal:repeat statement on the table row means "repeat this row for each item in my container's list of object values". The repeat statement puts the objects from the list into the item variable one at a time, and makes a copy of the row using that variable. The value of "item/id" in each row is the Id of the object for that row.

You can use any name you like for the "item" variable, as long as it starts with a letter and contains only letters, numbers, and underscores (_). It only exists in the <tr> tag; If you tried to use it above or below that tag you would get an error.

You also use the tal:repeat variable name to get information about the current repetition. By placing it after the builtin variable repeat in a path, you can access the repetition count starting from zero (index), from one (number), from "A" (Letter), and in several other ways. So, the expression repeat/item/number is 1 in the first row, 2 in the second row, and so on.

Since one tal:repeat loop can be placed inside of another, more than one can be active at the same time. This is why you must write repeat/item/number instead of just repeat/number. You must specify which loop you are interested in by including the loop name.

Conditional Elements

View the template, and you'll notice that the table is very dull looking. Let's improve it by shading alternate rows. Copy the second row of the table, then edit the code so that it looks like this:

      <table border="1" width="100%">
        <tr>
          <th>#</th><th>Id</th><th>Meta-Type</th><th>Title</th>
        </tr>
        <tbody tal:repeat="item container/objectValues">
          <tr bgcolor="#EEEEEE" tal:condition="repeat/item/even">
            <td tal:content="repeat/item/number">#</td>
            <td tal:content="item/id">Id</td>
            <td tal:content="item/meta_type">Meta-Type</td>
            <td tal:content="item/title">Title</td>
          </tr>
          <tr tal:condition="repeat/item/odd">
            <td tal:content="repeat/item/number">#</td>
            <td tal:content="item/id">Id</td>
            <td tal:content="item/meta_type">Meta-Type</td>
            <td tal:content="item/title">Title</td>
          </tr>
        </tbody>
      </table>

The tal:repeat has not changed, you have just moved it onto the new tbody tag. This is a standard HTML tag meant to group together the body rows of a table, which is how you are using it. There are two rows in the body, with identical columns, and one has a grey background.

View the template's source, and you see both rows. If you had not added the tal:condition statements to the rows, then the template would generate both rows for every item, which is not what you want. The tal:condition statement on the first row ensures that it is only included on even-indexed repetitions, while the second row's condition only lets it appear in odd-indexed repetitions.

A tal:condition statement does nothing if its expression has a true value, but removes the entire statement tag, including its contents, if the value is false. The odd and even properties of repeat/item are either zero or one. The number zero, a blank string, an empty list, and the builtin variable nothing are all false values. Nearly every other value is true, including non-zero numbers, and strings with anything in them (even spaces!).

Defining Variables

Note: In Plone 4 or newer, use container/values instead of container/objectValues below.

Your template will always show at least one row, since the template itself is one of the objects listed. In other circumstances, you might want to account for the possibility that the table will be empty. Suppose you want to simply omit the entire table in this case. You can do this by adding a tal:condition to the table:

      <table border="1" width="100%"
             tal:condition="container/objectValues">

Now, when there are no objects, no part of the table will be included in the output. When there are objects, though, the expression "container/objectValues" will be evaluated twice, which is mildly inefficient. Also, if you wanted to change the expression, you would have to change it in both places.

To avoid these problems, you can define a variable to hold the list, and then use it in both the tal:condition and the tal:repeat. Change the first few lines of the table to look like this:

      <table border="1" width="100%"
             tal:define="items container/objectValues"
             tal:condition="items">
        <tr>
          <th>#</th><th>Id</th><th>Meta-Type</th><th>Title</th>
        </tr>
        <tbody tal:repeat="item items">

The tal:define statement creates the variable items, and you can use it anywhere in the table tag. Notice also how you can have two TAL attributes on the same table tag. You can, in fact, have as many as you want. In this case, they are evaluated in order. The first assigns the variable items and the second uses items in a condition to see whether or not it is false (in this case, an empty sequence) or true.

Now, suppose that instead of simply leaving the table out when there are no items, you want to show a message. To do this, you place the following above the table:

      <h4 tal:condition="not:container/objectValues">There
      Are No Items</h4>

You can't use your items variable here, because it isn't defined yet. If you move the definition to the h4 tag, you can't use it in the table tag any more, because it becomes a local variable of the h4 tag. You could place the definition on some tag that enclosed both the h4 and the table, but there is a simpler solution. By placing the keyword global in front of the variable name, you can make the definition last from the h4 tag to the bottom of the template:

      <h4 tal:define="global items container/objectValues"
          tal:condition="not:items">There Are No Items</h4>
      <table border="1" width="100%"
          tal:condition="items">

The not: in the first tal:condition is an expression type prefix that can be placed in front of any expression. If the expression is true, not: is false, and vice versa.

Changing Attributes

Most, if not all, of the objects listed by your template have an icon property, that contains the path to the icon for that kind of object. In order to show this icon in the meta-type column, you will need to insert this path into the src attribute of an img tag, by editing the meta-type column in both rows to look like this:

      <td>
          <img src="/misc_/OFSP/Folder_icon.gif"
               tal:attributes="src item/icon">
          <span tal:replace="item/meta_type">Meta-Type</span>
      </td>

The tal:attributes statement replaces the src attribute of the image with the value of item/icon. The value of src in the template acts as a placeholder, so that the image is not broken, and is the correct size.

Since the tal:content attribute on the table cell would have replaced the entire contents of the cell, including the image, with the meta-type text, it had to be removed. Instead, you insert the meta-type inline in the same fashion as the URL at the top of the page.

Based on the Zope Book, © Zope Corporation

5.2.1.3. Macros and Slots

Macros

So far, you've seen how Page Templates can be used to add dynamic behavior to individual web pages. Another feature of page templates is the ability to reuse look and feel elements across many pages.

For example, with Page Templates, you can have a site that has a standard look and feel. No matter what the "content" of a page, it will have a standard header, side-bar, footer, and/or other page elements. This is a very common requirement for web sites, and this is exactly how Plone works.

You can reuse presentation elements across pages with macros. Macros define a section of a page that can be reused in other pages. A macro can be an entire page, or just a chunk of a page such as a header or footer. After you define one or more macros in one Page Template, you can use them in other Page Templates.

Using Macros

You can define macros with tag attributes similar to TAL statements. Macro tag attributes are called Macro Expansion Tag Attribute Language (METAL) statements. Here's an example macro definition:

<p metal:define-macro="copyright">
  Copyright 2008, <em>Foo, Bar, and Associates</em> Inc.
</p>

This metal:define-macro statement defines a macro named "copyright". The macro consists of the p element (including all contained elements, ending with the closing p tag).

Macros defined in a Page Template are stored in the template's macros attribute. You can use macros from other Page Templates by referring to them through the macros attribute of the Page Template in which they are defined. For example, suppose the copyright macro is in a Page Template called "master_page". Here's how to use the copyright macro from another Page Template:

<hr />
<b metal:use-macro="container/master_page/macros/copyright">
  Macro goes here
</b>

In this Page Template, the b element will be completely replaced by the macro when Zope renders the page:

<hr />
<p>
  Copyright 2008, <em>Foo, Bar, and Associates</em> Inc.
</p>

If you change the macro (for example, if the copyright holder changes) then all Page Templates that use the macro will automatically reflect the change.

Notice how the macro is identified by a path expression using the metal:use-macro statement. The metal:use-macro statement replaces the statement element with the named macro.

Macro Details

The metal:define-macro and metal:use-macro statements are pretty simple. However there are a few subtleties to using them which are worth mentioning.

A macro's name must be unique within the Page Template in which it is defined. You can define more than one macro in a template, but they all need to have different names.

It should also be noted that, despite the define-macro attribute, the macro is anyway a regular section of the template; so, when you call the whole template, the macro section is rendered in the output page just like any other section in the template. By using the define-macro attribute you are simply adding some sort of "anchor" to that section, so that you can call it from outside; but you are not changing anything regarding the behaviour of that same section in the template itself.

Normally you'll refer to a macro in a metal:use-macro statement with a path expression. However, you can use any expression type you wish so long as it returns a macro. For example:

<p metal:use-macro="python:context.getMacro()">
  Replaced with a dynamically determined macro,
  which is located by the getMacro script.
</p>

In this case the path expression returns a macro defined dynamically by the getMacro script. Using Python expressions to locate macros lets you dynamically vary which macro your template uses.

You can use the default variable with the metal:use-macro statement:

<p metal:use-macro="default">
  This content remains - no macro is used
</p>

The result is the same as using default with tal:content and tal:replace. The "default" content in the tag doesn't change when it is rendered. This can be handy if you need to conditionally use a macro or fall back on the default content if it doesn't exist.

If you try to use the nothing variable with metal:use-macro you will get an error, since nothing is not a macro. If you want to use nothing to conditionally include a macro, you should instead enclose the metal:use-macro statement with a tal:condition statement.

Zope handles macros first when rendering your templates. Then Zope evaluates TAL expressions. For example, consider this macro:

<p metal:define-macro="title"
   tal:content="template/title">
  template's title
</p>

When you use this macro it will insert the title of the template in which the macro is used, not the title of the template in which the macro is defined. In other words, when you use a macro, it's like copying the text of a macro into your template and then rendering your template.

Using Slots

Macros are much more useful if you can override parts of them when you use them. You can do this by defining slots in the macro that you can fill in when you use the template. For example, consider a side bar macro:

<div metal:define-macro="sidebar">
  Links
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a></li>
    <li><a href="/support">Support</a></li>
    <li><a href="/contact">Contact Us</a></li>
  </ul>
</div>

This macro is fine, but suppose you'd like to include some additional information in the sidebar on some pages. One way to accomplish this is with slots:

<div metal:define-macro="sidebar">
  Links
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a></li>
    <li><a href="/support">Support</a></li>
    <li><a href="/contact">Contact Us</a></li>
  </ul>
  <span metal:define-slot="additional_info"></span>
</div>

When you use this macro you can choose to fill the slot like so:

<p metal:use-macro="container/master_page/macros/sidebar">
  <b metal:fill-slot="additional_info">
    Make sure to check out our <a href="/specials">specials</a>.
  </b>
</p>

When you render this template the side bar will include the extra information that you provided in the slot:

<div>
  Links
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a></li>
    <li><a href="/support">Support</a></li>
    <li><a href="/contact">Contact Us</a></li>
  </ul>
  <b>
    Make sure to check out our <a href="/specials">specials</a>.
  </b>
</div>

Notice how the span element that defines the slot is replaced with the b element that fills the slot.

Customizing Default Presentation

A common use of slot is to provide default presentation which you can customize. In the slot example in the last section, the slot definition was just an empty span element. However, you can provide default presentation in a slot definition. For example, consider this revised sidebar macro:

<div metal:define-macro="sidebar">
  <div metal:define-slot="links">
  Links
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a></li>
    <li><a href="/support">Support</a></li>
    <li><a href="/contact">Contact Us</a></li>
  </ul>
  </div>
  <span metal:define-slot="additional_info"></span>
</div>

Now the sidebar is fully customizable. You can fill the links slot to redefine the sidebar links. However, if you choose not to fill the links slot then you'll get the default links, which appear inside the slot definition.

You can even take this technique further by defining slots inside of slots. This allows you to override default presentation with a fine degree of precision. Here's a sidebar macro that defines slots within slots:

<div metal:define-macro="sidebar">
  <div metal:define-slot="links">
  Links
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a></li>
    <li><a href="/support">Support</a></li>
    <li><a href="/contact">Contact Us</a></li>
    <span metal:define-slot="additional_links"></span>
  </ul>
  </div>
  <span metal:define-slot="additional_info"></span>
</div>

If you wish to customize the sidebar links you can either fill the links slot to completely override the links, or you can fill the additional_links slot to insert some extra links after the default links. You can nest slots as deeply as you wish.

Combining METAL and TAL

You can use both METAL and TAL statements on the same elements. For example:

<ul metal:define-macro="links"
    tal:repeat="link context/getLinks">
  <li>
    <a href="link_url"
       tal:attributes="href link/url"
       tal:content="link/name">link name</a>
  </li>
</ul>

In this case, getLinks is a (imaginary) Script that assembles a list of link objects, possibly using a Catalog query.

Since METAL statements are evaluated before TAL statements, there are no conflicts. This example is also interesting since it customizes a macro without using slots. The macro calls the getLinks Script to determine the links. You can thus customize your site's links by redefining the getLinks Script at different locations within your site.

It's not always easy to figure out the best way to customize look and feel in different parts of your site. In general you should use slots to override presentation elements, and you should use Scripts to provide content dynamically. In the case of the links example, it's arguable whether links are content or presentation. Scripts probably provide a more flexible solution, especially if your site includes link content objects.

Whole Page Macros

Rather than using macros for chunks of presentation shared between pages, you can use macros to define entire pages. Slots make this possible. Here's an example macro that defines an entire page:

<html metal:define-macro="page">
  <head>
    <title tal:content="context/title">The title</title>
  </head>

  <body>
    <h1 metal:define-slot="headline"
        tal:content="context/title">title</h1>

    <p metal:define-slot="body">
      This is the body.
    </p>

    <span metal:define-slot="footer">
      <p>Copyright 2008 Fluffy Enterprises</p>
    </span>

  </body>
</html>

This macro defines a page with three slots: headline, body, and footer. Notice how the headline slot includes a TAL statement to dynamically determine the headline content.

You can then use this macro in templates for different types of content, or different parts of your site. For example here's how a template for news items might use this macro:

<html metal:use-macro="container/master_page/macros/page">

  <h1 metal:fill-slot="headline">
    Press Release:
    <span tal:replace="context/getHeadline">Headline</span>
  </h1>

  <p metal:fill-slot="body"
     tal:content="context/getBody">
    News item body goes here
  </p>

</html>

This template redefines the headline slot to include the words "Press Release" and call the getHeadline method on the current object. It also redefines the body slot to call the getBody method on the current object.

The powerful thing about this approach is that you can now change the page macro and the press release template will be automatically updated. For example you could put the body of the page in a table and add a sidebar on the left and the press release template would automatically use these new presentation elements.

Based on the Zope Book, © Zope Corporation

5.2.1.4. Advanced Usage

In this part we'll look at some more advanced features of the Template Attribute Language, including a more in-depth look at the TAL Expression Syntax (TALES).

Mixing and Matching Statements

As you have seen in the example template, you can put more than one TAL statement on the same tag. There are three limits you should be aware of, however:

  1. Only one of each kind of statement can be used on a single tag. Since HTML does not allow multiple attributes with the same name, you can't have two tal:define on the same tag.

  2. Both of tal:content and tal:replace cannot be used on the same tag, since their functions conflict.

  3. The order in which you write TAL attributes on a tag does not affect the order in which they execute. No matter how you arrange them, the TAL statements on a tag always execute in the following order: define, condition, repeat, content / replace, attributes.

To get around these limits, you can add another tag and split up the statements between the tags. If there is no obvious tag type that would fit, use span or div.

For example, if you want to define a variable for each repetition of a paragraph, you can't place the tal:define on the same tag as the tal:repeat, since the definition would happen before all of the repetitions. Instead, you would write either of the following:

      <div tal:repeat="ph phrases">
        <p tal:define="n repeat/ph/number">
        Phrase number <span tal:replace="n">1</span> is
        <b tal:content="ph">Phrase</b>.</p>
      </div>

      <p tal:repeat="ph phrases">
        <span tal:define="n repeat/ph/number">
        Phrase number <span tal:replace="n">1</span> is
        <b tal:content="ph">Phrase</b>".</span>
      </p>

Note: the definition of "n" is actually not much useful in this example because we could have directly used "repeat/ph/number" in the replace attribute which only occurs once, but it serves our purpose.

Statements with Multiple Parts

If you need to set multiple attributes on a tag, you can't do it by placing multiple tal:attributes statements on the tag, and splitting them across tags is useless.

Both the tal:attributes and tal:define statements can have multiple parts in a single statement. You separate the parts with semicolons (;), so any semicolon appearing in an expression in one of these statements must be escaped by doubling it (;;). Here is an example of setting both the src and alt attributes of an image:

      <img src="default.jpg"
           tal:attributes="src item/icon; alt item/id">

Here is a mixture of variable definitions:

      <span tal:define="global logo context/logo.gif; ids context/objectIds">

Note: in Plone 4 or newer you can use context/items instead of context/objectIds.

String Expressions

String expressions allow you to easily mix path expressions with text. All of the text after the leading string: is taken and searched for path expressions. Each path expression must be preceded by a dollar sign ($). If it has more than one part, or needs to be separated from the text that follows it, it must be surrounded by braces ({}). Since the text is inside of an attribute value, you can only include a double quote by using the entity syntax &quot;. Since dollar signs are used to signal path expressions, a literal dollar sign must be written as two dollar signs ($$). For example:

      <span tal:replace="string:Just text."/>
      <span tal:replace="string:© $year, by Me."/>
      <span tal:replace="string:Three ${vegetable}s, please."/>
      <span tal:replace="string:Your name is ${user/getUserName}!"/>
      <span tal:replace="string:She answered &quot;$answer&quot;."/>
      <span tal:replace="string:This product costs $price $$."/>

Nocall Path Expressions

An ordinary path expression tries to render the object that it fetches. This means that if the object is a function, Script, Method, or some other kind of executable thing, then the expression will evaluate to the result of calling the object. This is usually what you want, but not always. For example, if you want to put a DTML Document into a variable so that you can refer to its properties, you can't use a normal path expression because it will render the Document into a string.

If you put the nocall: expression type prefix in front of a path, it prevents the rendering and simply gives you the object. For example:

      <span tal:define="doc nocall:context/aDoc"
            tal:content="string:${doc/id}: ${doc/title}">
      Id: Title</span>

This expression type is also valuable when you want to define a variable to hold a function or class from a module, for use in a Python expression.

Other Builtin Variables

You have already seen some examples of the builtin variables template, user, repeat, and request. Here is a more complete list of the other builtin variables and their uses:

nothing
A false value, similar to a blank string, that you can use in tal:replace or tal:content to erase a tag or its contents. If you set an attribute to nothing, the attribute is removed from the tag (or not inserted), unlike a blank string. Equivalent to None in Python.
default
A special value that doesn't change anything when used in tal:replace, tal:content, or tal:attributes. It leaves the template text in place.
options
The keyword arguments, if any, that were passed to the template.
attrs
A dictionary of attributes of the current tag in the template. The keys are the attributes names, and the values are the original values of the attributes in the template.
root
The root Zope object. Use this to get Zope objects from fixed locations, no matter where your template is placed or called.
context
The object on which the template is being called. This is often the same as the container, but can be different if you are using acquisition. Use this to get Zope objects that you expect to find in different places depending on how the template is called.
here
An (older) alias for context.
container
The container (usually a Folder) in which the template is kept. Use this to get Zope objects from locations relative to the template's permanent home.
request
Contains the complete information about the current HTTP request that Zope is processing. See this page in the zope.org wiki for further info about the request object.
modules
The collection of Python modules available to templates. See the section on writing Python expressions.
view
For templates called from a Zope 3-style view only, this variable refers to the associated view class. This may then contain functions and variables prepared explicitly for the template to output

Alternate Paths

The path template/title is guaranteed to exist every time the template is used, although it may be a blank string. Some paths, such as request/form/x, may not exist during some renderings of the template. This normally causes an error when the path is evaluated.

When a path doesn't exist, you often have a fallback path or value that you would like to use instead. For instance, if request/form/x doesn't exist, you might want to use context/x instead. You can do this by listing the paths in order of preference, separated by vertical bar characters (|):

      <h4 tal:content="request/form/x | context/x">Header</h4>

Two variables that are very useful as the last path in a list of alternates are nothing and default. Use nothing to blank the target if none of the paths is found, or default to leave the example text in place.

You can also test the existence of a path directly with the exists: expression type prefix. A path expression with exists: in front of it is true if the path exists, false otherwise. These examples both display an error message only if it is passed in the request:

      <h4 tal:define="err request/form/errmsg | nothing"
          tal:condition="err" tal:content="err">Error!</h4>

      <h4 tal:condition="exists:request/form/errmsg"
          tal:content="request/form/errmsg">Error!</h4>

Dummy Elements

You can include page elements that are visible in the template but not in generated text by using the builtin variable nothing, like this:

      <tr tal:replace="nothing">
        <td>10213</td><td>Example Item</td><td>$15.34</td>
      </tr>

This can be useful for filling out parts of the page that will take up more of the generated page than of the template. For instance, a table that usually has ten rows will only have one row in the template. By adding nine dummy rows, the template's layout will look more like the final result.

Inserting Structure

Normally, the tal:replace and tal:content statements quote the text that they insert, converting < to &lt;, for instance. If you actually want to insert the unquoted text, you need to precede the expression with the structure keyword. Given a variable copyright with a string value of "© 2008 By <b>Me</b>", the following two lines:

      <span tal:replace="copyright">Copyright 2008</span>
      <span tal:replace="structure copyright">Copyright 2008</span>

...will generate "© 2001 By <b>Me</b>" and "© 2001 By Me", respectively.

This feature is especially useful when you are inserting a fragment of HTML that is stored in a property or generated by another Zope object. For instance, you may have news items that contain simple HTML markup such as bold and italic text when they are rendered, and you want to preserve this when inserting them into a "Top News" page. In this case, you might write:

      <p tal:repeat="article topnewsitems"
         tal:content="structure article">A News Article</p>

Basic Python Expressions

A Python expression starts with python:, followed by an expression written in the Python language. Python is a simple and expressive programming language. If you have never encountered it before, you should read one of the excellent tutorials or introductions available at the official website http://www.python.org.

A Page Template Python expression can contain anything that the Python language considers an expression. You can't use statements such as if and while, and Zope's security restrictions are applied.

Comparisons

One place where Python expressions are practically necessary is in tal:condition statements. You usually want to compare two strings or numbers, and there isn't any other way to do that. You can use the comparison operators < (less than), > (greater than), == (equal to), and != (not equal to). You can also use the boolean operators and, not, and or. For example:

        <p tal:repeat="widget widgets">
          <span tal:condition="python:widget.type == 'gear'">
          Gear #<span tal:replace="repeat/widget/number">1</span>:
          <span tal:replace="widget/name">Name</span>
          </span>
        </p>

Sometimes you want to choose different values inside a single statement based on one or more conditions. You can do this with the test function, like this:

        You <span tal:define="name user/getUserName"
              tal:replace="python:test(name=='Anonymous User', 'need to log in', default)">
              are logged in as
              <span tal:replace="name">Name</span>
            </span>

        <tr tal:define="oddrow repeat/item/odd"
            tal:attributes="class python:test(oddrow, 'oddclass', 'evenclass')">

Using other Expression Types

You can use other expression types inside of a Python expression. Each type has a corresponding function with the same name, including path(), string(), exists(), and nocall(). This allows you to write expressions such as:

        "python:path('context/%s/thing' % foldername)"
        "python:path(string('context/$foldername/thing'))"
        "python:path('request/form/x') or default"

The final example has a slightly different meaning than the path expression "request/form/x | default", since it will use the default text if "request/form/x" doesn't exists or if it is false.

Getting at Zope Objects

Much of the power of Zope involves tying together specialized objects. Your Page Templates can use Scripts, SQL Methods, Catalogs, and custom content objects. In order to use them, you have to know how to get access to them.

Object properties are usually attributes, so you can get a template's title with the expression "template.title". Most Zope objects support acquisition, which allows you to get attributes from "parent" objects. This means that the Python expression "context.Control_Panel" will acquire the Control Panel object from the root folder. Object methods are attributes, as in "context.objectIds" and "request.set". Objects contained in a folder can be accessed as attributes of the folder, but since they often have Ids that are not valid Python identifiers, you can't use the normal notation. For example, instead of writing "context.penguin.gif", you must write "getattr(context, 'penguin.gif')".

Some objects, such as request, modules, and Zope Folders support item access. Some examples of this are:

      request['URL'], modules['math'], and context['thing']

When you use item access on a Folder, it doesn't try to acquire the name, so it will only succeed if there is actually an object with that Id contained in the folder.

Path expressions allow you to ignore details of how you get from one object to the next. Zope tries attribute access, then item access. You can write "context/images/penguin.gif" instead of "python:getattr(context.images, 'penguin.gif')", and "request/form/x" instead of "python:request.form['x']".

The tradeoff is that path expressions don't allow you to specify those details. For instance, if you have a form variable named "get", you must write "python:request.form['get']", since "request/form/get" will evaluate to the "get" method of the form dictionary.

Using Scripts

Script objects are often used to encapsulate business logic and complex data manipulation. Any time that you find yourself writing lots of TAL statements with complicated expressions in them, you should consider whether you could do the work better in a script.

Each script has a list of parameters that it expects to be given when it is called. If this list is empty, then you can use the script by writing a path expression. Otherwise, you will need to use a Python expression, like this:

      "python:context.myscript(1, 2)"
      "python:context.myscript('arg', foo=request.form['x'])"

If you want to return more than a single bit of data from a script to a page template, it is a good idea to return it in a dictionary. That way, you can define a variable to hold all the data, and use path expressions to refer to each bit. For example, supposing we have a getPerson script which returns a dictionary like {'name':'Fred', 'age':25}:

      <span tal:define="person context/getPerson"
            tal:replace="string:${person/name} is ${person/age}">
      Name is 30</span> years old.

Calling DTML

DTML is another templating language made available by Zope, mostly replaced by ZPT nowadays, but still in use. You can read more about it in the relevant chapter of the Zope Book.

Unlike Scripts, DTML Methods don't have an explicit parameter list. Instead, they expect to be passed a client, a mapping, and keyword arguments. They use these to construct a namespace.

When Zope's ZPublisher publishes a DTML object, it passes the context of the object as the client, and the REQUEST as the mapping. When one DTML object calls another, it passes its own namespace as the mapping, and no client.

If you use a path expression to render a DTML object, it will pass a namespace with request, context, and the template's variables already on it. This means that the DTML object will be able to use the same names as if it were being published in the same context as the template, plus the variable names defined in the template.

Python Modules

The Python language comes with a large number of modules, which provide a wide variety of capabilities to Python programs. Each module is a collection of Python functions, data, and classes related to a single purpose, such as mathematical calculations or regular expressions.

Several modules, including "math" and "string", are available in Python Expressions by default. For example, you can get the value of pi from the math module by writing "python:math.pi". To access it from a path expression, however, you need to use the modules variable. In this case, you would use "modules/math/pi". Please refer to the Zope Book or a DTML reference guide for more information about these modules.

The "string" module is hidden in Python expressions by the "string" expression type function, so you need to access it through the modules variable. You can do this directly in an expression in which you use it, or define a global variable for it, like this:

      tal:define="global mstring modules/string"
      tal:replace="python:mstring.join(slist, ':')"

Modules can be grouped into packages, which are simply a way of organizing and naming related modules. For instance, Zope's Python-based Scripts are provided by a collection of modules in the "PythonScripts" subpackage of the Zope "Products" package. In particular, the "standard" module in this package provides a number of useful formatting functions that are standard in the DTML "Var" tag. The full name of this module is "Products.PythonScripts.standard", so you could get access to it using either of the following statements:

      tal:define="pps modules/Products.PythonScripts.standard"
      tal:define="pps python:modules['Products.PythonScripts.standard']"

Most Python modules cannot be accessed from Page Templates, DTML, or Scripts unless you add Zope security assertions to them. That's outside the scope of this document, and is covered by the Zope Security Guide.

Special HTML attributes

The HTML boolean attributes checked, selected, nowrap, compact, ismap, declare, noshade, disabled, readonly, multiple, selected and noresize are treated differently by tal:attributes. The value is treated as true or false (as defined by tal:condition). The attribute is set to attr=”attr” in the true case and omitted otherwise. If the value is default, then it is treated as true if the attribute already exists, and false if it does not. For example, each of the following lines:

<input type="checkbox" checked tal:attributes="checked default">
<input type="checkbox" tal:attributes="checked string:yes">
<input type="checkbox" tal:attributes="checked python:42">

will render as:

<input type="checkbox" checked="checked">

while each of these:

<input type="checkbox" tal:attributes="checked default">
<input type="checkbox" tal:attributes="checked string:">
<input type="checkbox" tal:attributes="checked nothing">

will render as:

<input type="checkbox">

This article contains information and examples from the Zope Book, © Zope Developers Community.

5.2.1.5. Global Template Variables

Plone defines a few useful global variables to use them in your templates

While writing templates for Plone, you will notice a set of variables you use more often, like the URL of the portal or the currently authenticated member.

For your convenience, Plone defines a few global template variables that are pulled into main_template via global_defines. Some of the most useful ones are:

portal
The portal object.
portal_url
The url of the portal.
member
The current user (None if user is anonymous)
checkPermission
A function to check if the current user has a certain permission in the current context, e.g. checkPermission('View portal content', context).
isAnon
True if the current user is not logged in.
is_editable
True if the current user has edit permissions in the context.
default_language
The default language of the portal.
here_url
The URL of the current object.

To see the full list list of these variables, see the docstring for globalize() in the interface Products.CMFPlone.browser.interfaces.IPlone.

5.2.1.6. Customizing AT Templates

This tutorial describes the steps to produce a fully-customized view of an Archetypes object. This is applicable to tweaking little details of the default AT behavior as well as gutting and re-building the view from scratch. (contributed by Floyd May)

5.2.1.6.1. Introduction

Goals, Pre-Requisites, and Tools

So you think Archetypes' way of automagically generating HTML to view your object isn't pretty enough? Well, you've come to the right place! I'm going to teach you how to dress up those boring, drab views and make them shine!

Goals: What will I learn?

  • How Archetypes generates views for content objects
  • How much control Archetypes gives you
  • How to change the HTML output for a particular field by creating a custom widget template
  • How to use the Archetypes template framework to make minor changes to the default AT-generated view
  • How to customize the HTML output for the entire view of an Archetypes object by using the title,body,folderlisting, and footer macros

Pre-Requisites: What do I need to know?

  • How to read and write Python code
  • How to read and write Zope Page Templates (ZPT)
  • How to create Archetypes-based products (ArchGenXML is acceptable)

Tools: What do I need to have installed?

  • Plone 2.0 or 2.1
  • Archetypes (included by default in Plone 2.1)
  • The ATViewTutorial product - this product has examples of the concepts in this tutorial

5.2.1.6.2. What Makes It Tick?

This page describes how Archetypes uses different templates to generate HTML, and how AT-based template customization can be applied.

Archetypes has a very clever system for generating HTML pages for AT-based objects. The same set of templates generates all of the content areas for all AT-based objects. This buys you the following benefit, useful for site consistency:

  • All the pages look the same.

However, it also has the following drawback:

  • All the pages look the same.

Different types of content need to be displayed differently. Let's dig into how Archetypes does things, so we can figure out how to make your content types shine!

The base_view Template

The base_view template (found in the archetypes skin) handles selecting the appropriate macros from the appropriate templates, and using those macros to display content objects. If you look at this piece of code from 'base_view':

        <tal:block define="portal_type python:here.getPortalTypeName().lower().replace(' ', '_');
                           view_template python:'%s_view' % portal_type;
                           view_macros python:path('here/%s/macros|nothing' % view_template);
                           macro view_macros/css | nothing"
                   condition="macro">

You can see that it defines the variable view_template as the object's Portal Type Name converted to lowercase, with spaces replaced with underscores (_), followed by _view. So, MyType's view template, for instance, would be called mytype_view.

Now, before we move on, I must warn you: don't edit base_view. Seriously. Don't.[1]

No, really. Don't customize base_view. If you think you need to customize base_view, first, well... don't. Keep reading the tutorial. If you're certain, after reading the tutorial, that you need to customize base_view, again, don't! Write a clear, concise example indicating why, after reading this tutorial, you believe you should customize base_view, and send it to the archetypes-users mailing list. If you really do need to customize base_view, you've found a shortcoming in Archetypes, and the people on the list will inform you if that's the case. So, repeat after me: "Don't customize base_view." Good!

Now, there are six important macros to be aware of. These six macros give you the power to insert template code that is customized for your class. These macros are:

js
A macro to insert javascript into the <head> tag of the generated HTML page
css
A macro to insert CSS includes and style code into the <head> tag of the page
header
The macro that defines the topmost portion of the content area. By default, this macro has an <h1> tag that contains the title, and links for printing, emailing, etc. on the right.
body
The macro that defines the "body" area of the content. This is where the fields and their values are displayed.
folderlisting
This macro shows a list of the child content for the object. Don't confuse this with folder_contents, this is what the view tab shows for folderish objects. Folderish objects use both the body macro and the folderlisting macro.
footer
This is where AT puts the byline.

This image shows the areas generated by the header, body, and folderlisting macros

As you can see, the header macro generates what's outlined in the area marked ''header'' in red, the body macro generates the content just beneath it, and the folderlisting macro generates the listing of the objects within the folderish object.

The base_view template automatically pulls the appropriate macro from the custom view template (mytype_view, from our earlier ad-hoc example), or from the next template that we are going to explore: base.

The base Template

The base template contains four of the six macros that base_view looks for:

  • header
  • body
  • folderlisting
  • footer

The only reason why I mention base is so that you know where AT's default behavior comes from. This is important if you only want to change a little bit of a type's view. It's often helpful to copy the macro from base into your custom view template, and then start tweaking and customizing.

Widgets

Widgets are what Archetypes uses to display fields. Widgets have two parts:

The widget class
This class defines data and behavior for the widget. In most cases, you'll never need to create a custom 'Widget'-derived class. See Archetypes/Widget.py for examples.
The widget template
This is a ZPT that provides three macros: view, edit, and search. These macros display the field. Some of the macros depend on certain variables being defined in the calling template, so pay close attention. Most often, you'll only need to provide a custom widget template, and not a custom widget class.

There are all kinds of widgets out there to do all kinds of things. The Archetypes Quick Reference Manual covers the details for the various widgets in Archetypes.

[1] Unless you're wiggy.

5.2.1.6.3. Customizing Widgets

This page shows you how to customize widgets, and gives some examples of what kinds of neat tricks can be done with widget customization.

As we've already said, widgets are what Archetypes uses to display individual fields. Archetypes' built-in templates, base_view,base, and widgets/field use each field and the field's associated widget (specified in the schema) to determine which widget template to use. However, you can override a widget's template, as we will show below. Furthermore, you can create a whole new widget class, which will have data and operations specific to the display of your custom data type. Read the next section to determine how much widget hacking you need to do.

How to Determine If You Need to Create A Custom Widget Class

If you cannot find a widget in Archetypes or in a readily-available third-party product that does what you need, use the following set of questions to determine if you can just customize the template versus creating a new widget class. If you answer "no" to the following questions, a custom template is all you need:

  • Does the display of your field require helper functions to do conversions or formatting that would be difficult or cumbersome in TALES?
  • Do you have multiple AT-based classes where some fields in those classes share all of the the following characteristics?
    • The same data type
    • Similar, needs for display
    • One or more attributes that are class-specific that apply to the display (i.e. the most appropriate place to set these attributes is in the schema definition)
  • Does your custom data type need some super-specific marshalling when edited or searched that you can't get from any standard AT widget class?
  • Do you need to override or change the way that Archetypes handles the processing of the edit form for a particular field?

If you answered yes to most of the questions, then you might need to create a Widget class. If the questions aren't clear, take a look at the RichDocument tutorial . If you've got a specific enough use-case (like RichDocument) that you need custom widget classes, you're probably able to make it happen just by the sheer fact that you know you need them. [1]

Customizing Widget Templates

Creating custom widget templates is not hard, so don't be afraid. I assume if you're reading this far, you've already determined that you don't need a custom widget class, and just need to tinker a little bit with how things are done by default.

First, you should understand what you have control over by customizing a widget template itself. You're controlling the display of the widget's data, but not its label. For a StringField called myfield, the default display is something like:

myfield: some value

The only thing we can control is the display of what comes after "myfield:", which is just the data contained within the field itself (we'll talk about how to customize the display of labels later). However, if we customize the template, we can insert all kinds of nifty HTML into there! So, let's look at StringWidget's template, 'widget/string':

It's pretty straight-forward. As you can see, there are three important macros in a widget template:

  • view
  • edit
  • search

Don't concern yourself with the edit and search macros; remember, we're customizing the view. Let's start by creating a new template called my_string_widget [2]. Start like this:

Notice how we use the same pass-through macro call in the edit macro that the string template originally uses in the search macro. It's important to remember the following concept: Widget templates must define all three macros: view, edit, and search. Also, notice how there is no display code for the label; that's handled elsewhere. If you're wondering where the accessor variable comes from, that's part of the widget display code. The widget class defines the following local variables that are accessible inside widget templates:

accessor
The accessor method for the field. Call it to retrieve the value of the field.
fieldName
The name of the field.
widget
The widget object for the field.
field
The instance of the field class itself.
mode
Will be view or edit, based on the action being taken. For our purposes, it should always be view.

Now, let's modify the way that our StringField displays. For brevity, I'll just show the view macro:

Then, we should tell our type's schema to point at the new template:

    StringField('myfield',
        widget=StringWidget(
            label='Myfield',
            label_msgid='ATViewTutorial_label_myfield',
            description_msgid='ATViewTutorial_help_myfield',
            i18n_domain='ATViewTutorial',
            macro='my_string_widget',
        )
    ),

Be sure to restart Zope and reinstall using portal_quickinstaller. Now, our StringField, when rendered, looks like this:

Custom Widget

Yes, folks, it's just that easy.

[1] Again, this probably only applies to wiggy.

[2] Notice I'm breaking AT's convention here. You don't have to do that, but I find it more convenient and understandable to add a _widget to the names of my widget templates.

 

5.2.1.6.4. Total Control: The View Template

This page describes how you can control every portion of the HTML output in the content area by creating a custom view template.

Okay, so you've hacked up some custom widget templates, and they're basking in the glory of your newfound power, yet you're still not satisfied. You want to control it all! Well, I've got the information for you!

Archetypes and Type-Specific View Templates

Archetypes automatically recognizes templates with specific names, and uses the code within those templates to display your AT-based object. All of this magic happens within the base_view template. To create a custom view template, convert your type name to lowercase (the name that's listed in portal_types, or what's returned from myObject.portal_type). Now, replace spaces with underscores ( _ ). Finally, add _view to the end of the name, and you've almost got a custom view template.

See below for examples of type names and their corresponding view templates.

Type Name

View Template Name

My Type

my_type_view

SomeTypeV2

sometypev2_view

Now, to remedy that "almost" part of the above paragraph, define one or more of the following macros in your template:

  • header
  • body
  • folderlisting
  • footer

Voila! You've got a custom view template. To see how this works, create a simple template (named appropriately, of course) that contains the following code:

          Foo

          Foo

          Foo

          Foo

And, just like magic, you should see, rendered in your content area:

The Infamous "Foo" View

But Wait! Where Are All My Fields?

So now you want your data back. You said you wanted total control, and now you don't want total control. But the point of this tutorial isn't control, it's shine. So, let's explore how to mix and match existing AT templates with your custom code to make a shiny template that renders exactly how you want it.

First, keep the above "Foo" template around. It's very useful when you aren't quite sure which of the four macros is generating a portion of the content area. Simply comment out one or more of the macros, and you'll see which macro generates what portion.

Now, do you remember when I talked about using the base template as a starting point for creating custom templates? Well, that's what we'll do. Let's start by customizing the footer. The footer macro in the following template is copied directly from 'base':

Get the byline - contains details about author and modification date.

Now, let's add something below the byline, say, some important information that applies to every instance of your custom type::

Get the byline - contains details about author and modification date.

Important information that applies to every instance of my custom type.

Notice that all we had to do was copy the macro from base , and add the <p> tag with some text in it. Notice that, for example, we could have used tal:content="here/getCustomFooterData" in the </p> <p> tag if we had defined a getCustomFooterData() method in our class.

Now, let's apply this concept to the body macro, and play around with displaying fields. First, we'll start by coping the body from base into our template.

Now, we'll change up some things by adding a little bit of code into the macro. First, notice that the tal:repeat is repeating over all the fields that are not metadata. Therefore, if you want to do something for every field, put it inside this macro. You could (conceivably) rearrange the macro so that the tal:repeat loop is inside another containing block, and put TAL code before and after the display of the fields, or make use of the first and last repeat variables to achieve the same thing. So, let's do two things to customize our body macro:

  • Surround all the fields with a </p> <div> that has a custom CSS class, my-custom-at-body
  • Surround each field with a <div> that has a different custom CSS class, my-custom-at-field
 

These changes, as I'm sure you've figured out, aren't going to make much of a difference (if any) in the look of the rendered page without actually writing some custom CSS code. We now introduce the css macro:

<link href="#" type="text/css" rel="stylesheet" />
<div class="my-custom-at-body">
<div class="my-custom-at-field">&nbsp;</div>
</div>

Now, we can define a CSS stylesheet called my_custom_css.css that contains our custom CSS code:

    .my-custom-at-body {
        border: thin dashed;
        background-color: #cccccc;
        padding-top: 1em;
    }

    .my-custom-at-field {
        background-color: #ffffff;
    }

Archetypes inserts the css macro into the '' tag of the rendered page, making our custom CSS code, linked files, and includes available within the page. Our end-result would look something like this:

Custom Body Macro

If we had created custom widget templates, those would be applied to the rendered page as well.

Customizing Labels

There's still one element of control that we're missing: we still can't override the display of a field label. By customizing the display of the label, we can insert images, links, etc. into the page instead of the default label.

The macro included in our custom view template below will do that magic for us:

<link href="#" type="text/css" rel="stylesheet" />
<div class="my-custom-at-body">
<div class="my-custom-at-field">&nbsp;</div>
</div>
<label>Now presenting... Field1!</label>

Notice that I've only overridden the default label for fields labeled "myfield". The label macro in widgets/field is where the default behavior can be found. The final result looks like this:

Customized Label

Also, don't forget that you have the power to omit head,body,folderlisting, and footer by simply writing in do-nothing macros into your view template. Furthermore, you can reach into your object and retrieve field values without using the widget framework.

5.2.1.6.5. Conclusion

Some final notes about customizing Archetypes view templates

So, now you should know all of the following information:

  • How to identify which parts of an Archetypes view template are generated by the header,body,folderlisting, and footer macros
  • How to create a custom view template that overrides one or more of the header,body,folderlisting, and footer macros
  • How to create a custom widget template that works in the Archetypes framework
  • How to create a custom body template that uses Archetypes' widget rendering templates
  • How to inject custom CSS code and links to custom CSS files into your view template

Some Final Notes

I want to cover a few details about how to apply all of these tools. Some wise guy somewhere said something like, "To a man whose only tool is a hammer, every problem tends to look like a nail." Your success with Archetypes is very contingent upon selecting the appropriate tool for your specific problem. So, use the following outline of the basic AT page layout as a guideline to determine what should be customized:

  • header macro
    • Title (or id if no title is present)
    • Document actions (e.g. print, send to)
  • body macro
    • List of fields
      • Field label (from the label macro in the view template, if one exists)
      • Field value (from the widget template's view macro)
  • folderlisting macro
    • List of links to each sub-object
  • footer macro
    • byline

So, based on which parts of this standard layout you need to customize, use the appropriate macro. Keep the infamous "Foo" template around to help you with debugging. See the next page for a reference on customizing Archetypes view templates.

5.2.1.6.6. Reference

A reference for customizing Archetypes view templates

View Templates

View templates are named according to the portal_type of the class. To create the name for a view template, follow these steps to create the name of the template:

  1. Change the portal_type to lowercase.
  2. Replace all spaces with underscores (_).
  3. Append _view to the end of the name.

Archetypes will automatically locate templates with names created according to the above steps, and will make use of the macros defined within the template. View templates can define one or more of the following macros:

css
A macro for inserting type-specific CSS code, including <link> tags pointing to custom CSS files. There is no default macro for this within Archetypes; Archetypes uses the existing CSS styles in Plone to render AT-based objects.
header
This macro, by default, generates the <h1> tag containing the object's title and the document actions (print, rss, e.g.)
body
The location where the fields and values are displayed by default. When rendering fields using the existing widget mechanism, be sure to tal:define the variable field as the current field; the widget templates depend on this variable being set.
folderlisting
This is the folder listing display when viewing the view tab of a folderish object. This is not the same as the contents view.
footer
By default, this is where Archetypes puts the byline.
label
This template generates field labels.

For any of these macros that is not defined in the custom view template, Archetypes will use the default behavior in its place, taken from base or widgets/field.

Widget Templates

Use custom widget templates by naming them in the schema - insert a macro parameter into the widget's constructor in the schema, and set the value to the name of the template. For example, macro="my_widget_template". Widget templates must have the following three macros:

  • view
  • edit
  • search

Widget templates have the following local variables available within TALES expressions:

accessor
The accessor method for the current field. The code <p tal:content="accessor" /> will cause the field's value to be written within the <p> tag.
fieldName
The name of the field.
widget
The widget object for the field.
mode
Will always be view for view templates, but is useful for, say, error checking.

5.2.1.7. How to customise view or edit on archetypes content items

Explains one way to customise the view or edit templates without having to change the action of an object.

Reasons/Use Cases

I usually like to customise as little as possible so that more of my page templates are just like the plone default templates. I find this helps when I move to a new version and also makes doing styling using CSS easier.

Another use case is if I want to generate a form using the schema but I need it to do different things based on which button is pushed, you can accomplish this with putting named buttons on the form in combination by using portal_formcontroller to override what happens on a submit. E.g. importing data from CSV, in a seperate schemata I have a form.button.Import button and on this schemata I only show this button and the cancel button (instead of save, nex previous etc.) and then I customise the portal_formcontroller action (and validation) so content_edit (the script that does the saving) goes to a script that does the importing before going back to the view action.

Archetypes base_view and base_edit

Both of these templates have several macros which are gotten by from other page templates. They are setup in such a way that they will look for a template named with the content type for these macros and then default to the generic archetypes macros. I.e. say you have a content type 'Newsletter' base_view looks for a template named 'newsletter_view' if it finds it and it contains the right macros it will use those instead of the default 'view_macros' (found in 'portal_skins/archetypes' skin folder.

Below is a skeleton example of a custom view template showing the different things you can customise. See base.pt

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"

      lang="en"

      metal:use-macro="here/main_template/macros/master"

      i18n:domain="plone">

<body>



<metal:main fill-slot="main">

        <!-- header, H1 with title in it -->

        <metal:header define-macro="header">

        

        </metal:header>

        

        <!-- body macro where all the fields are -->

        <metal:body define-macro="body">

                

        </metal:body>

        

        <!-- folderlisting that shows sub-objects if there are any -->

        <metal:folderlisting define-macro="folderlisting">

                

                

        </metal:folderlisting>

        

        <!-- footer, by line created date etc. -->

        <metal:footer define-macro="footer">

                

        </metal:footer>

        

</metal:main>

</body>

</html>

Below is an skeleton of a custom edit template:

<html xmlns="http://www.w3.org/1999/xhtml"

      xml:lang="en"

      lang="en"

      xmlns:tal="http://xml.zope.org/namespaces/tal"

      xmlns:metal="http://xml.zope.org/namespaces/metal"

      xmlns:i18n="http://xml.zope.org/namespaces/i18n"

      i18n:domain="plone">



  <metal:head define-macro="topslot">

  </metal:head>

  

  <metal:head define-macro="javascript_head">

  </metal:head>



  <body>

        <!-- header, h1 of Edit <Type>, schemata links and webdav lock message -->

        <metal:header define-macro="header">



        </metal:header>

        

        <!-- typedesription, typeDescription from the content type -->

        <metal:typedescription define-macro="typedescription">



        </metal:typedescription>



        <!-- body, editform , fields, buttons, the default macro 

             contains a number of slots which usually provide enough

             ways to customise so often I use that macro and just 

             fill the slots

        -->

        <metal:body define-macro="body">

            <metal:default_body use-macro="here/edit_macros/macros/body">

              <!-- inside the fieldset but above all the fields -->

              <metal:block fill-slot="extra_top">

              </metal:block>

              

              <!-- listing of the fields, usually I won't customise this

              <metal:block fill-slot="widgets">

              </metal:block>

              -->



              <!-- below the fields above the formControls (hidden fields for refernce stuff is above buttons) -->

              <metal:block fill-slot="extra_bottom">

              </metal:block>



              <!-- within the formControls these are the default previous, next, save, cancel buttons -->

              <metal:block fill-slot="buttons">

              </metal:block>



              <!-- within the formControls a slot for extra buttons -->

              <metal:block fill-slot="extra_buttons">

              </metal:block>



            </metal:default_body>

        </metal:body>



        

        <!-- footer, by line created date etc. -->

        <metal:footer define-macro="footer">

        

        </metal:footer>



  </body>



</html>



See the templates into Products.Archetypes:skins/archetypes for examples about how does Archetypes work by default: get the field lists, hook up translation, handle form processing and more. Using them as a base and customizing only the neccessary bits can make the job much easier than starting from scratch.

How to do it

Lets say your content type is 'Newsletter'

Steps for View

  1. Create a page template (either file system of in ZMI) called 'newsletter_view'
  2. Use the skeleton and comment out the macros you wish to keep the same. I.e. the ones you want to use from view_macros template (in portal_skins/archetypes)
  3. Put your code into the relevant macros/slots.
  4. Test and you are done.

Steps for Edit

  1. Create a page template called 'newsletter_edit'
  2. Use the skeleton and then comment out the macros you wish to use the default for. (from edit_macros).
  3. Put your code into the relevant macros/slots.
  4. Test and you are done.

5.2.1.8. Create an Alternative Edit Template

Suppose you've a content type and you want to keep the default "edit" template, but you want also to create another edit template suited for editing only some particular metadata.

Think of the standard edit as a "full" edit, while your new edit is a custom version. They co-exist together. This method also works if you've already a mycustomtype_edit edit template, and you want to have another one.

Limiting the Fields Available to Edit

Step 1: Copy the base_edit and the edit_macros (you can do it by simply customizing them) and change their id (rename) to mynewedit and mynewmacros

Step 2: Modify mwynewedit to point to mynewmacros:

  • edit_template python:'newemacros';
  • edit_macros python:path('here/newemacros/macros | nothing');

so this edit template will pick up the macros from your custom version.

Step 3: Modify mynewmacros as you need. For example, if you want to display only some fields, you can (in<metal:block define-slot="widgets"> which manages widgets display) change the template as below:

<tal:fields repeat="field 
python:schematas[fieldset].editableFields(here, visible_only=True)">

   <div tal:omit-tag="" 
         tal:condition="python:field.getName() 
         in ['title','myfield1','myfield2','myfield3','myfield4']">

       <metal:fieldMacro use-macro="python:here.widget(field.getName(),
        mode='edit')" />

     </div>

</tal:fields>

So only 'title','myfield1','myfield2','myfield3','myfield4' will be displayed and you will avoid people editing unwanted fields (even if they can).

Step 4: If there's a failure, the user should go back to your form, so in mynewedit -> actions, change failure from string:edit to string:mynewedit 

Dealing with Validation

Suppose you've some validated fields, or you want to run custom code when the user saves the form. The validation will kick in when you'll press "save". If you've some required fields, but they're not in the fields listed above, you'll get the red warning and your data will not be saved. So you have to bypass it, and we need to do it in two places:

Step 1:

  • edit mwynewedit -> validation -> delete the validation step.
  • edit mwynewedit -> actions -> change string:content_edit to string:mycontent_edit (a custom content edit)

Step 2:

  • Find content_edit in portal_skins/archetypes and customize it. Then rename it to mycontent_edit. Here you can add custom code, sending mail and so on.
  • Edit it -> actions -> delete the validate_integrity step
  • Edit it -> actions -> change any string:edit to string:mynewedit , so after saving you'll go to your edit form if there are any failures (should not if you remove the validation)

Now your form can edit your content type without any restrictions. If you need some restrictions, just don't delete the validation steps above and customize the validation scripts validate_base and validate_integrity (renaming them and pointing to them in the validation steps above).

This is enough to perform your "very" own custom edit, with custom saving and custom validation, leaving the default one untouched.

5.2.2. Where to find what you need

Where the skin lives in Plone and in your own theme product.

Through the Web

You can customize all page templates, skins and CSS very easily through the web.

  • Site Setup > Zope Management Interface > portal_skins

Locate the item you want to change, click the customize button and a copy will be dropped into the custom layer for you.

You can also add new page templates, Python scripts and files (for CSS and JavaScript) to the custom layer by using the drop-down list in the ZMI. In most cases, though, you will find it easier to locate a template you want to base your new template on, customize it and then rename it through the ZMI.

Don't forget that, if you're hunting for something, the Find tab in the ZMI can be very useful.

Plone Default Skin on the File System

All the page templates, style sheets, scripts and JavaScript for the Plone Default skin can be found in the CMFPlone product:

[your products directory]/CMFPlone/skins
You'll see there are a number of directories corresponding to specific skin layers. Most of this should be self-explanatory, but its worth remembering that only the generic templates live in plone_templates. If you want to track down a specific content view (e.g., document_view) then you'll need to look in plone_content.

In your own Theme Product

The skins folder in your theme product/skins/[your theme namespace].[your theme name]_custom_templates | custom_images | styles
These directories will form your skin layers. Your templates, images, and style sheets can go here. If you asked it to, the plone3_theme paster template will have provided blank style sheets to override the Plone Default ones.
/skins.zcml
When your Zope instance starts up, this turns your directories into skin layers
Subsidiary files used for installing and setting up the Skin/profiles/default/skins.xml | cssregistry.xml | jsregistry.xml
When your theme is installed in your Plone site, these set up the hierarchy of skin layers, and register your style sheets and JavaScript with the registries
 

5.2.3. Style Sheets

Style Sheets

5.2.3.1. The Custom Style Sheet and Base Properties

You can do a great deal by simply overriding Plone's existing styles. There's a stylesheet available for just this purpose.

You'll find an empty stylesheet called ploneCustom.css in

  • [your products directory]/CMFPlone/skins/plone_styles or
  • Site Setup > Zope Management Interface > portal_skins > plone_styles

This stylesheet is always loaded last on a page, and can be used, therefore, to override any other styles. There's an excellent and comprehensive tutorial on this here:

 

DTML

You'll see that ploneCustom.css has a .dtml extension, and the CSS inside is wrapped in

/* <dtml-with base_properties> */
 .......
/* </dtml-with> */

DTML is another Zope templating language, which in this case is deployed so that particular variables can be picked up from a properties sheet (base_properties.props) - for example:

#portal-column-one {
    vertical-align: top;
    width: <dtml-var columnOneWidth missing="16em">;
    border-collapse: collapse;
    padding: 0;
}


We wouldn't recommend using this technique as it is likely to be phased out, but it is as well to know that it is there. You can sometimes get caught out if you're customizing ploneCustom.css and accidentally delete the top or bottom "dtml-with" statement, or forget to add the .dtml extension.

5.2.3.2. CSS

Getting familiar with Plone's style sheets

5.2.3.2.1. CSS Quick Start

instructions for finding and modifying Plone's base_properties and CSS.

Purpose, Prerequisites & Audience

This tutorial describes the use of CSS (Cascading Style Sheets) in Plone 3.x and is intended for site customizers who are familiar with CSS and have administrative privileges on a Plone site. The approach here is strictly for making through-the-web modifications to stylesheets.

No previous knowledge of Python, Plone or Zope is required and examples will walk those new to Plone through every step required to make CSS modifications to Plone.  If you have set up a Plone site but are otherwise new to Plone, this tutorial is for you.  If you are a web designer who needs to work as part of a Plone team, this tutorial might help clarify how CSS is used in Plone.

Using the Right Tools

By far, the most popular website CSS introspector tool is Mozilla's Firebug tool. No matter the level of your experience, Firebug is the ultimate CSS debugging tool and all themers should use it.

The basic idea is that you can step through the HTML that frames your Plone pages, see the CSS that is applied to the HTML. You can even change the CSS settings on the fly to experiment with the layout of your site. For help on using Firebug, click here.

Introduction

Plone's extensive use of CSS gives customizers a great amount of control over the appearance of a Plone site.  The quickest way to get a sense for this is to look at a Plone site with styles disabled in your browser.  In Firefox you can disable styles with "View > Page Style > No Style".  (Try this now.)  Clearly, CSS is very heavily used which makes for an excellent separation of form and content.

Custom styling of a Plone site can be performed in one of the following ways:

  1. modifying 'base_properties' 
  2. overriding existing style by adding styling information to ploneCustom.css
  3. adding, deleting or reordering stylesheets

This tutorial will describe techniques 1 and 2.

Note that serious customizations of the Plone interface are best done by creating custom products.  These allow you to encapsulate all of your style and template changes in one place, save them and reapply them elsewhere.  The instructions here apply to 'quick and dirty' customizations for an individual site.

Navigating the ZMI

The following folders in the ZMI (Zope Management Interface) allow you control what stylesheets are used and their contents:

ZMI > portal_CSS
Controls the registration and ordering of stylesheets within Plone. 
 ZMI > portal_skins > custom
 Location for locally customized versions of the stylesheets found in ZMI > portal_skins > plone_styles.
ZMI > portal_skins > plone_styles
Location of default base_properties and stylesheets.

Simple Customizations

Enabling development mode

Before beginning any CSS customization you should turn on debug-/development mode.  This will guarantee that caching and compression of CSS is disabled.

Here is how you enable debug-/development mode:

  1. log on to your Plone site as the 'admin' user or with your manager account
  2. add '/manage' to the URL to access the ZMI
  3. navigate to ZMI > portal_css
  4. click the Debug/development mode checkbox
  5. click the "Save" button

When you are finished making your CSS modifications you should disable debug-/development mode as it does impact the performance of your Plone site.

Modifying base_properties

Plone provides a set of base_properties that control some of Plone's color, font, logo and border defaults.  These properties allow you to modify the basic look-and-feel of a site without working directly with Plone's CSS files and provides the simplest way to do basic customization.  Individual property names are reasonably self-explanatory (linkColor, borderStyle, etc.) and accept standard CSS style values.

Here is how you modify the base_properties:

  1. enable development mode
  2. navigate to ZMI > portal_skins > plone_styles > base_properties
  3. click the Customize button
  4. modify individual properties using CSS style values
  5. click the "Save Changes" button (at the bottom of the page)

Section 3 of this tutorial provides more detailed descriptions of each of the base properties.

Making CSS modifications

The next step beyond base_property modifications is overriding Plone's CSS with custom CSS of your own.  Plone provides the ploneCustom.css stylesheet for site customizations.  The difficult part of this for newcomers to plone is figuring out the CSS selectors that are used within Plone.

For many people, the Firebug or Web Developer Firefox extensions provide the easiest way to inspect the style associated with HTML elements in a web page.  Either of these provides easy access to the CSS selectors and style information needed to create a custom stylesheet.

Note that the .css files in ZMI > portal_skins > plone_styles are actualy dtml templates, meaning that they can utilize base_properties to make global changes via variables.  This means that they may contain references to base_properties alongside standard CSS as in the following example from public.css:

h1, h2 {
    border-bottom: &dtml-borderWidth; &dtml-borderStyle; &dtml-globalBorderColor;;
    font-weight: normal;
}d

Here is how you add CSS customizations to your Plone site:

  1. enable development mode
  2. navigate to ZMI > portal_skins > plone_styles > ploneCustom.css
  3. click the Customize button
  4. add CSS
  5. click the "Save Changes" button (at the bottom of the page)

Sections 4 and 5 of this tutorial describe Plone's stylesheets and the CSS selectors associated with various elements of the Plone interface.

Restoring default styling

When you first start playing with base_properties and stylesheets you will want the freedom to make lots of changes knowing that you can easily get back to the default settings.  Plone makes this easy by always keeping custom versions of base_properties and stylesheets in a separate folder. When Plone assembles the CSS stylesheets it looks for custom versions first and uses these when found.  If custom versions are not found it uses the default versions.  To restore default settings you only need to delete the custom versions.

Here is how you restore default styling to your Plone site:

  1. enable development mode
  2. navigate to ZMI > portal_skins > custom
  3. check base_properties and/or ploneCustom.css (or anything else you've modified)
  4. click the "Delete" button

5.2.3.2.2. CSS Customization Examples

Examples of Plone 3.0.x customization through base_properties and CSS.

The following examples are provided to get you started making style changes to your site.  They are not intended to be complete examples.  In each case we will take an existing website to use as our target and make change that imitate the target style.  Finishing up the styling is left as an exercise for the student.

Example 1:  'Austin Neon' style

One of the easiest ways to see what the base_properties control is to create a 'dark' style for your site.  As an example of where this style is appropriate we will use Austin Neon as our target site.  As always, the Firebug extension for Firefox is invaluable for inspecting the style in our target site.  If you haven't already, please install and familiarize yourself with Firebug before attempting to discover how the target site is styled.

Initial base_properties settings

The first step will be to modify our Plone site's base_properties as outlined in section 1.  The following screenshot shows base_properties that come close to mimicing the colors that are found in our target site.  Go ahead and make these changes to your base_properties now. 

neon_base_properties

Further styling with CSS

TODO

Example 2:  'New York Times' style

Anyone wish to make a contribution here?

5.2.3.2.3. base_properties

Description of all base_properties in Plone 3.0.x

The following list of base_properties are used for styling throughout Plone.  They can be edited through the ZMI at

ZMI > portal_skins > plone_styles > base_properties

Plone 3.0.x base_properties

logoName
the file name of the portal logo
fontFamily
the font family used for all text that is not headers
fontBaseSize
the base font size that everything is calculated from
fontColor
the main font color
fontSmallSize
used for various elements like buttons and discreet text
discreetColor
the font color of discreet text
backgroundColor
the background color
linkColor
the color used on normal links
linkActiveColor
color used on active links
linkVisitedColor
color used on visited links
borderWidth
the width of most borders in Plone
borderStyle
the style of the border lines, normally solid
borderStyleAnnotations
style of border lines on comments etc
globalBorderColor
the border color used on the main tabs, the portlets etc
globalBackgroundColor
background color for the selected tabs, portlet headings etc
globalFontColor
the color of the font in the tabs and in portlet headings
headingFontFamily
font family for h1/h2/h3/h4/h5/h6 headlines
contentViewBorderColor
the content view tabs border color
contentViewBackgroundColor
the content view tabs background color
contentViewFontColor
the font color used in the content view tabs
inputFontColor
the font color used for input elements
textTransform
whether to lowercase text in portlets, tabs etc.
evenRowBackgroundColor
the background color of even rows in listings
oddRowBackgroundColor
the background color of even rows in listings
notifyBorderColor
border color of notification elements like the status message, the calendar focus
notifyBackgroundColor
background color of notification elements like the status message, the calendar focus
lpBackgroundColor
background color of information pop-ups (currently not used)

5.2.3.2.4. Default CSS Stylesheets

Description of the default CSS stylesheets in Plone 3.0.x.

Plone's default stylesheets are described below along with list of the CSS selectors defined in each.  The stylesheets are presented in the same order they are defined in ZMI > portal_css so that style defined in stylesheets lower on the page will override style defined in earlier stylesheets.

member.css

Styles for workflow states of logged in users.

.state-private { ... }
.state-visible { ... }
.state-published { ... }
.state-pending { ... }
.state-expired { ... }
.syndicated { ... }

RTL.css

Right-to-Left styles for Arabic and Hebrew.

base.css

Styles for base elements (HTML tags).

body { ... }
table { ... }
a { ... }
img { ... }
p { ... }
p img { ... }
hr { ... }
h1, h2, h3, h4, h5, h6 { ... }
h1 a{ ... }
h1 { ... }
h2 { ... }
h3 { ... }
h4 { ... }
h5 { ... }
h6 { ... }
ul { ... }
ol { ... }
li { ... }
dt { ... }
dd { ... }
abbr, acronym, .explain { ... }
abbr .explain { ... }
q { ... }
blockquote { ... }
code, tt { ... }
pre { ... }
ins { ... }
del { ... }

public.css

Lots of styles for public-facing elements.

/* Accessibility elements, applied by JS */
body.largeText { ... }
body.smallText { ... }

/* Padding for the columns */
#portal-column-one .visualPadding { ... }
#portal-column-two .visualPadding { ... }

/* Content area */
h1, h2 { ... }
body.kssActive h2.inlineEditable:hover, body.kssActive h1.inlineEditable:hover { ... }
h3, h4, h5, h6 { ... }

.documentFirstHeading { ... }
.documentContent { ... }
.documentContent ul { ... }
.documentContent ol { ... }

/* Links with differently colored link underlines - only for content */
.documentContent p a { ... }
.documentContent p a:visited { ... }
.documentContent p a:active { ... }
#content a:target { ... }
.documentContent li a { ... }
.documentContent li a:visited { ... }
.documentContent li a:active { ... }
.documentContent dd a { ... }
.documentContent dd a:visited { ... }
.documentContent dd a:active { ... }
/* End links */

#visual-portal-wrapper { ... }

/* Logo properties */
#portal-logo img { ... }

/* The skin switcher at the top, only shows up if you have multiple skins available */
#portal-skinswitcher { ... }
#portal-skinswitcher a { ... }
#portal-top { ... }

/* Site-wide action menu - font size, contact, index, sitemap etc */
#portal-siteactions { ... }
#portal-siteactions li { ... }
#portal-siteactions li a { ... }
#portal-siteactions li.selected a { ... }
#portal-siteactions li a:hover { ... }

/* Searchbox style and positioning */
#portal-searchbox { ... }
#portal-advanced-search { ... }
#portal-advanced-search a { ... }

/* Search results elements */
dl.searchResults dt { ... }
form.searchPage { ... }
input.searchPage { ... }
form.searchPage input.searchButton { ... }

/* LiveSearch styles */
.LSRes { ... }
#LSHighlight, .LSHighlight { ... }
.LSRow { ... }
.LSRow a { ... }
.LSDescr { ... }
.LSResult { ... }
.LSShadow { ... }
.livesearchContainer { ... }
* html .livesearchContainer { ... }
#livesearchLegend { ... }
* html #livesearchLegend { ... }

/* Workaround for Internet Explorer's broken z-index implementation */
.LSIEFix { ... }
.LSBox { ... }
#LSNothingFound { ... }
.LSBox label { ... }

/* The global section tabs. */
#portal-globalnav { ... }
#portal-globalnav li { ... }
#portal-globalnav li a { ... }
#portal-globalnav li.selected a { ... }
#portal-globalnav li a:hover { ... }
/* Bar with personalized menu (user preferences, favorites etc) */
#portal-personaltools { ... }
#portal-personaltools .portalUser { ... }
/* Used on all descriptions relevant to those not logged in */
#portal-personaltools .portalNotLoggedIn { ... }
#portal-personaltools li { ... }
#portal-personaltools li a { ... }
#portal-personaltools .visualIconPadding { ... }
.visualCaseSensitive { ... }
#portal-languageselector { ... }
#portal-languageselector li { ... }
/* The path bar, including breadcrumbs and add to favorites */
#portal-breadcrumbs { ... }
#portal-breadcrumbs a { ... }
.breadcrumbSeparator { ... }
.addFavorite { ... }
.documentEditable { ... }
#content-news h1 { ... }

/* Only h5/h6 headlines in the content area should have the discreet color */
#content h5, #content h6 { ... }
.newsItem { ... }
.newsImage { ... }
.newsImageContainer { ... }
.newsContent { ... }
.newsContent ul, .newsContent li { ... }
.newsAbout { ... }
.newsAbout li { ... }
.newsFooter { ... }
.newsFooter li { ... }
.documentActions { ... }
.documentActions ul { ... }
.documentActions li { ... }
.documentActions a { ... }

/* Status messages */
dl.portalMessage { ... }
dl.portalMessage a { ... }
dl.portalMessage dt { ... }
dl.portalMessage dd { ... }
dl.warning dt { ... }
dl.error dt { ... }
dl.warning dd { ... }
dl.error dd { ... }

/* The summary text describing the document */
.documentDescription { ... }
.documentByLine { ... }
dl.searchResults span.documentByLine { ... }
#category ul { ... }
#category ul li { ... }
.discussion { ... }
.even { ... }
.odd { ... }
.visualHighlight { ... }
.discreet { ... }
.pullquote { ... }
.callout { ... }
.notify, .documentEditable * .notify { ... }
.card { ... }
.card a { ... }
.portrait { ... }
.portraitPhoto { ... }

/* The table used for listings - horizontal and vertical variants */
table.listing, .stx table { ... }
table.listing th, .stx table th { ... }
table.listing .top { ... }
table.listing .listingCheckbox { ... }
table.listing td, .stx table td { ... }
table.listing a { ... }
table.listing a:hover { ... }
table.listing img { ... }
table.listing td a label, .stx table td a label { ... }

/* Vertical addition class */
table.vertical { ... }
table.vertical th { ... }
table.vertical td { ... }

/* grid addition class */
table.grid td { ... }

/* plain table class with light gray borders */
table.plain, table.plain td, table.plain th { ... }

/* Batch selector */
.listingBar { ... }
.listingBar span.previous, .listingPrevious { ... }
.listingBar span.next, .listingNext { ... }
.listingBar img { ... }
.listingBar a { ... }
.tileItem { ... }
.tileHeadline { ... }
.tileHeadline a { ... }
.tileBody { ... }
.tileImage { ... }
.eventDetails { ... }

/* Useful deviations from regular style on elements */

/* List classes without markers */
ul.visualNoMarker, ol.visualNoMarker { ... }
ul.discreet { ... }
textarea.proportional { ... }
.productCredits { ... }
#portal-footer { ... }
#portal-footer p { ... }
#portal-footer a { ... }
#portal-footer a:visited { ... }
#portal-footer a:hover { ... }
#portal-colophon { ... }
#portal-colophon ul { ... }
#portal-colophon ul li { ... }
#portal-colophon ul li a { ... }

.feedButton { ... }
.poweredBy { ... }

/* Sitemap styles */
#portal-sitemap { ... }
#portal-sitemap a { ... }
#portal-sitemap a:hover { ... }
#portal-sitemap .navTreeLevel1 { ... }
#portal-sitemap .navTreeLevel2 { ... }

/* Album view classes */
.photoAlbumEntry { ... }
.photoAlbumEntry img { ... }
.photoAlbumEntryWrapper { ... }
.photoAlbumEntry a { ... }
.photoAlbumFolder { ... }
.photoAlbumEntryTitle { ... }

/* Link types */
a.link-parent { ... }
#content .link-category { ... }
#content .link-user { ... }
#content .link-comment { ... }
#content .link-anchor { ... }
#content .link-presentation { ... }
#content .link-wiki-add { ... }

/* Handling external/internal links, we first set the icon on all links, then
remove it from the ones that are local - for both http and https */
#content a[href ^="http:"], #content a.link-external { ... }
#content a[href ^="https:"], #content a.link-https { ... }
#content a[href ^="&dtml-portal_url;"] { ... }

/* Protocol-specific links */
#content a[href ^="mailto:"], #content a.link-mailto { ... }
#content a[href ^="news:"], #content a.link-news { ... }
#content a[href ^="ftp:"], #content a.link-ftp { ... }
#content a[href ^="irc:"], #content a.link-irc { ... }
#content a[href ^="callto:"], #content a.link-callto { ... }
#content a[href ^="webcal:"], #content a.link-webcal { ... }
#content a[href ^="feed:"], #content a.link-feed { ... }

#content .link-plain { ... }

/* For ghosted elements */
.visualGhosted { ... }

/* Fullscreen */
body.fullscreen #portal-logo, body.fullscreen #portal-siteactions { ... }
body.fullscreen #portal-globalnav { ... }
body.fullscreen #portal-searchbox { ... }

/* Kupu image alignment classes */
.image-left { ... }
.image-inline { ... }
.image-right { ... }
dd.image-caption { ... }
dl.captioned { ... }

/* Dashboard */
#dashboard-info-message { ... }
#dashboard { ... }
#dashboard-portlets1,
#dashboard-portlets2,
#dashboard-portlets3 { ... }
#dashboard-portlets4 { ... }
#dashboard-portlets1 a,
#dashboard-portlets2 a,
#dashboard-portlets3 a,
#dashboard-portlets4 a { ... }
#dashboard-portlets1 dl.portlet,
#dashboard-portlets2 dl.portlet,
#dashboard-portlets3 dl.portlet,
#dashboard-portlets4 dl.portlet { ... }
div.managedPortlet.portlet { ... }
#dashboard select { ... }
.portletAssignments { ... }
#dashboard-portlets1 div.managedPortlet a,
#dashboard-portlets2 div.managedPortlet a,
#dashboard-portlets3 div.managedPortlet a,
#dashboard-portlets4 div.managedPortlet a { ... }
#dashboard-portlets1 div.managedPortlet span a,
#dashboard-portlets2 div.managedPortlet span a,
#dashboard-portlets3 div.managedPortlet span a,
#dashboard-portlets4 div.managedPortlet span a{ ... }
#dashboard-actions { ... }
#dashboard-actions ul { ... }
#dashboard-actions ul li { ... }
#dashboard-actions ul li.portalUser { ... }

/* manage portlets */
.section div { ... }

columns.css

Styles for table-based columns also known as "left slot", "right slot", etc.

#portal-columns { ... }
#portal-column-one { ... }
#portal-column-content { ... }
#portal-column-two { ... }
body.fullscreen #portal-column-one, body.fullscreen #portal-column-two { ... }
body.fullscreen #portal-column-content { ... }

authoring.css

Styles associated with authoring elements visible to content providers.

/* Editable border */
.contentViews { ... }
.contentViews li { ... }
.contentViews li a { ... }
.contentViews .selected a { ... }
.contentViews li a:hover { ... }
.configlet .contentViews { ... }

/* begin ECMAScript Content Action Menus */
.contentActions { ... }
.contentActions ul, .contentActions li { ... }
.contentActions li { ... }
.contentActions a { ... }
.contentActions span.subMenuTitle { ... }
.contentActions a span.subMenuTitle { ... }
.actionMenu { ... }
.actionMenu .actionMenuHeader { ... }
.actionMenu.activated .actionMenuHeader { ... }
.actionMenu .actionMenuHeader a { ... }
.arrowDownAlternative { ... }
.actionMenu .actionMenuContent { ... }
.actionMenu.activated .actionMenuContent { ... }
.actionMenu.activated .actionMenuContent { ... }
.actionMenu.deactivated .actionMenuContent { ... }
.actionMenu .actionMenuContent ul { ... }
.actionMenu .actionMenuContent li { ... }
.actionMenu .actionMenuContent li a { ... }
.actionMenu .actionMenuContent .selected { ... }
.actionMenu .actionMenuContent li a:hover { ... }
.actionMenu .actionMenuContent .actionSeparator a { ... }
#templateMenu li a { ... }
/* end ECMAScript Content Action Menus */

ul.configlets { ... }
ul.configlets li { ... }
ul.configlets li a { ... }
ul.configlets li a:visited { ... }
ul.configlets li a:active { ... }
ul.configlets li label { ... }
ul.configletDetails { ... }
ul.configletDetails li { ... }
ul.configletDetails li a { ... }
ul.configletDetails li label { ... }

/* Additional STX workaround classes */
.stx table p { ... }
.stx table { ... }
.stx table td { ... }

.reviewHistory { ... }
.comment { ... }
.comment h1, .comment h2, .comment h3, .comment h4, .comment h5, .comment h6 { ... }
.comment h3 a { ... }
.commentBody { ... }
.spacer { ... }

/* Collapsible elements */
dl.collapsible { ... }
dl.collapsible dt.collapsibleHeader { ... }
dl.collapsible dd.collapsibleContent { ... }

/* for IE the following isn't needed, that's why the css2 selector is used */
dl.collapsible dd.collapsibleContent > dl { ... }
dl.expandedInlineCollapsible dt.collapsibleHeader, dl.expandedBlockCollapsible dt.collapsibleHeader { ... }
dl.collapsedBlockCollapsible { ... }
dl.collapsedBlockCollapsible dt.collapsibleHeader { ... }
dl.collapsedInlineCollapsible dd.collapsibleContent, dl.collapsedBlockCollapsible dd.collapsibleContent { ... }
dl.collapsedInlineCollapsible { ... }
dl.collapsedInlineCollapsible dt.collapsibleHeader { ... }

.configlet .documentEditable { ... }
.documentEditable .documentContent { ... }
.label { ... }
.optionsToggle { ... }

#portal-column-content fieldset > * input:focus, #portal-column-content fieldset > * textarea:focus { ... }

.highlightedSearchTerm { ... }
dl.searchResults .highlightedSearchTerm { ... }
.noInheritedRoles { ... }
.currentItem { ... }
tr.dragging td { ... }
.draggingHook { ... }
.notDraggable { ... }

.managePortletsLink { ... }
ul.formTabs { ... }
li.formTab { ... }
li.formTab a { ... }
li.formTab a { ... }
li.firstFormTab a { ... }
li.lastFormTab a { ... }
li.formTab a.selected { ... }
li.formTab a:hover { ... }
li.formTab a.notify { ... }
li.formTab a.required span { ... }
li.formTab a.notify:hover { ... }
.formPanel { ... }
.formPanel.hidden { ... }
div.formControls input.hidden { ... }

portlets.css

Styles associated with components of individual portlets.

/* Main portlet elements */
.portlet { ... }
.portlet a { ... }
.portlet a.tile { ... }

.portletItem a:visited, .portletFooter a:visited { ... }
.portletHeader { ... }
.portletHeader a { ... }
.portletItem { ... }
.portletItem ol { ... }
.portletItemDetails { ... }
.portletFooter { ... }

/* Elements that enable layout with rounded corners */
.portletTopLeft { ... }
.portletTopRight { ... }
.portletBottomLeft { ... }
.portletBottomRight { ... }

/* Calendar elements - used in the calendar rendering */
.dayPopup { ... }
.date { ... }
.portletCalendar { ... }
.portletCalendar dt { ... }
.portletCalendar dd { ... }
.portletCalendar a { ... }
.portletCalendar a:hover { ... }
.ploneCalendar { ... }
.ploneCalendar td { ... }
.ploneCalendar .weekdays th { ... }
.ploneCalendar .event { ... }
.ploneCalendar .todayevent { ... }
.ploneCalendar .todaynoevent { ... }

.managePortletsLink { ... }
div.portlets-manager div.section { ... }
div.managedPortlet { ... }
.managedPortlet .portletHeader { ... }
.managedPortlet a { ... }
.managedPortletActions { ... }
.managedPortletActions a { ... }
.managedPortletActions a.up,
.managedPortletActions a.down { ... }
.managedPortletActions a.delete { ... }

/* Table of Contents styling - essentially a portlet with smaller fonts and aligned right + limited in width */
.toc { ... }

controlpanel.css

Styles associated with the Plone control panel.

.inlineDisplay { ... }

table.controlpanel-listing { ... }
table.controlpanel-listing td, table.controlpanel-listing th { ... }
table.controlpanel-listing dl { ... }
table.controlpanel-listing dd { ... }
table.controlpanel-listing dl dt a .trigger{ ... }
table .controlpanel-listing td { ... }
table.controlpanel-listing td.checker{ ... }
table.controlpanel-listing th.smallcolumn { ... }

.chooser-right { ... }

.rule-element { ... }
.rule-element dl { ... }
.rule-element dl dd { ... }
.rule-updown, .rule-operations { ... }

print.css

Print styles for CSS2-capable browsers.  Much of this stylesheet has to do with hiding components that an inappropriate for printed documents.

deprecated.css

Styles for deprecated elements that will disappear from plone in a future release.

navtree.css

Styles associated with the navigation tree.

.portletNavigationTree { ... }
.navTree { ... }
.navTree li { ... }
.navTreeItem { ... }
.navTreeItem a, dd.portletItem .navTreeItem a { ... }
.navTreeItem a:hover, dd.portletItem .navTreeItem a:hover { ... }
.navTreeCurrentItem { ... }
li.navTreeCurrentItem { ... }
li.navTreeCurrentItem a, li.navTreeCurrentItem a:hover { ... }

.navTreeLevel0 { ... }
.navTreeLevel1 { ... }
.navTreeLevel2 { ... }
.navTreeLevel3 { ... }
.navTreeLevel4 { ... }
.navTreeLevel5 { ... }

invisibles.css

Styles for invisible and accessibility elements.

/* List classes without markers */
ul.visualNoMarker, ol.visualNoMarker { ... }
.visualOverflow { ... }
.visualOverflow pre, .visualOverflow table, .visualOverflow img { ... }

/* Accessibility and visual enhancement elements */
.hiddenStructure { ... }
.contentViews .hiddenStructure, .contentActions .hiddenStructure { ... }
.hiddenLabel { ... }

/* Helper element to work with CSS floats */
.visualClear { ... }

/* Hiding helper elements for old browsers */
.netscape4 { ... }

forms.css

Styles associated with forms.

textarea { ... }
input { ... }

input[type=checkbox] { ... }

#searchGadget { ... }

button { ... }
select { ... }
form { ... }
fieldset { ... }
legend { ... }
label { ... }
optgroup { ... }
option { ... }
optgroup > option { ... }

dl.enableFormTabbing dd { ... }

#login-form { ... }
#login-form .field { ... }
#login-form input { ... }
#login-form input.context { ... }

#forgotten-password { ... }

.standalone, .documentEditable * .standalone { ... }
.context, .formControls .actionButtons .button, .documentEditable * .context { ... }
.destructive, .documentEditable * .destructive { ... }
input.searchButton { ... }
.searchSection { ... }
.searchSection label:hover { ... }

/* The edit form elements */
.field { ... }
.field .field { ... }
.fieldRequired { ... }
.fieldUploadFile { ... }
.fieldTextFormat { ... }
.formHelp { ... }
.formHelp:hover { ... }
div.error { ... }
.error .fieldRequired { ... }

/* Styles to make the editing widgets look more like their view counterparts */
#archetypes-fieldname-title input, input#form\.title { ... }
#archetypes-fieldname-description textarea, textarea#form\.description { ... }
input.inputLabelActive { ... }

textarea#form\.description { ... }

tr.selected { ... }

.kupu-save-message { ... }

ploneKss.css

Not accessible from ZMI > portal_skins > plone_styles.

ploneCustom.css

This is where your locally modified styles should go.  By default, no styles are defined in this stylesheet.

kupustyles.css

Not accessible from ZMI > portal_skins > plone_styles.

kupuplone.css

Not accessible from ZMI > portal_skins > plone_styles.

kupudrawerstyles.css

Not accessible from ZMI > portal_skins > plone_styles.

5.2.4. Skin Layers

Skin layers

5.2.4.1. Skin Layers

Templates, scripts, images, CSS and JavaScript files are organized with skin layers.

Note: in the context of components, "layer" has a slightly different meaning.

A skin is comprised of a series of skin layers. On the file system, each layer is a directory. In the Zope Management Interface (ZMI), each layer appears in portal_skins as a separate folder (containing page templates, style sheets or Python scripts).

These have two uses.

  • Firstly they keep things organized. If you take a look at the Plone Default Skin (part of which is shown in portal_skins in the screenshot above) you'll see that they've separated out templates, scripts, styles and images into separate skin layers.
  • More importantly they have an order of precedence. This means that an item named main_template in the top layer will be found and used before an item named main_template in the bottom layer. We will go into this in more detail on the next page.

To create a skin layer through the web, simply add a new folder. On the file system, add a directory to your skins directory. You will also need to add a small amount of configuration to ensure that your directory is found and registered as a skin layer on installation.

Firstly, in [your theme package]/skins.zcml

<cmf:registerDirectory
       name="[Your Skin Directory Name]"/>

Next, in [your theme package]/profiles/default/skins.xml

<object name="[Your Skin Directory Name]"
    meta_type="Filesystem Directory View"
    directory="[your namespace].[your theme name]:skins/[Your Skin Directory Name]"/>

and

<skin-path name="[your skin name]" based-on="Plone Default">
  <layer name="[Your Skin Directory Name]"
     insert-after="custom"/>
 </skin-path>

 

5.2.4.2. Customizing through Order of Precedence

How skin layers work and how they can be used in customization.

If you've worked with Plone 2, you'll be familiar with this type of customization. As we mentioned earlier, the order of layers in a skin determines which page templates, CSS files and Python scripts are processed first.

To inspect the order of precedence:

  • Site > Zope Management Interface > portal_skins
  • click the Properties tab

You should see the layers of the Plone Default skin listed there. Layers such as 'plone_templates' come from the main Plone theme but there will also be layers providing templates from specific add-on products (the visual editor kupu for instance).

screenshot of Skin layers in the ZMIWhen asked to process a specific template, Plone will work from the top of this list downwards, looking in each layer in turn to retrieve the template.

At the top is a custom layer; any template placed in here will be found and used first. So, to create your own version of a Plone template or CSS file, give it the same name as the Plone version but put it in the custom layer.

This is the simplest approach, but just ensuring that your version lives in a layer higher in the order of precedence in a skin than the main Plone theme layers will be enough to ensure that Plone finds it first and ignores the original version.

This technique can be used in two ways

using the custom folder
through the Zope Management Interface, you can add your own versions of templates, style sheets etc to the custom folder. This always comes at the top, so you can be sure your versions will be found first.
adding your own skin layers
in your own theme product on the file system, create one or two skin layers, and ensure that on installation these layers are put just below the custom folder in the order or precedence. There's more information on how to do this in the next section.

Probably the most comprehensive description of skins, layers and order or precedence can be found in the first two sections of Chapter 7 of The Definitive Guide to Plone (note that most of this book refers to Plone 2, but these sections are still relevant for Plone 3).

5.2.4.3. Making and Naming your own Skin

How do you actually create a Skin?

Through the ZMI

  • Go to Site Setup > Zope Management Interface > portal_skins
  • Click the Properties tab
  • Choose Add New and give your skin a name
  • You can now type in a list of the layers you want to use, in the order you want to use them
  • Finally, at the bottom of the page, set your new skin as the default

On the File System

If you use the plone3_theme paster template, code will be provided which, when your theme product is installed, will register your skin directories as skin layers and put these together into a new skin.

The paster template gives you the option of basing your skin on Plone Default. That is, when you install the theme in your site, the Plone skin layers will be added to yours - but below yours in the order of precedence. This is a good idea, you can then re-use bits of Plone Default without duplicating it, and overwrite the bits you don't want.

The key steps are:

  1. Register your skin directories as Filesystem Directory Views, so that they can become skin layers. This happens in two places:[your theme package]/skins.zcml and [your theme package]/profiles/default/skins.xml
    <cmf:registerDirectory
           name="[Your Skin Directory Name]"/>
    
    <object name="[Your Skin Directory Name]"
        meta_type="Filesystem Directory View"
        directory="[your namespace].[your theme name]:skins/[Your Skin Directory Name]"/>
  2. Add and organize your skin layers into a skin in [your theme package]/profiles/default/skins.xml
    <skin-path name="[your skin name" based-on="Plone Default">
      <layer name="[Your Skin Directory Name]"
         insert-after="custom"/>
     </skin-path>
  3. Set your skin as the default skin in [your theme package]/profiles/default/skins.xml by wrapping this node around the nodes in the previous two examples.
    <object name="portal_skins" allow_any="False" cookie_persistence="False"
       default_skin="[your skin name]">
        .........
    </object>
    

 About the Skin Name

The name of your skin is required in a few places in your theme product. It is worth knowing where and why, so, for reference, the occurrences are listed here.

Where

Attributes/Directives used

Use

profiles/default/skins.xml

<skin_path name="[your skin name]"

Used to name your set of skin layers.

profiles/default/skins.xml

<object name="portal_skins"

default_skin="[your skin name]">

Used to set your set of skin layers as the default skin.

browser/configure.zcml

<interface …

name="[your skin name]"

/>

Used to name the theme specific interface (see Components section)

profiles/default/viewlets.xml

<order manager="plone.portalfooter" skinname="[your skin name]"

>

Used to specify the theme when reordering viewlets in viewlet managers

(see Components section)

5.3. Components

The page furniture, viewlets, portlets, and their managers. How to make your own and where to find the bits you need.

5.3.1. Component Wiring and ZCML

About components and how they are wired together

diagram of a componentComponents are powerful and flexible tools in Plone 3, but a little more abstract than page templates or Python scripts. As the diagram on the right attempts to show, they are normally combinations of Python classes and page templates wired together in Zope Configuration Language (ZCML) and given a name.

There are two important things to remember about components

Components are compounds of classes, templates, interfaces, permissions etc.
To track components down you need to look in .zcml files first, locate their names, and that will lead you to the classes and templates that contribute to them.
Components come into existence when your Zope Instance is started up
Provided Zope has read the .zcml file, a component will be available to use. You can't overwrite existing components, better to create your own, reusing some of the parts.

Parts of a Component

A component comes into being via a ZCML "directive" (there's an example of one of these below). The directive will have a series of "attributes" which will point to the various parts that go into its creation. These parts have four main functions.

  1. To identify the component (in the case of a viewlet this will usually be done with a "name" attribute).
  2. To compute the information the component is supposed to display (this is usually done with a Python class, and pointed to with a "class" attribute). For example, in the case of the navigation tree, this would be working out which part of the tree should be displayed for each page.
  3. To display the information the component's class has computed (this is usually done with a page template).
  4. To restrict the display of the component. In the case of a viewlet, this could be restricting it to display only to certain logged-in users (by using the "permission" attribute) or restricting it to display only with specific content types (by using the "for" attribute).

There's more about this in the component parts section.

Zope Configuration Language (ZCML)

The Five Tutorial on WorldCookery.com will walk you through ZCML, and there are plenty of examples in tutorials on the plone documentation site.

Here's a sample ZCML directive conjuring up the presentation viewlet (which simply provides a link to a presentation version of a page):

<configure    xmlns="http://namespaces.zope.org/zope"
    xmlns:browser="http://namespaces.zope.org/browser">  
    <browser:viewlet      
         name="plone.presentation"      
         for="Products.ATContentTypes.interface.IATDocument"      
         manager="plone.app.layout.viewlets.interfaces.IAboveContentBody"      
         class=".presentation.PresentationViewlet"      
         permission="zope2.View"      
    />
</configure>

There are three things to note:

  • Like any kind of XML, ZCML uses namespaces - watch out for these if you're writing your own ZCML file. For theme components, you'll mostly use the browser namespace.
  • ZCML attributes often refer to interfaces rather than actual content types, classes or components (see the for and manager attributes in the example above). You'll find more about interfaces in a later section.
  • Look at the class attribute and you'll see it begins with a leading dot. This means you can find it in the same directory as the ZCML file itself. If it isn't within the same directory you'll need to give the full name.

You can get detailed information about ZCML directives in the ZCML Reference section of the Zope 3 API - http://apidoc.zope.org/++apidoc++/. If you want to be very disciplined and tidy, consult the ZCMLStyleGuide http://wiki.zope.org/zope3/ZCMLStyleGuide.

 

5.3.2. Viewlets, Portlets and Other Components

Types of component.

Viewlet

This is a new feature in Plone 3 and is used to provide aspects of the page furniture - those elements of the page which generally don't change throughout the site. These are organized by another type of component - a Viewlet Manager.

For more information you can look at

Portlet

Portlets in Plone are boxes of information, usually in the right or left column of a page, containing aggregated content or additional information, which may or may not be directly relevant to the content item being displayed. Behind the scenes these used to be constructed from ordinary page templates, but now, in Plone 3, they are wired together as components and are managed by another component - a Portlet Manager.

For more information take a look at:

View (Browser View)

We gave one definition of the term "view" above in the skin section. However, behind the scenes, in the context of components, View has a more technical meaning. It refers to a component which is usually made up of a Python class or a template or both and, put simply, processes the data from a content item before it reaches the page. There's a technical explanation in the Plone Developer Manual.

You'll sometimes see it referred to as BrowserView or <browser:page> and in templates you'll see a browser view's name prefaced by @@. We look at browser views again in the section on putting a page together.

Note that the term browser and the browser namespace are used to demarcate presentational components – that is, those bits of code which go to make up elements which will find their way to a web browser at some point.

Resource (Browser Resource) & ResourceDirectory

Although we've indicated that the skin and layers are the usual home of page templates, images and style sheets, it is also possible to turn them into components by registering them in ZCML. In this case you'll see them referred to like this ++resource++[resource name]. The same can be done for a directory containing templates and style sheets.

“Oh great”, I can hear you saying, “so which should I use, components or skins?” Go to the section Skin or Components? for a discussion of the pros and cons. At the time of writing we suggest the simpler option is to keep your templates, images and style sheets in your skin. We're just mentioning browser resources so that you know what they are if you encounter them.

5.3.3. Customizing or Creating New

You can customize through the web, but on the file system, the way to customize or create components for your theme is to wire up new ones.

Through the Web

Just as for Skins and Layers, it is possible to customize the templates used by components through the Zope Management Interface.

  • Site Setup > Zope Management Interface > portal_view_customizations

You will need to know the name of your component (plone.presentation for instance). The Elements section of this manual will help if the name isn't obvious. You can only rewrite the template, which might be limiting.

On the File System

You can achieve much more if you are building your own theme product on the file system, and in this case the approach is slightly different.

Rather than overwrite a component (as you could for skins), it is far easier to create your own version. This involves some rewiring or new wiring in your own .zcml file, but is actually simpler than it sounds.

Here's an example of the presentation viewlet - as it is used by Plone:

<browser:viewlet
      name="plone.presentation"
      for="Products.ATContentTypes.interface.IATDocument"
      manager="plone.app.layout.viewlets.interfaces.IAboveContentBody"
      class=".presentation.PresentationViewlet"
      permission="zope2.View"
      />

Imagine, for your purposes, you need to use a new class to get this viewlet as you want. In your own configure.zcml file, give it a new name and wire in your own class.

<browser:viewlet
      name="[your namespace].[your presentation viewlet]"
      for="Products.ATContentTypes.interface.IATDocument"
      manager="plone.app.layout.viewlets.interfaces.IAboveContentBody"
      class=".[your viewlet module].[your viewlet class]"
      permission="zope2.View"
      />
  • Remember that the dot in front of your class namespace indicates that it can be found in the same directory as this configure.zcml file.
  • If you're not sure where your configure.zcml file lives, consult the Where to Find What you Need page of this section.

5.3.4. Component Parts

Further information on some of the parts that go to make up components.

5.3.4.1. Interfaces and why they matter

Interfaces are a bit techie and something a non-developer would probably rather not think about. However, they are an important part of component wiring, so it is as well to know a bit about what they are and do.

Interfaces as Markers

ZCML attributes often refer to interfaces rather than actual classes - for instance the example below wires up the presentation viewlet for content types that have the IATDocument interface.

<browser:viewlet
      name="plone.presentation"
      for="Products.ATContentTypes.interface.IATDocument"
      manager="plone.app.layout.viewlets.interfaces.IAboveContentBody"
      class=".presentation.PresentationViewlet"
      permission="zope2.View"
      />

In effect this is saying that the presentation viewlet is available for any content type which is ATDocument-like or behaves like an ATDocument. So, in this case, the interface is a marker.

The convenience of this is that a content type can have one (or more) interfaces, and several content types can share the same one. If you develop a new content type and mark it with the IATDocument interface, you can use this presentation viewlet with it - no extra wiring required.

Components and Interfaces

Components themselves can be marked with an interface - the technical term is "provides". Note that in the presentation viewlet example, the viewlet manager is referred to by its interface, not its name:

 manager="plone.app.layout.viewlets.interfaces.IAboveContentBody"

To track down the actual component, look in the configure.zcml file in the same directory as the interfaces. For instance, in plone/app/layout/viewlets/configure.zcml you'll see the interface has been wired up with a Python class to create a viewlet manager component:

      <browser:viewletManager
        name="plone.abovecontentbody"
        provides=".interfaces.IAboveContentBody"
        permission="zope2.View"
        class="plone.app.viewletmanager.manager.OrderedViewletManager"
        />

How to spot an interface

It is usually fairly easy to spot a reference to an interface. By convention, their names will be prefixed with an "I", and they will live in an interface or interfaces namespace. If you investigate interfaces.py or interface.py in any egg or product, you won't find very much code, but you'll often find useful information – effectively it is documentation about what a component providing (i.e. marked by) that interface should do. For example:

class IAboveContentBody(IViewletManager):
    """A viewlet manager that sits above the content body in view templates    """

If you've used the plone3_theme paster template, you'll find you have a ready-made interfaces.py file to which you can add your own interfaces if you need to create them.

5.3.4.2. Python Classes

You'll have noticed that Python classes are often part of the wiring of Components, and you will find that you can't really avoid understanding a little bit about them, particularly if you want to make your own viewlets.

Having to deal with something as advanced as Python classes can be daunting for the non-developer. The good news is that using Python classes will be more a case of copying and changing little bits of code than writing anything from scratch.

What's a Class?

It's best to think of a class as a discrete piece of code containing a collection of methods ('actions' of some sort) and attributes ('variables' which can hold a value).

In the case of components, the main purpose of a class is to compute the pieces of information a component needs to display. The class for the logo viewlet is a good example. You can find it in:

  • [your egg location]/plone/app/layout/viewlets/common.py - look for LogoViewlet

After a bit of preparatory work, the LogoViewlet class first finds out the name of the image that is to be used for the logo (and is defined in the base_properties property sheet):

logoName = portal.restrictedTraverse('base_properties').logoName

Then it works out the logo's vital statistics, size, alt text etc and turns this into an HTML anchor tag:

self.logo_tag = portal.restrictedTraverse(logoName).tag()

Finally, just in case you might need it, it looks up the title of the site:

self.portal_title = self.portal_state.portal_title()

In the page template associated with this viewlet you can get hold of this information (self.logo_tag, self.portal_title) using the variable "view":

<img src="logo.jpg" alt=""
         tal:replace="structure view/logo_tag" />

Do I have to use Classes?

Viewlets tend to be wired up with a Python class which points to a template. So, even though you might only want to create a new template, you'll find that you have to write a class to point to your new template. The Elements section of this manual should help you by giving you a snippet of code for each element to copy and paste into your own product.

Here's an example. The standard logo template doesn't actually make use of view/portal_title. So if you wanted to incorporate this into your logo in some way, then you would need to write your own template and then also your own class:

from plone.app.layout.viewlets.common import LogoViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile

class [your class name](LogoViewlet):
    render = ViewPageTemplateFile('[your template name]')
  • First, pull in ("import") all the bits and pieces with which to build your class using from ….. import …… .

  • Next, define your class. The important thing here is to base it on a pre-existing class so that you don't have to start from scratch. Put the name of the pre-existing class in brackets after your class name (make sure that you've imported it first). Don't forget the colon!
  • Finally, rewrite any of the methods or attributes you need. Here, we've just rewritten the render method to display our own template.

Note: indenting is very important in Python code, the convention is to use four spaces (rather than a tab). If you are having problems, double check the indentation first.

If you're feeling brave or want to know more, a straightforward introduction is here:


5.3.4.3. Permission

The permission attribute can be used to restrict visibility of a component.

When a user logs in to a site, they will be given a role ('manager' or 'editor' for instance). This role is, effectively, a set of permissions, giving them particular rights over particular aspects of the site.

To find out more about permissions consult the Understanding Permissions and Security Tutorial:

In the case of components, the permission attribute allows the site to decide whether a user has a right to see, or interact with a component. Most viewlets have the permission Zope2.View or Zope2.Public, which are permissions assigned to everyone, even anonymous visitors. However, look at the Lock Info viewlet:

<browser:viewlet
        name="plone.lockinfo"
        manager=".interfaces.IAboveContent"
        class="plone.locking.browser.info.LockInfoViewlet"
        permission="cmf.ModifyPortalContent"
        for="plone.locking.interfaces.ITTWLockable"
        />

By using cmf.ModifyPortalContent, this viewlet is restricted only to those who have the right to edit content (those who don't wouldn't be interested in whether an item was locked or not).

The list of available permissions is buried rather deeply in the Five product which comes with your installation of Zope - look in permissions.zcml for the most up-to-date list.

 

zope2.Public

Public, everyone can access

zope2.Private

Private, only accessible from trusted code

zope2.AccessContentsInformation

Access contents information

zope2.ChangeImagesFiles

Change Images and Files

zope2.ChangeConfig

Change configuration

zope2.ChangePermissions

Change permissions

zope2.CopyOrMove

Copy or Move

zope2.DefinePermissions

Define permissions

zope2.DeleteObjects

Delete objects

zope2.FTPAccess

FTP access

zope2.ImportExport

Import/Export objects

zope2.ManageProperties

Manage properties

zope2.ManageUsers

Manage users

zope2.Undo

Undo changes

zope2.View

View

zope2.ViewHistory

View History

zope2.ViewManagementScreens

View management screens

zope2.WebDAVLock

WebDAV Lock items

zope2.WebDAVUnlock

WebDAV Unlock items

zope2.WebDAVAccess

WebDAV access

cmf.ListFolderContents

List folder contents

cmf.ListUndoableChanges

List undoable changes

cmf.AccessInactivePortalContent

Access inactive portal content

cmf.ManagePortal

Manage portal

cmf.ModifyPortalContent

Modify portal content

cmf.ManageProperties

Manage properties

cmf.ListPortalMembers

List portal members

cmf.AddPortalFolders

Add portal folders

cmf.AddPortalContent

Add portal content

cmf.AddPortalMember

Add portal member

cmf.SetOwnPassword

Set own password

cmf.SetOwnProperties

Set own properties

cmf.MailForgottonPassword

Mail forgotten password

cmf.RequestReview

Request review

cmf.ReviewPortalContent

Review portal content

cmf.AccessFuturePortalContent

Access future portal content

 

5.3.5. Making Components Theme Specific

You may want to make components only available for your particular theme. To do this you will need an interface.

As components come into being as soon as Zope starts up and reads the .zcml files, they are available for every Plone site you have in a Zope instance. You might not want this to happen.

A Theme Interface

You can specify that your components are available only for your theme with a marker interface and a layer attribute in ZCML. Here's a rewired version of the presentation viewlet:

<browser:viewlet
      name="[your namespace].[your presentation viewlet]"
      for="Products.ATContentTypes.interface.IATDocument"
      manager="plone.app.layout.viewlets.interfaces.IAboveContentBody"
      class=".[your viewlet module].[your viewlet class]"
      layer=".interfaces.IThemeSpecific"
      permission="zope2.View"
      />

Note: Don't confuse the layer attribute with a skin layer. Here, layer refers to the whole theme rather than just one slice of it.

There are two methods for creating a theme interface:

Using plone.theme

In Plone 3.0, plone.theme is used:

  • A marker interface is defined in [your theme package]/browser/interfaces.py:
from plone.theme.interfaces import IDefaultPloneLayer

class IThemeSpecific(IDefaultPloneLayer):
    """Marker interface that defines a Zope 3 browser layer.    """
  • and this is registered in ZCML in [your theme package]/browser/configure.zcml
<interface
        interface=".interfaces.IThemeSpecific"
        type="zope.publisher.interfaces.browser.IBrowserSkinType"
        name="[your skin name]"
        />

Note: [your skin name] crops up here; refer back to the skins section if you are wondering what this is.

Using plone.browserlayer

In Plone 3.1, plone.browserlayer is available to you.

  • Create your interface (e.g. in [your theme package]/browser/interfaces.py)
from zope.interface import Interface
    class IThemeSpecific(Interface):
        """A layer specific to my product        """
  • Register this in the configuration (in [your theme package]/profiles/default/browserlayer.xml):
<layers>
 <layer name="[your skin name]"
   interface="[your namespace].[your theme name].browser.interfaces.IThemeSpecific"
 />
</layers>

If you generate your file system product or egg using the plone3_theme paster template, then the basics will be done for you (using the plone.theme method), you will simply need to track down the interface to find its name. Look in

  • [your theme package]/browser/interfaces.py or configure.zcml

and you should find it with the name IThemeSpecific. When you refer to it, use its path

layer=".interfaces.IThemeSpecific"

5.3.6. Skin or Components?

You’ll have noticed that you can turn any template or css file, or any directory containing these into a component. So why bother with the Skin building block?

The product created by the plone3_theme paster template does the following:

  • overrides and rewrites of the standard Plone Default templates and CSS files go in the Skin section – the skins directory.
  • new style sheets and images go in the Components section – the browser directory.

This manual suggests putting all your templates, style sheets and images in the Skin section - leaving just the viewlet and portlet templates in the components. There are a few reasons for this

  • it is simpler to do this when you're just starting out
  • it follows the way in which Plone Default is constructed
  • it makes it quick and easy to adjust your theme on-the-fly after it's installed. At that point, you can make further customizations of the Skin through the Zope Management Interface.

At the time of writing there's a big discussion going on about this very question.

 

If you want to strip the browser resources out of the product created by the plone3_theme paster template

  • remove the images and stylesheets directories in the [your theme package]/browser
  • remove the <browser:resourceDirectory /> entries in [your theme package]/browser/configure.zcml
  • remove the register stylesheet entry for main.css in [your theme package]/profiles/default/cssregistry.xml
  • if you have already installed your product you may need to check the CSS registry in the Zope Management Interface (portal_css) and delete the main.css entry there too

 

5.3.7. Where to find what you need

Where to put components in your own product and how to track them down in the Zope Management Interface and on the file system.

Through the Web

The templates for most components can be customized through the web:

  • Site Setup > Zope Management Interface > portal_view_customizations

The Elements section can help you identify the component you need.

Plone Default Components on the File system

If you're planning to wire up your own components, you may need to track down the relevant files of existing components to copy. This can be tricky. They are packaged up into a number of different eggs, so you need first to locate where your eggs are stored, and then work out which of these contains the component elements you need.

  • To work out where your eggs are stored, look at the Where is What section of this manual?
  • The Elements section of this manual will help you track down the egg containing the component you need.

In your own Theme Product

The browser folder in your theme product/browser/viewlet.py | viewlet.pt
An example viewlet component
/browser/interfaces.py
This is used to create your theme interface
/profiles/default/viewlets.xml
Use this file to order your viewlets within viewlet managers
/browser/configure..zcml
Use this file to wire up your components
/browser/templates | styles
These directories can be used for templates, styles, and images. You will need to register these as directories as resources in configure.zcml

5.4. Configuration

How to write a configuration file and where to put it.

5.4.1. Profiles

Configuration and profiles

Configuration refers to the default behaviour of a site (for instance, whether you allow people to sign up to your site, or how dates are displayed). You're likely to want some of this behaviour to be embedded in your theme.

There is also some overlap between Components, Skins, and Configuration. For instance, the order of skin layers and the order in which viewlets appear within a viewlet manager are considered aspects of configuration.

Profile

A profile is a set of configuration files. Each file is written in fairly simple XML and refers to a particular group of components or page elements. There are two different types of profile, base profiles and extension profiles. For theme purposes you will only ever need to use an extension profile (i.e., a profile that extends the configuration of an existing site).

A profile comes into being when it is wired up by ZCML. Here's the version created by the plone3_theme paster template:

<genericsetup:registerProfile
 name="default"
 title="[your skin name]"
 directory="profiles/default"
 description='Extension profile for the "[your skin name]" Plone theme.'
 provides="Products.GenericSetup.interfaces.EXTENSION"
/>

You'll see that it points at a directory for the location of the XML files and indicates that it is an extension profile by using an interface.

5.4.2. Generic Setup XML

The language used to define profiles.

The XML used for profile files is straightforward. There's no DTD available, but there are plenty of examples to reuse or adapt for your purposes. If all of this seems too much, the good news is that you can get Generic Setup to write the files for you by exporting the configuration from an existing site. There's more information on how to do this on the Generic Setup Tool page.

The root node of an XML profile is usually an object:

<object name="portal_javascripts" meta_type="JavaScripts Registry">
     .......
</object>

which corresponds to a particular site tool (in this case the JavaScripts registry). Sub-nodes represent sub-objects and XML attributes correspond to the attributes of those classes.

<javascript cacheable="True" compression="none" cookable="True"
            enabled="True" expression="" id="jquery.js" inline="False"/>

So, in this case, the sub-node represents an entry in the JavaScripts registry and its tick boxes.

screenshot of the javascripts registry in the ZMI

In the very unlikely event that you need to work out for yourself what attributes to use, you'll need to investigate the API (or the interfaces and classes) of the tool in question. Use http://api.plone.org or dig into the source code.

5.4.3. The Generic Setup Tool

The Generic Setup tool is used to apply your profiles to your site.

You can find the Generic Setup tool here

  • Site Setup > Zope Management Interface > portal_setup

You can run the tool manually, but for theme purposes, if you have created a product using the plone3_theme paster template, Generic Setup will be triggered automatically when you install your theme in your site.

You'll find more extensive information about the Generic Setup Tool in this tutorial:

However, there are two useful facts to know about it.

No Undo

Although you can uninstall your theme using portal_quickinstaller, at present, you can't undo the profiles Generic Setup applied during installation. For the most part, this isn't a problem, but it can get confusing - if, for instance, you are experimenting with the order of your viewlets and have tried several versions of viewlets.xml in successive installations. In this case, exporting a profile (explained below) can help you make sense of what you've done.

Exporting Profiles

You can export the current configuration of your site as a set of XML files. This can be helpful if you're not quite sure what you've done, if you're searching for a profile to base your own configuration on, or if you just want the Generic Setup Tool to write out a configuration for you.

  • Site Setup > Zope Management Interface > portal_setup
  • Click the Export tab
  • Select the steps you wish to export
  • Click the Export Selected Steps button
  • You'll be given a zip file with the relevant XML files

It isn't always obvious which export step you need to get the exact configuration you want, you may need to experiment.

5.4.4. Where to find what you need

How configuration works through the web and how to track down files on the file system.

Through the Web

There are a number of different routes to configure your site through the web. The Elements section of this manual should give you pointers on where to look to configure particular page elements. In general

  • Site Setup leads you to configlets for the site settings
  • Site Setup > Zope Management Interface will lead you to the style sheet and JavaScript registry (portal_css and portal_javascripts)
  • Adding /@@viewlet_manager to a URL will enable you to order viewlets

Plone Default Configuration on the File System

You will find most of the configuration files you need in:

  • [your products location]/CMFPlone/profiles/default

However, be aware that some configuration files may be located in third-party products. For instance, if you want to add some styles to the visual editor, Kupu, as part of your theme, then you will need kupu.xml which you'll find in [your products location]/kupu/plone/profiles/default.

There's an alternative to hunting around the file system, and that's to use the Generic Setup Tool to export the profile.

In your own Theme Product

The configuration directory in your theme product/profiles/default/
This directory holds the XML for Generic Setup. The plone3_theme paster template will have provided you with some ready-made files - for setting up your skin layers, registering your style sheets and JavaScript, and ordering your viewlets.
/profiles/default/import_steps.xml
Is an essential file for installation, you shouldn't need to change this.
/profiles/default/cssregistry.xml | jssregistry.xml
will register any style sheets and JavaScript in your skin. You will have to edit these yourself if you have any css or Javascript files to add.
/profiles/default/skins.xml
Will drop your skin layers into the right order of precedence. You won't need to change this unless you've renamed, removed, or added directories in the skins directory of your theme egg.
/profiles/default/viewlets.xml
will determine in what order viewlets appear in viewlet managers. You will need to edit this yourself if you want to add your own viewlets.
/profiles.zcml
When your Zope instance starts up, this file makes the profile available for Generic Setup to use.

6. Putting a Page Together

How do all these bits and pieces go together to make up a web page? And, more importantly, how do you get content onto the page?

6.1. Content to Template

Content to Template

6.1.1. Content to Template

How content reaches a Page Template.

There are three ways in which content from your content items can reach a page.

  • directly from a content item
  • from the catalog
  • via a view component (using Python)

Getting content directly from a content item

A page template can pull data directly from the content item it is displaying. Here’s a snippet of the RSS template, calling the description field of a Collection content item:

<description>
    <metal:block define-slot="description">
       <tal:block content="context/Description">
          Default rss description goes here
       </tal:block>
    </metal:block>
</description>
  • context refers to the current content item
  • Description is the accessor of the description field

Accessors

An accessor is simply the method by which data in a field is extracted. In most cases the name of an accessor is the field name with the first letter capitalized and prefaced by 'get' (e.g., getStartTime). There's an exception to this rule. The title and description field, common to most content types, have 'Title' and 'Description' as their accessors (i.e. no 'get', but the first letter is capitalized).

Widgets

This snippet from the news item template does exactly the same thing but calls a specific display "widget" macro for the field rather than just the data.

<p class="documentDescription">
 <metal:field use-macro="python:here.widget('description',  mode='view')">
     .....
</metal:field>
</p>

Getting content from the catalog

Every content item is catalogued on creation and editing. Some of its fields are indexed for quick searching and sorting, while the values of others are stored in what's called the "brains" or "metadata" for quick access.

Pages pulling together a number of content types - a folder or collection listing for instance - often get their content from a catalog query and the brains, rather than waking up every content item in turn. You'll normally find a variable defined somewhere which holds the results of a catalog query:

folderContents here.queryCatalog(contentFilter);

Then the template will loop through the results and call values from the brains/metadata:

item_url item/getURL;
item_id item/getId;

These look pretty much like normal accessors, in fact they are the names of fields in the catalog brains/metadata. This can get confusing - if you try to access a field which isn't in the brains/metadata you'll get an error.

You can see what fields are available to you via

  • Site Setup > Zope Management Interface > portal_catalog > metadata tab

If you want to understand more about the catalog, there is a useful, general overview in the Zope book, and a more Plone-specific runthrough in The Definitive Guide to Plone (this book is for Plone 2 only, but the catalog section is still relevant to Plone 3).

Getting content via Python (using a view component)

It is often more efficient to use a view to process the data from the content item (or a group of content items) and then drop it into the page template. In this case, by "view" we mean a specific component defined in ZCML.

Here’s a snippet calling a view to render the sitemap:

<ul id="portal-sitemap"
    class="navTreeLevel0 visualNoMarker"
    tal:define="view context/@@sitemap_view;">
     <tal:sitemap replace="structure view/createSiteMap" />
</ul>
  • context/@@sitemap_view is assigned to a variable called (helpfully) 'view'
  • createSiteMap is a method of @@sitemap_view
  • @@ indicates that this is a view component

Here's the wiring in ZCML that creates @@sitemap_view:

<browser:page
     for="*"
(there’s no restriction on where I can be used)
     name="sitemap_view"
(this is my name)
     class=".sitemap.SitemapView"
(this is where you can find the code to deliver my content)
     permission="zope.Public"
(you can see me if you have the Public permission)
     allowed_interface=".interfaces.ISitemapView"
/>

In summary

  • the content is processed by a Python class
  • ZCML wires this class up into a component
  • the template calls this component

 

 

6.2. Templates and Components to Page

Templates and Components to Page

6.2.1. Templates and Components to Page

An overview of how templates, viewlets, and portlets mesh together to create a page.

Plone's page templates can be frustrating at first. There's no single template which seems to contain everything you need.

Content Views

Since each content type is likely to have a different combination of fields, each content type requires a separate template for display. As we saw in the templates and templating language section, these usually have _view appended to their name. You can find those for the standard Plone content types in

  • [your zope instance]/Products/CMFPlone/Skins/plone_content.

main_template

Knowing about content views only gets you so far, however. It is the main template (main_template.pt) which draws the content together with the page furniture and design. You can find this in

  • [your zope instance]/Products/CMFPlone/skins/plone_templates.

It is important to remember that the content view templates aren't complete in themselves, they merely provide a snippet of content which is dropped into a "slot" in the main_template - called 'main'.

If you feel unsure about slots, then have a look back at the templates and templating language section.

Around this main slot, the components - viewlets and portlets come into play - supplying the page furniture and decoration around the content. The main template simply pulls these in via viewlet managers and portlet managers.

Viewlets are so flexible that they can even be pulled into the content view. The abovecontentbody manager, for instance, is used in a number of content views, and handles, amongst other things, the presentation viewlet we looked at in previous sections.

In more detail

You might find it helpful to look at an example in context.

Have a look at:

  • Products/CMFPlone/Skins/plone_templates/main_template

and

  • Products/CMFPlone/Skins/plone_content/document_view

About document_view (a content view template)

1. Although document_view looks like a complete HTML page, ignore this. Just note that right at the top it calls the main_template.

metal:use-macro="here/main_template/macros/master"

The code that gets used from document_view is actually the bit between these tags:

<metal:main fill-slot="main"> …… </metal:main>

This gets dropped into a slot in the main_template:

<metal:bodytext metal:define-slot="main" 
                tal:content="nothing">
... 
</metal:bodytext>

2. Going back to the fill-slot in the document_view you’ll see a few tags calling the relevant fields from the content type – like this:

<metal:field 
       use-macro="python:here.widget('title', mode='view')">
</metal:field>

You’ll also see a few tags like calling viewlet managers which, in turn, will summon up groups of viewlets:

<div tal:replace="structure provider:plone.abovecontentbody" />

These enable you to drop extra bits of page furniture around the specific content from the fields (e.g., the presentation mode link).

About the main template

1. Jump back to main_template and you’ll see similar calls to other viewlet managers managing groups of viewlets for more page furniture:

<div tal:replace="structure provider:plone.portaltop" />

2. And calls to portlet managers to pull up the portlets defined for that particular page:

<tal:block replace="structure provider:plone.leftcolumn" />

3. You’ll also see a number of additional slots (define-slot), which could also be filled (fill-slot) from the content view template if you wanted. Here's one you could use to add a bit of css:

<metal:styleslot define-slot="style_slot" />

Jump back to your content view template and simply add an additional fill-slot (outside of the main fill-slot):

<metal:mystyleslot fill-slot="style_slot">
 ..... 
</metal:mystyleslot>

We'll go into other ways of providing styles in more detail in the next section.

6.2.2. How to show full content in folder views

This how-to only makes sense for folders, smart folder, or other similar views with a reasonably small number of items. It shows how to display the full view of content in listings by using the macros already defined for the content types. The same approach can be used to define viewlets for layout products like compositepack.

I was looking for a layout product for the front page of a site I am working on, and the existing products did not meet my needs out of the box because they only showed summary views of content rather than the full view. Instead of writing viewlets for different content types from scratch, I used the existing view macros of the content types as follows, in a new folder view I called folder_full_view (this is just a code snippet):

        <tal:listing condition="folderContents">

                <div tal:repeat="item folderContents">
                <tal:block tal:define="here item/getObject;
                                       actions nothing;
                                       view here/defaultView;
                                       object_title item/pretty_title_or_id"
                           tal:on-error="nothing">                  
                   <div metal:use-macro="here/?view/macros/main"/>

                </tal:block>
                </div>

        </tal:listing>

The setting of actions to nothing is so that the action icons are not displayed for every content item. The on-error="nothing" may not be necessary for you. I have it because I allow the catalog to return results for which there is no View permission.

Similarly, for the CompositePack product, I defined a viewlet

<div class="viewlet default_view">
<tal:block on-error="nothing"
     tal:define="here nocall: context;
                 actions nothing;
                 view here/defaultView;
                 object_title here/pretty_title_or_id">
  <metal:block use-macro="context/global_defines/macros/defines" />
  <div metal:use-macro="context/?view/macros/main"/>
</tal:block>
</div>

so that complete content can be displayed in a layout.

 

Use these ideas at your own risk. Seems to work for me so far.

6.2.3. How to scale images using PiL in Page Templates

A quick description of how to scale images from an image field using the Python Image Library in your Page Templates using TAL.

PROBLEM:

I have a custom type with an ImageField.  I'm customizing a folder listing of these types and I wanted a thumbnail of each image shown in the folder listing.  This is very straightforward using PiL (Python Imaging Library), if you know what to do.  I was also presented with the problem of working with brains rather than the object itself.

ASSUMPTIONS:

  • You have PiL installed and working.
  • You know how to make a custom Archetype

OVERVIEW:

When you get the folder contents for a folder listing, brains objects are returned and iterated over to produce the list.  This is of course, much more efficient than waking up each object.  The problem being, you cannot get to your Image field in a brain (that I know of).  The following is a snippet from 'folder_listing.pt' showing this.

<tal:foldercontents define="contentFilter contentFilter|request/contentFilter|nothing;

                            contentsMethod python:test(here.portal_type=='Topic', here.queryCatalog, here.getFolderContents);

                            folderContents folderContents|python:contentsMethod(contentFilter);">



	<tal:entry tal:repeat="item folderContents">

	<tal:block tal:define="item_url item/getURL|item/absolute_url;">

As you can see, while iterating over 'item' you're accessing brains-y things in a brains-y way, like 'item/getURL'.  But you'll notice that you cannot do 'item/my_image' because it's not in the brain.  What to do?! you may wail.  Well, you could wake up the objects, get the image field, and then call the image scaling on it in a pythonic way, but this is a performance hit, puts python in your TAL, whcih you should avoid.

Instead you'll just be crafty.  You already have 'item_url' and you know the name of your image field (my-image) so put those together and you'll get right at the image.  Try this in your browser:

http://full/url/to/your/object/my-image

and you should see your image! Translating this into TAL, you would go:

<img src="#" tal:attributes="src string:${item_url}/my-image" />

Now to add the image scaling bit, and this is where I went wrong.  Much of the Plone documentation about PiL assume you're working with an ATImage object, but you're not.  You're working with an AT ImageField.  An AT ImageField only defines ONE image scale size by default:

sizes = {'thumb': (80,80)}

 whereas ATImage defines a bunch:

sizes = {'large'   : (768, 768),

           'preview' : (400, 400),

           'mini'    : (200, 200),

           'thumb'   : (128, 128),

           'tile'    :  (64, 64),

           'icon'    :  (32, 32),

           'listing' :  (16, 16),

          },

To make matters worse, notice that the sizes defined for the same size key are different.  Bad dog.  No cookie.  Anyhow, what this means is that in order to access the size you want, you have to define it in your schema in advance, like so:

    ImageField(

        name='my-image',

        widget=ImageWidget(

            label="My Image",

            description="An image!",

        ),

        storage=AttributeStorage(),

        sizes= {'large'   : (768, 768),

           'preview' : (400, 400),

           'mini'    : (200, 200),

           'thumb'   : (128, 128),

           'tile'    :  (64, 64),

           'icon'    :  (32, 32),

           'listing' :  (16, 16),

          },

    ),

Ok, so now that you have defined the sizes you want in your custom type's schema, you're ready to use it in your Page Template.  Remember the way we accessed it before?

<img src="#" tal:attributes="src string:${item_url}/my-image" />

To access the sizes defined in your schema, just add the name to the end of your image, preceded by an underscore.

<img src="#" tal:attributes="src string:${item_url}/my-image_mini" />

It's that easy, and it should be.  You shouldn't have to access and therefore wake up your objects!  There are also other ways to get at PiL's image scaling, but this I found was easiest and didn't throw any bizarro "Unauthorized" or "TypeError: a float is required" errors.

Enjoy!
~Spanky

ALSO SEE:

http://plone.org/documentation/manual/archetypes-developer-manual/fields/fields-referencehttp://plone.org/documentation/tutorial/richdocument/pil

6.3. CSS and JavaScript to Page

CSS and JavaScript to Page

6.3.1. CSS and JavaScript to Page

How style sheets and JavaScript reach the page.

Style sheets and JavaScript files are included in the skin section of the Plone Default theme, so the process of customizing or rewriting in general follows the concept of order of precedence described in the Skin section of this manual (i.e. placing a replacement in a higher layer in the Skin).

If you create your own style sheet, you can either put it in the custom folder of portal_skins through the web or in the skins directory of your theme on the file system.

If you like, and are feeling ambitious, you can turn your stylesheet into a resource type component by putting it in the browser directory and registering it in ZCML. Indeed, this is done for you if you create your own theme product using the paster plone3_template. There are pros and cons to the latter approach, look back at the Skin or Components section if you want to explore these further.

Resource Registries

Before the CSS and JavaScript reach the page they go through an extra step. You'll note that there are quite a few style sheets and JavaScript files available (and not all are always required). So a Registry tool picks and chooses them as required and merges only those it needs for speed and efficiency.

There's a detailed tutorial on how to use these registries in the following section.

including how to use conditions to specify that you only want a particular resource loaded in a particular context (for instance with a document view).

Registering style sheets and JavaScript

  • Through the web you can add or remove style sheets and JavaScript by going to the Zope Management Interface > portal_css or portal_JavaScripts.
  • On the file system, registering style sheets and JavaScript is part of the Configuration. So you'll need to look in profiles/default/jssregistry.xml and cssregistry.xml.

DTML

Some of the default Plone style sheet files have a .dtml extension, and the CSS inside is wrapped in

/* <dtml-with base_properties> */
 .......
/* </dtml-with> */

DTML is another Zope templating language, which in this case is deployed so that particular variables can be picked up from a properties sheet (base_properties.props) - for example:

#portal-column-one {
    vertical-align: top;
    width: <dtml-var columnOneWidth missing="16em">;
    border-collapse: collapse;
    padding: 0;
}


We wouldn't recommend using this technique as it is likely to be phased out, but it is as well to know that it is there. You can sometimes get caught out if you're customizing an existing style sheet and accidentally delete the top or bottom "dtml-with" statement.

6.3.2. Using the Resource Registries to Control CSS and JavaScript

Plone has two neat tools for managing Cascading Stylesheets and JavaScript in a handy way. This tutorial explains some whys and hows and even has a minimal practical example of how it works.

6.3.2.1. Why do we even have these registries?

Why were the Resource Registries written, what do they do, and why are they useful?

The ResourceRegistries spring out of ( as most features in Plone ) frustrations with the state of the world before their existence: If you wanted to add a CSS style sheet or a Javascript library to your Plone site, beyond using the ploneCustom.css-file, you would have to override the header templates. This was painful from a maintenance point of view, and it also didn't allow it happening more than once. There was no way of Products supplying their own css files without massive conflicts if more than one Product attempted the same. Having several style sheets and Javascript files to make things more manageable on the filesystem was painful too.

With the existence of ResourceRegistries we were also able to split up Plone's CSS and Javascript into more manageable parts that can be turned on or off at the click of a button. We couldn't do this in earlier versions of Plone as splitting the style sheets would increase the number of separate http-requests needed to display a page incredibly.

The resource registries should:

  • Make it easier to work with and supply your own Cascading Style Sheets and JavaScripts in Plone
  • Make it possible/easier to add conditions to your Cascading Style Sheets and JavaScripts so they only apply when you want them to
  • Make it easy for Product authors to supply Cascading Style Sheets and JavaScripts for their products without  getting conflicts with other products
  • Reduce the number of http-requests needed to display a Plone page in a browser
  • Pack Cascading Style Sheets and JavaScripts to reduce the download sizes of files to users, and the bandwidth usage from serving a Plone site
  • Make it easy to change priorities amongst Cascading Style Sheets and JavaScripts
  • Make it easy to disable all or parts of Plone's default Cascading Style Sheets and JavaScripts

What are the registries?

The Resource Registries currently consist of two Plone tools living in the ZMI in the root of your Plone site. They have been part of the Plone bundle/installers since Plone 2.1. (They can be used with Plone 2.0.x as well. There is a special readme-file in the product for installation instructions for Plone 2.0.x)

How do the registries work?

Each registry has an ordered list of resources, CSS or JavaScript files, each with its own set of attributes. Entries in the registries will be linked automatically in Plone pages in the standard templates when the browser requests a page. The registries have been configured to have very sensible defaults — so for almost all use-cases (probably 90%+) it is merely the matter of adding the id of a resource you want to use.

NOTE: For experimentation, for example when reading this document, make sure to check the "debug/development mode" checkbox on the top of the configuration panel. (Read more about why in part 2.)

 

6.3.2.2. Conditions, merging, caching and debugging

some more details on how the ResourceRegistries function

Conditions

When a user agent (i.e a browser) makes a page request, all the resources registered in the registry are evaluated against their condition field. If the condition is true, the resource is served to the browser. If the condition evaluates to false, the resource is not served.

This gives you the ability to conditionally serve different style sheets or scripts based on logic like whether the user is logged in, whether you are in the "Human Resources"-section of your intranet of if the content-type is a news-item.

Conditions have to be TALES expressions. You can use global template variables inside them.

Merging

Whenever you click the save-button in a registry, a new set of css or javascript files are created. The registry will try to assemble the different resources into bigger lumps/files to server to the browser. This is to reduce the number of http-requests necessary and can improve performance considerably.

The rules for merging resources

  1. The registry will only try to merge resources that are adjacent in the list. Otherwise there's risk that the order of operations in the browser would be affected (order of code in JavaScript and the order of the cascade in CSS). This would possibly affect rendering or execution in the browser in a negative way, and is therefore not available.
  2. Two adjacent resources are only merged if their condition is exactly the same (more about conditions in the next chapter)
  3. Two adjacent resources are not merged if they don't have the 'allow merging' checkbox checked

The magically assembled 'Files' are assigned a new random number as their name. So you'll find entries like <link rel="stylesheet" href="plone2341.css" /> in your html source. You can inspect how the resources will be merged if you click the second tab in the registry; "composition". It will present you with a list of 'magic' files, like 'plone2341.css', and nested within them the component resources they are constructed from. You can click each entry to inspect what it will look like when served.

The CSS-composition UI

 

The css-registry composition UI

Each merged resource will have a small snippet in the source identifying its origin so that you can get hold of the full unmodified source if you need.

The comment stuff usually looks like the following.

/* ----- base.css ----- */
@media screen {
/* http://yourhost/plone/portal_css/base.css?original=1 */
/* */

 

Caching and HTTP

The merged resources are automatically served with HTTP-headers optimized for being cached a long time. Since the auto-generated number is being changed every time you save the registry settings, the browser will request the new 'files' and not use its old cached values anyway. The scheme is actually rather clever.

The potential problem with the caching scheme is that the registry doesn't know if you change the files in the skins directories unless you touch the "save" button in the registry. This  is only an issue while you are working on your CSS (or JavaScripts) to get them right, but can cause massive confusion if you make changes and only see cached styles/scripts in the browser. To avoid this issue, we have added a handy "debug and development" mode to the registries.

Debug and development mode

To enable the debug and development mode, check the corresponding checkbox in the registry configuration page and click "save".

This:

  • Disables merging of resources (great for finding bugs and specific declarations with the inspectors and development tools like Firebug)
  • Sets HTTP-headers for not caching so that you get a fresh copy every time
  • Disables compression

 

These settings affect performance in an adverse way, so make sure to disable the debug mode when your Plone goes live.

6.3.2.3. Resource Parameters

The parameters for a registry entry in the CSS (and JavaScript) registry.

Each entry in the CSS and JavaScript registries has some parameters that can be tweaked.

id
The id of the style sheet or JavaScript to be used. In Plone 3.3 onwards you can specify an externally hosted resource by entering the full URL here.
expression
A TALES expression to be evaluated to check if the style sheet or JavaScript should be included in output or not. You can use global template variables here.
conditionalcomment (available from Plone version 3.3 onwards)
A small string to be included in a conditional comment around the resource. For example, entering simply 'IE' in the field will result in a conditional comment of:
<!--[if IE]>...<![endif]--
This behavior is currently enabled for the CSS and JavaScript registries. The KSS registry is the only registry that does not have full support for conditional comments. For more information see: http://msdn.microsoft.com/en-us/library/ms537512.aspx
media
The media for which the style sheet should apply, normally empty or 'all'. other possible values are 'screen', 'print' etc. Read more about the media settings of CSS at w3.org
rel
Link relation. defaults to 'stylesheet', and should almost always stay that way. For designating alternative style sheets. This is used for the toggle between large, medium and small fonts in default Plone. Don't change this one unless you know what you are doing.
title
the title of an alternate stylesheet.
rendering
How the stylesheet is linked from the html page. This is an advanced setting. Leave it set to the default 'import' unless you know the effects of different ways of rendering and linking stylesheets
import
the default. normal css import
link
works better for old browsers and is needed for alternate stylesheets
inline
render the stylesheet inline instead of linking it externally. Shouldn't be used at all! It isn't possible to create sites which validate if you do.
For more information see: http://developer.mozilla.org/en/docs/Properly_Using_CSS_and_JavaScript_in_XHTML_Documents
compression
Whether and how much the resource should be compressed
none
the original content will not be changed
safe
the content will be compressed in a way which should be safe for any workarounds for browser bugs. Conditional code for Internet Explorer is preserved since ResourceRegistries 1.2.3 and 1.3.1.

full
the content will be compressed with some additional rules.For css all comments and most newlines are removed, this may break special browser hacks, so use with care. For JavaScript this encodes variables with special prefixes according to the rules described here (Special Characters):
http://dean.edwards.name/packer/usage/ The source code needs to be written according to those rules, otherwise it's more than likely that it will break.
safe-encode
- only available for JavaScript
- 'full-encode' - only available for JavaScript. Additionally encodes keywords. This heavily compresses the JavaScript, but it needs to be decoded on the fly in the browser on each load. Depending on the size of the scripts this could lead to timeouts in Firefox. Use with special care!
applyPrefix
- only available for CSS. 
- If your stylesheet uses relative URLs in a url() statement, e.g. to refer to another stylesheet or image, you may experience problems when using the registry in non-debug-mode, as portal_css alters the URL seen by the browser. If so, set this option to True or tick the "Replace relative paths in url() statements with absolute paths?" option in the portal_css management screen to replace any relative path inside a url() statement with an absolute path (prefixed with the Plone site root path) during the resource merging stage. This does not modify the original stylesheet. It may have a slight performance impact, but it should not be an issue if resources get cached properly. It has no effect in debug mode.
 

6.3.2.4. Practical: Adding a Style Sheet to the Registry Through the Web

Basic operations of the CSS-and Javascript-registries – and a truly minimal example of how to use them in real life. As a simple example of the most basic functionality, we'll add and register a minimal stylesheet that adds to the background of your Plone a terrible red color.

To use one of the registries, you have to

  1. Have a resource (for example a File) in your Skin with some CSS or JavaScript in it (this could, for instance, be in the custom folder of portal_skins).
  2. Make an entry in the corresponding registry (for example portal_css) with the id of the resource.

Make a Style Sheet

  • Go to Site Setup > Zope Management Interface and navigate to portal_skins/custom
  • From the add-item menu, select File
  • Give your new file an id of 'css-demo.css'
  • Paste the following content into the file:
    body{ background-color : red }
  • …and save it

Add it to the CSS Registry

The registries are two tools that live only in the Zope Management Interface (ZMI); they have no interface in the Plone user-interface itself. However, you can easily find them when browsing the ZMI of your Plone site - look for portal_css and portal_javascript

  • Go to Site Setup > Zope Management Interface and click portal_css.  Once selected, the CSS Registry ( the one we use for this example - the JavaScript one is almost exactly the same) will present an interface displaying all the registered resources (in the case of the CSS Registry, the resources are CSS files).
  • Make sure that you tick the debug mode checkbox. This will ensure that you see your changes immediately.
  • Scroll to the bottom of the form, where there is a form for adding a style sheet

  • In the id field, enter 'css-demo.css'  (leave the other values as they are by default for now)
  • Voila! – view your Plone site and you'll easily notice its (admittedly quite horrifying) shiny red background color!

The Technical Explanation

Each entry in the registries has an id that references a resource that can be found in the current Plone acquisition context. Technically it could be a tool or a utility or anything that has a name and is available, but most commonly it will be a File object ( for static CSS and JS) or a DTML Method (for dynamic variable replacement). The Resource Registries makes no difference as to what the object is, as long as it can be found and called, rendered or printed as a string.

6.3.2.5. Practical: Adding a Style Sheet to the Registry in your own Theme Product

Style sheets and JavaScript are added to the registries on installation of your theme product, via Generic Setup. You simply need to write the Generic Setup XML.

Create your Style Sheet

  • Create a normal CSS file [mytheme.css] using your favourite CSS editor.
  • You now have two options:
    • The simplest place to put your style sheet is in the skins directory of your theme product. Paster will have created a styles directory for you  (skins/plonetheme_mytheme_styles).
    • Paster will also have created a stylesheets directory for you in the browser directory of your theme product and will have registered this as a resource in the configure.zcml file. You can, if you like, put your style sheet in that directory instead.

Write your Generic Setup File

The relevant files for the configuration of your registries are profiles/default/cssregistry.xml and profiles/default/jsregistry.xml. Paster should have created these for you, but if they aren't there, just make them yourself.

An entry for a style sheet in the skins directory of your theme product, will look like this:

<?xml version="1.0"?>
<object name="portal_css" meta_type="Stylesheets Registry">
 <stylesheet title="" cacheable="True" compression="safe" cookable="True"
    enabled="1" expression="" id="mytheme.css" media="screen"
    rel="stylesheet" rendering="import"/>
</object>

Note that the parameters are just the same as the through-the-web form. You need only give the ID of the style sheet; Plone will find it as it is part of the skin.

An entry for a style sheet in the browser directory of your theme product will look like this:

<?xml version="1.0"?>
<object name="portal_css">
 <stylesheet title=""
    id="++resource++plonetheme.mytheme.stylesheets/mytheme.css"
    media="screen" rel="stylesheet" rendering="import"
    cacheable="True" compression="safe" cookable="True"
    enabled="1" expression=""/>
</object>

Note the special ++resource++plonetheme.mytheme.stylesheets syntax to refer to the directory registered as a resource in browser/configure.zcml

Once written, you can reimport the cssregistry.xml file using the portal_setup tool in the ZMI to make this change take effect. Make sure you choose the correct profile (your theme product) before importing the CSS step. Re-installing your theme product will also work, though you should be careful to guard against GenericSetup clashes. It's safer to import the individual step if your theme has been installed previously.

Plone Version 3.3 Onwards

In Plone 3.3 onwards you can also tell the CSS Registry to put conditional comments around your stylesheet:

<stylesheet title="" cacheable="False" compression="none" cookable="False"
 rel="stylesheet" expression="" id="IEFixes.css" media="all" enabled="1"
 rendering="import" conditionalcomment="IE" />

You can also specify an external resource:

<javascript cacheable="False" compression="none" cookable="False"
 enabled="True" expression="" id="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"
 inline="False" insert-before="jquery-integration.js" />

 If you are using SSL and specifying an external resource, you may find it necessary to specify both an http and an ssl entry in the registry. You can use the expression parameter to decide which one will be used:

<javascript cacheable="False" compression="none" cookable="False"
 enabled="True" expression="python:request.get('ACTUAL_URL','').startswith('https://')"
 id="https://jqueryjs.googlecode.com/files/jquery-1.2.6.min.js"
 inline="False" insert-before="jquery-integration.js" />

<javascript cacheable="False" compression="none" cookable="False"
 enabled="True" expression="python:request.get('ACTUAL_URL','').startswith('http://')"
 id="http://jqueryjs.googlecode.com/files/jquery-1.2.6.min.js"
 inline="False" insert-before="jquery-integration.js" />

6.3.2.6. Example Conditions

Some examples of limiting Style Sheets by the condition parameter, to help you get started.

In these examples we've given the condition as it would appear in profiles/default/jsregistry.xml and cssregistry.xml. If you are working through the web, then the text within the inverted commas "...." can simply be dropped into the condition box for the relevant CSS or JavaScript file in portal_css or portal_javascripts.

Content Type

If your CSS or JavaScript is only relevant to a particular content type:

expression = "python:object.meta_type == 'ATFolder'" 

View

You can check a specific browser view for a property (this example is from Products.Maps):

<javascript 
    cacheable="True" 
    ...
    expression="object/@@maps_googlemaps_enabled_view/enabled 
                                                | nothing" />

Here's how to look up one of the global views (see the Using Other Information about your Site Section for more about these views). This example delivers the Plone RTL Style Sheet:

<stylesheet 
    ...
    expression="python:portal.restrictedTraverse
                             ('@@plone_portal_state').is_rtl()"
    id="RTL.css" 
    .../>

Role

The following will check whether the visitor is logged in or not - in this case it delivers the standard member.css if the visitor is not anonymous:

<stylesheet 
    ...
    expression="not: portal/portal_membership/isAnonymousUser"
    id="member.css" 
    .../>

This example does the same, but uses the plone_portal_state view

expression="python: not here.restrictedTraverse
                        ('@@plone_portal_state').anonymous()"

Section and/or place in the site

Note that the body tag of every page has a section and template CSS class. So you may not need to use the resource registries at all.

However, if you want to change styles for a folder that isn't at the top level of your site, or, if you want to supply some JavaScript at a specific point in the site, then you will need to write a script and call this via the resource-registry expression. The section class on the body tag in main_template is generated by a Python script getSectionFromURL. This is a good starting point for your own script.

Here's an example expression and script. This will return True for a folder of your site with the shortname 'news' and any object within it. In this case - unlike the default Plone setup - the news folder is one level down from the top level:

expression="python: here.getSectionInTree(object,1)=='news'"
## Script (Python) "getSectionInTree"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=object, position
##title=Returns a section name
##
contentPath = object.portal_url.getRelativeContentPath(context)
if not contentPath:
    return ''
else:
    if len(contentPath)-1 >= position:
         return contentPath[position]
    else:
         return ''

 

6.3.3. How to Add New Class Styles to Kupu

This document explains how to add and define new custom class styles for use in kupu.

 Pre-requisites: CSS knowledge

1. Go to Site Setup in your Plone site.

2. Select the kupu visual editor add-on product configuration icon, as seen below:

Site-setup kupu

 

3. Scroll down to the paragraph styles box.

Kupu paragraph styles

4. Add your new paragraph style in the box. Format is title|tag or title|tag|className, one per line. For example:

Smalltext|p|smalltext

5. Open your ZMI.

6. Make sure that your site is in debug mode when first making changes. This will allow you to see your changes right away. 

7. Go to http://yoursite/portal_skins/plone_styles/ploneCustom.css/manage_main.

8. Click the customize button.

9. Enter your new paragraph style where it says “add your custom stuff here”.

.smalltext {font-size: 70%;}

Note: scroll down the page for available elements. See base_properties for definitions of those elements.

 

6.4. Using Other Information about your Site on a Page

How to get information about the state of your site and other global variables.

At some point or other you'll find you need to use the title of your site in a template; or you'll want your template to deliver something depending on the roles or permissions of your visitor or user. There are two approaches for obtaining this information:

1. Browser Views (recommended)

The first, newer, and recommended approach is to use the methods available in one of three browser views:

  • @@plone_portal_state
  • @@plone_context_state
  • @@plone_tools

These are kept in

  • [your egg location]/plone/app/layout/globals OR
  • [your egg location]/plone.app.layout-[version]/plone/app/layout/globals

You can find a description of each method in interfaces.py in that directory, but the main methods are outlined below. This excerpt from the main_template in the core Plone Default templates in Plone 4, demonstrates how these views, or their individual methods, are made available to every page:

<html xmlns="http://www.w3.org/1999/xhtml"
      ...
      tal:define="portal_state context/@@plone_portal_state;
                  context_state context/@@plone_context_state;
                  ...
                  lang portal_state/language;
                  ...
                  portal_url portal_state/portal_url;
                  ..."
     ...
>

Here's an excerpt from the newsitem_view template in the core Plone Default templates illustrating how the @@plone_context_state can be used to establish whether an item is editable or not:

        <p tal:define="is_editable context/@@plone_context_state/is_editable"
           tal:condition="python: not len_text and is_editable"
           i18n:translate="no_body_text"
           class="discreet">
            This item does not have any body text, click the edit tab to change it.
        </p>


2.  Global Defines (deprecated)

The second approach has been around for a long time, but is being phased out (as it is slower) in Plone 3 and has been pretty much removed in Plone 4. This is to use a set of variables that are available to every single page.

In Plone 3:

These are called by main_template:

<metal:block use-macro="here/global_defines/macros/defines" />

If you want to investigate them further, you'll find them in

  • [your products directory]/CMFPlone/browser/ploneview.py.

These variables are used in a number of the default Plone templates in Plone 3 and so they are listed below alongside their equivalent in the available views.

In Plone 4:

The global_defines macro is not used at all and the variables have been entirely replaced in all Plone templates. However, should it be required, the global_defines macro is still available in the core Plone Default skin layers in the plone_deprecated folder.  For more information on making a Plone 3 theme compatible with Plone 4, consult the upgrade guide.

Available Views and Methods

About the site

View @@plone_portal_state

Method What you get global defines
portal Portal Object portal
portal_title The title of your site portal_title
portal_url The URL of your site portal_url
navigation_root_path Path of the navigation root
navigation_root_url The URL of the navigation root navigation_root_url
default_language The default language of the site
language The current language
locale The current locale
is_rtl Whether the site is being viewed in an RTL language isRTL
member The current authenticated member member
anonymous Whether or not the current visitor is anonymous
isAnon
friendly_types Get a list of types that can be deployed by a user

About the current context

View @@plone_context_state

Method

what you get

global defines

current_page_url

The URL of the current page

current_page_url

current_base_url

The actual URL of the current page

 


canonical_object

The current object itself

 


canonical_object_url

The URL of the current object

 


view_url

The URL used for viewing the object

 


view_template_id

The id of the view template

 


is_view_template

True if the current URL refers to the standard view

 


object_url

The URL of the current object

 


object_title

The 'prettified' title of the current object

 


workflow_state

The workflow state of the current object

wf_state

parent

The direct parent of the current object

 


folder

The current folder

 


is_folderish

True if this is a folderish object

isFolderish

is_structural_folder

True if this is a structural folder

isStructuralFolder

is_default_page

True if this is the default page in a folder

 


is_portal_root

True if this is the portal root or the default page in the portal root

 


is_editable

True if the current object is editable

is_editable

is_locked

True if the current object is locked

isLocked

 actions
(Plone 4)
The filtered actions in the context. You can restrict the actions to just one category.  
 portlet_assignable
(Plone 4)
 Whether or not the context is capable of having locally assigned portlets.  

Tools

view @@plone_tools

method

what you get

global defines

actions

The portal actions tool

atool

catalog

The portal_catalog tool

 


membership

The portal_membership tool

mtool

properties

The portal_properties tool

 


syndication

The portal_syndication tool

syntool

types

The portal_types tool

 


url

The portal_url tool

utool

workflow

The portal_workflow tool

wtool

 

6.5. Using jQuery and jQuery Tools

Plone includes the jQuery and jQuery Tools JavaScript libraries out of the box, which you can use in your own scripts right away.

jQuery is a popular JavaScript Library that simplifies HTML document traversal, event handling, animating, and Ajax interactions. jQuery Tools is a collection of user-interface components including overlays, tabs, accordions and tooltips.

jQuery has been shipped with Plone since 3.1. jQuery Tools was added with Plone 4.0.

Using jQuery

jQuery has excellent documentation available at http://api.jquery.com. Note, though, that it is never wise to depend on the availability of the "$" alias for the jQuery function since other libraries may redefine it.

So, Instead of:

$(document).ready(function(){
   $("a").click(function(event){
     alert("Thanks for visiting!");
   });
 });

you should embed and jQuery code that uses the "$" alias in a wrapper like:

(function($) {
 $(document).ready(function(){
   $("a").click(function(event){
     alert("Thanks for visiting!");
   });
 });
})(jQuery);

Using jQuery Tools

jQuery Tools is a jQuery plugin, and Plone 4 includes the tabs, tooltip, scrollable, overlay and expose toolset. The remainder of the jQuery Tools kit plugins are available by enabling the plone.app.jquerytools.plugins.js resource Plone's JavaScript registry.

The integration with jQuery Tools is provided through the package plone.app.jquerytools, which includes a set of overlay helpers for common AJAX overlay needs. This kit is used to provide many of Plone's overlayed forms. See the plone.app.jquerytools pypi page for documentation and examples.

7. Page Elements

A reference for the viewlets, portlets, viewlet managers, and portlet columns which make up a page. There's a quick reference to each component type with links and reminders on how to handle them, a visual index of page elements plus code snippets to make your life easier.

7.1. Viewlet

What goes to make up a viewlet, plus cheat sheets on how to move, remove or customize them, and links to useful tutorials.

7.1.1. Anatomy of a Viewlet

The bits that go to make up a viewlet component.

Directive in ZCML

<browser:viewlet />

Attributes in ZCML

name
e.g. [your namespace].[your viewlet name]
manager
a manager interface
layer
a marker interface for your particular theme
class
a Python class. This class requires a 'render' attribute, which, in most cases, will point to a template. You don't need to specify the template in the ZCML, however, in Plone version 3.1.3 and higher, you can override this template using the template attribute below
template
in Plone version 3.1.2 and lower, you can only use this if you aren't using a class; in Plone version 3.1.3 and higher, you can use this to override the template you've set in the class you specified above
permission
in most cases this will be Zope.Public
for
specify an interface marking a group of content types, if you wish. The viewlet will then be restricted to those content types (for an example see the Presentation viewlet in the Elements section)
view
specify an interface marking a specific browser view, if you wish. The viewlet will be restricted to items with that specific view (for an example investigate the source code of the Content Actions viewlet - you'll find directions on where to locate this code on the Content Actions page of the Elements section)

7.1.2. Moving, Removing or Hiding a Viewlet

Moving, Removing or Hiding a Viewlet

7.1.2.1. Overview and Cheat Sheet

A cheat sheet of what you need to do to move viewlets in your page layout, or remove or hide them from your page.

You'll find detailed information and a tutorial on how to move viewlets here:

Quick Cheat Sheet of the Basics

Through the Web

  • Add @@manage-viewlets to your site URL.
  • If you want to move viewlets that only appear on a page, be sure to append @@manage-viewlets to the URL of the page.
  • You will find that you can move, hide or remove viewlets with this method, but that you cannot move them from one viewlet manager to another.

In your own product

Moving or removing viewlets is part of your site configuration:

  • Add or edit [your theme package]/profiles/default/viewlets.xml

You'll find general information about the site configuration in the Configuration section of this manual. It's worth reading this through before you launch in here, as configuring viewlets and viewlet managers can be a bit tricky. It will tell you

  • how you can get the Generic Setup tool to write out the configuration for you
  • why things might not be working as you expect

GloWorm is a useful tool here too. It will help you move the viewlets around through the Plone user interface and inspect the resulting configuration.

Removing a viewlet from a viewlet manager

You can't do anything more than hide your viewlet in the viewlet manager

<object>
    <hidden manager="[Viewlet Manager Name]" skinname="[your skin name]">
        <viewlet name="[Viewlet Name]" />
    </hidden>
</object>

Note that you can do this process through the web and then get the Generic Setup tool to write out the configuration for you to transfer into your own theme package.

Moving a viewlet within a viewlet manager

<object>
    <order manager="[Viewlet Manager Name]" skinname="[your skin name]">
Specify all the viewlets you want to see in this viewlet 
in the order you want them with this directive:
        <viewlet name="[Viewlet Name]">
    </order>
</object>

Note that you can do this process through the web and then get the Generic Setup tool to write out the configuration for you to transfer into your own theme package.

Moving a viewlet from one viewlet manager to another

If you are basing your theme on the Plone Default theme, then you'll find that reassigning a Plone Default viewlet is a two step process

  • hide it in its current viewlet manager
  • add it and give it a position in a different viewlet manager
<object>
Hide it from the current viewlet manager
    <hidden manager="[current Viewlet Manager Name]" skinname="[your skin name]">
        <viewlet name="[Viewlet Name]" />
    </hidden>
Add it to a different viewlet manager
    <order manager="[a different Viewlet Manager]" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[Viewlet Name]"
                 insert-before="[Name of Viewlet Below]" />
    </order>
OR Add it to your own viewlet manager
    <order manager="[Your Viewlet Manager]" skinname="[your skin name]">
        <viewlet name="[Viewlet Name]"/>
    </order>
</object>

  • you can also use 'insert-after="[Name of Viewlet Above]"' or use an asterisk to place the viewlet at the top or bottom of the manager (e.g 'insert-after'=*).
  • based-on="Plone Default" means that it will take the Plone Default ordering and then apply the insert-after and insert-before adjustments you've specified.

7.1.3. Overriding or Creating a New Viewlet

Overriding or Creating a New Viewlet

7.1.3.1. Overview and Cheat Sheet

A quick cheat sheet on how to customize or create a new viewlet.

You can customize a viewlet template through the web, but you can't alter the underlying Python class.

On the file system, rather than customize, the process is to wire up a new viewlet, or re-wire an existing viewlet.

You'll find a detailed tutorial on creating a viewlet in this article.

Quick Cheat Sheet

Through the Web

  • Use Site Setup > Zope Management Interfaces > portal_view_customizations to customize the template of an existing Plone Default viewlet.
  • You cannot create a new viewlet through the web.

In your own product

You will need to know the name of:

1. The viewlet manager interface
Look this up in the Elements section of this manual
2. Your theme specific interface
This is optional, but ensures that your viewlet 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 at the Elements section or by using GloWorm):

3. browser viewlet directive
This will go in [your theme package]/browser/configure.zcml
4. configuration file
[your theme package]/profiles/default/viewlets.xml
5. page template
[your theme package]/browser/[your template name].pt 
6. Python class
This is optional (but see the note below for Plone version 3.1.2 or lower)
put this in [your theme package]/browser/[your module].py

Sample configuration.zcml directives

Re-wiring a Plone Default viewlet to use your own template (note the layer attribute is really important here)

<browser:viewlet
 name="plone.[viewlet name]"
 manager="[viewlet manager interface]"
 class="plone.app.layout.viewlets.common.[viewlet class name]"
 template="templates/[your template name]"
 layer="[your theme specific interface]"
 permission="zope2.View"
 /> 

Wiring up a new viewlet but borrowing a Plone Default viewlet class

<browser:viewlet
 name=[your namespace].[your viewlet name]"
 manager="[viewlet manager interface]"
 class="plone.app.layout.viewlets.common.[viewlet class name]"
 template="templates/[your template name]"
 layer="[your theme specific interface]"
 permission="zope2.View"
 />

Wiring up with a brand new viewlet with your own class or your own template

<browser:viewlet
 name=[your namespace].[your viewlet name]"
 manager="[viewlet manager interface]"
 class=".[your module].[your class name]"
 (or: template="templates/[your template name]")
 layer="[your theme specific interface]"
 permission="zope2.View"
 />

Notes for Plone version 3.1.2 or lower:

Sample Python class

In Plone version 3.1.2 or lower, you will need to use this to override a Plone Default viewlet, even if you only want to change the page template.

from [element namespace] import [element class name]
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFileclass 

[your class name]([element class name]):
    render = ViewPageTemplateFile("[your template name]")


7.2. Portlet

A cheat sheet of the bits that make up a portlet, plus information on moving, hiding or overriding a portlet.

7.2.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

7.2.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>

7.2.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])

7.2.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!

7.3. Viewlet Manager

How to move or hide viewlet managers and how to create a new one.

7.3.1. Anatomy of a Viewlet Manager

The bits that go to make up a Viewlet Manager.

Directive in ZCML

<browser:viewletManager />

Attributes in ZCML

name
e.g., [your namespace].[your viewlet manager name]
provides
a marker interface defining what this manager does
layer
a marker interface for your particular theme
class
this will be plone.app.viewletmanager.manager.OrderedViewletManager
permission
in most cases this will be Zope.Public
for
specify an interface marking a group of content types, if you wish. The viewlet manager will then be restricted to those content types
view
specify an interface marking a view, if you wish. The viewlet manager will be restricted to items with those views.

7.3.2. Moving, Removing or Hiding a Viewlet Manager

Some hints on moving or hiding viewlet managers.

Viewlet managers are called by page templates. Moving or removing them is simply a case of customizing the template. Most are called by the main_template, but you may also need to look into specific content views for some of them.

Quick Cheat Sheet

Through the Web

  • Site Setup > Zope Management Interface > portal_skins > plone_templates or plone_content
  • Click the Customize button, and look for
    <div tal:replace="structure provider:[viewlet manager name]" />
  • (use the Elements key to identify exactly which manager you're interested in)

In your own product

  • Put your own version of main_template or of the content views in

    [your theme package]/skins.

7.3.3. Creating a New Viewlet Manager

A quick cheat sheet for creating a new viewlet manager.

Through the Web

You cannot create a new viewlet manager through the web. To override the order in which viewlets appear in a viewlet manager, use the instructions for viewlets.

In your own product

If you're basing your new viewlet manager on a Plone Default viewlet manager, look up the details in the Elements section of this manual.

You will need to know the name of

Your theme specific interface
This is optional, but ensures that your viewlet 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):

browser viewletManager directive
[your theme package]/browser/configure.zcml
Your viewlet manager interface
[your theme package]/browser/interfaces.py
configuration file directives
[your theme package]/profiles/default/viewlets.xml

Sample Interface

from zope.viewlet.interfaces import IViewletManager

class [your viewlet manager interface](IViewletManager):
    """ [A description of your viewlet manager goes here]  """

Sample configure.zcml directive

<browser:viewletManager
 name=[your namespace].[your element name]"
 provides=".interfaces.[your viewlet manager interface]"
 class="plone.app.viewletmanager.manager.OrderedViewletManager"
 layer="[your theme interface]"
 permission="zope2.View"
 />

7.4. Portlet Manager

Tips on moving and hiding portlet managers. Cheat Sheet for creating a new portlet manager.

7.4.1. Moving or Removing a Portlet Manager

Tips on how to move or remove portlet managers.

Portlet managers are called by main_template. Moving or removing them is simply a case of customizing the template.

Through the Web

  • Site Setup > Zope Management Interface > portal_skins > plone_templates > main_template
  • Customize this, and look for
    <div tal:replace="structure provider:[portlet manager name]" />
  • (use the Elements key to identify exactly which manager you're interested in)

In your own product

  • Put your own version of main_template in

    [your theme product]/skins.

7.4.2. Hiding a Portlet Manager

There are several methods for hiding a portlet manager.

A portlet manager won't display if there are no portlets assigned to it to display or if the assigned portlets have no data.

In the case of the portlet columns, if the portlet manager is empty, then it is also useful to have the surrounding block elements disappear too, so that you don't get a wide blank margin on your page. For this reason, the columns containing the portlet managers in the main_template are wrapped around with slots. Hiding the portlet managers is, therefore, a matter or manipulating these slots. There are various techniques:

Defining an empty slot
Use the following in a content view template to ensure that the right hand column is removed:
  • <metal:column_one fill-slot="column_one_slot" />
Using the sl and sr global variables
These are set as conditions on the slots; they check the respective portlet managers for content and, if they are empty, evaluate to false. You can override these in the template itself.
Using show_portlets option
show_portlets=false can be passed as an option to a template to set both sl and sr to false. To see this in action, have a look at
  • CMFPlone/skins/plone_templates/standard_error_message.py and
  • CMFPlone/browser/ploneview.py

7.4.3. Creating a New Portlet Manager

How to create a new portlet manager.

A practical example of creating a new portlet manager can be found here

Here's a quick checklist of what you need to do.

Quick Cheat Sheet

Through the Web

You cannot create a new portlet manager through the web.

In your own product

You will need to provide the name of

Your theme-specific interface
This is optional but ensures that your portlet manager is available for your theme only. 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):

Interface
This will go in [your theme package]/browser/interfaces.py. You can give it any name you like, but by convention, it should be prefaced with "I".
configuration directive
[your theme package]/profiles/default/portlets.xml
browser:page directive (for the management view)
[your theme package]/browser/configure.zcml
page template (for the management view)
[your theme package]/browser/[your template].pt

Sample interface

from plone.portlets.interfaces import IPortletManager

class [your portlet manager interface](IPortletManager):
 """A description goes here    """

Sample portlets.xml

<?xml version="1.0"?>
<portlets>
 <portletmanager
    name="[your namespace].[your portlet manager]"
    type="[your namespace].[your theme name].browser.interfaces.[your portlet manager interface]"
 />
</portlets>

Sample configure.zcml directive (for the management view)

<browser:page
 for="plone.portlets.interfaces.ILocalPortletAssignable"
 class="plone.app.portlets.browser.manage.ManageContextualPortlets"
 name="[your view name]"
 template="[your template name].pt"
 permission="plone.app.portlets.ManagePortlets"
/>

7.4.4. Practical

Practical

7.4.4.1. Adding Portlet Managers

You need portlets at an additional place in your Plone. In this example we put contextual portlets above the content (contributed by Jens Klein)

This is about adding Portlet MANAGERS, hint: PortletManager != Portlet. A PortletManager is a kind of container for the portlets, like the ViewletManager is for Viewlets. So, after reducing the momentum of misunderstanding, lets start:

Prerequsites

I assume you're familar with GenericSetup based setups for Plone 3. Take a look at DIYPloneStyle and related tutorials if not.

You need Plone 3 installed and a Product NEWTHEME for your own skin (based on DIYPloneStyle works fine).

Strategy

In my example I don't want to customize the main-template. So the idea is to add a viewlet to the plone.app.layout.viewlets.interfaces.IContentViews viewletmanager. So the steps need to be done is

  1. Add a viewlet to the viewlet-manager
  2. Add a portlet-manager
  3. Add a management view for the portlet-manager.

Step One: Add a viewlet

in Products.NEWTHEME add a file abovecontentportlets.pt containing:
<tal:block replace="structure provider:my.abovecontentportlets" />

Here we call the portlet manager, we create it in step two.
But first lets register our new viewlet for the viewletmanager.
Edit your Products/NEWTHEME/configure.zcml and add:

<browser:viewlet
    name="my.abovecontentportlets"
    manager="plone.app.layout.viewlets.interfaces.IContentViews"
    template="abovecontentportlets.pt"
    permission="zope2.View" 
/> 

Step Two: Add a portlet manager

Create a marking interface for the manager and add or edit Products/NEWTHEME/interfaces.py

from plone.portlets.interfaces import IPortletManager

class IMyAboveContent(IPortletManager):
    """we need our own portlet manager above the content area.
    """

Add (or edit) your Products/NEWTHEME/profiles/default/portlets.xml and register a portlet manager:

<?xml version="1.0"?>
<portlets> 
 <portletmanager 
   name="my.abovecontentportlets"
   type="Products.NEWTHEME.interfaces.IMyAboveContent"
 />
</portlets>

Thats all you need if you don't want to manage the portlets through the web. Oh, you want to? So you need a third step:

Step Three: Add a management view for the portlet manager

The management view is rendered for the left and right slots directly on the main-template. But we use a viewlet and in here we have a different view. so we need to call explicitly our view and call the our manager within its context.

 

We need to register a new browser view for an own page template directly calling our manager. Again add some lines to your configure.zcml:
<browser:page
    for="plone.portlets.interfaces.ILocalPortletAssignable"
    class="plone.app.portlets.browser.manage.ManageContextualPortlets"
    name="manage-myabove"
    template="templates/managemyabove.pt"
    permission="plone.app.portlets.ManagePortlets"
/>
And finally we need the template, so add an file managemyabove.pt and edit it:

 

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:metal="http://xml.zope.org/namespaces/metal"
      xmlns:tal="http://xml.zope.org/namespaces/tal"
      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
      metal:use-macro="context/main_template/macros/master"
      i18n:domain="plone">
<head>
    <div metal:fill-slot="javascript_head_slot" tal:omit-tag="">
        <link type="text/css" rel="kinetic-stylesheet"
            tal:attributes="href string:${context/absolute_url}/++resource++manage-portlets.kss"/>
    </div>
</head>
<body>
<div metal:fill-slot="main">
  <h1 class="documentFirstHeading">Manage My Portlets</h1>
  <span tal:replace="structure provider:my.abovecontentportlets" />
</div>
</body>
</html>
That's it. After restarting your zope you can call http://DOMAIN.TLD/plone/path/to/some/context/@@manage-myabove

and assign portlets over your content.

 

7.5. Page Elements Index - Plone Default and Classic Theme

A visual index of the main page elements.

At the moment, this index gives you only viewlets, but the intention is to expand it to include portlets and viewlet- and portlet- managers.

An alternative to using this index is to install GloWorm in your Plone instance. This is a visual inspector which will give you further information about aspects of the Plone Default theme directly through your browser, and enable you to make through-the-web customizations of viewlet templates.

Key Title HTML Name
Manager
Type

Skip Links <p class="hiddenStructure"> … </p> plone.skip_links
plone.portalheader
viewlet

HTML Head Title <title> ...</title> plone.htmlhead.title
plone.htmlhead
viewlet

Next Previous Links <link title="Go to previous item" … /> plone.nextprevious.links
plone.htmlhead.links
viewlet

Favicon Link <link rel="shortcut icon" … /> plone.links.favicon
plone.htmlhead.links
viewlet

Search Link <link rel="search" … /> plone.links.search
plone.htmlhead.links
viewlet

Author Link <link rel="author" … /> plone.links.author
plone.htmlhead.links
viewlet

Navigation Link <link title="Front Page" …> and <link title="Site Map" ..> plone.links.navigation
plone.htmlhead.links
viewlet

Analytics (code snippet defined by the site manager) plone.analytics
plone.portalfooter
viewlet

Header <div id="portal-header"> … </div> plone.header
plone.portaltop
viewlet

Language Selector <ul id="portal-languageselector"> … </ul> plone.app.i18n.locales.languageselector
Portal Top
viewlet
plone.site_actions
Site Actions <ul id="portal-siteactions">...</ul> plone.site_actions
plone.portalheader
viewlet
plone.searchbox
Search Box <div id="portal-searchbox">…</div> plone.searchbox
plone.portalheader
viewlet
plone.logo
Logo <a id="portal-logo" ...>... </a> plone.logo
plone.portalheader
viewlet
plone.global_sections
Global Sections <h5 class="hiddenStructure">Sections</h5> <ul id="portal-globalnav"> … </ul> plone.global_sections
plone.portalheader
viewlet
plone.personal_bar
Personal Bar <div id="portal-personaltools-wrapper"> …</div> plone.personal_bar
plone.portaltop
viewlet

Path Bar (Portal Breadcrumbs) <div id="portal-breadcrumbs">...</div> plone.path_bar
plone.portaltop
viewlet

Content Views <ul class="contentViews"> … </ul> plone.contentviews
plone.contentviews
viewlet

Content Actions
plone.contentactions
plone.contentviews
viewlet
plone.tableofcontents
Table of Contents <dl id="document-toc" class="portlet toc" style="display: none"> … </dl> plone.tableofcontents
plone.abovecontentbody
viewlet
plone.presentation
Presentation <p id="link-presentation">...</p> plone.presentation
plone.abovecontentbody
viewlet

Keywords <div id="category" class="documentByLine">…</div> plone.belowcontenttitle.keywords
plone.belowcontenttitle
viewlet
plone.byline
Byline <div id="plone-document-byline" class="documentByLine">... </div> plone.belowcontenttitle.documentbyline
plone.belowcontenttitle
viewlet

Lock <div id="plone-lock-status" /> plone.lockinfo
plone.abovecontent
viewlet
plone.document_actions
Document Actions <div class="documentActions"> … </div> plone.abovecontenttitle.documentactions
plone.belowcontentbody
viewlet
plone.comments
Comments <div class="discussion"> … </div> plone.comments
plone.belowcontent
viewlet

Content History <div class="contentHistory" id="content-history">…</div> plone.belowcontentbody.contenthistory
plone.belowcontentbody
viewlet
plone.nextprevious
Next Previous <div class="listingBar">…</div> plone.nextprevious
plone.belowcontent
viewlet

Footer <div id="portal-footer">…</div> plone.footer
plone.portalfooter
viewlet
plone.colophon
Colophon <div id="portal-colophon">…</div> plone.colophon
plone.portalfooter
viewlet

7.6. Page Elements Index - Sunburst Theme - Plone 4

A visual index of the main page elements.

At the moment, this index gives you only viewlets, but the intention is to expand it to include portlets and viewlet- and portlet- managers.

An alternative to using this index is to install GloWorm in your Plone instance. This is a visual inspector which will give you further information about aspects of the Plone Default theme directly through your browser, and enable you to make through-the-web customizations of viewlet templates.

Key Title HTML Name
Manager
Type

Skip Links <p class="hiddenStructure"> … </p> plone.skip_links
plone.portalheader
viewlet

HTML Head Title <title> ...</title> plone.htmlhead.title
plone.htmlhead
viewlet

Dublin Core Metadata <meta ... /> plone.htmlhead.dublincore
plone.htmlhead
viewlet

KSS Base Url <link rel="kss-base-url" ... /> plone.htmlhead.kss-base-url
plone.htmlhead
viewlet

Next Previous Links <link title="Go to previous item" … /> plone.nextprevious.links
plone.htmlhead.links
viewlet

Favicon Link <link rel="shortcut icon" … /> plone.links.favicon
plone.htmlhead.links
viewlet

Search Link <link rel="search" … /> plone.links.search
plone.htmlhead.links
viewlet

Author Link <link rel="author" … /> plone.links.author
plone.htmlhead.links
viewlet

Navigation Link <link title="Front Page" …> and <link title="Site Map" ..> plone.links.navigation
plone.htmlhead.links
viewlet

RSS Link <link rel="alternate" title="RSS 1.0" .. /> plone.links.RSS
plone.htmlhead.links
viewlet

Analytics (code snippet defined by the site manager) plone.analytics
plone.portalfooter
viewlet

Header <div id="portal-header"> … </div> plone.header
plone.portaltop
viewlet

Language Selector <ul id="portal-languageselector"> … </ul> plone.app.i18n.locales.languageselector
Portal Top
viewlet
plone.siteactions-sunburst
Site Actions <ul id="portal-siteactions">...</ul> plonetheme.sunburst.site_actions
plone.portalfooter
viewlet
plone.searchbox-sunburst
Search Box <div id="portal-searchbox">…</div> plone.searchbox
plone.portalheader
viewlet
plone.logo-sunburst
Logo <a id="portal-logo" ...>... </a> plone.logo
plone.portalheader
viewlet
plone.global_sections-sunburst
Global Sections <h5 class="hiddenStructure">Sections</h5> <ul id="portal-globalnav"> … </ul> plone.global_sections
plone.portalheader
viewlet
plone.personal_bar-sunburst
Personal Bar <div id="portal-personaltools-wrapper"> …</div> plonetheme.sunburst.personal_bar
plone.portaltop
viewlet
plone.pathbar-sunburst
Path Bar (Portal Breadcrumbs) <div id="portal-breadcrumbs">...</div> plone.path_bar
plone.portaltop
viewlet
plone.contentviews-sunburst
Content Views <ul class="contentViews"> … </ul> plone.contentviews
plone.contentviews
viewlet
plone.contentactions-sunburst
Content Actions
plone.contentactions
plone.contentviews
viewlet
plone.toc-sunburst
Table of Contents <dl id="document-toc" class="portlet toc" style="display: none"> … </dl> plone.tableofcontents
plone.abovecontentbody
viewlet
plone.presentation-sunburst
Presentation <p id="link-presentation">...</p> plone.presentation
plone.abovecontentbody
viewlet
plone.keywords-sunburst
Keywords <div id="category" class="documentByLine">…</div> plone.belowcontenttitle.keywords
plone.belowcontenttitle
viewlet
plone.byline-sunburst
Byline <div id="plone-document-byline" class="documentByLine">... </div> plone.belowcontenttitle.documentbyline
plone.belowcontenttitle
viewlet

Lock <div id="plone-lock-status" /> plone.lockinfo
plone.abovecontent
viewlet

Document Actions <div class="documentActions"> … </div> plone.abovecontenttitle.documentactions
plone.belowcontentbody
viewlet
plone.relateditems-sunburst
Related Items
<div class="relatedItems"> … </div> plone.belowcontentbody.relateditems
plone.belowcontentbody
viewlet
plone.comment-sunburst
Comments <div class="discussion"> … </div> plone.comments
plone.belowcontent
viewlet
plone.contenthistory-sunburst
Content History <div class="contentHistory" id="content-history">…</div> plone.belowcontentbody.contenthistory
plone.belowcontentbody
viewlet
plone.nextprevious-sunburst
Next Previous <div class="listingBar">…</div> plone.nextprevious
plone.belowcontent
viewlet
plone.footer-sunburst
Footer <div id="portal-footer">…</div> plone.footer
plone.portalfooter
viewlet
plone.colophon-sunburst
Colophon <div id="portal-colophon">…</div> plone.colophon
plone.portalfooter
viewlet

7.7. Structural Elements

Elements forming the underlying page structure (viewlet and portlet managers).

7.7.1. Header

Calls the viewlet managers for the site header.

Snippet:
<div id="portal-header"> … </div>
Name:
plone.header
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.header

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
portal_header.pt
Class Name:
none
Manager:
plone.portaltop (name)
plone.app.layout.viewlets.interfaces.IPortalTop (interface)

Sample files & directives

Put a version of portal_header.pt in [your theme package]/browser/templates)

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IPortalTop"
    template="templates/[your template name].pt"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.portaltop" skinname="[your skin name]">
        <viewlet name="plone.header" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.portaltop" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.8. Hidden Elements

Hidden page elements (appearing in the HTMLhead or with css set to display:none).

7.8.1. Skip Links

Hidden links at the top of the page to skip to the content and the navigation.

 

Snippet:
<p class="hiddenStructure"> … </p>
CSS:
invisibles.css
Name:
plone.skip_links
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.skip_links

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
skip_links.pt
Class Name:
plone.app.layout.viewlets.common.SkipLinksViewlet
Manager:
plone.portalheader (name)
plone.app.layout.viewlets.interfaces.IPortalHeader (interface)

Sample files & directives

Put a version of skip_links.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.common import SkipLinksViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](SkipLinksViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IPortalHeader"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.portalheader" skinname="[your skin name]">
        <viewlet name="plone.skip_links" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.portalheader" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.8.2. HTML Head Title

The page title in the HTML head.

Snippet:
<title> ...</title>
CSS:
none
Name:
plone.htmlhead.title
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.htmlhead.title

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
none
Class Name:
plone.app.layout.viewlets.common.TitleViewlet
Manager:
plone.htmlhead (name)
plone.app.layout.viewlets.interfaces.IHtmlHead (interface)

Sample files & directives

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.common import TitleViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile

class [your class name](TitleViewlet):
    [your code here]

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IHtmlHead"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.htmlhead" skinname="[your skin name]">
        <viewlet name="plone.htmlhead.title" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.htmlhead" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.8.3. Next Previous Links

Provides next/previous links in the HTML head.

Snippet:
<link title="Go to previous item" … />
CSS:
none
Name:
plone.nextprevious.links
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.nextprevious.links

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/nextprevious/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/nextprevious/
Template Name:
links.pt
Class Name:
plone.app.layout.nextprevious.view.NextPreviousLinksViewlet
Manager:
plone.htmlhead.links (name)
plone.app.layout.viewlets.interfaces.IHtmlHeadLinks (interface)

Sample files & directives

Put a version of links.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.nextprevious.view import NextPreviousLinksViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](NextPreviousLinksViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IHtmlHeadLinks"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.htmlhead.links" skinname="[your skin name]">
        <viewlet name="plone.nextprevious.links" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.htmlhead.links" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.8.4. Favicon Link

The favicon link in HTML head.

Snippet:
<link rel="shortcut icon" … />
CSS:
none
Name:
plone.links.favicon
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.links.favicon

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/links/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/links/
Template Name:
favicon.pt
Class Name:
plone.app.layout.links.viewlets.FaviconViewlet
Manager:
plone.htmlhead.links (name)
plone.app.layout.viewlets.interfaces.IHtmlHeadLinks (interface)

Sample files & directives

Put a version of favicon.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.links.viewlets import FaviconViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](FaviconViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IHtmlHeadLinks"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.htmlhead.links" skinname="[your skin name]">
        <viewlet name="plone.links.favicon" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.htmlhead.links" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.8.5. Search Link

The search link in HTML head.

Snippet:
<link rel="search" … />
CSS:
none
Name:
plone.links.search
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.links.search

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/links/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/links/
Template Name:
search.pt
Class Name:
plone.app.layout.links.viewlets.SearchViewlet
Manager:
plone.htmlhead.links (name)
plone.app.layout.viewlets.interfaces.IHtmlHeadLinks (interface)

Sample files & directives

Put a version of search.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.links.viewlets import SearchViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](SearchViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IHtmlHeadLinks"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.htmlhead.links" skinname="[your skin name]">
        <viewlet name="plone.links.search" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.htmlhead.links" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.8.6. Author Link

The author link in the HTML head.

Snippet:
<link rel="author" … />
CSS:
none
Name:
plone.links.author
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.links.author

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/links/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/links/
Template Name:
author.pt
Class Name:
plone.app.layout.links.viewlets.AuthorViewlet
Manager:
plone.htmlhead.links (name)
plone.app.layout.viewlets.interfaces.IHtmlHeadLinks (interface)

Sample files & directives

Put a version of author.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.links.viewlets import AuthorViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](AuthorViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IHtmlHeadLinks"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.htmlhead.links" skinname="[your skin name]">
        <viewlet name="plone.links.author" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.htmlhead.links" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.8.7. Navigation Link

The navigation link in the HTML head.

Snippet:
<link title="Front Page" …> and <link title="Site Map" ..>
CSS:
none
Name:
plone.links.navigation
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.links.navigation

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/links/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/links/
Template Name:
navigation.pt
Class Name:
plone.app.layout.links.viewlets.NavigationViewlet
Manager:
plone.htmlhead.links (name)
plone.app.layout.viewlets.interfaces.IHtmlHeadLinks (interface)

Sample files & directives

Put a version of navigation.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.links.viewlets import NavigationViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](NavigationViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IHtmlHeadLinks"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.htmlhead.links" skinname="[your skin name]">
        <viewlet name="plone.links.navigation" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.htmlhead.links" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.8.8. Analytics

Google analytics code snippet.

Notes:
Provide the code snippet for your site through the web: Site Setup > Site settings
Snippet:
(code snippet defined by the site manager)
CSS:
none
Name:
plone.analytics
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.analytics

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/analytics/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/analytics/
Template Name:
none
Class Name:
plone.app.layout.analytics.view.AnalyticsViewlet
Manager:
plone.portalfooter (name)
plone.app.layout.viewlets.interfaces.IPortalFooter (interface)

Sample files & directives

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.analytics.view import AnalyticsViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile

class [your class name](AnalyticsViewlet):
    [your code here]

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IPortalFooter"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.portalfooter" skinname="[your skin name]">
        <viewlet name="plone.analytics" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.portalfooter" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.8.9. Dublin Core Metadata

The Dublin Core metadata in the HTML head.

Snippet:
<meta .... />
CSS:
none
Name:
plone.htmlhead.dublincore
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.htmlhead.dublincore

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
dublin_core.pt
Class Name:
plone.app.layout.viewlets.common.DublinCoreViewlet
Manager:
plone.htmlhead (name)
plone.app.layout.viewlets.interfaces.IHtmlHead (interface)

Sample files & directives

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.common import DublinCoreViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile

class [your class name](DublinCoreViewlet):
    [your code here]

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IHtmlHead"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.htmlhead" skinname="[your skin name]">
        <viewlet name="plone.htmlhead.dublincore" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.htmlhead" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.8.10. KSS Base Url

Link rel tag in the HTML head with the real url of the published page.

Snippet:
<link rel="kss-base-url" .... />
CSS:
none
Name:
plone.htmlhead.kss-base-url
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.htmlhead.kss-base-url

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/kss/
  • [your egg location]/plone.app.kss-[version].egg/plone/app/kss/
Template Name:
none
Class Name:
plone.app.kss.headerViewlet.KSSBaseUrlViewlet
Manager:
plone.htmlhead (name)
plone.app.layout.viewlets.interfaces.IHtmlHead (interface)

Sample files & directives

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.kss.headerViewlet import KSSBaseUrlViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile

class [your class name](KSSBaseUrlViewlet):
    [your code here]

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IHtmlHead"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.htmlhead" skinname="[your skin name]">
        <viewlet name="plone.htmlhead.kss-base-url" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.htmlhead" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.8.11. RSS Link

The RSS link in the HTML head.

Snippet:
<link rel="alternate" title="RSS 1.0" ... />
CSS:
none
Name:
plone.links.RSS
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.links.RSS

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/links/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/links/
Template Name:
rsslink.pt
Class Name:
plone.app.layout.links.viewlets.RSSViewlet
Manager:
plone.htmlhead.links (name)
plone.app.layout.viewlets.interfaces.IHtmlHeadLinks (interface)

Sample files & directives

Put a version of navigation.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.links.viewlets import RSSViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](RSSViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IHtmlHeadLinks"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.htmlhead.links" skinname="[your skin name]">
        <viewlet name="plone.links.RSS" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.htmlhead.links" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9. Visible Page Elements

Page elements visible on the page (logo, site actions, search box etc)

7.9.1. Language Selector

Provides a drop down list to select a different language.

Snippet:
<ul id="portal-languageselector"> … </ul>
Name:
plone.app.i18n.locales.languageselector
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.app.i18n.locales.languageselector

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/i18n/locales/browser/
  • [your egg location]/plone.app.i18n-[version].egg/plone/app/i18n/locales/browser/
Template Name:
languageselector.pt
Class Name:
plone.app.i18n.locales.browser.selector.LanguageSelector
Manager:
Portal Top (name)
plone.app.layout.viewlets.interfaces.IPortalTop (interface)

Sample files & directives

Put a version of languageselector.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.i18n.locales.browser.selector import LanguageSelector
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](LanguageSelector):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IPortalTop"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="Portal Top" skinname="[your skin name]">
        <viewlet name="plone.app.i18n.locales.languageselector" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="Portal Top" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.2. Site Actions

Links, available on every page, to provide specific functionality or information.

Notes:
You can reorder, add, or remove individual site actions
  • through the web: Site Setup >Zope Management Interface > portal_actions > site_actions
  • in your product: profiles/default/actions.xml
Snippet:
<ul id="portal-siteactions">...</ul>
CSS:
public.css
Name:
plone.site_actions
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.site_actions

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
site_actions.pt
Class Name:
plone.app.layout.viewlets.common.SiteActionsViewlet
Manager:
plone.portalheader (name)
plone.app.layout.viewlets.interfaces.IPortalHeader (interface)

Sample files & directives

Put a version of site_actions.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.common import SiteActionsViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](SiteActionsViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IPortalHeader"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.portalheader" skinname="[your skin name]">
        <viewlet name="plone.site_actions" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.portalheader" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.3. Search Box

Site search facility.

Notes:
To customise the search box behaviour
  • through the web: Site Setup > Search
  • in your product: profiles/default/propertiestool.xml
Snippet:
<div id="portal-searchbox">…</div>
CSS:
public.css
Name:
plone.searchbox
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.searchbox
Further information:
http://plone.org/documentation/kb/the-search-box

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
searchbox.pt
Class Name:
plone.app.layout.viewlets.common.SearchBoxViewlet
Manager:
plone.portalheader (name)
plone.app.layout.viewlets.interfaces.IPortalHeader (interface)

Sample files & directives

Put a version of searchbox.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.common import SearchBoxViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](SearchBoxViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IPortalHeader"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.portalheader" skinname="[your skin name]">
        <viewlet name="plone.searchbox" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.portalheader" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>
'

7.9.4. Logo

The site logo.

Snippet:
<a id="portal-logo" ...>... </a>
CSS:
public.css
Name:
plone.logo
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.logo
Further information:
http://plone.org/documentation/kb/where-is-what/the-logo
See also the Quick Start Section of this manual.

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
logo.pt
Class Name:
plone.app.layout.viewlets.common.LogoViewlet
Manager:
plone.portalheader (name)
plone.app.layout.viewlets.interfaces.IPortalHeader (interface)

Sample files & directives

Put a version of logo.pt in [your theme package]/browser/templates)

Modify the logo.pt to suit your needs. For example, if you want to use an image named something other than logo.jpg, you could use this code and style #header in your mytheme.css file.

<a metal:define-macro="portal_logo"
   id="portal-logo"
   accesskey="1"
   tal:attributes="href view/navigation_root_url"
   i18n:domain="plone">
    <!-- <img src="logo.jpg" alt=""
         tal:replace="structure view/logo_tag" /> --> <!--commenting out the code that normally looks for logo.jpg -->
    <div id="banner"><!-- style this div in your mytheme.css --></div></a>

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.common import LogoViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](LogoViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IPortalHeader"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.portalheader" skinname="[your skin name]">
        <viewlet name="plone.logo" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.portalheader" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>
'

7.9.5. Global Sections

The top level sections of the site.

Notes:
The sections are either auto-generated from top level content items or can be set up manually
  • through the web: Site Setup > Navigation (for auto-generation)
    Site Setup > Zope Management Interface > portal_tabs (for manually defined sections)
  • in your product: profiles/default/actions.xml and propertiestool.xml
Snippet:
<h5 class="hiddenStructure">Sections</h5> <ul id="portal-globalnav"> … </ul>
CSS:
public.css
Name:
plone.global_sections
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.global_sections
Further information:
http://plone.org/documentation/kb/where-is-what/the-navigation-tabs

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
sections.pt
Class Name:
plone.app.layout.viewlets.common.GlobalSectionsViewlet
Manager:
plone.portalheader (name)
plone.app.layout.viewlets.interfaces.IPortalHeader (interface)

Sample files & directives

Put a version of sections.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.common import GlobalSectionsViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](GlobalSectionsViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IPortalHeader"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.portalheader" skinname="[your skin name]">
        <viewlet name="plone.global_sections" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.portalheader" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>
'

7.9.6. Personal Bar

Provides the Log in link and further links for users once logged in.

Notes:
You can reorder, add, or remove specific links in the personal bar
  • through the web: Site Setup >Zope Management Interface > portal_actions > user
  • In your product: profiles/default/actions.xml
Snippet:
<div id="portal-personaltools-wrapper"> …</div>
CSS:
public.css
Name:
plone.personal_bar
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.personal_bar
Further information:
http://plone.org/documentation/kb/where-is-what/the-personal-bar

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
personal_bar.pt
Class Name:
plone.app.layout.viewlets.common.PersonalBarViewlet
Manager:
plone.portaltop (name)
plone.app.layout.viewlets.interfaces.IPortalTop (interface)

Sample files & directives

Put a version of personal_bar.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.common import PersonalBarViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](PersonalBarViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IPortalTop"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.portaltop" skinname="[your skin name]">
        <viewlet name="plone.personal_bar" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.portaltop" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>
'

7.9.7. Path Bar (Portal Breadcrumbs)

Provides the breadcrumb trail.

Snippet:
<div id="portal-breadcrumbs">...</div>
Note:
In the Sunburst theme, the breadcrumbs have been styled not to appear until the third level down. Customize the CSS to change this behaviour.
CSS:
public.css
Name:
plone.path_bar
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.path_bar
Further information:
http://plone.org/documentation/kb/where-is-what/the-path-bar

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
path_bar.pt
Class Name:
plone.app.layout.viewlets.common.PathBarViewlet
Manager:
plone.portaltop (name)
plone.app.layout.viewlets.interfaces.IPortalTop (interface)

Sample files & directives

Put a version of path_bar.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.common import PathBarViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](PathBarViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IPortalTop"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.portaltop" skinname="[your skin name]">
        <viewlet name="plone.path_bar" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.portaltop" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>
'

7.9.8. Content Views

The View, Edit, and other tabs in the editing interface.

Snippet:
<ul class="contentViews"> … </ul>
CSS:
authoring.css
Name:
plone.contentviews
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.contentviews

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
contentviews.pt
Class Name:
none
Manager:
plone.contentviews (name)
plone.app.layout.viewlets.interfaces.IContentViews (interface)

Sample files & directives

Put a version of contentviews.pt in [your theme package]/browser/templates)

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IContentViews"
    template="templates/[your template name].pt"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.contentviews" skinname="[your skin name]">
        <viewlet name="plone.contentviews" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.contentviews" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.9. Content Actions

Provides the display drop-down and other actions in editing mode. There are three content actions components, registered for different view interfaces (as different sets of actions are required in different contexts).

Name:
plone.contentactions
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.contentactions

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
contentactions_blank.pt & contentactions.pt
Class Name:
plone.app.layout.viewlets.common.ContentActionsViewlet
Manager:
plone.contentviews (name)
plone.app.layout.viewlets.interfaces.IContentViews (interface)

Sample files & directives

Put a version of contentactions_blank.pt & contentactions.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.common import ContentActionsViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](ContentActionsViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IContentViews"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.contentviews" skinname="[your skin name]">
        <viewlet name="plone.contentactions" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.contentviews" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.10. Table of Contents

Provides a set of bookmarks for the current page.

Notes:
Turned on per content item through Edit > Settings.
Snippet:
<dl id="document-toc" class="portlet toc" style="display: none"> … </dl>
CSS:
portlets.css
Name:
plone.tableofcontents
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.tableofcontents

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
toc.pt
Class Name:
plone.app.layout.viewlets.common.TableOfContentsViewlet
Manager:
plone.abovecontentbody (name)
plone.app.layout.viewlets.interfaces.IAboveContentBody (interface)

Sample files & directives

Put a version of toc.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.common import TableOfContentsViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](TableOfContentsViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IAboveContentBody"
    class=".[your module].[your class name]"
    for="Products.ATContentTypes.interface.IATDocument"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.abovecontentbody" skinname="[your skin name]">
        <viewlet name="plone.tableofcontents" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.abovecontentbody" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.11. Presentation

Provides a link to a presentation view of a document.

Notes:
Only available for a document. The link to a presentation view can be turned on or off
  • through the web: on an individual item (Edit > Settings > Presentation )
    or Site Setup > Types (site-wide per type)
  • in your product: profiles/default/types (per type)
Snippet:
<p id="link-presentation">...</p>
CSS:
none
Name:
plone.presentation
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.presentation

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/presentation/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/presentation/
Template Name:
none
Class Name:
plone.app.presentation.PresentationViewlet
Manager:
plone.abovecontentbody (name)
plone.app.layout.viewlets.interfaces.IAboveContentBody (interface)

Sample files & directives

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.presentation import PresentationViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile

class [your class name](PresentationViewlet):
    [your code here]

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IAboveContentBody"
    class=".[your module].[your class name]"
    for="Products.ATContentTypes.interface.IATDocument"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.abovecontentbody" skinname="[your skin name]">
        <viewlet name="plone.presentation" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.abovecontentbody" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.12. Keywords

The categories (a.k.a. keywords / tags / labels) that have been assigned to the item.

Notes:
This will only appear if some categories have been assigned using Edit > Categories.
Snippet:
<div id="category" class="documentByLine">…</div>
CSS:
public.css
Name:
plone.belowcontenttitle.keywords
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.belowcontenttitle.keywords

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
keywords.pt
Class Name:
none
Manager:
plone.belowcontenttitle (name)
plone.app.layout.viewlets.interfaces.IBelowContentTitle (interface)

Sample files & directives

Put a version of keywords.pt in [your theme package]/browser/templates)

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IBelowContentTitle"
    template="templates/[your template name].pt"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.belowcontenttitle" skinname="[your skin name]">
        <viewlet name="plone.belowcontenttitle.keywords" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.belowcontenttitle" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.13. Byline

The 'about' information (who created a content item and when it was modified).

Notes:
You can turn off the Byline for anonymous viewers
  • through the web: Site Setup > Security
  • In your product: profiles/default/propertiestool.xml
Snippet:
<div id="plone-document-byline" class="documentByLine">... </div>
CSS:
public.css
Name:
plone.belowcontenttitle.documentbyline
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.belowcontenttitle.documentbyline

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
document_byline.pt
Class Name:
plone.app.layout.viewlets.content.DocumentBylineViewlet
Manager:
plone.belowcontenttitle (name)
plone.app.layout.viewlets.interfaces.IBelowContentTitle (interface)

Sample files & directives

Put a version of document_byline.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.content import DocumentBylineViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](DocumentBylineViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IBelowContentTitle"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.belowcontenttitle" skinname="[your skin name]">
        <viewlet name="plone.belowcontenttitle.documentbyline" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.belowcontenttitle" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.14. Lock

Indicates that the content item is locked for editing.

Snippet:
<div id="plone-lock-status" />
CSS:
public.css
Name:
plone.lockinfo
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.lockinfo

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/locking/browser/
  • [your egg location]/plone.locking-[version].egg/plone/locking/browser/
Template Name:
info.pt
Class Name:
plone.locking.browser.info.LockInfoViewlet
Manager:
plone.abovecontent (name)
plone.app.layout.viewlets.interfaces.IAboveContent (interface)

Sample files & directives

Put a version of info.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.locking.browser.info import LockInfoViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](LockInfoViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IAboveContent"
    class=".[your module].[your class name]"
    for="plone.locking.interfaces.ITTWLockable"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.abovecontent" skinname="[your skin name]">
        <viewlet name="plone.lockinfo" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.abovecontent" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.15. Workflow History

Summarizes workflow transitions on the current content item.

Snippet:
<div class="reviewHistory" id="review-history">…</div>
CSS:
authoring.css
Name:
plone.belowcontentbody.workflowhistory
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.belowcontentbody.workflowhistory

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
review_history.pt
Class Name:
plone.app.layout.viewlets.content.WorkflowHistoryViewlet
Manager:
plone.belowcontentbody (name)
plone.app.layout.viewlets.interfaces.IBelowContentBody (interface)

Sample files & directives

Put a version of review_history.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.content import WorkflowHistoryViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](WorkflowHistoryViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IBelowContentBody"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.belowcontentbody" skinname="[your skin name]">
        <viewlet name="plone.belowcontentbody.workflowhistory" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.belowcontentbody" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.16. Content History

Summarizes workflow transitions and version history on the current content item (this replaces the workflow viewlet in Plone 3.3).

Snippet:
<div class="contentHistory" id="content-history">…</div>
CSS:
authoring.css
Name:
plone.belowcontentbody.contenthistory
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.belowcontentbody.contenthistory

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
content_history.pt
Class Name:
plone.app.layout.viewlets.content.ContentHistoryViewlet
Manager:
plone.belowcontentbody (name)
plone.app.layout.viewlets.interfaces.IBelowContentBody (interface)

Sample files & directives

Put a version of review_history.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.content import ContentHistoryViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](ContentHistoryViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IBelowContentBody"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.belowcontentbody" skinname="[your skin name]">
        <viewlet name="plone.belowcontentbody.contenthistory" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.belowcontentbody" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.17. Document Actions

The Print and RSS links.

Notes:
You can reorder, add, or remove individual document actions
  • through the web: Site Setup >Zope Management Interface > portal_actions > document_actions
  • In your product: profiles/default/actions.xml
Snippet:
<div class="documentActions"> … </div>
CSS:
public.css
Name:
plone.abovecontenttitle.documentactions
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.abovecontenttitle.documentactions
Further information:
http://plone.org/documentation/kb/where-is-what/document-actions

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
document_actions.pt
Class Name:
plone.app.layout.viewlets.content.DocumentActionsViewlet
Manager:
plone.belowcontentbody (name)
plone.app.layout.viewlets.interfaces.IBelowContentBody (interface)

Sample files & directives

Put a version of document_actions.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.content import DocumentActionsViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](DocumentActionsViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IBelowContentBody"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.belowcontentbody" skinname="[your skin name]">
        <viewlet name="plone.abovecontenttitle.documentactions" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.belowcontentbody" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>
'

7.9.18. Related Items

Items related to the content

Notes:
This viewlet displays links to additional content items selected by the editor under the categorization tab.
Snippet:
<div class="relatedItems"> … </div>
CSS:
public.css
Name:
plone.belowcontentbody.relateditems
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.belowcontentbody.relateditems

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
document_relateditems.pt
Class Name:
plone.app.layout.viewlets.content.ContentRelatedItems
Manager:
plone.belowcontentbody (name)
plone.app.layout.viewlets.interfaces.IBelowContentBody (interface)

Sample files & directives

Put a version of document_relateditems.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.content import ContentRelatedItems
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](ContentRelatedItems):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IBelowContentBody"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.belowcontentbody" skinname="[your skin name]">
        <viewlet name="plone.belowcontentbody.relateditems" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.belowcontentbody" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>
'

7.9.19. Comments

Supplies the commenting interface.

Notes:
Comments can be turned on or off
  • through the web: on an individual item (Edit > Settings > Allow Comments )
    or Site Setup > Types (site-wide per type)
  • in your product: profiles/default/types (per type)
Snippet:
<div class="discussion"> … </div>
CSS:
public.css
Name:
plone.comments
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.comments

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
comments.pt
Class Name:
plone.app.layout.viewlets.comments.CommentsViewlet
Manager:
plone.belowcontent (name)
plone.app.layout.viewlets.interfaces.IBelowContent (interface)

Sample files & directives

Put a version of comments.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.viewlets.comments import CommentsViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](CommentsViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IBelowContent"
    class=".[your module].[your class name]"
    for="Products.CMFCore.interfaces.IContentish"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.belowcontent" skinname="[your skin name]">
        <viewlet name="plone.comments" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.belowcontent" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.20. Next Previous

Provides next/previous functionality for a folder.

Notes:
Turn this on per folder using Edit > Settings.
Snippet:
<div class="listingBar">…</div>
CSS:
public.css
Name:
plone.nextprevious
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.nextprevious

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/nextprevious/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/nextprevious/
Template Name:
nextprevious.pt
Class Name:
plone.app.layout.nextprevious.view.NextPreviousViewlet
Manager:
plone.belowcontent (name)
plone.app.layout.viewlets.interfaces.IBelowContent (interface)

Sample files & directives

Put a version of nextprevious.pt in [your theme package]/browser/templates)

Create your own version of the class in [your theme package]/browser/[your module].py

from plone.app.layout.nextprevious.view import NextPreviousViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class [your class name](NextPreviousViewlet):
    render = ViewPageTemplateFile("[your template name]")

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IBelowContent"
    class=".[your module].[your class name]"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.belowcontent" skinname="[your skin name]">
        <viewlet name="plone.nextprevious" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.belowcontent" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.21. Footer

Contains copyright information.

Snippet:
<div id="portal-footer">…</div>
CSS:
public.css
Name:
plone.footer
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.footer

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
footer.pt
Class Name:
none
Manager:
plone.portalfooter (name)
plone.app.layout.viewlets.interfaces.IPortalFooter (interface)

Sample files & directives

Put a version of footer.pt in [your theme package]/browser/templates)

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IPortalFooter"
    template="templates/[your template name].pt"
    for="*"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.portalfooter" skinname="[your skin name]">
        <viewlet name="plone.footer" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.portalfooter" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

7.9.22. Colophon

Contains links to plone.org etc.

Snippet:
<div id="portal-colophon">…</div>
CSS:
public.css
Name:
plone.colophon
Type:
viewlet

Customizing through the Zope Management Interface

Use:
Site Setup > Zope Management Interface > portal_view_customizations
Go to:
plone.colophon

Customizing in your own product

The following details will help you locate the files that you will need to copy into your own product. They will also help you to provide the correct information to create your own zcml directives, Python classes, and interfaces.See viewlet for more information.

Located in:
  • [your egg location]/plone/app/layout/viewlets/
  • [your egg location]/plone.app.layout-[version].egg/plone/app/layout/viewlets/
Template Name:
colophon.pt
Class Name:
none
Manager:
plone.portalfooter (name)
plone.app.layout.viewlets.interfaces.IPortalFooter (interface)

Sample files & directives

Put a version of colophon.pt in [your theme package]/browser/templates)

Wire up your viewlet in [your theme package]/browser/configure.zcml

<browser:viewlet
    name="[your namespace].[your viewlet name]"
    manager="plone.app.layout.viewlets.interfaces.IPortalFooter"
    template="templates/[your template name].pt"
    for="*"
    layer=".interfaces.[your theme specific interface]"
    permission="zope2.View"
/>

In [your theme package]/profiles/default/viewlets.xml

Hide the original viewlet (if you wish)

<object>
    <hidden manager="plone.portalfooter" skinname="[your skin name]">
        <viewlet name="plone.colophon" />
    </hidden>

Insert your new viewlet in a viewlet manager

    <order manager="plone.portalfooter" skinname="[your skin name]"
           based-on="Plone Default">
        <viewlet name="[your namespace].[your viewlet name]"
                 insert-before="*" />
    </order>
</object>

8. Where is What

How to locate the bits and pieces you need. Links to useful visual aids for identifying page elements, pointers to locating your product and eggs directories, diagrams of a theme egg on the file system.

8.1. Where is What on the Page

How can you track down the files related to an individual page element?

At the time of writing, there's no in-built magic wand to point at an element on a Plone web page and find out exactly what templates and code are involved in its creation. There may be soon though, and the adventurous might like to explore Weblion's Gloworm tool.

If you're not ready for an adventure yet, then there are a number of good tutorials available with diagrams and guides to where is what.

Understanding how the CSS maps to the Page

The Weblion project has an excellent Wiki page to help you with this

 

 

Firebug (an add-on for Firefox), of course, is an essential tool for inspecting code and CSS for a page.

 

 

Page Elements

Page elements are consistently named in Plone, so once you know the name of an area of the page, you're well on the way to tracking down the relevant files

  • you can find a visual key to page elements in the Elements section of this manual
  • you'll also find an excellent overview in the What Controls What You See tutorial on Plone.org
  • and a mapping of the viewlet and portlet managers on the Weblion wiki

 

8.2. Where's my Zope Instance?

Where your Zope instance lives depends on the Plone installer or installation process you used.

Plone Version 3.1.2 onwards

Buildout
In a Buildout based installation, you don't need to worry much about your Zope instance. If you really want to investigate you'll find the actual instance in [your buildout]/parts/instance. However most of the key bits (your Plone products, 3rd party products, your Data.fs) don't actually live there. They are all assembled together from various parts of your file system by the zope.conf file which is generated when you run buildout.

Plone Version 3.1.1 or lower

Plone Installer
The Plone installers (apart from the Plone 3.1 Universal Installer) usually drop a Zope instance directory alongside the Zope and Python software directories. So, for example, a standard Windows installation, locates your Zope instance at c:\Program Files\Plone 3\Data. On a Mac, it will be called 'instance' and will probably live in a Plone folder in your applications folder.
The Plone 3.1 Universal Installer, however, will have given you a buildout based installation.
Plone Product Package
If you've installed Zope yourself, you'll have been prompted to create a Zope instance, so you should have a good idea of where that is on your system.

8.3. Where's my Products Directory?

How to track down your products directory. It'll differ according to the Plone installer or installation process you used.

The products directory is where old-style 2.5 products live. To track this down, you'll need to know where your Zope Instance or your Buildout is first.

For theming purposes, the main reason you'll need to investigate the products directory is to locate Plone Default theme files - as parts of Plone are still in old-style product form.

Plone version 3.1.2 onwards

In a Buildout based installation, you'll find products in various directories.
Core Plone products (such as CMFPlone)
For these, have a look in
  • [your buildout]/parts/plone.
Products you download yourself
These should go in
  • [your buildout]/products.
If you find you haven't got a products directory there, then it is OK to create one yourself.
Products you asked buildout to download
If you asked buildout to go and fetch some old-style products, then these will have been dropped into
  • [your buildout]/parts/[directory name].
(Buildout will also have created the directory and will have called it something like "productdistros").

Plone version 3.1.1 or lower

Plone Installer and Plone Product Package
It should be easy to locate all your products (those belonging to the core Plone installation and those you've downloaded yourself) in
  • [your zope instance]/products
However, if you used the Plone 3.1 Universal Installer your installation will be buildout based.

8.4. Where's my Egg Location?

It is easy enough for Zope to find your eggs, harder for humans.

Plone version 3.1.2 onwards

Core Plone Default Products
For core products used in the Plone Default Theme, buildout has an eggs directory
  • [your buildout]/eggs
which is where eggs are automatically dropped when Plone is installed.
Your own theme product
Because your own theme product will be under development, this will go in a separate place in your buildout
  • [your buildout]/[zinstance|zeocluster|]/src

(note that to share eggs between buildouts you can specify a different location for this in a buildout defaults file, check the buildout tutorial on plone.org for more information).

Using Omelette to get at your eggs quickly

It is a bit of a drag navigating your way around all the eggs used by Plone. David Glick's Omelette recipe creates a unified directory structure of all namespace packages, symlinking to the actual contents, via buildout. Full instructions and documentation on this can be found here:

http://pypi.python.org/pypi/collective.recipe.omelette

Once you've re-run buildout with the omelette recipe, you'll find that you have a new section here:

  • [your buildout]/[zinstance|zeocluster]/parts/omelette

and eggs such as plone.app.layout can be found in:

  • [your buildout]/[zinstance|zeocluster]/parts/omelette/plone/app/layout

Plone version 3.1.1 or lower

Plone Installer
If you have installed Plone with an installer, then the eggs will probably have been dropped into
  • [your plone installation]/Python/Lib/site-packages.
However, if you've used the Plone 3.1 universal installer, then you will have a buildout based installation.
The Plone Product Package
If you used the product package (i.e. installed from source), then you may well find them in
  • [your Zope instance]/lib/python.

8.5. Location of files in your own Theme Product

The egg created for you by the plone3_theme paster template should have a file system layout very similar to this diagram.

If the diagram doesn't help, then consult the next few pages where sections of the diagram are combined with a text explanation.

your theme egg

 

8.6. Files for the Skin

These files and directories will be relevant when working on the Skin part of your theme.

/skins/[your theme namespace].[your theme name]_custom_templates | custom_images | styles
These directories will form your skin layers. Your templates, images, and stylesheets can go here. If you asked it to, the plone3_theme paster template will have provided blank style sheets to override the Plone Default ones.
/skins.zcml
When your Zope instance starts up, this turns your directories into skin layers
/profiles/default/skins.xml | cssregistry.xml | jsregistry.xml
When your theme is installed in your Plone site, this sets up the hierarchy of skin layers, and registers your style sheets and JavaScript with the registries
your theme egg - the skin files

 

8.7. Files for Components

These files and directories will be relevant when working on the Components part of your theme.

/browser/viewlet.py | viewlet.pt
An example viewlet component
/browser/interfaces.py
This is used to create your theme interface (and any other interfaces you might need)
/profiles/default/viewlets.xml
Use this file to order your viewlets within viewlet managers
/browser/configure.zcml
Use this file to wire up your components
/browser/templates | styles
These directories can be used for templates, styles, and images. You will need to register these as directories as resources in configure.zcml.
your theme egg - the components files

 

8.8. Files for Configuration

These files and directories will be relevant when working on the Configuration part of your theme.

/profiles/default/
This directory holds the xml for Generic Setup. The plone3_theme paster template will have provided you with some ready-made files - for setting up your skin layers, registering your style sheets and JavaScripts, and ordering your viewlets.
/profiles.zcml
When your Zope instance starts up, this file makes the profile available for Generic Setup to use.
your theme egg - the configuration files

 

8.9. Files for Installing your Egg

These are the files and directories required to install your egg in your python path and make it available to Zope on start up.

your theme egg - the files used for installing the egg

8.10. Files for Installing your Theme

These are the files used when you install your theme product using Site Setup > Add / Remove Products or Zope Management Interface > portal_quickinstaller

/profiles/default/
Generic Setup will install your theme profile when your theme is installed. import_steps.xml points to a 'handler' for installation steps which aren't yet part of Generic Setup or can't be expressed as XML.
/setuphandlers.py
This contains the handler for non-Generic Setup installation steps.
your theme egg - the files used by quick installer

9. Illustrations

The illustrations used in this manual - if you just want to look at the pictures.

9.1. Elements

Snippet Images of the individual page elements

10. Rules-Based Theming

An overview of how to theme a site using a transform approach.

10.1. collective.xdv

The collective.xdv documentation has now moved to its own area in the Downloads section of the Plone website.