Building Blocks
Plone Theme Reference
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:

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
2. Skin
Templates, style sheets, Javascript files, how to customize them, where to find them.
2.1. Templates and the Templating Language
Templates and the Templating Language
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>
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:
-
Play nicely with editing tools.
-
What you see is very similar to what you get.
-
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
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
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:
-
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:defineon the same tag. -
Both of
tal:contentandtal:replacecannot be used on the same tag, since their functions conflict. -
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 ". 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 "$answer"."/>
<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:replaceortal:contentto erase a tag or its contents. If you set an attribute tonothing, the attribute is removed from the tag (or not inserted), unlike a blank string. Equivalent toNonein Python. - default
- A special value that doesn't change anything
when used in
tal:replace,tal:content, ortal: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 <,
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.
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 (
Noneif 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.
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)
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, andfootermacros
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
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 theviewtab shows for folderish objects. Folderish objects use both thebodymacro and thefolderlistingmacro. footer- This is where AT puts the byline.

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:
headerbodyfolderlistingfooter
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, andsearch. 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.
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.
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.
Customizing Labels
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, andfootermacros - How to create a custom view template that overrides one or more of the
header,body,folderlisting, andfootermacros - How to create a custom widget template that works in the Archetypes framework
- How to create a custom
bodytemplate 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:
headermacro- Title (or id if no title is present)
- Document actions (e.g. print, send to)
bodymacro- List of fields
- Field label (from the
labelmacro in the view template, if one exists) - Field value (from the widget template's
viewmacro)
- Field label (from the
- List of fields
folderlistingmacro- List of links to each sub-object
footermacro- 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.
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:
- Change the
portal_typeto lowercase. - Replace all spaces with underscores (
_). - Append
_viewto 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:definethe variablefieldas the current field; the widget templates depend on this variable being set. folderlisting- This is the folder listing display when viewing the
viewtab of a folderish object. This is not the same as thecontentsview. 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:
vieweditsearch
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
viewfor view templates, but is useful for, say, error checking.
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.
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.
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.
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
/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
/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
2.3. Style Sheets
Style Sheets
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.
2.3.2. CSS
Getting familiar with Plone's style sheets
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:
- modifying 'base_properties'
- overriding existing style by adding styling information to ploneCustom.css
- 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:
- log on to your Plone site as the 'admin' user or with your manager account
- add '/manage' to the URL to access the ZMI
- navigate to ZMI > portal_css
- click the Debug/development mode checkbox
- 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:
- enable development mode
- navigate to ZMI > portal_skins > plone_styles > base_properties
- click the Customize button
- modify individual properties using CSS style values
- 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:
- enable development mode
- navigate to ZMI > portal_skins > plone_styles > ploneCustom.css
- click the Customize button
- add CSS
- 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:
- enable development mode
- navigate to ZMI > portal_skins > custom
- check base_properties and/or ploneCustom.css (or anything else you've modified)
- click the "Delete" button
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.
Further styling with CSS
TODO
Example 2: 'New York Times' style
Anyone wish to make a contribution here?
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)
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.
2.4. Skin Layers
Skin layers
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>
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).
When 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).
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:
- 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]"/>
- 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>
- 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.
|
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) |
3. Components
The page furniture, viewlets, portlets, and their managers. How to make your own and where to find the bits you need.
3.1. Component Wiring and ZCML
About components and how they are wired together
Components 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.
- To identify the component (in the case of a viewlet this will usually be done with a "name" attribute).
- 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.
- To display the information the component's class has computed (this is usually done with a page template).
- 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.
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
- Anatomy of a Viewlet section in this reference manual
- http://plone.org/documentation/tutorial/customizing-main-template-viewlets
- http://plone.org/documentation/tutorial/customization-for-developers/viewlets/
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:
- The Anatomy of a Portlet section of this manual
- http://plone.org/documentation/how-to/override-the-portlets-in-plone-3.0/
- http://plone.org/documentation/tutorial/customization-for-developers/portlet-renderers/ (for a much more technical explanation)
- http://plone.org/documentation/how-to/adding-portlet-managers
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.
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.
3.4. Component Parts
Further information on some of the parts that go to make up components.
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.
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:
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 |
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"
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
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
-
/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
4. Configuration
How to write a configuration file and where to put it.
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.
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.

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.
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.
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
/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.





