The Definitive Guide to Plone
The Definitive Guide to Plone by Enfold Systems
The Definitive Guide to Plone
First Edition
16th May 2005
1
Table of Contents
1 Chapter 1
1.1 Introducing Plone
1.1.1 What Is a Content Management System?
1.1.2 Do You Want a Content Management System?
1.1.3 Introducing Plone’s Features
1.1.4 Getting Involved in Plone’s Development
2 Chapter 2
2.1 Installing Plone
2.1.1 Installing Plone on Windows
2.1.2 Installing Plone on Mac OS X, Unix, and Linux
2.1.3 Installing from Source
2.1.4 Configuring the Web Server
3 Chapter 3
3.1 Adding and Editing Content
3.1.1 Joining a Site
3.1.2 Enabling Cookies
3.1.3 Setting Up Your Folder and Preferences
3.1.4 Adding and Editing Documents
3.1.5 Adding and Editing Other Types of Content
3.1.6 Organizing Content
3.1.7 Discussing and Finding Content
3.1.8 Example: Creating the Plone Book Web Site
4 Chapter 4
4.1 Making Simple Customizations
4.1.1 Administering Sites
4.1.2 Using the ZMI
4.1.3 Customizing Plone’s Look and Feel
5 Chapter 5
5.1 Introducing Basic Plone Templating
5.1.1 Understanding the Underlying Templating Machinery
5.1.2 Using the Zope Page Templates System
5.1.3 Understanding the Page Template Basic Syntax
6 Chapter 6
6.1 Introducing Advanced Plone Templating and Scripting
2
6.1.1 Understanding Advanced Plone Templating
6.1.2 Scripting Plone with Python
6.1.3 Useful Tips
6.1.4 Using Forms
7 Chapter 7
7.1 Customizing the Look and Feel of Plone
7.1.1 Introducing Plone Skins
7.1.2 Customizing Skins
7.1.3 Making New Skins and Layers
7.1.4 Case Study: Examining the NASA Skin
8 Chapter 8
8.1 Managing Workflow
8.1.1 What Is Workflow?
8.1.2 Understanding Workflow in Plone
8.1.3 Adding and Editing Workflow
8.1.4 Common Tasks and Examples
9 Chapter 9
9.1 Setting Up Security and Users
9.1.1 Administering Users
9.1.2 User Registration Tools
9.1.3 Setting Permissions
9.1.4 Scripting Users
9.1.5 Integrating Plone with Other Services
10 Chapter 10
10.1 Integrating with Other Systems
10.1.1 Open-Source Licensing
10.1.2 Installing Plone Products
10.1.3 Using a Different Web Server
10.1.4 Integrating Plone with the File System
11 Chapter 11
11.1 Manipulating and Categorizing Content Types
11.1.1 An Overview of Content Types
11.1.2 Searching and Categorizing Content
12 Chapter 12
12.1 Writing a Product in Python
3
12.1.1 Writing a Custom Content Type
12.1.2 Writing a Custom Tool
12.1.3 Adding Some User Interface Elements
13 Chapter 13
13.1 Developing with Archetypes
13.1.1 Introducing Archetypes
13.1.2 Putting Together the Rest of the Content Type
13.1.3 Archetypes based Development
13.1.4 Setting Up a Content Type
13.1.5 Setting Up a Transformation on Windows
13.1.6 Testing the Content Type
13.1.7 Making a Content Type Persist in a Relational Database
14 Chapter 14
14.1 Administering and Scaling Plone
14.1.1 Administering a Plone Site
14.1.2 Improving Plone Performance
14.1.3 Using Zope Enterprise Objects
1 Chapter 1
1.1 Introducing Plone
A company without a Web site is unthinkable—and most companies and organizations have
more than one site. Whether it’s an external site for communicating with clients, an intranet
for employees to use, or a site for direct client communication and feedback, all Web sites have
a common problem—how to manage the content on them. This is a challenge that can often
cost organizations large amounts of time and effort. Producing a powerful yet flexible system
for these sites that meets ever-changing requirements while growing to meet your company’s
emerging needs isn’t easy.
No matter what the requirements for your Web site are or the amount of content or users,
Plone is a user-friendly, powerful solution that lets you easily add and edit any type of content
through the Web, produce navigation and searches for that content, and apply security and
workflow to that content.
Plone enables you to put together almost any Web site and easily update it. This lets you
build content-rich sites quickly so you can gain a competitive advantage. Finally, probably the
best things about this system are that it’s free and it’s open source. With its large and impres-
sive feature set, it’s comparable, if not better than, many closed-source content management
systems that cost hundreds of thousands of dollars.
Mike Sugarbaker says the following when reporting on the Open Source
Content Management Conference (OSCOM) in 2002 for the Mindjack site
(http://www.mindjack.com/events/oscom.html):
“I won’t do the complete rundown of all the ’competing’ open-source content management
frameworks. I’ll cut to the chase: The winner is Plone. This ’productized’ take on the six-year-
old web application framework Zope was the package with the most tools, the most profes-
sionalism, the most traction, and, above all, the most buzz.”
You can find the Plone Web site at http://www.plone.org, as shown in Figure 1-1. To try Plone
easily, a demonstration site is available at http://demo.plone.org. There you can quickly and
easily add and edit content through the Web. Specifically, you can add events, upload pictures,
add documents, and process them all through the framework that Plone provides.
4
5
Figure 1-1. The Plone Web site
1.1.1 What Is a Content Management System?
One simple definition for a Content Management System (CMS) is that it’s a system for man-
aging content. This is a rather unhelpful definition, so I’ll break it down into smaller parts for
a fuller explanation. I’ll start with a broad definition of content: Content is a unit of data with
some extra information attached to it. That piece of data could be a Web page, information
about an upcoming event, a Microsoft Word document, an image, a movie clip, or any piece of
data that has meaning to the organization deploying the system.
6
All these items are called content, and they all share similar attributes, such as the need to be
added or edited by certain users and be published in various ways. A system called workflow
controls these attributes. Workflow is logic defined by the organization’s business rules, and it
describes a system for managing the content.
Historically there has been a difference between document management systems and CMSs,
but mostly these two systems have converged. The essential difference is the items being
managed; it’s often considered that content is any unit of information, and a document refers to
something that’s created and edited by humans using software such as Microsoft Office. Take,
for example, a book: A book contains many units of data and may require management slightly
different from that required by content. However, in most cases, this is a small difference, and
products such as Plone are able to manage the small units of a larger piece of content and
reassemble them.
With the ubiquitousness of the Web, many CMSs are now classified as Web CMSs, either be-
cause they have a Web-based interface or because they focus on a Web-based delivery system
over the Internet or an intranet. Plone provides a Web management interface and Web-based
delivery system.
The following is one definition of a CMS (http://www.contentmanager.eu.com/history.htm):
A CMS is a tool that enables a variety of (centralized) technical and (decentralized)
nontechnical staff to create, edit, manage and finally publish a variety of content
(such as text, graphics, video, and so on) whilst being constrained by a centralized
set of rules, process, and workflows that ensure a coherent, validated Web site
appearance.
1.1.2 Do You Want a Content Management System?
Although not the only advantage of a CMS, the most obvious benefit of a CMS is coordinating
a Web site easily. Take a situation where one person, a Webmaster, coordinates a Web site,
either an intranet or an external site. Content comes from users in a variety of formats, and
the Webmaster turns these into usable Web pages by converting them to Hypertext Markup
Language (HTML). If a user has to change those pages, then the user sends the changes to the
Webmaster, who changes the pages, and so on.
This presents many problems for the organization, the biggest being that all content
is flowing through one person - an obvious bottleneck. That one person can do
only so much work, and if that person is sick or leaves the company, a great deal of
productivity is lost in finding a replacement. The publishing process can be quite
frustrating as e-mails fly between the Webmaster and the user trying to get content
published.
What’s needed is a system that does the following:
Separates the content of a page from the presentation: If the actual content is
separate from the presentation method, then the content author doesn’t need to
know any HTML or how the page is delivered. In fact, one piece of content could
7
have many different templates applied to it, including formats other than HTML,
such as Portable Document Format (PDF), or Scalable Vector Graphics (SVG). When
you want to change the look and feel of the site, you have to change only that one
template rather than all the content.
Allows certain users to add and edit content: If specified users can add and edit
content easily, then there’s no need to send content to the Webmaster or Web team.
Instead, the user who wants to create a page can do so and edit it as much as
necessary.
Applies rules to whom can publish what and when: Your business rules might
not want just anybody publishing content on your Web site; for example, people in
marketing would be able to publish to the press release part of the site and not to
the engineering section.
Can apply business rules to content: If a person from marketing creates a press
release, somebody in legal might need to review that document. In this case, the
document will be passed through a review process that ensures it won’t go live until
these reviews are done.
Can search and index information intelligently: Since the CMS can keep track
of structured information about the content (such as author’s name, publication
date, modification dates, categories, and so on), it can produce listings of content
by author, recent content, and so on. It can also provide searching capabilities that
are much smarter and more useful than just a simple textual search.
Although this example portrays paybacks that are more significant for large organizations,
organizations of all levels benefit from this approach. In fact, typically small organizations
that don’t employ a full-time Webmaster can be one of the key beneficiaries of such a system.
By installing a CMS, you can resolve all these issues and more.
The key factor of any CMS is that it provides a clear separation of the key elements in it:
security, workflow, templates, and so on. For example, the templates presenting an item are
separate from the content. This allows you to easily modify the presentation.
1.1.3 Introducing Plone’s Features
Plone is open source, licensed under the General Public License (GPL), which is a common
open-source license that allows anyone to use the source for free. For more information about
the GPL, go to the Free Software Foundation Web site at http://ww.gnu.org. You can examine
any aspect of Plone’s code and alter it to fit your application. There are no licensing fees to
pay, there’s no license that will expire, and all the code is visible. This open-source philosophy
means that Plone already has a large user base and legion of developers, usability experts,
translators, technical writers, and graphic designers who are able to work on Plone. By choos-
ing Plone, you’re not locked into one company; rather, nearly a dozen companies offer different
Plone services.
8
1.1.3.1 Packaging
Plone maintains easy installers for Windows, Linux, and Mac. Other third-party products and
add-ons also come with the installers. Maintaining quality releases of these products makes
installation and management easy. Also, each new release maintains migration paths and
updates so that your Plone site will keep working and stay up-to-date.
1.1.3.2 Internationalization
The whole Plone user interface is translated into more than 20 languages, including Korean,
Japanese, French, Spanish, and German. Inserting your own translation is easy (see Chapter
4).
1.1.3.3 Usability
Plone offers an excellent user experience that provides high levels of usability and accessibility.
This isn’t just a matter of presenting pretty HTML but instead goes to the core of Plone. Plone
provides an interface that’s compatible with the industry and government standard WAI-AAA
and U.S. Section 508. This allows sites built with Plone to be used by people with vision
disabilities. In addition, this provides the unexpected but related benefit that your page may
index better in search engines such as Google.
1.1.3.4 Skinnable
Plone separates the content from the actual templates used to present the content, often called
skins. The skins are written in the excellent HTML templating system, Zope Page Templates,
and a large amount of Cascading Style Sheets (CSS). With little knowledge of Plone, you can
apply multiple skins, achieve multiple looks, and totally customize your Web site’s appearance.
1.1.3.5 Registration and Personalization
Plone features a complete user registration system. Users register with a Plone site using their
own username, password, and any other information you might want to add about the user.
You can then personalize the whole user interface for that user. In addition, with add-ons, you
can use information you already have about users, coming from many places, such as relational
databases, Lightweight Directory Access Protocol (LDAP), Active Directory, and more. Chapter
8 covers how to register and configure users.
1.1.3.6 Workflow and Security
Workflow controls the logic of processing content through the site. You can configure this logic
through the Web using graphical tools. Site administrators can make sites as complex or as
simple as they’d like; for example, you can add notification tools such as sending e-mails or
instant messages to users. Chapter 8 covers workflow in great detail.
9
For every item of content in a Plone site, you can set up access control lists to decide who has
access to that item and how they’ll be able to interact with it. Will they be able to edit it, view
it, or comment on it? All this is configurable through the Web (see Chapter 9).
1.1.3.7 Extensible
Since Plone is open source, it can be easily altered. You can change and configure almost any
aspect of Plone to suit your needs. Countless packages and tools for Plone provide a wide ar-
ray of options for smaller sites and for large-scale enterprises. Repositories of free add-ons for
Plone are available at http://www.plone.org. With development tools such as Archetypes (cov-
ered in Chapter 13), you can generate and alter Plone code easily through the Web or using
Unified Modeling Language (UML) tools. Chapter 10 covers integration of Plone with enter-
prise solutions such as LDAP Apache, Microsoft Internet Information Services (IIS), Macrome-
,
dia Dreamweaver, and so on.
1.1.3.8 Content Customization
Users of a Plone site can add all manner of content, but the data added isn’t limited or con-
strained. Plone developers can create their own content types so that almost any type of
content can be managed; the only limit is your own imagination. In Chapters 11 and 12,
I’ll discuss how to customize the content types. Chapter 13 will introduce Archetypes, which
is a very powerful system for generating content types that don’t require programming; for
instance, you can generate new types of content from UML tools.
1.1.3.9 Documentation
The Plone project maintains documentation, including this book, which is published under the
Creative Commons license. The best starting place for the community documentation is at
http://www.plone.org/documentation.
1.1.3.10 Community
One of the best things about Plone is the community of developers and companies that sup-
ports and develops Plone. With more than 60 developers involved to some degree in the project
around the world, it’s almost always possible to find a Plone developer online who is willing
and able to help you. Alan Runyan, Alexander Limi, and Vidar Andersen started Plone; how-
ever, it quickly grew into a thriving open-source project as more developers became involved.
The contributions from these developers form the Plone product that’s now available.
1.1.3.10.1 Example Plone Sites
Many Plone sites exist; some are obvious because of their looks, and some aren’t. The following
is just a small sample of the more diverse sites:
10
Plone (*http://www.plone.org*) Plone Demo Site (*http://demo.plone.org*) Zope.org
(*http://www.zope.org*) Liquidnet (*http://www.liquidnet.com*) Design Science Toys
(*http://www.dstoys.com*) Give Kids the World (*http://www.gktw.org*) Propane
(*http://www.usepropane.com*) Maestro Headquarters (*http://mars.telascience.org*)
More Plone sites are available at http://www.plone.org/about/sites, including sites that provide
a quite different user interface. Without knowing about the development of these sites, it
would in fact be hard to tell that these sites use Plone.
1.1.4 Getting Involved in Plone’s Development
Although Plone has an impressive list of features, its list of ’wants’ even more impressive. For
this reason, the project is always on the lookout for new people willing to contribute time for
the project.
Fortunately, because Plone is focused on the end user, there’s a need for a very broad spectrum
of disciplines. Volunteers in a range of areas, rather than just coders or Web developers,
are welcomed. Plone needs user interface developers, usability experts, graphic designers,
translators, writers, and testers. You can find the current development status on the Plone Web
site at http://plone.org/development, and the best way to get involved is to join the mailing
lists or join the developers on an Internet Relay Chat (IRC) channel.
Plone is built on top of Zope and the Content Management Framework (CMF). To understand
Plone, you have to understand Zope and the CMF as the underlying architecture. For this
reason, I’ll explain these two items and how they integrate with Plone in this section.
Zope is a powerful and flexible open-source Web application server developed
by Zope Corporation (http://www.zope.org). Originally, Zope was developed as
a stand-alone CMS, but over time it didn’t satisfy the needs of its users. Then
Zope Corporation developed the CMF an open-source project. The CMF provides
developers with the tools necessary to create complex CMSs; it enables workflow,
provides site skinning, and offers other functions.
The CMF is a framework for a system; in other words, it provides the tools for developers to
build a product, rather than just providing an out-of-the-box system that users can use imme-
diately. Plone takes this and many other features and improves upon them to provide the user
with a high-quality product. Plone is a layer on top of the CMF, which is an application running
on top of Zope. Understanding the CMF is key to understanding Plone. Most administration
functions require the use of Zope’s administration interface, and developing Plone requires an
understanding of Zope and its objects.
This book doesn’t go into depth about Zope; rather, it gives you enough information
to complete tasks in Plone. Just reading this book will give you enough information
to customize and modify almost anything you want in Plone. For more information on
Zope, I recommend The Zope Book. Originally published by New Riders, it has since
been placed online and is updated by community members. It’s available free online at
http://www.zope.org/Documentation/Books/ZopeBook/2_6Edition.
Both Zope and the CMF are key technologies that Plone needs; without them, Plone wouldn’t
exist. The Plone team owes a great deal of thanks to everyone at Zope Corporation for having
11
the vision to create and then offer both Zope and the CMF as open source. The list of people
I’d like to thank there and in the CMF communities is long. Thank you, everyone involved.
Zope is written in Python, a powerful object-oriented, open-source programming language
comparable to Perl or Tcl. Knowledge of Python isn’t required to use Plone or even to do some
basic administration; however, customizing products and scripting Plone does require some
Python.
Tommy Burnette, a senior technical director at Industrial Light Magic, says this about Python
(http://www.python.org/Quotes.html):
Python plays a key role in our production pipeline. Without it a project the size of
Star Wars: Episode II
If you plan to do anything sophisticated with Plone, take a day or two to learn the basics of
Python. Not only will this allow you to customize Plone substantially, but it’ll also familiarize
you with objects and how they interact in the Plone environment. Teaching you Python is
outside the scope of this book; instead, I assume you have a basic knowledge of Python. That
fundamental knowledge of Python will be enough to get you through this book and allow you
to customize the Plone installation easily.
Fortunately, Python is an easy programming language to learn; on average, it takes an experi-
enced programmer a day to become productive in it. New programmers take a little longer. If
you’re installing Plone using the Windows or Mac installers, then the correct version of Python
will be included. To download Python as separate product, for almost any operating system,
go to http://www.python.org.
The best way to master Python is to try it from the command Python interpreter. If you have a
Windows installation of Plone, there’s a link for the Pythonwin, a Python Integrated Develop-
ment Environment (IDE) already in the Start menu; go to Start - Programs - Plone - Pythonwin
(see Figure 1-2).
12
Figure 1-2. The Python prompt on Windows
On Linux and Mac OS X, usually typing python will start the Python interpreter:
$ python
Pyython 2.3.2 (#1, Oct 6 2003, 10:07:16)
[GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
Since Python is an interpreted language, instead of the whole Python script being compiled
and run, you can just send lines of code to the interpreter as you write them. This makes the
interpreter an amazingly useful place for testing and debugging code. In the interpreter, each
line waiting for input is prefixed with >>>.
For example, the simplest ’Hello, World” program is as follows:
>>> print "Hello, world!"
Hello, world!
>>>
To exit the interpreter, press Ctrl+D (press the D key while holding down Ctrl) on Linux
or press Ctrl+Z on Windows. (You’ll also use this later for more advanced Zope and Plone
interaction.) You can execute normal Python scripts by passing them to the interpreter; for
example, given the following script called hello.py:
13
print "Hello, world!"
you can run this using the following command:
$ python hello.py
Hello, world!
The Python Web site at http://www.python.org has excellent documentation, especially the
tutorial. Also, the following books provide a good overview of Python:
• *Dive Into Python* (Apress,2004): Based on Mark Pilgrim’s popular Web-
based tutorial, this books treats readers to a fast-paced introduction to the
Python language. This is a great book geared toward experienced program-
mers.
• *Learning Python*, Second Edition (O’Reilly, 2003): This book covers ver-
sion 2.3 of Python and provides a good overview of Python and all the new
features. This is good for relatively new programmers.
• *Practical Python* (Apress, August 2002): This highly practical introduc-
tion to Python offers insight into the language’s array of features. The reader
can immediately put this knowledge into practice, following along with the
creation of ten interesting projects, including a Web-based bulletin board and
a Graphical User Interface–based file-sharing application.
• *Python Essential Reference*, Second Edition* *(Sams, 2001): A refer-
ence book that provides a great overview of all the key libraries and functions.
This is an excellent book for experienced programmers.
This book uses the following conventions:
• Italics: New terms are italicized. (Appendix C contains an extensive glossary
that defines all acronyms.) Also, links that appear in the user interface are
italicized.
• Bold: If there are instructions within the text that include something you
should type on your keyboard, these words are in bold.
• Code font: A monospaced font indicates filenames, folder paths, code, vari-
ables, and Uniform Resource Locators (URLs).
This book contains lots of screen shots of Zope, Python, and Plone. Since Plone is a rapidly
developing product, the screen shots might vary slightly from the version of software you’re
using; these changes should be minor and shouldn’t affect your understanding of the system.
For this book, the following versions of software are used; although this book is specifically
written with these versions in mind, all the software should work on these and later versions
for some time to come.
Plone 2.0 was the most recently released version of Plone at the time of writing. This is the
second major release of the software, and it provides many new features over 1.0, including
group user management, a new interface, and an improved Zope distribution. It’s strongly
14
suggested you start any new projects in 2.0 or later, rather than using the earlier versions of
Plone.
Version 2.0 of Plone has the following dependencies: Zope 2.7, CMF 1.4.2, and Python 2.3.3.
All of the code examples in this book have been specifically designed not to be dependent upon
these versions or a particular operating system. However, there may be situations where this
isn’t the case; I apologize for any inconvenience.
Originally, a group of Plone users keen to produce quality documentation came up with the
idea of this book. We released the first version of that book on the Plone Web site as an open-
source documentation project. All the content added to the Plone Web site was under the open
publication license.
Growing interest in Plone made a commercial book more feasible, and in the summer of
2003, Apress and I started this book. I used some of the material from the old book with
the original owners’ permissions. With the change to Plone 2, I added a large amount of
new material. This book is now published under the Creative Commons license, which al-
lows for the reuse of this work as long as the original author is attributed. However, you may
not use this work for commercial purposes. For more information, see the license online at
http://creativecommons.org/licenses/by-nc-sa/1.0/.
2 Chapter 2
2.1 Installing Plone
This chapter explains how to install Plone on a variety of platforms and set the basic configu-
ration options for Plone. If you want to try Plone really quickly, then your best bet is to go to
the live demonstration site at http://demo.plone.org; you can try adding and editing content
immediately without installing anything.
Unlike the other chapters, reading this chapter from one end to the other may not make the
most sense. Instead, I’ve broken this chapter down by operating system, so you can read only
the sections you need to in order to install Plone. Plone will install on any of the platforms
that Zope supports: Windows, Mac OS X, Linux, most Unix platforms, and Solaris.
For a Plone server, a high-performance computer will obviously make Plone perform better.
Plone is a complicated system that requires processing power and memory. In general, it’s
recommended you don’t go into production with a machine slower than 2GHz with less than
1GB of Random Access Memory (RAM) if you’re serving a large Web site. It works fine with
setups as low as 500MHz and 64MB of memory for more modest sites, however. For advanced
information about the performance, caching, and acceleration of Plone, see Chapter 14. For
a base installation of Plone, you’ll need about 50MB of hard drive space. If you already have
installations of Zope or Python, then this will be a great deal less; you’ll need about 2MB. You
must also account for the Plone object database, which can grow to almost any size depending
upon the amount of data you store.
To use Plone, you need a Web browser that can access the server. If users want to log into
your site, then they must have cookies enabled. JavaScript isn’t required but will provide a
richer user experience. Because of the large amount of Cascading Style Sheets (CSS) in Plone,
modern browsers will see the correct Plone interface in a richer, more attractive way; however,
it should be quite functional in any reasonable browser.
I recommend any of the following browsers:
• Microsoft Internet Explorer 5.5 and later
• Netscape 7.0 and later
• Mozilla 1.0 and later
• Opera 7.0 and later
• Konqueror 3.0 and later
• Safari 1.0 and later
15
16
Plone also is fully functional in the following browsers but may look different from the original
Plone:
• Netscape 4.*x*
• Microsoft Internet Explorer 5.0
• Microsoft Internet Explorer 4.0
• Konqueror 2.*x*
• Lynx (text-based)
• w3m (text-based)
• AWeb
• Links (text-based, with optional graphics)
• Any browser that handles a basic set of Hypertext Markup Language (HTML)
and form input cookies, including most mobile/Personal Digital Assistant
(PDA) browsers
2.1.1 Installing Plone on Windows
By far the easiest way to install Plone is to use the Plone Windows installer, which automates
the installation of Plone on Windows. The installation includes extra packages and options,
a Hypertext Transfer Protocol (HTTP)–loaded database, the setup of services, and Python for
Windows packages. You can download this installer from http://www.plone.org/download.
2.1.1.1 Using the Installer
The installer has been tested on Windows 9*x*, ME, NT 3.51+, 2000, and XP but it should
,
also work on other Windows versions. It’s recommended you have administrator access on the
computer you want to install on since the installer will try to set up as a service and install
settings into the Windows Registry. If you already have Zope or Python installed, you may
want to install the source separately to save hard drive space.
Before installing Plone, you should make note of any current Web servers you have running.
For example, later versions of Windows automatically install and start Microsoft Internet Infor-
mation Services (IIS), which listens to port 80. The installer starts Plone on ports 80 and 8080.
To test if something is using port 80 already, the easiest way to test is by opening a browser
to http://127.0.0.1/ and seeing if it finds a page. You can either disable that Web server or
change the ports for Plone; see ’Configuring the Web Server” later in this chapter. If you want
to run Plone behind IIS or run both Plone and IIS on the same server at the same time, then
see Chapter 14 for more information. At the moment, however, it’s easiest to just disable that
Web server.
Once you’ve downloaded the installer, double-click the installer to begin (see Figure 2-1).
17
Figure 2-1. The start of the Plone installer
The installer goes through the usual steps for installing software; click Next to
continue the setup or Cancel to exit. The Plone installer lets you choose a location
to install the software; the default is c:Program FilesPlone 2 (see Figure 2-2).
Figure 2-2. Selecting a directory
When you get to the password screen, as shown in Figure 2-3, you must enter a username and
a password. This will create a user for you, and this will make the Plone site in that user’s
name. Often people create one user called admin or similar for this role. You’ll need this
18
username and password later, so remember it; however, if you do lose this password, you can
enter a new one later.
Figure 2-3. Entering a username and password
The installation takes about five minutes, depending upon the speed of your computer. The
installation performs a few tasks at the end of the installation, such as compiling all the Python
files and setting up the database. When the installation has finished, a message displays to let
you know that it’s done (see Figure 2-4).
Figure 2-4. Final setup screen
19
To start Plone, access the Plone controller by going to Start - Programs TRA Plone TRA Plone.
The controller is an application that provides a nice user interface for starting and stopping
Plone. It begins with the Status page, which lets you easily start or stop your Plone installation
(see Figure 2-5).
Figure 2-5. Plone isn’t running.
As shown in Figure 2-5, the screen displays the status of your Plone installation. Plone doesn’t
start automatically; you’ll have to click Start to start Plone. Once you’ve clicked this, you may
have to wait a minute as it completes the startup process (see Figure 2-6).
20
Figure 2-6. Plone is now running.
When Plone has started, you can access the Plone site by clicking the View Plone button. This
starts a browser and accesses the Plone site; you should then see the Plone welcome page.
Note that the address in the browser is http://localhost/; this is the address to access your
Plone site. Clicking the Zope Management Interface button starts a browser and accesses the
management interface; the address in the browser for this is http://localhost::8080/manage,
which gives you access to the underlying application server. When you click the Manage button
and access Plone, it’ll ask you for your username and password. This is the username and
password you added in the installer.
The controller will know whether you’ve installed Plone as a service or not as a service. If
Plone has been installed as a Windows service, then you can stop and start Plone using the
standard service management screens and commands. If it hasn’t been installed as Windows
service, you’ll see a little icon appear in the task bar. At this point if you want to edit content,
move on to Chapter 3.
2.1.1.2 Configuring the Server on Windows
The configuration for Plone is contained in a text file that you can edit to configure your Plone
instance. You can change the ports Plone listens to, the log files used, and a whole host of
options. On Windows some of the key features are available through the controller and the
Graphical User Interface (GUI). If you want to alter some of the other configuration options,
refer to Appendix A for a full list of configuration options. To access the controller, select Start
- Programs - Plone - Plone; this will start the controller.
As discussed earlier, the first page you’ll see is the Status page, which allows you to stop or
start Plone. On the left of the controller are a few other screens that I’ll now discuss.
2.1.1.2.1 Changing the Ports
The Ports choice, as shown in Figure 2-7, allows you to specify the ports that Plone listens to for
incoming connections such as HTTP File Transfer Protocol (FTP), and Web-based Distributed
,
Authoring and Versioning (WebDAV).
21
Figure 2-7. The Ports page displays the ports on which Plone is running
As mentioned when installing Plone, you’ll want to ensure that no other server is listening to
the same port as Plone servers such as II:S, Apache, and Personal Web Server (PWS) could
be listening to port 80. At the time of writing, only the Plone HTTP and Zope Management
HTTP ports are enabled; to enable them, you have to go into a text file to configure them. The
following are the four fields on the Ports page:
Plone HTTP: This field specifies the port to access Plone for the user. The default
is port 80, the standard default for a Web server. Although this port isn’t required,
without it you won’t be able to access Plone with a Web browser. If this port is
enabled and Plone is running, the View Plone button is enabled on the Status page.
Zope Management HTTP: This field specifies the port to access Plone as the man-
ager. The default is port 8080. This port gives you access to Zope Management
Interface (ZMI) for the root of Zope. You can still get to this through the HTTP
port; however, it’s easier and more convenient to have a separate port. If this port
is enabled and Plone is running, the Manage Plone button is enabled on the Status
page.
FTP Access: This field specifies the port to access Plone via FTP The default is
.
blank, meaning that this isn’t enabled; if you want to enable this, the usual port is
21. You can use FTP to transfer large files to and from Plone.
WebDAV Source: This field specifies the port to access Plone via WebDAV The .
default is blank, meaning that this isn’t enabled; if you want to enable this, the
usual port is 8081. (WebDAV is a protocol for remotely authoring content in Plone.
With WebDAV you’re able to perform tasks such as mapping your Plone server to a
,
Windows drive letter.)
22
2.1.1.2.2 Using the Emergency User Page
Chapter 9 covers the Emergency User page, but, put briefly, it allows you to get emergency
access to your system should you forget your username or password.
2.1.1.2.3 Starting Plone in Debug Mode
Up to this point, you’ve started and stopped Plone in production mode. This is the fastest way
to run Plone and is recommended. For developing add-ons in Plone or debugging problems,
you’ll need to start Plone in debug mode. This mode is the recommended way of running Plone
when you’re developing products and skins, as you’ll do in later chapters. This method isn’t
the default because you’ll note that Plone is about ten times slower than normal.
To start Plone in debug mode, select Start - Programs - Plone - Plone (Debug Mode), and
a command prompt will appear; all the log information will be printed to this window (see
Figure 2-8).
Figure 2-8. Running Plone from the command line
To test that Plone is running, start a browser and go to http://localhost/; if Plone is installed
successfully, you’ll see the Plone welcome screen.
2.1.2 Installing Plone on Mac OS X, Unix, and Linux
The installations for Mac OS X, Unix, and Linux are different, but the configurations are similar.
Specific packages exist for different operating systems, including Mac OS X, Debian, Gentoo,
FreeBSD, OpenBSD, and RPM Package Managers (RPMs) for Red Hat, SuSE, and Mandrake.
In the following sections, I’ll cover some of the more popular: Mac OS X, Red Hat, and Debian.
For information about your specific operating system, consult the installation instructions for
the specific installation system.
23
2.1.2.1 Installing on Mac OS X
The installer automates the installation of Plone on Mac OS X and has been tested on ver-
sion 10.2.3 and later. You’ll need administrator access on the computer on which you want
to install. You can download this installer from http://ww.plone.org/download. Once you’ve
downloaded the installer, double-click the installer to decompress the archive, and double-click
the resulting installer package to begin the install. You should see the screen shown in Figure
2-9.
Figure 2-9. Authorizing the installation using your Mac OS X password
Enter your Mac OS X account password to authorize the installation; your account must have
administrator privileges to do this. If your account doesn’t have administrator privileges, log
out and log back in as someone who does and then relaunch the installer. You may want to
move the installer package to /Users/Shared before you log out so you can access it from the
other account. Once the installation is authorized, you’ll see the screen shown in Figure 2-10.
24
Figure 2-10. Welcome to the installer.
The installer goes through the usual steps for installing software. Click the Continue and Go
Back buttons at the bottom as necessary; most of the steps are self-explanatory. However,
when presented with the choice of volumes to install Plone on, you must choose the partition
on which Mac OS X is installed (see Figure 2-11).
::image:: img/3294f0211.png
Figure 2-11. Choosing the boot volume
The installation takes about five minutes, depending upon the speed of your computer. When
the installation has finished, Plone isn’t started by default. The ReadMe.rtf file in /Applica-
tions/Plone contains a lot of useful information about running and managing your Plone in-
stallation, including how to start Plone. For example, running the following command will
start Plone:
sudo /Library/StartupItems/Plone/Plone start
To test whether Plone has worked, use a browser to go to http://localhost:9090/; you should
see the Plone welcome page. Also in that ReadMe file you’ll find the username and password
that Plone has set up for you to access the server.
25
2.1.2.2 Installing Using an RPM
RPMs are available for the Red Hat, Mandrake, and SuSE distributions. You can download
the latest packages from http://www.plone.org/download. The RPM requires that Python 2.3 is
installed. To find out which version of Python you have, run the following command in a shell:
$ python -V
Python 2.3.2
In this case, Python 2.3.2 is installed; if you don’t have this, RPMs for Python are available
from the Python Web site at http://www.python.org. After downloading the files, install us-
ing the standard rpm command; fortunately, the Plone installation prints some really useful
information. For example:
[root@lappi i386]# rpm -ivh Plone2-2.0.0rh-2.i386.rpm
Preparing... ###########################################
[100%]
Making group plone (not altered if already exists).
Making user plone.
~ 1:Plone2 ###########################################
[100%]
Creating initial ’main’ instance...
Instance created. Listening on 127.0.0.1:8080, initial user: ’plone’
with password: ’plone’.
Setup of initial database in ’main’ instance...
/usr/lib/plone2/lib/python/AccessControl/Owned.py:79:
DeprecationWarning: Owned.getOwner(1) is deprecated; please use
getOwnerTuple() instead.
~ DeprecationWarning)
Created initial database content.
look at /etc/plone2/main/zope.conf.
Run then "/etc/rc.d/init.d/plone2 start" to start Plone2.
you may create new Plone instances with mkploneinstance.
As shown in the previous output, to start Plone, run the following:
/etc/rc.d/init.d/plone2 start
To test that Plone is working, use a browser to go to http://localhost:8080/; you should see
the Plone welcome page. The username plone* and the password *plone have been created for
you*.*
2.1.2.3 Installing on Debian Linux
Plone is a standard package in Debian and moves through the standard release process, so
you’ll want to either get the stable or get the unstable version of Plone, depending upon how
your Debian installation is configured. To install Plone, simple use Debian’s apt system to get
the package. This is an example installation:
26
agmweb:/home/andy# apt-get install plone
Reading Package Lists... Done
Building Dependency Tree... Done
The following extra packages will be installed:
zope zope-cmf zope-cmfcalendar zope-cmfcore zope-cmfdefault
zope-cmfplone zope-cmftopic zope-cmfworkflow
zope-formulator zopectl
Suggested packages:
zope-cmfwiki python-unit zope-devguide zope-book
Recommended packages:
zope-cmfforum zope-localizer
The following NEW packages will be installed:
plone zope zope-cmf zope-cmfcalendar zope-cmfcore zope-cmfdefault
zope-cmfplone zope-cmftopic zope-cmfworkflow
zope-formulator zopectl
0 upgraded, 11 newly installed, 0 to remove and 49 not upgraded.
Need to get 4743kB of archives.
After unpacking 24.9MB of additional disk space will be used.
Do you want to continue? [Y/n]
Press Y to continue and install all the required packages. To start and stop Zope, an install
script has been created in the init.d directory, called zope. To start Plone, run the following:
/etc/init.d/zope start
The Debian installer starts Zope on the nonstandard port 9673. Since the Debian installer
is quite unusual, it’s recommended that you read the documentation for the packages at
/usr/share/doc/zope and /usr/share/doc/zope-cmfplone.
2.1.3 Installing from Source
As an alternative to using an installer or package, you can install from the source tarball. If
you’re familiar with installing from source, it’s actually rather simple but does require famil-
iarity with basic tools such as tar. The following sections demonstrate how to install it on
Linux.
This installation assumes you’re familiar with basic operations such as ’untarring” files and
moving files. This requires a working Zope installation.
NOTE To install Zope, see the Zope installation instructions in the
doc/INSTALL.txt file of your Zope download. For more information, see
http://zope.org/Documentation/Books/ZopeBook/2_6Edition/InstallingZope.stx).
Follow these steps to install Plone:
1. Download Plone 2 from http://www.plone.org/download, and select the
tarball file.
27
2. Unzip the archive using the following: tar xzf CMFPlone2.0.tar.gz
3. You’ll find that a directory has been created called CMFPlone-xxx, where
xxx is the version (for example, CMFPlone-2.0).
4. Move the contents of that directory into your Zope installation’s Product
directory. For example, if the Zope Products directory is in /var/zope,
then do this: mv CMFPlone2.0/ /var/zope/Products*
After completing this installation, restart Zope. Once Zope has restarted, access Zope by open-
ing a browser and going to http://localhost:8080/manage. You’ll need a username and pass-
word for this (for example, the username and password you gave during the Zope installation).
In the ZMI, there’s a drop-down list of products you can add located in the top-right corner.
Ensure that Plone Site is one of the options. If so, your installation is complete (see Figure
2-12).
Figure 2-12. Plone Site in the drop-down list
2.1.3.1 Installing from CVS
Concurrent Versioning System (CVS) access is recommended only for experi-
enced users and developers. You can find current CVS access information at
http://ww.plone.org/development/cvs. The current CVS checkout command is as follows:
cvs -d:pserver:anonymous@cvs.sf.net:/cvsroot/plone login
cvs -d:pserver:anonymous@cvs.sf.net:/cvsroot/plone co CMFPlone
Plone 2 has a whole host of other dependencies (such as DCWorkflow, Formulator, Group User
Folder, and so on) that aren’t in Plone CVS, meaning users will have to go and find all these
dependencies. When you start Plone, it’ll print any errors regarding packages not found. For
example:
28
2003-11-21T12:23:11 ERROR(200) Plone Dependency
CMFActionIcons not found. Please download it from http://cvs.zope.org/Products/
2.1.3.2 Adding a Plone Site
Once you’ve installed Plone from source, you need to create an instance of Plone. To do
this, you’ll need to log into the ZMI and add a Plone site. You can access this by going
to the Uniform Resource Locator (URL) for the management interface, which is normally
http://localhost:8080/manage (this port will change depending upon your installation). You’ll
need a manager’s username and password for access to the ZMI; this password is created
during the Zope installation.
NOTE If you’ve forgotten the password for Plone that was created in the installa-
tion, don’t panic. You can create a new one; see Chapter 9 for more information.
You add all objects via the drop-down list in the top-right corner, as shown in Figure 2-12.
Scroll down the list until you find Plone Site, and click Add.
After selecting the option Plone Site, a form will display that prompts for some more informa-
tion (see Figure 2-13):
• Id: This is the unique ID of the Plone site (for example, enter Plone
or Site).
• Title: This is the title of the Plone site (for example, enter My Por-
tal).
• Membership source: For the moment, leave this as the default op-
tion, Create a New User Folder in the Portal. This allows you to have
user authentication somewhere other than the portal (see Chapter 9
for more information).
• Description: This is a description of the portal that members will
see in e-mails, (for example, enter A site all about the exciting
new widget technology). Don’t worry too much about this; you
can always change this later in the portal’s properties.
29
Figure 2-13. Adding a Plone site
After clicking Add Plone Site, a Plone site will be created. This may take a minute or two on
slower machines because a great deal of processing occurs. The screen will then redirect you
to the Plone welcome page.
2.1.4 Configuring the Web Server
Once Plone has been installed, you may want to configure the Plone site so that it runs on a
different port, has FTP capabilities, logs to a different file, and so on. This section covers these
basic setup issues. Note that you aren’t configuring the Plone sites themselves; you’re altering
the configuration of the underlying Web server.
NOTE If you’ve installed on Windows using the Windows installer, then most of this
configuration is provided through a nice user interface program; see ’Configuring
the Server on Windows” earlier in this chapter.
NOTE If you’ve installed using Mac OS X or the Windows installer, then you’ll find
one extra file (plone.conf), which contains port definitions used in the main Zope
configuration file.
30
Zope 2.7 creates a configuration file inside each instance installed. All the configuration for the
server is located in that one file. A full list of the configuration options is available in Appendix
A. To find the configuration file, look for a file called zope.conf located in the etc folder of your
Plone installation. Some installers (Windows and Mac OS X, for example) create a second
configuration file called plone.conf that contains Plone-specific configuration options. If your
installation contains a plone.conf file, then use that configuration file to make changes; they’ll
be included in the main configuration file.
The configuration file is extremely verbose and contains a great deal of useful comments and
examples. If you’re familiar with Unix configuration files such as Apache, then you’ll find the
Zope configuration file familiar. To alter Zope configuration, open the configuration in a text
editor, and change the lines as needed; after altering the configuration, you’ll need to restart
Zope.
It’s possible to run Plone 2.0 with a version of Zope prior to 2.7; however, Zope 2.7 offers
increased stability and new features, including easier configuration. If you’re using a version
of Zope prior to 2.7, you’ll need to read the documentation on how to change the configuration.
2.1.4.1 Adjusting the Ports
To change a port, add the address lines for that port. For example, to run Plone on port 80
instead of the default, change the following bold line in zope.conf:
<http-server>
# valid keys are "address" and "force-connection-close"
address 8080
# force-connection-close on
</http-server>
to the following:
<http-server>
# valid keys are "address" and "force-connection-close"
address 80
# force-connection-close on
</http-server>
If you used the Windows or Mac OS X installer, then you’ll find these port definitions in
plone.conf. These values are then imported into the main configuration file. So, on a Mac
to change the port, you’ll edit plone.conf from this:
## PLONE_WEBSERVER_PORT
## --------------------
## This is the port you will access your Plone site from. Set this to a port
## number above 1024 not used for any other server on your computer.
%define PLONE_WEBSERVER_PORT 8080
to the following:
%define PLONE_WEBSERVER_PORT 80
31
2.1.4.2 Using the Debug Mode
By default in Zope 2.7 debug mode is enabled. Note that Plone runs significantly slower in
debug mode, approximately 10-20 times slower. To turn this off, add the following line to the
configuration file:
debug-mode off
To make the out-of-the-box experience more impressive for Windows users (debug mode slows
Plone down on Windows even more than on Linux), it ships with debug mode off already.
If you have a Plone site running and want to know if debug mode is running, go to por-
tal_migration in the ZMI and look at the variables listed there; this will tell you if debug mode
is enabled.
2.1.4.3 Using Logs
By default there are two logs in Plone: an access log that you can produce site statistics from
and an event log that contains debug information about Plone products. The event log is the
place to find errors and messages in Plone. The default configuration looks like the following:
<eventlog>
level all
<logfile>
path $INSTANCE/log/event.log
level INFO
</logfile>
</eventlog>
<logger access>
level WARN
<logfile>
path $INSTANCE/log/Z2.log
format %(message)s
</logfile>
</logger>
This is where you can change the path to the file by defining a new file. The values that are
logged are based upon a level sent with error messages; more serious messages are sent with
higher levels. By default, only information and the previous message are sent to the log, but
that value could be one of the following: CRITICAL, ERROR, WARN, INFO, DEBUG, and ALL. If
you wanted to log only errors, then you’d change level INFO to level ERROR.
3 Chapter 3
3.1 Adding and Editing Content
Adding and editing content is a simplification of the sheer power that Plone has available to
leverage. Creating content-rich and feature-rich Web pages with Plone is an absolute breeze.
If you have Plone installed locally, then this chapter shows you how Plone works straight out
of the box. However, if you don’t have Plone installed, then don’t worry; you can try Plone
online by going to http://demo.plone.org.
Before you can alter or edit a Plone site, you need to log into a Plone site. If you installed
Plone, you should have the username and password that came with the installation. This user
has the role of an administrator user, which allows you to log in and alter any content. Most
users of a Plone site will join the site and log in through the login process described in the
’Joining a Site’ section. Users can, of course, view a Plone site without logging in, but they
won’t be able to add or edit content.
In this chapter, I’ll go through the steps a user takes to create content on a Plone site. First, I’ll
cover how to join the site and log in. Once you’ve accomplished this, I’ll cover how to create
and then edit a document. Finally, I’ll show how you can search and publish this content. In
short, this chapter covers how to use Plone.
3.1.1 Joining a Site
When you join a Plone site, you create an account on the server. That account gives you the
right as a member to add content such as images, documents, and so forth. To join a site, click
the join link in the top-right corner of the Web site (see Figure 3-1).
Figure 3-1. Clicking the join link in the top-right corner of the page
This will take you to a registration form that you’ll have to complete (see Figure 3-2). Because
this is the first Plone form you’ve encountered, take note of the following:
• Some fields are required; a little red box next to the text indicates
the required fields.
32
33
• For most fields, some grayed-out help text beneath the field name
indicates what you should enter.
Figure 3-2. The registration page
NOTE Because many of the Plone pages are quite large, the figures in this book
have been cropped to show only the key parts (in this case, the form) and not the
Plone logo or the footer. These elements are still there, but they’re superfluous.
34
To complete the form, complete the fields that are reasonably obvious. The values of the fields
are as follows:
Full Name: Enter your full name. This field is optional.
User Name: Enter the username you want to use. Most people choose an alphanu-
meric value without spaces, such as bob or jane97. This username will be used
throughout the Web site to refer to you. This field is required.
E-mail: A valid e-mail address is required. This will allow the site administrator
to contact you and to send a password to you. You can change this e-mail address
later by editing your member preferences. This field is required.
Password and Confirm Password: This is the password you want to use; it must
be more than four characters and can contain letters, numbers, and the underscore
(_) character. Passwords are case sensitive (in other words, SomePassword isn’t the
same as somepassword). These fields are required.
Send a mail with the password: Check this box if you’d like your password sent
to the e-mail address you provided. This field is optional.
Once you’ve completed this form, click Register to submit your information. If you’ve made
any errors on this form, then you’ll see a message at the top and the key fields that have an
error highlighted. In Figure 3-3 I entered a password but didn’t enter a value for the Confirm
Password field. Again, this is the standard way that Plone forms will show errors to you.
35
Figure 3-3. Errors on a form
If you’ve completed the form correctly, then you’ll be given the option of logging in immedi-
ately. Click the Log In button to log in. You’ll see the page shown in Figure 3-4.
36
Figure 3-4. After registering
If you already have a username and password or are returning to a site you’ve previously
joined, then you can enter your name and password in the boxes in the left column of the site
and click the Log In button.
3.1.2 Enabling Cookies
To log into a Plone site, you must have cookies enabled. If you access a Plone site and try to log
in with cookies disabled, you’ll get a friendly message telling you that cookies must be enabled
with a link to more information. To enable cookies, perform the following steps, depending on
your browser.
Internet Explorer 6.x 1. Select Tools > Internet Options.
2. Click the Privacy tab at the top of the screen.
3. Move the slider to Medium, and click OK.
Internet Explorer 5.x 1. Select Tools > Internet Options.
2. Click the Security tab at the top of the screen.
37
3. Click Custom Level, and scroll down to the Cookies section.
4. Set Allow Per-Session Cookies to Enable, and click OK.
Internet Explorer 4.x 1. Select View > Internet Options.
2. Click the Security tab at the top of the screen.
3. Click Custom Level, and scroll down to the Cookies section.
4. Select Always Accept Cookies or Prompt Before Accepting Cookies, and click
OK.
Mozilla 1.x 1. Select Edit > Preferences.
2. Find Privacy & Security in the menu on the left. If there’s a plus sign (+) to
the left of Privacy & Security, click it.
3. Select Cookies under Advanced.
4. Select Enable Cookies for the Originating Web Site Only or Enable All Cookies,
and click OK.
Opera 1. Press F12.
2. Click Enable Cookies.
Netscape Navigator 6.x 1. Select Edit > Preferences.
2. Find Privacy & Security in the menu on the left. If there’s a triangle pointing
to the right next to Privacy & Security, click it.
3. Select Cookies under Privacy & Security.
4. Select Enable Cookies for the Originating Web Site Only or Enable All Cookies,
and click OK.
If you forget your password at some point, you can get it sent to the e-mail address provided
when you registered with the Plone site. To have your password mailed to you, click the
Forgot your password? link located in the left column of the Web site. This will bring up the
forgotten password form, as shown in Figure 3-5; enter your login name, and a password will
be e-mailed to you.
38
Figure 3-5. Getting a forgotten password
Unfortunately, if you have no longer access to that e-mail account, or you can’t even remember
the username, you’ll have to contact a site administrator. Using the techniques discussed in
Chapter 9, the administrator can change your e-mail and find your user account. Once logged
in to the Plone site, you’ll see a log out link in the upper-right corner. When your work is
finished, it’s good practice to log out of Plone site, especially if you’re accessing it from a
computer that’s likely to be used by other people.
3.1.3 Setting Up Your Folder and Preferences
After you’ve logged in, the member bar in the top-right corner will change to represent the
options available to you as a member of the site (see Figure 3-6).
39
Figure 3-6. Your personal choices in the top-right corner have changed.
One of these options is that each member has a folder created for them when they join a site.
This folder is set up with particular security so only that member (and administrators) can add
and edit the content in that folder. To access your personal folder, click the my folder link in the
personal bar in the upper-right corner of the site. In the upper-right corner you’ll also see a my
preferences link; clicking this will open a list of personalization options. You’ll see two choices
at the moment; you can change your password, or you can go to the personal preferences and
change key preferences in your site.
The change password form allows you to change your password. To complete the form, give
your current password and the new password twice. After you’ve changed your password, the
change will be immediate. You don’t have to log in again; just remember your new password
when you return.
The personal preferences form allows you to set a number of preferences that change how you
see the site. These preferences are stored on the server so they’re retained between uses of the
site (see Figure 3-7).
40
Figure 3-7. Changing preferences
The options are as follows:
Full name: This is the full name you gave when you registered with the site.
E-mail: This is the e-mail address associated with your membership and is used
a number of places in a Plone site. Most important, if you lose or forget your
password, this is the address to which the system will send it.
41
Content editor: When editing complex content, you may want the help of an
editor. If your site administrator has made one available, you can select it here. It
will then be used when you click the edit tab of an object. If you’re unsure, leave
this as the default.
Listed status: This property specifies whether your profile will show up on the
members tab and when someone searches the members listing.
Editing of Short Names: Objects have an ID or Short Name property that’s used for
the internal representation of the content object. This also shows up in the item’s
Web address and the item’s Uniform Resource Locator (URL). By default these look
something like News_Item.2002-11-16.4102, but you could make it much simpler,
such as november_news, by changing the Short Name value.
NOTE When you change an object’s name value, anything that references the older
name will no longer be valid and will result in the page not being found. It’s best
not to change the name value after you submit an object for review or link to it
from elsewhere. For this reason, I recommend setting this option to No.
Portrait: In larger organizations and in community Web sites, it’s useful to see
pictures of other members. The Portrait field allows you to upload a picture of
yourself. The picture should be 75 by 100 pixels.
Once you’ve made the desired changes, click the Save button to commit the changes. Now that
you’ve logged in, it’s time to start adding and editing content.
3.1.4 Adding and Editing Documents
As mentioned, now that you’re a site member, a folder has been created for you where you
can store content. Of course, you can add content to any folder that the site administrator has
given you the right to do so, but by default every user can add content to their member folder.
Each type of content you can add is distinct, and you can edit and view it in different ways.
For this reason, Plone references each type of content differently; for example, you can add
images, links, documents, and so on. Out of the box, Plone provides the following content
types:
• Document: This is an item that presents some static information to the user.
This is the most common type of content added and most closely represents a
typical Web page.
• News item: This is a document that’s to be shown under the news tab (for
example, a press release).
• Link: This is a link to another item, which may be internal or external to
another Web site.
• Image: This is an image, such as a .gif or .jpeg file.
• Event: This is an upcoming event, meeting, conference, or other event.
• Folder: This is like a folder on a hard drive; this is a folder for putting content
into so that it’s easy to find later.
42
• Topic: This is a grouping of other content. This is essentially a saved search
criteria that you can reuse later. Only privileged site users can add topics.
• File: This is another piece of content such as a movie, sound clip, text file,
spreadsheet, compressed file, or anything else you’d like to upload.
I’ll go through each of these items using the document as an example, showing in detail how
to add and edit documents easily and quickly. Using these basic content types, I’ll show you
how to build a dynamic site through a browser, without doing any programming.
Actually, you have many ways of adding and editing content in a Plone site than just through a
Web browser. Access via File Transfer Protocol (FTP), via Web-based Distributed Authoring and
Versioning (WebDAV), or via scripts is all possible. I’ll discuss how to set these up in Chapter
10 and just deal with the Web browser interface for now. In Chapters 11 to 13 of this book, I’ll
discuss how to make new custom content types that you can tailor to a particular site’s needs.
3.1.4.1 Understanding the Document Content Types
Rather than detailing how to add and edit all the different types of content available, I’ll
cover adding one type of content, a document, in detail. After adding and editing a few of
these documents, the approach to adding content should be second nature, and editing other
content will be easy.
A document is a page of content, usually a self-contained piece of text. Although all items
added to Plone are accessible as Web pages, if there’s one content type you could think of as
a Web page, this is it. The default home page for a Plone site that you’ve seen already - the
now-famous Welcome to Plone page - is one example of a document (see Figure 3-8).
43
Figure 3-8. Welcome to Plone, a simple document
3.1.4.2 Adding a Document
You have two ways to add any piece of content using a Web browser. First, ensure you’re
logged in, because only logged-in users can add content. Second, select the my folder link from
the top-right navigation bar. This will take you to your home folder, an area that you control.
44
If you’re able to add content to a folder, then the folder will show up with the green border
around the top (see Figure 3-9).
Figure 3-9. My content
If the green border doesn’t appear, then you won’t be able to add content; this border contains
the actions you can perform in the current location. In Figure 3-9, you can see that the page
shows the contents of the folder, because that’s the highlighted tab. Other tabs appear such as
view, sharing, and properties for more advanced options. In the top-right corner of the green
border, you’ll see an Add New Item drop-down menu and a State drop-down menu. Click the
Add New Item menu to open a drop-down list of items to add (see Figure 3-10).
45
Figure 3-10. Adding a document from the green drop-down menu
To add a new document, select Document. Alternatively, if you look in the body of the page,
you can see another Add New Item drop-down box. Again, click the down arrow to open a list
of items that can be added and then select the item you’d like to add (see Figure 3-11).
Figure 3-11. Adding a document from the main folder’s content menu
Using the Add New Item list from the green border is a handy menu since it’s available most
of the time.
NOTE **If you’re familiar with Zope, you should never, never, never add con-
tent from the Zope Management Interface (ZMI). Depending upon how you’ve
installed Plone, you may have already seen the ZMI and used it for customiz-
ing and developing Plone through the Web. However, adding content through
the ZMI will create content items that are incomplete and don’t ***Begin Side-
bar*
3.1.4.3 Understanding Where to Add Content
The easiest place to add content at the beginning is in the user’s member folder, accessi-
ble by clicking the my folder link. Although this is useful, it’s probably not the best ap-
proach for a long-term solution. Most noticeably it creates long URLs (for example, /Mem-
bers/andy/Docum....). It also means your content isn’t accurately reflected in the navigation
tree.
As you’ll see later, a few solutions exist for this; the most common solution is to make a folder
and give certain users the right to access it. For example, that folder may be Help or News. The
’Using Folder” section later in this chapter discusses adding folders, and Chapter 9 discusses
using group workspaces and security.
3.1.4.4 Editing a Document
Once you’ve clicked to add a document, you’ll be taken directly to the edit page with a mes-
sage telling you that the document has been created. If this doesn’t happen, you can click a
document and then click the edit tab. Again, you’ll see that the edit tab becomes highlighted
in green (see Figure 3-12).
46
Figure 3-12. Editing a document
Now you can edit the document in your Web browser, using the form provided. If you look at
the URL in the address bar of your browser, you’ll note that a short name for the object has
been created for you, something such as Document.2003-12-29.43787. The following is a list
of the fields and their meanings:
Short name: The short name will become part of the document’s URL, so keep the
name short and descriptive, preferably without spaces. Keeping to these rules will
make URLs easier to read. For example, use something such as audit-report-2003.
If you don’t provide a name, Plone will create one for you.
47
NOTE This field won’t appear if you selected No for the short names in your pref-
erences page.
Title: This is the title for the item, and it’ll be shown throughout the site (for
example, at the top of the page, in the search interface, in the title of the browser,
and so on). This field is required.
Description: This is a short lead-in to the document, usually about 20 words to
introduce the document and provide a teaser for the remainder of the document.
This is useful for pages that show summaries of documents, such as search results
and folder contents.
Body text: This contains the body of the document. The format for the content is
set using the Format field (described next).
Format: You have three choices for the format of body content: Structured Text,
HTML, and Plain Text. These types of text are discussed in the ’Choosing a Text
Format” sidebar; if you’re unsure, leave this field alone and type the body text as
usual.
Upload document: If you do have your document as a file on your computer, you
can upload it instead of typing the content into the Body Text field. Use the Upload
button at the bottom of the page to select a file. The contents of an uploaded file
will replace any content in the Body Text field.
Once you’ve finished editing your document, click the Save button to commit your changes.
You’ll be returned to the view tab where you can see how the document will be shown to users
(see Figure 3-13); to edit it again, click the edit tab.
48
Figure 3-13. Saving the content will take you to the view tab.
If you don’t provide the correct input on the edit form, when you save the document you’ll
be returned to the edit page, and your errors will be highlighted. At this point your changes
haven’t been applied—you must correct the mistakes and click Save again before the changes
will be committed. The view tab shown in Figure 3-13 shows the document you’ve created.
You’ll see that the title, description, and content are all shown in slightly different styles. At
the bottom of the page is a byline that contains information about the author of the document,
including the date the page was created.
You’ll note that if you go back to folder contents after saving your changes, you’ll see two
documents in your folder: the existing one that’s created for you and the new one you’ve just
added. You can edit either of these documents by clicking them to open the view tab, which
allows you to select the edit tab.
49
3.1.4.5 Choosing a Text Format
As mentioned previously, you can edit the document content in at least three formats: struc-
tured text, HTML, and plain text. This rather confusing state of affairs is brought about by
trying to produce easy systems for users to write rich marked-up content in plain text without
having to use fancy editors.
Unfortunately, in most cases, this really doesn’t work; training is required to understand the
formatting. Structured text requires quite a bit of understanding in itself because it has a frus-
trating syntax and doesn’t internationalize well. If I had to pick one format that I’d recommend
over all the others, I’d pick HTML because it’s widely understood, and you can use What You
See Is What You Get (WYSIWYG) editors such as Epoz to produce it.
*HTML* HTML is the most standard format; if a document is entered as HTML, it will be
rendered in the same format. This HTML shouldn’t be a complete page but rather a snippet.
For example:
<p>Here is a sample in <i>HTML</i> for a demonstration.</p>
Ideally the HTML should also be valid Extensible HTML (XHTML) to comply with the rest of
the Plone system; if it’s not, your pages don’t comply with Web standards. Entering text as
XHTML isn’t for the faint of heart, so in Chapter 9, you’ll see how into integrate rich-editing
tools into Plone that allow users to easily write content in XHTML. The following screen shot
shows Plone using Epoz so users don’t have to understand HTML:
*Plain Text* Plain text is simple. It does no major conversion or manipulation of the text
entered; it’s just plain text. The only modification made is that new lines are converted into
HTML when rendered so that new lines appear in the Web browser. No other altering happens.
For example:
Here is a sample in plain text for a demonstration
*Structured Text* Structured text is a system for writing plain-text documents in a particular
format, which can then be interpreted in different ways. For example, if a piece of text needs
to be highlighted, then it can written as italics; this will then be shown as italics. This series of
rules means that a user can write a page that contains formatting information easily. For a full
list of structured text rules and examples, please see Appendix A. The following is a sample of
structured text:
Here is a sample in *structured text* for a demonstration
50
3.1.4.6 Setting Document Metadata
Any piece of content can have any number of properties assigned to it. These properties are
known as metadata and provide information such as keywords, copyrights, and contributors of
an item.
This entire set of properties is optional and is usually used only if there are special requirements
for this piece of content, especially since this information isn’t normally shown to the person
viewing the content. So the main reason for entering this data is to add information for tasks
such as searching or categorizing the content.
You can access properties on an object by selecting the green properties tab. This properties
form has the following fields, which are common to all content types:
Allow Discussion: This lets this document be discussed by users who have the
right to do so. If the value is left as default, it’ll use the sitewide policy for that
content type.
Keywords: Each item can have keywords assigned to it to enable grouping and
sorting of the items. For example, an article about recent events in politics may
have the keywords politics and prime minister. Keywords are flexible, and you can
use any keyword from the given list. By default there are no keywords in the Plone
system; site administrators may add new keywords so that other users can select
them.
Effective Date: The effective date is the first day a piece of content should be
available. You can specify this date by entering the values in the form or clicking
the little calendar icon, which opens a calendar, and picking a date (see Figure
3-14).
Figure 3-14. Entering an effective date
Expiration Date: The expiration date is the last day a piece of content should be
available. Usually the Effective Date and Expiration Date fields are left blank.
Format: This is the Multipurpose Internet Mail Extensions (MIME) type of the
item. The term MIME type refers to a computer definition of the type of content
51
(for example, application/msword or image/jpeg). This is set at a default value; if
you’re unsure about this field, just ignore it.
Language: This is the language in which the document is written; the default is
English.
Copyright: This is the copyright information for the content, which is usually
blank.
Contributors: This includes the names of the people outside the Plone system who
contributed to the object. Each person’s name should be on its own line.
After completing the values for this form, click Save to commit the changes. As stated, usually
you won’t need to edit the values on this tab. Editing the contents of this tab is usually based
upon the requirements for your site and the type of site you’re building.
3.1.4.7 What Are Effective and Expiration Dates?
Any item in the Plone system can have effective and expiration dates if the person editing the
content wants. Both of these are optional, and leaving the fields blank will ensure that these
values aren’t set.
One example of an item that may have an effective date is a press release. In the ideal world,
the news item is crafted, prepared, and reviewed in Plone. However, suppose the news item
has to go live on the Web site at midnight, but that’s exactly when you plan to be sleeping.
Not a problem—give the press release an effective date and a time of midnight. Up until the
effective date, it won’t be visible in the calendar, in navigation, in searches, or in pages that
use a search as the listing under the news tab. However, anybody who knows about the press
release will be able to access the page directly. Once the effective date has passed, the item
will appear in all the aforementioned places and be live to the world.
The effect is similar with expiration dates. If you have a special offer that stops being effective
on a particular day, then you could set an expiration date of that day. After that, date it
wouldn’t appear in calendar, navigation, searches, and so on.
The effective and expiration dates don’t actually change the state of the item in workflow (see
Chapter 7 for more information on workflow); rather, they just change where it displays. You
can also set effective and expiration dates on the state tab, which you’ll learn about in the next
section.
3.1.4.8 Publishing Your Document
When a document is created, it’s given an initial state, called visible. By default, content isn’t
automatically published and available to the world; instead, others can view your content, but
it doesn’t show up in searches or the navigation tree. This is a useful state because you can
point other users to this content, but because it won’t show up in navigation or searches, it’s
not visible unless users know about it.
At any point in time, each item of content in your Plone site is in a particular state. This state
describes its permissions and roles within the Plone site. By having items in different states, it’s
52
possible to apply different security to each item of content. For example, sometimes an item
may take a week or two to prepare and involve multiple revisions. Eventually you’ll want to
publish the content so that it’s visible for all users and shows up in the navigation and search.
You can publish the content using the State drop-down menu located at the top right of the
main navigation (see Figure 3-15).
Figure 3-15. State drop-down menu
To publish an object, select Submit from the drop-down menu. By default you can’t directly
publish content, but you can submit it for review. When an item is submitted for review, it
moves into the review state. This is an intermediary state between visible and published. It
allows for the review of content by users of your site with the reviewer role, before it goes live
for the entire world to see. After you’ve submitted the content, you’ll notice that the content is
now in the review state by looking at the box in the top-right corner. You’ll also notice that in
Figure 3-16, there’s no longer an edit tab.
Figure 3-16. The content has been submitted for review, the state has changed to pending, and
the edit tab is no longer an option.
NOTE If you’re logged in as a manager, then you’ll note there will be one extra option in the
drop-down publishing list called Publish. This lets you put content straight into the published
state with no intermediate step.
In the workflow drop-down list in the top-right corner, there’s also an option for Advanced,
which opens the state form for changing the status of an object. This form is the same as
clicking the state tab. It has the following fields:
Effective Date: This is the same as the Effective Date field in the properties (see
the ’Setting Document Metadata’ action).
Expiration Date: This is the same as the Expiration Date field in the properties
(see the ’Setting Document Metadata’ section).
Comments: This includes any comments you want to make for this change in state
that will be recorded in history. For example, you could enter First draft; Bob,
please see second paragraph.
53
Change State: These mirror the choices available in the drop-down menu. For ex-
ample, the options are Publish, Submit, and so on. One further option, No Change,
is available if no change is necessary.
Select the change of state you’d like to occur, and click Save to commit the changes.
3.1.4.8.1 What Are the Workflow States?
At this point you may be asking yourself what this workflow thing is and what the states mean.
Workflow, as discussed in Chapter 7, is the ability to apply different states to the content. The
following are the default states:
Visible: Content is created in the visible state. All users can find visible content
through the search function and can access it directly by visiting the object URL.
Visible content doesn’t show up in the navigation tree. Visible content is editable
by their owners and site managers.
Pending: Pending content includes items that have been submitted for publishing
by site members. From a user standpoint, pending content behaves like content in
the visible state. The difference between the two types is that pending items are
flagged for review; site reviewers are prompted to publish or reject pending items.
Pending items are editable only by managers and reviewers.
Published: Published items are visible to all site visitors. They appear in search
results and the navigation tree. They may also appear in other areas specific to
that type (news items, for example, also appear when you click the news tab).
Published items are editable only by managers, but owners can retract them for
editing (retracting reverts an item to the public draft state).
Private: Items in the private state are visible and editable only by their owners and
others with manager access to the folder in which they exist. They won’t appear in
search results or on the navigation tree for other users. Private items are editable
by managers.
3.1.4.8.2 How Does Content Get Reviewed?
If you’re a reviewer, then in the right column of the home page you’ll see a new review list
when you first log in. This is a list of the items that have been submitted for review and need
reviewing by you or another reviewer (see Figure 3-17).
54
Figure 3-17. The review list
The review list will appear on the right when you log in as a user with the review role and
there are items to review. In my case, I logged in as admin, which was the user created
during my install process. You can tell you’re logged in because your name will appear in the
member bar. The review list gives a list of items to review - in this case, you need to review
the test document. Click the document to open the item. At this point you essentially have the
following choices for this item:
Reject it: Reject it by selecting Reject from the drop-down choices. This will move
the content back into the visible state and assumes that as a reviewer you’re un-
happy with it. Usually you may want to click the Advanced option to open the
comments form and add some comments stating why you’re rejecting it.
Approve it: Approve it by selecting Publish; this will change the content into the
published state. This will make the content publicly available.
Do nothing: Leave it by doing nothing. This leaves the content in limbo but some-
times happens when you need to check information or talk to others. Eventually
you should return to do something with this content because it’ll continue to show
up in your list of items until you make one of the previous actions.
Edit it: Edit it, and then perform one of the previous actions. As the reviewer, you
can make any change you’d like to do, so feel free to change the content by using
the edit tab.
Once you’ve moved content out of the review state by publishing or retracting it, it will no
longer show up in the review list. Of course, this assumes you do have someone as a reviewer
for your site; this usually (although not necessarily) is also the user who created the Plone site
as an administrator. In Chapter 8, I’ll discuss how to add and edit users and give some users
the review role.
3.1.4.8.3 How Do You Edit a Published Document?
Once a document has been published, it must be retracted to be edited. To do this, select
Retract from the workflow drop-down menu, which will move the item back into the visible
state. Once it has returned to the visible state, you can reedit it and placed it back into the
review queue.
This step, although a little annoying, is necessary to ensure that all content goes through a
review step. For example, you have to ensure that any edits made to a page are appropriate by
reviewing the content. Users with the manager role can edit the content at any time, so they
can quickly go in and fix a typo without having to go through the review step. It’s assumed
that users with a manager role are trustworthy! If you’re a manager, as defined in Chapter 9,
you can go to any piece of content and will see the edit tab. At that point, click Edit to alter
the document and make your changes.
55
3.1.4.9 Sharing Your Document
This allows you to assign more rights to other users or groups of users of the system to your
document. This is an advanced feature and is covered in more detail in Chapter 9.
3.1.5 Adding and Editing Other Types of Content
I’ve just covered how to add and edit documents in detail. All the other content types are
similar. They all have the same or similar actions to edit; it’s just the forms and the data in
them that change. In the following sections, I’ll cover some of these other types of content. All
the following types of content use the same workflow process, so they need to be published in
the same manner as documents.
3.1.5.1 Adding and Editing Images
Images are graphical pieces of content; you add them by selecting Image from the drop-down
list. When you add an image, the name of the content changes to the file of the image. So, if
you upload an image called photo.gif, it’ll be accessible in Plone as photo.gif. When adding or
uploading a new image, you can select the image from your hard drive by clicking the Browse
button and selecting the file (see Figure 3-18).
56
Figure 3-18. Uploading an image
It’s common for image filenames to end with an extension such as .gif, .jpg, .jpeg, .png, or
.pict. You can display images inside Plone on a Web page without having to download them
to the local computer if the type of the image uploaded is viewable in the user’s Web browser.
The most common image types are .gif, .jpg, and .png, which are visible on almost computer
system. Figure 3-19 shows an image of the Plone logo.
57
Figure 3-19. Viewing the image
You can’t edit images directly; instead, you can edit the image on your hard drive using any
program, such as Adobe Photoshop or GNU Image Manipulation Program (GIMP). Once com-
plete, clicking the edit tab allows you upload your new image into Plone. If you do a lot of
image manipulation, you can refer to Chapter 10, which covers External Editor, a tool that lets
you edit images using a program without having to upload and download them.
3.1.5.2 Adding and Editing Files
A file is any arbitrary file that can be uploaded from your hard drive. To add a file, select File*
from the drop-down list. On the edit tab, you’ll see an Upload button that lets you pick the file
from your hard drive. This could be any sort of item, including a plain-text file, a Microsoft Word
document, a Microsoft Excel spreadsheet, an executable program, an Adobe Acrobat document,
and so on. When you add a file, the name of the item in Plone changes to the name of the file
uploaded. So, if you upload a file called *book.pdf, it’ll be accessible in Plone as book.pdf. Figure
3-20 shows a plain-text file.
58
Figure 3-20. Adding a plain-text file
If the file is recognized as being text, then the contents of the file are shown in the Web page
and are editable through the edit tab. Otherwise, the file is downloadable, and users must
download it to their local hard drive and edit it there. Afterward they can upload it to the
system. You’ll note that a file object also has an extra download tab that lets you directly
download the file.
59
3.1.5.3 Adding and Editing Events
An event can be something that will happen in the future or something that happened in past.
You can add events to Plone, and they show up on the calendar. To add an event, select Event
from the drop-down list. An event has more information than most Plone objects; however,
most of it is self-explanatory (see Figure 3-21).
Figure 3-21. Adding an event
As usual, the only required field is Title; however, if you want the event to show up in the
calendar, then you must provide a start and end time. Events can span multiple days or be in
60
the past—as long as the start date is before the end date. To enter a date, select the appropriate
dates from the drop-down menu, or click the little date icon to open a graphical date picker.
Once the event is published, it’ll show up in the calendar. Moving a mouse over the item in the
calendar will show the start and end dates for the event, as well as the event’s title (see Figure
3-22).
Figure 3-22. Viewing events in the calendar
3.1.5.4 Adding and Editing Links
Link content types are the primary way for users to share links. These URLs can be resources
on the Internet or an intranet, an internal resource, or anything to which the users have access.
To add a link, select Link from the drop-down menu.
If you’re going to link to an Internet resource, you should preface your link with the suitable
protocol (for example, http://). For instance, if I was visiting an interesting page on the BBC ’s
Web site and wanted to share this, I could add a link. The value of the URL will be the text in
the address bar (for example, http://news.bbc.co.uk), as shown in Figure 3-23.
61
Figure 3-23. Adding a link
3.1.5.5 Adding and News Items
News items are commonly used in Web sites to display news that’s of interest to the reader.
Actually, a news item contains the same information as a document. The only real difference is
that a news item will show up when a visitor clicks the news tab (once the item is published),
as shown in Figure 3-24.
62
Figure 3-24. A list of news items
If I wanted to write a Web page that was going to be relevant for a long period of time, such
as directions to my company’s office, I’d use a document. If I wanted a page that detailed my
exciting new product and drew attention to it, I’d use a news item. That news item would be
visible under the news tab, and as new things happened, it’d slowly move down the page.
63
3.1.6 Organizing Content
So far you’ve seen how to add and edit content in a Plone site, but without clear organization,
this can become a mess quite quickly. You have two main ways of organizing content: folders
and topics. A folder is the simplest and most powerful mechanism for organizing content and
works just like a folder or a directory on a computer’s hard drive. A folder can contain any
item of content; content can be copied and pasted between folders, and of course folders can
contain other folders.
To organize content that’s spread all over a site, a more sophisticated and less-used tool called
a topic is available. A topic searches your Plone site and finds all objects that match a certain
criteria, allowing you to group lots of disparate content.
3.1.6.1 Using Folders
A folder is just like a folder or a directory on a hard drive, except that the folder and its
contents exist inside Plone. You use a folder the same way; when you need to categorize
content or make things a little clearer, you can group items and place them in a folder. To add
a folder to your site, select Folder from the drop-down list. This will add a folder and take you
to the edit properties page for that form. A folder has just three rather simple attributes that a
user can edit: Name, Title, and Description. I’ve discussed all these attributes for documents,
and nothing is different for folders.
Folders have two green tabs that represent slightly different views: contents and view. Actually,
you may have already noticed that there’s a contents tab accessible from any piece of content
that you’ve added to the site; for example, when you were editing a document, the contents
tab was there. That contents tab will always take you to the contents for that folder.
3.1.6.1.1 Viewing the Contents of a Folder
The folder has the concept of a default page, which is a page that will be shown to the user
when they view a folder. It’s a concept taken from Web sites where viewing a folder on a
Web site shows a default page if one is present; often that default page’s name is index.htm
or index.html. If a folder has a default page, then clicking the view tab will show that default
page. If the folder doesn’t have a default page, then it’ll show a folder listing of all the content
in that folder. When looking for a default page to display, Plone looks through the folder for
content with a certain name and shows this item. The page name is usually index.html or
index_html; however, the site administrator can add or alter these names.
This contents view of a folder allows a user to perform a variety of tasks, such as move content,
rename it, delete it, publish it, and change the order it’s listed. As shown in Figure 3-25, you’ll
also see a simple table of the folder contents. Each row of the table shows the title of the
content (plus an icon), the type, its size, when it was last modified, its current workflow
status, and order selectors. On the left is a checkbox to select the items you want to change
and a series of options across the bottom: Rename, Cut, Copy, Delete, and Change Status.
These functions are all pretty self-explanatory, and you can apply them to multiple objects at
once by clicking several checkboxes.
64
Figure 3-25. Contents of a folder after I’ve added some of the content types described in this
chapter
For example, to quickly rename a piece of content, click that item’s checkbox and then click
Rename. This will open the rename form and allow you to rename the title of each item in that
list. Click Save to have the changes take effect. The Cut and Copy buttons allow you to copy
or move content between different folders. The Delete button allows you to delete the item
from Plone. Just like on your hard drive, if you copy, move, or delete a folder, all the contents
of the folder will also be moved, copied, or deleted.
A new feature in Plone 2 is the ability to change the default order of items in a folder. By
default, items in a folder will display in the order the items were added. If one item is more
important and needs to be moved to the top, use the arrows on the right side of the table to
move the item. The following features will appear in the folder contents only when certain
things happen:
• If the content has an expiration date set and it has expired, you’ll see the word
expired appear in red next to the item.
• If the server has External Editor installed, you can click the pencil to edit in
External Editor (this is covered in Chapter 10).
• If the content is locked, you’ll see a lock icon appear next to the content.
65
3.1.6.1.2 Publishing a Folder
Folders have a much simpler workflow than documents. Earlier in this chapter you saw how to
publish content to make it publicly visible because this allows users to create and edit content
as much as possible before pushing it live. However, folders are a little different because they
contain content but don’t have any content of their own. For this reason, folders have no
review state. Anyone can directly publish or make private folders, so there are three states:
private, visible, and published.
After adding a folder, select Publish from the drop-down list. Then it’ll show up in the naviga-
tion. As per the earlier rules for workflow, if you don’t publish a folder, it won’t show up in the
navigation.
3.1.6.2 Using Topics
A topic allows you to collect content from disparate places throughout a Plone site and provide
it in one location. Topics work by creating a criterion that’s common to all the objects you’d like
to gather. This criterion could be all images or all news items with Plone in the text. Because
topics are a rather complicated type of content, only managers can add them initially. If you
can’t see Topic in the list of items to add, then you don’t have the permission to do so.
To add a topic, select Topic from the drop-down list. After adding the topic, you can create key
criteria on the criteria tab. The list of criteria and types is available in the drop-down box at the
bottom of the page. This is a rather confusing list; I won’t try to discuss it here. Unfortunately,
what those terms are and what they mean is based heavily on the underlying technology, such
as catalog indexes and object attributes. For this reason, Chapter 11 covers this in detail.
For example, to create a topic that shows all the images, you need to add a criterion that
searches for content based on portal_type. For this, select a field name of portal_type, select
String Criterion, and then click Add. These criteria will be added to the top of the page; in the
field to the right of portal_type, enter Image, and then click Save. You now have criteria for
your topic that will show all content that’s an image. Returning to the view tab, you can now
see all the images on a site.
As stated, topics are quite complicated, have a rather unfriendly interface, and are recom-
mended for only advanced users. Many people have found topics useful, which is why they’re
still available in Plone; however, a more user-friendly system will be developed in the future.
3.1.7 Discussing and Finding Content
Adding and editing content in Plone is much more useful if people can find and then discuss the
content. The primary ways users find content is through searching and navigation. Fortunately,
Plone automatically sets up searching and navigation for the users, so it’s easy to find the
content you’ve added.
66
3.1.7.1 Adding Comments to Content
Feedback from users is a vital part of any Web site. By allowing users to add comments, you
ensure that users can give feedback, correct typographical errors, or otherwise discuss the
content. You can discuss almost any piece of content in Plone; folders and topics are the only
exceptions.
You can enable discussions in one of two ways. First, the owner of the content (otherwise
known as the person who created it) turns on the discussion feature by clicking the properties
tab of the object and selecting Enabled under the Allow Discussion on This Item header, as
shown in Figure 3-26. Second, the default option applies the policy for that type of content
as defined by the site administrator; setting this option is described for the administrator in
Chapter 10.
Figure 3-26. Enabling discussions
Once discussions are enabled, click the Add Comment button to discuss the content, which
opens a form for adding the comment (see Figure 3-27).
67
Figure 3-27. Adding a comment to a piece of content
Enter the subject of the comment and the text of the comment. The text is entered as plain
text, so just type away as usual. Comments don’t go through any workflow, so comments show
up as soon as they’ve been added. Once a comment has been entered, it can be replied to,
forming a threaded list of comments on an item. Further, comments will be entered in the
catalog so they can be searched.
NOTE Administrators logged in as managers can remove any replies or entire threads. Dis-
abling replies doesn’t remove the comments; it just stops them being shown, so reenabling
comments will show the existing comments again.
3.1.7.2 Searching for Content
Plone contains a powerful search engine system based on Zope’s ZCatalog. This search engine
allows content to be cataloged in multiple ways and to be queried efficiently and quickly.
Chapter 10 covers the internals of how this works and how it can be queried.
When you’re searching for content, the content will be shown to a user if it’s one of the two
states: published or visible. At the top of your Plone site a search box provides an easy way to
do simple textual searches in the same way as a search engine (see Figure 3-28). For example,
enter Tuesday to find all content that contains the word Tuesday. A result of all matching
content will display; click the title to get to the content.
68
Figure 3-28. A search for Tuesday on Plone.org
The search provides a quite sophisticated search, with features similar to most search engines.
You can make this simple query quite complex. For example, you can use the following options:
Globbing: You can use an asterisk to signify any letters. For example, entering
Tues* matches Tuesday and Tuesdays. You can’t use the asterisk at the beginning of
a word, though.
Single wildcards: You can use a question mark anywhere to signify one letter.
For example, entering ro?e matches rope, rote, role, and so on. You can’t use the
question mark at the beginning of a word, though.
And: You can use the word and to signify that both terms on either side of the and
must exist. For example, entering Rome and Tuesday will return a result of when
both those words are in the content.
Or: You can use the word or to signify that either terms can exist. For example,
entering Rome or Tuesday will return a result of when either of those words are
in the content.
Not: You can use the word not to return results where the word isn’t present; a
prefix of and is required. For example, entering welcome and not page would
return matches for pages that contained welcome, but not page.
Phrases: Phrases are grouped with double quotes (’ ”) and signify several words
one after the other. For example, entering ’welcome page” matches This welcome
page is used to introduce you to the Plone Content Management System, but not
Welcome to the front page of my Web site.
Not phrase: You can specify a phrase with a minus (-) prefix. For example, entering
welcome -’welcome page” matches all pages with welcome in them, but not ones
that match the phrase welcome page.
69
NOTE All searches are case insensitive.
Large sites may have a lot of results, so only 20 results display at a time. To page through the
results, navigation bars will appear at the top and the bottom of the search result pages. The
values on an object used in a search are its title, description, and body text (if the content type
has any—for example, news items and documents).
3.1.7.3 Performing an Advanced Search
You can narrow down the search results by using an advanced search, which is accessible via
the search results of a standard search. In old Plone sites, a search tab brought users to this
page; you can reenable this tab if you want, as covered in Chapter 4. The advanced search
form enables a user to query content using a number of attributes, including title, keywords,
description, review state, creation date, content type, and even author, as well as the search
text (as used in the quick search available from the top-right corner), as shown in Figure 3-29.
Figure 3-29. Advanced search
Although the search text field searches both the title and description, you may want to search
the description or title only. For that reason, these fields are presented on the advanced search
form. You can’t search the title and description using the wildcards, globbing, or any of the
advanced search options. Any search result will match the input (if given) of all the fields; the
results will be an intersection of all the terms.
70
3.1.8 Example: Creating the Plone Book Web Site
To give an example Plone site and provide a series of examples, I set up a Web site for this
book. This is a Plone site with a few minor modifications. As I go through the book, I’ll
make references to the site and add new features as they’re covered in the book, including
new templates, skins, and so on. The Web site for this book is at http://plone-book.agmweb.ca;
initially I set this site up on Windows server, as described in Chapter 2. However, I later
transferred it to Linux.
The site serves the following purposes:
• It gives people a place to get information about the book and buy it.
• It allows easy access to the software used in the book.
• It gives code examples and allows users to interact with samples in the book.
• It contains any errata or issues found after publication.
Once I set up a Plone site, I created the following basic folder and page structure:
Home
|_ Software
|_ Chapters
|_ Chapter 1
|_ Chapter 2
...
To do this, I logged in as the user who was created with the installer—in my case, the admin
user. After logging in, I went to the home page, clicked the edit tab, and wrote some text for
the home page. I created links to the Chapters and Software folders. Then I clicked the contents
tab and added two folders, as shown in Figure 3-30.
Figure 3-30. The folder contents with my home page and the new folders
Next, I went to the Chapters folder and started adding a folder for each chapter. Because
I haven’t made a default page, Plone will happily create a listing of all the chapters. The
71
description for each chapter is the name of the chapter (for example, Introducing to Plone and
This Book), and the short name is the chapter number—this will keep my URLs nice and short
(for example, /Chapters/3). I’ve left everything in the visible state, so after this, it’s just a
matter of adding content.
4 Chapter 4
4.1 Making Simple Customizations
After you’ve figured out how to add and edit content, you’ll want to start customizing your
site. This chapter explains how to perform simple customizations in Plone using the options
available to administrators. Performing the customizations in this chapter requires a user to be
logged in with the manager role, as discussed in Chapter 2.
These customizations are all configuration options you can make through the Web. Rather than
explain all these parts in detail, this chapter gives an overview of many subjects and explains
how to accomplish certain tasks while showing some of the under-the-hood machinery. These
topics are then expanded and explained in later chapters throughout the book.
The first and most useful place to look is the Plone control panel, which offers a variety of
options for the site administrator. All the parts of a Plone site are designed to be easily changed
and customized; the blue tabs you can see across the top of the page are easy to add and
remove. Other examples are the boxes in the left and right columns, which are called portlets.
Plone comes with several portlets, and you can easily choose where to display certain portlets.
Finally, this chapter shows how to customize Cascading Style Sheets (CSS) and images in
Plone. CSS affects everything in a Plone site. In fact, as you’ll see in this chapter, CSS deter-
mines all the colors, all the positioning, and a lot of the images you see. If you have the ability
to change the CSS code, then you can change almost the entire look and feel of your Plone
site. All the options covered in this chapter show you the large degree of control you have over
your Plone site.
4.1.1 Administering Sites
The first place site administrators should visit is the Plone control panel. This is the way to
access some of the administration functions of a site, including the name and description of
your Plone site, user and group administration, and any errors that occur within your site.
The term control panel is common, so don’t confuse it with the Zope Management Interface
(ZMI) control panel that shows the low-level ZMI options. The Plone control panel is an
ongoing attempt to provide a more user-friendly interface for the functions provided in the
ZMI. Since the project is ongoing, it’s hard to predict what functionality will be available in the
future. Instead, I recommend you go to the control panel and see what functions are currently
available; if you can’t complete your task there, then you’ll have to go to the ZMI.
72
73
To access the control panel, log into Plone as a user with the manager role. If you don’t have
a user with that role and are an administrator of the site, see Chapter 9 for more information
on how to do this. If you aren’t the administrator of a site and want this level of access, ask
your site administrator. To access the control panel, click plone setup at the top of the page (see
Figure 4-1).
Figure 4-1. Accessing the control panel
This opens the control panel (see Figure 4-2).
Figure 4-2. Plone control panel
The following functions are available in the control panel:
74
• Add/Remove Products: Clicking this link allows you to automate the instal-
lation products (covered in detail in Chapter 10).
• Error Log: Clicking this link accesses the log of errors that have occurred in
the Plone site.
• Mail Settings: Clicking this link allows you to alter the Simple Mail Transfer
Protocol (SMTP) server Plone uses to send e-mail.
• Portal Settings: Clicking this link allows you to alter portal settings (discussed
in the ’Changing the Title, Description, and E-Mail Addresses” section of this
chapter).
• Skins: Clicking this link allows you to set the current skin (discussed in Chap-
ter 7).
• Users and Groups Administration: Clicking this link allows you to alter users
and groups (discussed in Chapter 8).
• Zope Management Interface: Clicking this link takes you to the ZMI.
Throughout the rest of the book, I reference the Plone control panel if the current feature is
accessible from there; however, the remainder of book uses the ZMI for altering properties.
4.1.2 Using the ZMI
The ZMI is the basic interface that gives you access to Plone’s underlying Zope interface. Before
Plone existed, the ZMI was the main way to access, edit, and manage a Zope site and its
content. This was originally the Web interface for the content management system. Of course,
nowadays Zope isn’t really an out-of-the-box content management system but instead is an
application that sits under a system such as Plone. After quickly playing with the ZMI, you’ll
see why it isn’t suited as an interface to a content management system.
One thing the ZMI does provide is a simple interface to the underlying Plone and Zope infras-
tructure. You can find many of the basic features mentioned in this chapter through Plone, but
you’ll need to use the ZMI eventually. If you haven’t gone to the ZMI before, then you’ll find
that there are a few simple ways to get there; the easiest way is to log in as a user with the
manager role, click plone setup, and then click Zope Management Interface. You’ll note that the
address of the ZMI is the uniform resource locator (URL) of your Plone site with /manage on
the end of it. The ZMI for your Plone site should look like this:
75
You may have a problem with virtual hosting, which occurs with the Windows and Mac in-
stallers. Virtual hosting is the ability to have the Plone site as the root object rather than the
root of your Zope instance. For more information on virtual hosting, see Chapter 10. So, to
get to the root, you need to access the manage port. On Windows, select Start - Plone - Plone
- Manage Root. You’ll note that this sets the address to http://localhost:8080/manage. For
information on virtual hosting with your installation, see the specific documentation.
You’ll need to get to the root of your Zope installation for two reasons. First, you’ll need to
get to the Zope control panel. Second, you’ll need to get to the root of your Plone site to
make, rename, and copy Plone sites. The Zope control panel gives you database information
and access to products and other add-ons (you’ll need access to this for Chapter 10), as shown
here:
TIP When dealing with the ZMI, I find having two different browsers open helpful. For exam-
ple, I use Mozilla and Firefox. Besides, as a site administrator, it’s always a good idea to have
two different browsers to test that your changes work in more than one browser.
76
4.1.2.1 Changing the Title, Description, and E-Mail Addresses
The title, description, and e-mail addresses are stored in a Plone site as properties on an object
inside Plone. You can access these fields by clicking the Portal Settings link in the Plone control
panel (see Figure 4-3).
Figure 4-3. Portal options
The portal settings are as follows:
• Portal title: This is the title of the site that will appear in the title for browsers,
breadcrumbs, navigation, e-mails, and so on. The default is Portal.
77
• Portal description: This is the description for the portal, which is currently
used only in syndication.
• Portal From’ name: This field is related to various functions, such as a lost
password or the ’send-to-a-friend” function. Plone sends the e-mail messages
with this name attached. The default is Portal Administrator.
• Portal From’ address: This is the address used for the e-mails that Plone
sends. The default is postmaster@localhost.
• Default Language: This is the default language that’s given in the properties
of an object.
• Password policy: New users have two options; they can either enter a pass-
word or enter a password that’s then e-mailed to them. Although in both cases
they have to enter an e-mail address, the latter choice ensures that the e-mail
address they enter is valid.
• Enable External Editor Feature: This will turn on External Editor, which is
an advanced editing tool. It requires that External Editor be installed on the
user’s computer. Chapter 10 covers this in more detail.
After selecting the options you want, click Save to commit the changes. All the changes on this
form will occur immediately.
4.1.2.2 Setting Up a Mail Server
Plone will send e-mail using the MailHost object, which provides an interface to an SMTP
server and allows the developer to write forms and tools that send e-mails. The ’send-to-a-
friend” function and the mailing of a forgotten password use the settings configured here.
The default configuration is for a mail server on the localhost at port 25. If the SMTP server is
located elsewhere in the network, then you can access the form by clicking plone setup and then
clicking Mail Settings. Then change the mail server and the port to suit your configuration. On
my network, the mail server is at monty.clearwind.ca on port 1025, so I set the server as shown
in Figure 4-4; however, in most cases, you won’t need to change this.
78
Figure 4-4. Setting up the mail server
NOTE The MailHost object is a Zope object accessible in the ZMI. This object doesn’t currently
allow for authentication with the server. If this is needed, change the settings on the server.
4.1.2.3 Logging Errors
The error log catches errors that may occur in a Plone site; these are features such as Page Not
Found (404) errors, unauthorized errors, and so on. This isn’t designed to trap errors from
forms. For instance, if somebody doesn’t enter a required field in a form, then this won’t be
reported; this isn’t an error since it’s captured by the validation framework. This error log is
designed to catch internal server errors that may occur.
From the Plone interface, click plone setup and then click Error Log to see the errors reported
by the Plone site. Click the exception in the list (if there’s one reported) to see the error. Figure
4-5 shows an error that occurred when incorrectly filling out the mail settings form. It’s a long
page that includes a complete Python traceback and the incoming request.
79
Figure 4-5. An example error
On the error log form you’ll see the following settings:
• Number of exceptions to keep: These are the exceptions to keep in the active
log on the screen. The default is 20.
• Copy exceptions to the event log: This copies each exception to the file-
based log file. Not doing this means that no permanent record will be kept for
exceptions. The default is that this is selected.
• Ignored exception types: This is a list (one per line) of exception types to
ignore. The default is Unauthorized, NotFound, and Redirect.
80
You can log each exception and view it on the screen. This means if a user is visiting your
site and an error occurs, then you can go to the error log and see what occurred. The three
components of an error are the error type (which is the type of the error), the error value
(which is a string explaining when an error occurs), and the traceback. These first two items
are shown to a user on a standard error page (see Figure 4-6).
81
Figure 4-6. An example error message
So, when a user reports an error, the report will often include a message with the error name
and a value in it. If user isn’t allowed to do something and an Unauthorized error is raised
or a Page Not Found (404) is triggered, then you’ll get a custom error page rather than the
82
standard page shown in Figure 4-6. The following standard error types occur:
• Unauthorized: This occurs when a user doesn’t have the right to perform a
function.
• NotFound: This occurs when the item a user is trying to access doesn’t exist.
• Redirect: This is an error that can raise a Hypertext Transfer Protocol (HTTP)
redirect.
• AttributeError: When an object doesn’t have this attribute, this error is raised.
• ValueError: This occurs when a value given is incorrect and isn’t caught cor-
rectly by the validation or other framework.
4.1.3 Customizing Plone’s Look and Feel
The following sections describe the other customizations you can make to a site; almost of all
these require access to the ZMI.
4.1.3.1 Understanding Portlets
On a Plone site, you’ll see three columns to a page by default: the left, middle, and right
columns. The middle column contains the content for the object being currently viewed. This
is where most of the user functionality is for adding and editing content, completing forms,
and so on. The left and right columns contain a series of boxes that display information. Each
of these boxes is called a portlet. A variable determines which portlets display at a particular
point in time. The best way to understand these portlets is to look at the default portlets that
ship with a Plone site. You can find the parameters for the portlets in the portal object. To
access this, go to the ZMI, ensure you’re on the root Plone site, and click the properties tab.
This opens a list of properties, including left_slots, right_slots, and document_action_slots (see
Figure 4-7).
NOTE In earlier versions of Plone, the portlets were called slots. This is a common
term that conflicts with the page template term slot, so it was changed to portlets
for version 2. In certain places in the code and in the text, you may see the term
slot used. In these contexts, the words slot and portlet are synonymous.
83
Figure 4-7. The default portlet properties
The left_slots properties refer to portlets shown on the left of the page, and the right_slots
properties list the portlets shown on the right of the page. The portlets are shown in the order,
from top to bottom, that they’re listed in the properties; notice that each portlet is on a new
line. However, most portlets have some code to ensure that the portlet displays only if it makes
sense. For example, a login portlet is pointless to show if the user is already logged in. In this
case, the login portlet is included in the list of portlet but will show up only when needed.
Each portlet value is actually a special value called a Template Attribute Languages Expression
Syntax (TALES) path expression (Chapter 5 covers this in detail). Site developers can add their
own portlets to a site by creating simple macros and page templates. The default portlets are
as follows:
84
left_slots: This includes the navigation, login, and related portlets.
right_slots: This includes the review, news, events, recently published, and calen-
dar portlets. All the available portlets aren’t configured in Plone by default. The
following sections describe portlet slot in Plone. Each section describes the portlet
and shows what it looks like. Then I give the path expression that you need to add
to the slots property so it’ll show up in your Plone site.
For example, to show the calendar portlet on left side, enter
here/portlet_calendar/macros/portlet in the left_slots property, and click Save Changes. If
you want to remove it from the right_slots property, then you could remove the same line from
the right_slots property and click Save Changes.
4.1.3.1.1 Calendar
The calendar portlet is one of the default portlets that displays the calendar on the right of
the Plone page. This portlet shows published events for that month in a little calendar. The
calendar portlet will appear regardless if there are events to show in the calendar. You can
further configure the calendar using the portal_calendar tool in the ZMI (see Figure 4-8).
Figure 4-8. Calendar portlet
The expression to add is here/portlet_calendar/macros/portlet.
4.1.3.1.2 Events
The events portlet displays a list of the upcoming published events. If you have this item in
the portlet list, it won’t show up unless there are some published events to display (see Figure
4-9).
Figure 4-9. Events portlet
The expression to add is here/portlet_events/macros/portlet.
85
4.1.3.1.3 Favorites
In the top-right corner of a Plone document, you’ll see a Plone icon. A user can click this logo
to add a favorite. A favorite is similar to the concept of a bookmark or link to the page to which
you want to return; however, this favorite is stored on the Plone site. Figure 4-10 shows the
icon to add a favorite.
Figure 4-10. The icon to add in a favorite
The favorites are added to the user’s home folder, and they display in the favorites portlet along
with a link to organize them (see Figure 4-11). The favorites that are shown are particular to
the favorites that user has saved, so even if you have this item in the portlet list, it won’t show
up unless the user has some favorites.
Figure 4-11. Favorites portlet
The expression to add is here/portlet_favorites/macros/portlet.
4.1.3.1.4 Login
The login portlet displays the login form so a user can log in using their username and pass-
word. If they’ve forgotten their password, they have an option to get their password e-mailed
to them. Even if you have this item in the portlet list, however, it won’t show if the user is
already logged in (see Figure 4-12).
86
Figure 4-12. Login portlet
The expression to add is here/portlet_login/macros/portlet.
4.1.3.1.5 Navigation
The navigation portlet shows a simple tree of the folders in the current position in the form of
a tree. It provides a powerful and simple navigation tool. The navigation portlet is extremely
customizable; you can alter it by clicking portal_properties and then clicking navtree_properties
inside the ZMI, which is covered in the ’Altering the Navigation Portlet“ section (see Figure
4-13).
Figure 4-13. Navigation portlet
The expression to add is here/portlet_navigation/macros/portlet.
4.1.3.1.6 News
The news portlet lists all the recent news items, with links to them (see Figure 4-14). Even
if you have this item in the portlet list, it won’t show up unless there are some news items
published. The news items on a site are also available by clicking the news tab.
87
Figure 4-14. News portlet
The expression to add is here/portlet_news/macros/portlet.
4.1.3.1.7 Recent
The recent portlet lists the recently published items on the site since the last time you logged
in (see Figure 4-15). If there are no such items, it’ll still display.
Figure 4-15. Recent portlet
The expression to add is here/portlet_recent/macros/portlet.
4.1.3.1.8 Related
The related portlet shows a list of items that are related to the item you’re currently viewing,
as determined by the keywords on that item. If a related item is a link to another Web site,
88
then it’ll show up in a separate list of external resources. Even if you have this item in the list
of portlets, it won’t display unless there are some related items (see Figure 4-16).
Figure 4-16. Related portlet
The expression to add is here/portlet_related/macros/portlet.
4.1.3.1.9 Review
The review portlet displays a list of items that are in the review state and are waiting to be
reviewed. This is shown only if the user logged in has the reviewer role and there are items
awaiting review (see Figure 4-17).
Figure 4-17. Review portlet
The expression to add is here/portlet_review/macros/portlet.
4.1.3.1.10 Book Web Site: Altering Slots
For the Plone book site, most of the slots on the right side made no sense. This book has no
events, so the calendar and event slots weren’t needed. I expect new things to be added to
the site, but really they’ll be minimal once the site is complete. So I decided to remove all the
right slots for my site. I did this by going to the portal root object in the ZMI and clicking the
Properties object. Then I deleted right slots. The navigation, login, and related portlets, which
normally occur on the left side, are all useful to me, so I kept those.
Here’s how the portlet properties for the Plone book site look at this point:
89
4.1.3.1.11 Having Different Portlets in Different Parts of Your Site
The underlying Zope database of Plone provides a feature called acquisition. In its simplest
form, this means when looking up an item, such as right_slots, Plone finds the closest object
that contains the property. So, when looking for what portlets to show in the right column,
normally Plone finds the root object and lists those.
That’s why you can change the properties in the root portal object to change the whole site.
You may notice that if you click the my folder link and go to your personal folder, there’s no
calendar. If you click Members and then click Properties in the ZMI, you’ll see that there’s an
entry for right_slots. The entry for that folder is an empty list. When the Plone site goes
looking for a value to show for the right column’s portlets, it moves up the folder hierarchy
until it reaches the Members folder. There it finds the right_slots value and uses that. Since the
value of right_slot in the Members folder is empty whenever you’re viewing content located in
Members, the right slot will be empty.
By adding and removing properties from folders through the ZMI, site administrators can cus-
tomize exactly what portlets appear on their sites. The properties tab is fortunately reasonably
straightforward. You just select the item in the ZMI and then click Properties. To add a left or
right slots property, use the add form at the bottom of the form and ensure that the property
type is list.
4.1.3.2 Altering the Navigation Portlet
Of all the portlets covered, probably the most useful and the most asked about is the navigation
portlet. Specifically, how can you alter the navigation portlet and the way that it’s displayed?
The navigation portlet is list of current folders and documents in the navigation slot. You can
alter the navigation slot by changing the code; however, you can make many changes through
the ZMI. The biggest thing to remember is that only published objects will be shown in the
navigation tree to members and anonymous users. To alter the navigation tree properties,
click portal_properties and then click navtree_properties in the ZMI.
The following is an abridged list of the options available:
• showMyUserFolderOnly: This displays only the user folder of the user logged
in. So when the Members folder is being shown in the navigation, it won’t
show every Members folder. This is selected by default.
90
• showFolderishSiblingsOnly: Only folders in parent folders will display if this
is selected; otherwise, it will show all content. This is selected by default.
• showFolderishChildrenOnly: When this is enabled on a folder, it shows only
folders in the same folder rather than all the other types of content. Select-
ing this option effectively shows you all the contents of the folder currently
viewed. This is selected by default.
• roleSeeUnpublishedContent: As mentioned, content is shown only if pub-
lished to members and anonymous users. Add more roles to this list, with
each role on a new line, to have nonpublished content display. This is unde-
sirable if the user doesn’t actually have access anyway.
• croppingLength: This determines how many characters of the names to show
in the navigation tree. The default is 256.
• idsnotToList: This is the item IDs not to show. Place each ID on a separate
line. The default is None.
Make any changes to that form, and then click Save Changes. The order of the items in the
navigation tree is determined by the order of the items in the folder contents form. As shown
in Chapter 3, using the up and down arrows, users can change this order to suit their own
needs.
4.1.3.3 Book Web Site: Altering the Navigation Tree
For most Web sites I prefer a fuller navigation tree than the one provided by default. So, I went
to the navigation tree options and deselected showFolderishChildrenOnly and showFolderishSi-
blingsOnly. This made the contents show up nicely; for example, here’s the software folder
with just a few items selected:
4.1.3.4 Altering the Date Formats
Throughout the portlets and the whole site, Plone presents dates in a consistent format that
are editable using formats set internally. Whenever a date is shown in Plone, it calls one of
two formats. You can find these formats by accessing the ZMI, clicking portal_properties, and
clicking site_properties. These are the formats:
91
• localTimeFormat: This is the time format to use for dates that should appear
in a short format in Plone.
• localLongTimeFormat: This is the time format to use for dates that should
appear in a long format in Plone, showing seconds.
The format for the date is based on Python’s time format module. The reference for the formats
are at http://www.python.org/doc/current/lib/module-time.html. For the short date, the default
value is %Y-%m-%d, which means year-month-day as decimal numbers (for example, 2003-10-
26). For long date, the default value is %Y-%m-%d %I:%M %p, which means year-month-day
hours:minutes am/pm (for example, 2003-10-26 07:32 PM).
The following is a quick summary of the options available:
%a: Locale’s abbreviated weekday name (for example, Mon)
•
%A: Locale’s full weekday name (for example, Monday)
•
%b: Locale’s abbreviated month name (for example, Jan)
•
%B: Locale’s full month name (for example, January)
•
%d: Day of the month as a decimal number
•
%H: Hour (24-hour clock) as a decimal number
•
%I: Hour (12-hour clock) as a decimal number
•
%m: Month as a decimal number
•
%M: Minute as a decimal number
•
%S: Second as a decimal number
•
%y: Year without century as a decimal number
•
%Y: Year with century as a decimal number
•
If you want to include the day name in the short date, it’s a simple matter of
changing the short date format to read %A, %b. %d, %y. This produces Thursday,
Oct. 24, 02. These dates are used in the boxes on the left and right of the screen,
in the search results, in the content byline, and so on.
4.1.3.5 Adding Keywords and Event Types
One of the tools in Plone, portal_metadata, allows the site administrator to define some of the
metadata elements. Plone uses the metadata defined in the portal_metadata tool in several
places.
For example, when you add an event, you’re given a list of possible types of events. You can
add to this list by clicking portal_metadata, clicking elements, and then clicking subject in the
ZMI. You’ll see a vocabulary for events that lists the subjects for that content type. It’s a simple
matter of adding or editing that list, one item per line, to have the relevant event types. These
event types will then appear in the forms for adding and editing events.
Another use of portal_metadata is the selection of keywords available on a site. On the form
at portal_metadata/elements/subject, you’ll also see a vocabulary form for a content type of
<default>. If you add items to the Vocabulary field of that page and click Update, you’ll add
these to the list of available keywords for every content type.
92
If you want keywords to appear for, say, only documents, then use the add form at the bottom
of the page. Select a content type, and add some vocabulary, one value for each line. These
will then become keywords that users can select for that piece of content only.
If you’re logged in as a user with the manager or reviewer role, clicking the properties tab of
an object in the Plone interface will display a New Keywords box for the addition of ad-hoc
keywords. These keywords won’t appear in the portal_metadata vocabulary but will appear on
all types of content for other users to enter.
4.1.3.6 Changing the Default Page
As discussed in Chapter 3, when a user is viewing a folder, the default page for that folder is
shown if present. In the old versions of Zope and Plone, the name for that default page was
index_html. You’ll see these a lot in Plone sites, where Web site addresses often have index_html
on the end. If you made this filename an extension that’s more commonly recognized as
index.html, then it’d be easier to edit using editing programs and Web site tools.
In Plone you can define a list of pages that will be looked up to be rendered as the default page
(see Figure 4-18). The default pages are index_html, index.html, index.htm, and FrontPage.
You set the list of pages in portal_properties/site_properties/default_page property, one name
per line. When the default page is looked for, Plone will look for each page in that list, starting
with the first until it finds one that matches. Further, if you’d like to change the value for a
folder only, you can access the folder through the ZMI, click the properties tab, and then add a
new list property called default_page.
93
Figure 4-18. Making index.asp the first default page
4.1.3.7 How Can You Make the News Items List the Default Page?
Exactly how this works involves knowing the underlying machinery a little too much. For now,
go to your portal root and click Properties. Then you need to go to the bottom of the page,
complete the add new property form with the following information, and then click the Add
button:
For the Name field: default_page For the Value field: news For the Type field:
lines
Now return to your Plone site. Instead of the standard home page, you’ll see the news page.
The news tab will still show you the news, as well, but in the following sections I’ll show how
to remove that.
4.1.3.8 Altering the Site Tabs
In a Plone site various tabs refer to different sections or parts of a site. Using tabs is a familiar
concept in Web site design and is common in sites such as Amazon, MSN, and Plone sites.
94
Two main types of tabs exist: portal tabs and content tabs. The portal tabs are blue and appear
at the top of the Plone site. The default ones are home, news, and members. The following
sections show how to customize these. The content tabs are green and appear when an item
can be edited. The content tabs, as the name suggests, are related to content. Chapter 11
covers how to alter these tabs. The tabs you see in a Plone site are formed by a collection
of actions, so to understand how to modify these tabs, you’ll take a quick look at actions in
general.
4.1.3.8.1 Introducing Actions
In Plone certain people can perform certain tasks at different times in different parts of the
site. These various tasks are called actions. Plone translates them into tabs, links, and other
elements. They’re a highly configurable way of providing navigational elements for a site.
Each action has the following properties that can be configured in the ZMI. Exactly where
you configure them depends upon where the action is stored. The following is a list of the
properties for a default action:
Name: This is a user-friendly name for the action. This name is often used in the
user interface. For example, when the action is used as a tab, this value is the text
in the tab.
Id: This is a unique ID for the action.
Actions: This is the action that’s to be performed. For example, when the action is
used as a tab, this action is used as the link. This field is a TALES expression (see
Chapter 5 for more information).
Condition: This is a condition that has to occur in order for the action to be used.
For example, when used as tab, if this condition is met, the tab will appear. This
field is a TALES expression (see Chapter 5 for more information).
Permission: This is the permission the user has to have in order to have this action.
This permission has to be met in order for the action to be used (see Chapter 9 for
more information on security).
Category: This categorizes the actions. In Plone this distinguishes the actions so
they’re used in different sections of the user interface. For portal tabs, the category
value is portal_tabs.
Visible: This indicates if the category is active. Since actions usually relate to visual
elements, the term visible is used.
4.1.3.8.2 Introducing the Top Tabs
In the following sections, you’ll alter the portal tabs in two different ways as an example. You’ll
change the home tab to say welcome, and you’ll move the members tab to the left of the news
tab. The actions for the portal tabs are stored in the portal_actions tool, so to alter these, click
portal_actions in the ZMI. As shown in Figure 4-19, this will open a large list of portal actions
present by default. Some of these actions will seem familiar in that they represent parts of the
Plone site.
95
Figure 4-19. The portal actions for your Plone site
Scroll through the actions until you find the Home item, and change the Name field to wel-
come. Then scroll down to the bottom of the page, and click Save. Returning to the Plone
interface, you’ll now notice that it says welcome on the tab.
The order of the tabs from left to right on the page is set by the order from top to bottom in
the list of actions. So, to move the tabs, it’s a matter of checking the tab and then scrolling
to the bottom of the page to the Move Up and Move Down buttons. It’s a little tedious, but
by repeatedly checking the actions and then using the up and down buttons, you can alter the
order. Do this, and you’ll note that the tabs now appear in a different order on your Plone site.
96
4.1.3.8.3 Why Is the Text in Lowercase?
Plone changes the case for many features, such as tabs, to lowercase in the style sheet. To
turn off this option, you can alter the style sheet, which is discussed later in this chapter in the
’Changing Images and CSS” section.
4.1.3.8.3.1 Book Web Site: Adding a New Tab
A nice navigation helper is to add a tab or remove one in the portal tabs. So, in this sidebar,
you’ll add a tab that points to the Software folder, and you’ll remove the news and members
tabs (which in my site is pointless). Return to the ZMI, and click portal_actions. Scroll to the
bottom of the form to the add form. I filled out the form with the following values:
• name: Software
• id: software_tab
• action: string:$portal_url/Software
• permission: View
• category: portal_tabs
• visible: selected
Further, I found the actions for news and members and deselected Visible (not forgetting to
hit Save, of course). Returning to the Plone interface, you’ll now see those new tabs. The key
value here is Action, which is a TALES expression. These are discussed fully in Chapter 5. The
Action value points to the URL of the folder you’re pointing to; in my case, it’s Software and is
in the root of my Plone site. Hence, the expression is string:$portal_url/Software.
4.1.3.8.4 Altering the Icons for a Document
If you’re looking at a list of links or options in a Plone site, then chances are that a series of
actions are producing that list. If it’s not actions, then it’s code, but many of the key features
of the Plone interface are generated dynamically from settings in the ZMI. Two other examples
of actions are the document actions and site actions.
The site actions appear in the top-right corner and are links to change the size of the text.
These links could be anything but just happen to reference some client-side script functions.
These links are again configured in portal_actions and are just actions that have a different
category. If you look through the actions in portal_actions, you’ll see these three actions at the
bottom of the page. They have the category site_actions. If you want to remove them, just
uncheck the Visible option. The icons come from the portal_actionicons tool, which is another
simple tool that maps the icon to the action. Looking in portal_actionicons, you’ll see a match
for normal_text of site_actions that matches up an icon (see Figure 4-20).
97
Figure 4-20. Site actions
Similarly, document actions are in portal_actions and have the category document_actions. You
can again edit the order, icons, and text and add or remove icons from the interface all through
editing those actions.
4.1.3.9 Changing Images and CSS
The look and feel of a Plone site is a big subject that takes three chapters of its own, Chapters
5-7. The following sections cover the basics and, rather than trying to explain everything, just
show how to quickly make a few changes.
A skin is a series of CSS, images, templates, and scripts that come together to form a look and
feel for the user. The idea of a skin is that you can change the skin and hence change the look
and feel of a site without having to change the content.
4.1.3.9.1 Changing the Skin
You can change the default skin for a site using the portal skin form, which is accessible from
the control panel. You can represent a Plone site in a few different ways by applying different
colors, style sheets, and templates to a site.
98
The portal skin form provides the following three choices:
• Default skin: This is the default skin to show to a user when they access the
site. There is only one skin that’s given by default, which is Plone Default.
• Skin flexibility: This sets whether you’re going to allow users the choice of
choosing their skin. If this is enabled, a user can go to their preferences and
choose a new skin. This is enabled by default.
• Skin cookie persistence: If a user can select a skin, then select this to have
the cookie last indefinitely. This means that a user will always see this skin
when logging into a site. This is disabled by default.
Select the changes you’d like to make, and click Save to commit the changes. To improve
performance sites, use image and style sheet caching. To ensure that you’re seeing the new
skin as it should be, clear your browser’s cache (on Internet Explorer, pressing Ctrl+F5 will do
this).
4.1.3.9.2 Setting a Different Logo
Changing the logo of a Plone site from the Plone logo is a simple operation, but the steps can
get a little confusing, so you should following them carefully.
First, access the ZMI, click portal_skins, click plone_images, and then click logo.jpg. This will
open the page for that object. It should look something like Figure 4-21.
99
Figure 4-21. The default logo
This object represents the logo as it’s used in Zope. In Figure 4-21 you can clearly see infor-
mation about the image, its size, its type, and its location on the file system. In the middle of
the page is the Customize button; click it. This will create a copy of the object called logo.jpg
in the custom folder (see Figure 4-22).
NOTE If at this point you get an error message about a bad request, return to por-
tal_skins/custom, and you’ll see an object called logo.jpg. Click that object. There can be
only one object called logo.jpg in the custom folder, and the error is warning you that this
procedure has been performed already. If you want to customize the original object (in other
words, repeat these steps), you’ll have to delete the object inside custom.
Figure 4-22. The customized image
This page may look similar to the previous page shown in Figure 4-21, but there are a couple of
differences. First, if you look in the top-left corner of the page, you’ll see that the meta_type and
location of this object has changed. No longer are you in portal_skins/plone_images/logo.jpg;
rather, you’re in portal_skins/custom/logo.jpg. Second, you’ll now see a Browse button that lets
you select an image and upload it, meaning you can change this image. Click that button to
find your new image, and click Save to commit the changes. In Figure 4-23, I’ll add a Canadian
Plone logo as an example.
100
Figure 4-23. The Canadian Plone logo
Now return to the Plone interface, and you’ll see that the image has changed. To ensure you’re
seeing the new image, clear your browser’s cache (on Internet Explorer, pressing Ctrl+F5 will
do this).
4.1.3.9.3 What If Your New Image Isn’t in JPG Format?
Zope doesn’t base the Multipurpose Internet Mail Extensions (MIME) type on the extension
but rather on the content. So, you can upload a GIF image into logo.jpg, and it’ll still work
since the correct MIME type of image/gif is applied. However, you may want to rename the
image to logo.gif or logo.png to be less confusing.
4.1.3.9.4 Changing the CSS Code
CSS determines the majority of the look and feel of your site, including the tabs, the images,
the boxes, and the overall layout. The fact that Plone’s CSS is totally customizable means that
from a few style sheets users can completely customize many aspects of a site.
101
Again, Chapter 7 covers what all the elements do; in this section, I’ll quickly show you how to
change the CSS code for a Plone site. First, access the ZMI, click portal_skins, click plone_styles,
and then click ploneCustom.css. This opens the page for that object. This style sheet is actu-
ally straightforward; in fact, it’s empty. Plone is using the cascading property of CSS. Because
the Hypertext Markup Language (HTML) for Plone first imports plone.css and then ploneCus-
tom.css, any changes to the latter overrides the standard style sheet. Why is this a good thing?
It means you can make small incremental changes to ploneCustom.css without breaking or
altering the core style sheet.
So, to customize the ploneCustom.css object, click portal_skins, click plone_styles, and then click
ploneCustom.css. Next, click the Customize button. Again, this object has been customized,
and instead of being at portal_skins/plone_styles/ploneCustom.css, you’ll notice you’re now at
portal_skins/custom/ploneCustom.css. Because file objects can now be edited through the Web,
you can directly edit the style sheet through the Web.
As an example, make the background have an image in the middle of it (this isn’t necessarily
the best user interface, but it’s a clear example of how to customize the CSS code). First, you
need to upload an image to Plone. To do this, click portal_skins, click custom, click the Add
button, and then select Image, as shown in Figure 4-24.
Figure 4-24. Adding the new image
For the file I chose an image I found on the Web (which is also available on the Plone book
Web site), but you could choose any image you have. Make sure that the ID of the image is
background.gif, as shown in Figure 4-25.
102
Figure 4-25. Checking the new image
Second, you need to change the CSS code to point to the new image. You’ve already cus-
tomized the CSS code, so return to portal_skins/custom/ploneCustom.css and change the text
from this:
/* DELETE THIS LINE AND PUT YOUR CUSTOM STUFF HERE */
to the following:
body {
background-image: url(background.jpg);
background-repeat: no-repeat;
background-position: center;
}
Click Save Changes to commit changes to this file. Then return to the Plone interface. If all
went well, you should see the new image (see Figure 4-26).
103
Figure 4-26. The new background image
5 Chapter 5
5.1 Introducing Basic Plone Templating
Plone puts together three layers of technology to create a page. Python and page templates
create some Hypertext Markup Language (HTML), which is sent to the browser. There, some
Cascading Style Sheets (CSS) render the nice page with which you’re now familiar. Those two
first elements, the Python code and the page templates, are the main areas of discussion in this
chapter and Chapter 6.
To understand how to generate and then edit a Plone template, you have to first learn about
some key underlying concepts. Some of these concepts are particularly unique to Plone, and
although they provide great advantages, it does take some time to get used to them.
In this chapter, I’ll start by covering object publishing. I’ll explain how to interact with objects
inside Plone. Then I’ll explain how to build expressions. Once you’re familiar with those two
concepts, I’ll cover how Plone pages are actually put together. At the end of the chapter, you’ll
create a new page inside your Plone site that uses the techniques you’ve learned so far.
This chapter will make more sense if you’re familiar with Python. However, at each stage
I’ll explain the concept behind the code, so even if you don’t understand Python, you should
be fine. The rest of the book references the Template Attribute Language Expression Syntax
(TALES) and Script (Python) objects, so you should take the time to get familiar with them in
this chapter. You should have a head start: I already introduced TALES in the previous chapter
because it’s used for generating portlets and actions.
5.1.1 Understanding the Underlying Templating Machinery
Diving straight into how Plone templating works would likely leave you confused, so I’ll start
by going through the underlying templating machinery. In an ideal world, this is something
you shouldn’t have to worry about, but in practice I’ve found that it’s the first block people hit
when trying to learn to use Plone.
Plone is rather unique in that everything in Plone is an object. If you’re unfamiliar with the
concept of an object, there isn’t much to know; an object is just a ’thing” that encapsulates some
behavior. Each object has methods that you can call on the object. One example is a computer
mouse. A computer mouse could have methods such as move, click, and right-click.
In Plone, a document is an object of a particular type. All this means is that the document
isn’t just a static bit of text; instead, it’s something a little more complicated and far more
104
105
useful. A document in Plone has a description method, for example, that will give you the
description that the user added. When using the templating system, you’ll see in more detail
that everything is an object. You’ll look first at some of the basic principles of object publishing.
5.1.1.1 Introducing Object Publishing
In Plone, you’re actually publishing objects that are located in Zope; most of them are objects
that are persisted in the object database. The concept is more complicated than standard
Common Gateway Interface (CGI) environments, where a script is executed and passed a
series of request variables. Everything in Plone is an object, everything in Zope is an object,
and everything in Python is an object. Until now I’ve tried to avoid using the word object;
instead I’ve used words such as template, script, and item, but all of these are really objects—
just ones that behave differently.
When you request a Uniform Resource Locator (URL) from Plone, an object in the
environment is called. Plone does this by translating the URL into a path. So, if the
URL is /Plone/login_form, what Plone is going to do is break that URL down into
a path and look up each of those objects in the database. It’s going find the Plone
object and then a login_form object inside the Plone object. Looking up this path is
called traversal; essentially, Zope traverses across those objects and then calls the
last one in the path.
When Zope calls the login_form object, the object is executed in its context. The term context
is something you’ll hear a lot of in Plone. It’s merely the current context of the object being
executed. In this case, it’s /Plone. The context changes a lot as you move through a Plone
site. If you called the URL /Plone/Members/login_form in a browser, then the context would
be /Plone/Members.
As mentioned, traversal is how you can programmatically access objects in Plone in the same
manner as you do in a URL. This is similar to accessing items in a file system—if you wanted to
access a picture in My Documents on Windows, you’d enter a directory such as c:Documents
and SettingsandymMy DocumentsMy Portrait.jpg. You could access an object in Plone by
entering Members/andy/My Portrait.jpg. This would work if you had a series of folders and
objects that looked like the following:
Members
|_ andy
|_ My Portrait.jpg
In the file system version, you go through the computer’s hard drive directory by directory. In
Plone, the same thing happens; it’s just that Members and andy are objects.
One catch is that Zope is case sensitive. In Windows, you can type My Portrait.jpg or my
portrait.jpg. That won’t work in Plone, however; you have to provide the same case as the
object ID. For this reason, it’s recommended that you try to keep all URLs lowercase so your
users have less chance of making a mistake.
Plone and Zope have added a twist, called acquisition, to this whole publishing system. The
concept behind acquisition is one of containment: Objects are situated inside other objects
106
called containers. In the previous example, the andy object is a container inside the Mem-
bers container inside the Plone site container (which in turn is inside the Zope application
container).
In a standard object-oriented environment, an object inherits behavior from its parent. In
Plone and Zope, an object also inherits behavior from its container. An object goes through a
container hierarchy to figure out how to get these behaviors.
So, take the example of accessing Members/andy/My Portrait.jpg. What if the object Some
Image.jpg didn’t exist in the andy folder but instead exists higher up in the hierarchy? Well,
acquisition would find it for you. Take the following hierarchy:
Members
|_ andy
|_ My Portrait.jpg
In this case, if you executed the URL, Plone would traverse to andy and then try to find My
Portrait.jpg*—but, sure enough, it doesn’t exist in the container. So, it’d look in the containment
hierarchy, which is the *Members folder, and it finds and returns My Portrait.jpg. The result is
that the user will see the image, just like usual.
However, if you compare this to the earlier example where the image was contained in the
andy folder, you’d find that the following key differences exist:
• First, the context is the same, even though the object is in a different place.
Context is based on the location from where the object is called.
• Second, the container is different, and the container of My Portrait.jpg is now
different. It’s Members, not andy.
So, what’s the point of all this? Well, you can now put an object in the root of a Plone site, and
any object can get to it because it’s looked up through acquisition.
Although this probably makes sense, acquisition can be quite complicated, especially look-
ing through the context hierarchy (which can occur). If you want to learn more about
it, you can read Zope lead developer Jim Fulton’s excellent discussion of acquisition at
http://www.zope.org/Members/jim/Info/IPC8/AcquisitionAlgebra/index.html.
5.1.1.2 Introducing Template Expressions
Before diving into the Zope Page Templates system, you must understand TALES. Often in an
application you need to write expressions that can be evaluated dynamically. These aren’t
scripts; rather, they’re one liners simple expressions that can do something simple and easy in
one line of code.
An expression is evaluated with a series of local variables passed into it. These variables are
determined by what’s calling the expression. Workflow passes one set of variables in, and
the Zope Page Templates system passes another. For the moment, I’ll use examples that have
context. Remember, as discussed, the context is the context in which an object is requested.
107
So far you’ve seen some TALES expressions, such as string:${portal_url}/Software. However,
this is merely one example of a wide range of expressions. The main use of TALES is in Zope
Page Templates, the HTML generation system for Plone. Although its name may suggest it’s
suitable only in templates, many tools in Plone use this syntax to provide simple expressions,
such as actions, workflow, and security. Different kinds of expressions exist, and I’ll run through
them one by one.
5.1.1.2.1 Using Path Expressions
The path expression is the default and most commonly used expression. Unlike all the other
expressions, it doesn’t require a prefix to denote the expression type. The expression comprises
one or more paths. Each path is separated by the pipe symbol (|). Each path is a series of
variables separated by forward slashes (/). The following are some simple examples:
context/message
context/folderA/title
context/Members/andy/My Portrait.jpg
When the expression is evaluated, the path is split on the forward slashes. It then starts at the
leftmost value and traverses to find that object, method, or value. It then places that object
on the current stack and moves onto the next value; it repeats that process until it reaches the
end of the expression or can’t find a matching value. If the object it finds is a Python dictionary
or mapping object, it’ll call that value of the dictionary. One nice feature of a path expression
in that the only restricted character is /, so names can contain spaces and periods and still be
evaluated.
When the end is reached, it’ll call that object (if it can be called). If it’s a noncallable object,
it’ll get the object’s string value, and this is what will be returned. If at any time there’s an
error in this lookup (the most common being that the requested attribute doesn’t exist), then
it’ll move onto the alternate expression, if there is one. You can specify an alternate expression
by separating it with a pipe symbol.
For example:
context/folderA/title|context/folderB/title
The previous example will render folderA’s title if it exists or folderB’s title if the first one doesn’t
exist. It’ll repeat this process for each expression, until there are no more expressions or until
one of them evaluates successfully.
5.1.1.2.2 Using Not Expressions
A not expression has the prefix not: at the beginning and simply inverses the evaluation of
the TALES expression that follows the prefix. Because the Zope Page Templates system doesn’t
have an if statement, you can use this to test for the opposite of a previous condition.
For example:
not: context/message|nothing
108
5.1.1.2.3 Using Nocall Expression
By default, when a path expression reaches the last item in the path sequence, it calls the item,
if possible. The nocall: prefix prevents this from happening. A nocall expression is rarely used
in Plone, but it does have occasional uses. For example, you can use it to reference another
object but not render it. Here’s an example:
nocall: context/someImage
5.1.1.2.4 Using String Expressions
String expressions allow you to mix up text and variables into one expression. All string
expressions start with the string: prefix. This is a useful function, and you’ll see it used quite a
bit. The text can contain anything that’s legally allowed inside an attribute, which essentially
includes alphanumeric characters plus spaces. Contained inside the text can be variables,
prefixed with a dollar sign ($). Here are some examples:
string: This is some long string
string: This is the $title
In the latter example, the variable $title is evaluated. The variable can actually be any path
expression. If the variable contains /, then the variable has to be wrapped with {} to signify
the start and end of the expression.
For example:
string: This is the ${context/someImage/title}.
If a dollar sign in the text needs to be escaped, use another dollar sign immediately before the
dollar sign you need to escape.
For example:
string: In $$US it costs ${context/myThing/cost}.
5.1.1.2.5 Using Python Expressions
Python expressions evaluate a line of Python code. All Python expressions start with a python:
prefix and contain one line of Python.
For example:
python: 1 + 2
The Python code is evaluated using the same security model that a Script (Python) object uses,
as discussed in Chapter 6. For these reasons, Python should be simple and limited to presenta-
tion functionality, such as formatting strings and numbers or performing simple conditions.
Further, almost all the other TALES expressions mentioned can be wrapped in Python and
called. The following are the expressions:
109
• path(string): Evaluates a path expression
• string(string): Evaluates a string expression
• exists(string): Evaluates a string expression
• nocall(string): Evaluates a nocall expression
For example, the following code:
python: path(’context/Members’)
is equivalent to the following:
context/Members
A few convenience functions have also been added to assist developers. The test function takes
three parameters: a statement to evaluate and the true and false conditions. The statement is
evaluated, and the appropriate value is returned. For example:
python: test(1 - 1, 0, 1)
The same_type function takes two variables and compares if they’re the same. For example:
python: same_type(something, ’’)
Some developers discourage using Python inside the Zope Page Templates system because it
means adding logic in the presentation templates. Often, as a developer, for each piece of
Python added, it can be useful to ask yourself if that piece of code would be better factored
out and placed in a separate Script (Python) object. This doesn’t mean you should move every
piece of Python out—just think about it before adding anything.
5.1.1.2.6 Book Web Site: Revisiting Actions
In Chapter 4, you added an action for pointing to the software part of the site so it appeared as
a portal tab. In that action, you added in the string expression string: ${portal_url}/Software.
This may make a bit more sense now that I’ve explained the variable portal_url. This is the
URL to your portal, which may vary depending upon if you’re using virtual hosting. It does
this by using acquisition to acquire the portal_url object and insert the resulting value into the
string. The result is that you’ll always get an absolute link to the Software folder.
5.1.1.2.7 Gotcha: Mixing Python and Strings
I’ve seen newcomers mixing up Python and strings a few times. All the expressions are dif-
ferent. In other words, you can’t place path-like expressions inside a Python expression. For
example, the expression python: here/Members/absolute_url + ”/danae“ doesn’t make sense.
The entire expression is interpreted as Python, so Plone will try to divide one thing by another,
and you’ll get errors. This is an ideal situation to use a string expression (which lets you do
variable substitution), so the variable contain a path expression. So, you could use string:
${here/Members/absolute_url}/danae.
110
5.1.2 Using the Zope Page Templates System
Now that you understand object publishing and expressions, you can get into the real meat of
the system, Zope Page Templates. This is the templating system that Plone uses for generating
HTML.
Many HTML generation systems are available, and some of the better known are JavaServer
Pages, Active Server Pages, and PHP To users of the other systems, the Zope Page Templates
.
system at first looks rather odd, but quickly you’ll see it’s an extremely powerful system.
The simplest template looks something like the following:
<p tal:content="here/message">The title</p>
If the value of message resolved to Hello, World! then the following would be output when the
template was rendered:
<p>Hello, World!</p>
For a moment I’ll gloss over a few of the finer points and show what has happened here.
A standard paragraph was written in HTML, yet the content of that paragraph isn’t the text
shown in the output. To the opening paragraph tag, a tal:content attribute was added, and
the here/message expression was written for that attribute. The content of the paragraph was
output, however, as the value of the message variable (in this case, Hello, World!).
At run time, the template is evaluated, and the tal:content attribute is called. The tal part stands
for Template Attribute Language and has a range of commands, including content. You’ll see all
these commands later; with them, you can do almost anything you want to do the HTML tags.
You can create loops, alter tags, alter attributes, remove tags, and so on. Before the template
runs, this will show up as valid Extensible HTML (XHTML) and will show up in an editor as a
paragraph with that text.
All these page templates are valid XHTML. This is a standard for HTML and is valid Extensible
Markup Language (XML) code. This means you must follow these rules:
• All tags must be lowercase.
• Attributes must always be quoted (for example, <input type=”checkbox“
checked=”1“ />).
• Empty elements must be terminated (for example, <br />, not <br>).
To define a page as XHTML, you must give a DOCTYPE declaration and use the XML namespace
set in the html tag. Plone uses the following declaration at the top of every page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
For more information on the XHTML specification, go to
http://www.w3.org/TR/xhtml1/#xhtml.
111
5.1.2.1 Another HTML Generation System?
In the first few years of the Web, programmers were the prime creators of HTML. Programmers
rapidly threw together systems to generate HTML programmatically so they could get on with
their real jobs. With tools such as Perl’s CGI modules, programmers could write complicated
server-side code for content.
However, soon everybody was generating content, and the process had to be made easier. This
brought about the wave of escape coding languages. These languages used a special kind of
HTML markup that was processed to produce output. As mentioned, some of the most popular
are Active Server Pages, JavaServer Pages, and even whole languages based on the concept,
such as PHP Zope followed this trend with Document Template Markup Language (DTML).
.
These systems take HTML and intersperse it with custom tags such as <% .. %> or <dtml-...
/>. This system was popular because it was easy to understand, and users who already knew
basic HTML could grasp the idea of a few more tags. Designers could ignore the content of
these tags and let the programmers deal with them. Programmers could alter the relevant code
parts without upsetting the content.
However, these systems have the following problems:
• The HTML templates can be hard to scale as more and more content gets
added to the script. Pages quickly become huge and hard to manage.
• Logic and content aren’t neatly separated. They can be separated with some
of these systems; however, the ability to intersperse any HTML with a piece of
programming code is too easy. Often, content, presentation, and logic become
one large, entangled mess.
• Pages can’t be easily edited. Often pages or templates come with the note ”just
leave these bits alone...“cause editing them would break the code. What You
See Is What You Get (WYSIWYG) editors can be set to not alter some tags, but
they can easily break others. In large organizations, users with different roles
all have to edit the same page.
• It can be hard to see a default result. Take, for example, a database query
that shows the result in a table. How can a designer see how that would look
without actually running the code?
For these reasons, the Zope Page Templates system was created. Page templates present a novel
approach; instead of providing another method of escape coding, code is added to existing tag
attributes. Not only is the Zope Page Templates system free and open source, it doesn’t require
Zope. Currently, versions of the system exist in Python, Perl, and Java.
5.1.2.2 Introducing Page Templates and Content
As you’re now aware, Plone is a content management system where users add content to a
Plone site through the Web. Those content objects are stored inside Plone and then rendered
back to the world using page templates.
Returning to the earlier example of accessing /Members/andy/My Portrait.jpg, I’ll now discuss
what actually happens to the content in Plone. First, Plone finds and calls the My Portrait.jpg
112
object; it’s called because there’s no specific method being called on the object. When a content
type is called, a certain template is located and rendered. The context for that template will
be the image you want to access, and the template will be the one for that image.
If a different action was being called on the image, such as /Members/andy/My Por-
trait/image_edit, then the action image_edit would be looked up for that object, and the corre-
sponding template would be returned. Chapter 11 discusses how this works in more detail.
So, in all the templates in Plone, you’ll see a referral to here or context. This is the context of
the content being accessed. In a template, you can now say context/something or other, and
this will be the something or other looked up relative to the piece of content, not the template.
You’ll now create your first template in Plone.
5.1.2.3 Creating Your First Page Template
The standard way to create a page template is through the Zope Management Interface (ZMI).
Unfortunately, because it means editing the template through a text area in a Web browser, the
ZMI is also the most painful to use as a developer. The text area provides limited functionality
compared with most editors; it’s lacking features such as line numbers, syntax highlighting,
and so on. In Chapter 9, I show you how to use External Editor to edit content; this allows
you to edit Web site content in local editors such as Macromedia Dreamweaver or Emacs. In
Chapter 6, I show you how to make Plone read page templates off a hard drive as files, and
then you can use any tool you’d like.
To create a template, go to the ZMI, click portal_skins, click custom, and then select Page
Template from the drop-down box (see Figure 5-1). Click Add, and you’ll see the page shown
in Figure 5-2.
Figure 5-1. Selecting the Page Template option
113
Figure 5-2. Adding a page template
Enter test for the page template’s ID. Then click the Add and Edit button, which takes you
to management screen (see Figure 5-3). You can then edit this template through the Web by
using the text area and clicking Save Changes to commit your changes.
Figure 5-3. Editing a page template
NOTE Before Plone 2, all the page templates passed through the variable here, which is equiv-
alent to context. If you see here in any code in a page template, it means context. The new
context variable was added to be clearer and bring the page templates in line with Script
(Python) objects.
After clicking Save Changes, the page template will be compiled. If you’ve made any errors in
the template, you’ll see them highlighted at the top of the page. Figure 5-4 shows an error with
an h1 tag that isn’t closed. (As previously mentioned, page templates must be valid XHTML.)
114
Figure 5-4. Page template error
Once you’ve saved the page template successfully, you can click the Test tab to see the rendered
value of the template. In Figure 5-5, you’ll see that the heading has been replaced with the ID
of the template, and the main paragraph now includes the ID of the template.
Figure 5-5. Generating the page
The management screen for a page template also has the following important features:
Title: This is the title for this template, and it’s optional. If you change this in
the previous example, for instance, after clicking Test, you’ll note that the resulting
HTML has changed.
Content-Type: This is the content type for this template; it’s usually text/html.
Browse HTML source: This will render the template unprocessed as HTML. This
is how the template would appear if it were loaded into an HTML editor.
Test: This will process and render the template.
Expand macros when editing: This checkbox will try to expand macros. I recom-
mend leaving this unchecked most of the time. Macros are an advanced feature
and are discussed in Chapter 6.
115
Now that you’ve created a page template, you’ll make a few modifications to it. This will
demonstrate the topics covered so far in this chapter. For example, if you want your page
template to demonstrate 1+2, you could add the following line to your page template:
<p>1+2 = <em tal:content="python: 1+2" /></p>
Then click the Test tab to see if it works. You should see the following:
1+2 = 3
To see an example of a path traversal, print the logo of your Plone site. You can include an
expression in the logo of your Plone site by adding the following to your page template:
<p tal:replace="structure context/logo.jpg" />
This will create the appropriate HTML for the image and show it on the page.
5.1.3 Understanding the Page Template Basic Syntax
Now that you’ve seen how to make a page template, I’ll explain the basic syntax of it. You
can break the syntax of page templates into a few different components, which I’ll cover in the
following sections.
5.1.3.1 Introducing Built-in Variables
You’ve seen the expression syntax, so now you’ll learn about the variables that are passed to it
when you render a page template. All of the following happen in the context of accessing the
image Some Image.jpg in the Members/andy folder, called with the URL /Members/andy/Some
Image.jpg:
container: This is the container in which the template is located. With Plone
this is usually the portal_skins folder. You should avoid using a container because
portal_skins can do unexpected things to the meaning of container (for example, a
reference to the andy folder).
context: This is the context in which the template is being executed. In Plone this
is the object being viewed if you’re viewing a portal object (for example, a reference
to the Some Image.jpg object).
default: Some statements have particular default behavior. This is noted in each
of the statements, and this variable is a pointer to that behavior.
here: This is equivalent to context.
loop: This is equivalent to repeat.
modules: This is a container for imported modules. For example, mod-
ules/string/atoi is the atoi function of the Python string module. This includes
116
all the modules that are safe to import into the Zope Page Templates system. For
more information, see ’Scripting Plone with Python” in Chapter 6.
nothing: This is the equivalent of Python’s None.
options: These are the options passed to a template, which occurs when the tem-
plate is called from a script or other method, not through the Web.
repeat: This is the repeated element; see the tal:repeat element in the ’Introducing
TAL Statement Syntax” section of this chapter.
request: This is the incoming request from the client (all the values from the in-
coming request are visible using the following test context script). All the GET and
POST parameters are marshaled into a dictionary for easy access. Here are some
examples:
request/HTTP_USER_AGENT: the users browser
request/REMOTE_ADDRR: the users browser
request/someMessage: the value of some message, in the query string
root: This is the root Zope object. For example, root/Control_Panel gives you the
control panel for Zope.
template: This is this template being called. For example, template/id is the ID of
the template being rendered.
traverse_subpath: This contains a list of the elements still to be traversed. This is
an advanced variable, and it’s recommend you understand traversal and acquisition
before using this.
user: This is the current user object. For example, user/getUserName is the user-
name of the current user.
CONTEXTS: This is a list of most of these values.
NOTE: With the exception of CONTEXTS, any of these variables can be redefined in a tal:define
statement if the user wants. However, this can be confusing for anyone using the code and
isn’t recommended.
The test_context page template shows all the values of these variables, plus the locations of
some of the objects (see Listing 5-1). It can be useful for debugging and explaining the vari-
ables. Add it as a page template called test_context, and then click Test to see the results.
Listing 5-1. test_context
<html>
<head />
<body>
<h1>Debug information</h1>
<h2>CONTEXTS</h2>
<ul>
<tal:block
tal:repeat="item CONTEXTS">
<li
117
tal:condition="python: item != ’request’"
tal:define="context CONTEXTS;">
<b tal:content="item" />
<span tal:replace="python: context[item]" />
</li>
</tal:block>
</ul>
<h2>REQUEST</h2>
<p tal:replace="structure request" />
</body>
</html>
The test_context page template will produce the output shown in Figure 5-6.
118
Figure 5-6. An example of all the default variables in a script
5.1.3.2 Introducing TAL Statement Syntax
The Template Attribute Language (TAL) provides all the basic building blocks for dynamic
presentation. TAL defines eight statements: attributes, condition, content, define, omit-tag, on-
error, repeat, and replace.
Since page templates are valid XML, all TAL attributes must be lowercase. Further, each ele-
ment can have each statement only once. In the following examples, I’ve inserted new lines in
119
the elements to increase legibility; this is perfectly valid code and quite common in the Plone
source. However, this is optional and isn’t required.
5.1.3.2.1 tal:attributes: Changing an Element’s Attributes
The tal:attributes allows you to replace one or more attribute of an element. A statement
contains the attribute to be changed, separated by a space from the statement. For example:
<a href="#"
tal:attributes="href context/absolute_url">
Link to here
</a>
This will change the href attribute of the link to the result of here/absolute_url. The href at-
tribute has already been defined on this element, so if a designer opens this page, the designer
will see a valid element (although the link may not make sense until the page is processed).
Some example output is as follows:
<a href="http://plone.org/Members/andy/book">Link to here</a>
Since each element can have multiple attributes, tal:attributes allows you to alter one or more
attributes simultaneously by having multiple statements. To change multiple attributes at once,
separate statements with a semicolon (;). If the attribute or statement contains a semicolon,
you can escape this with another semicolon immediately after it appears (;;). For example, to
change both the href and title element, do the following:
<a href="#"
tal:attributes="href context/absolute_url;
title context/title_or_id">Link</a>
The example output is as follows:
<a href="http://plone.org/Members/andy/book" title="Plone Book">Link</a>
The tal:attributes and tal:replace tag are mutually exclusive since replace eliminates the ele-
ment. If the Zope Page Templates system detects this, it’ll raise a warning, and it’ll ignore the
tal:attributes tag. If the expression evaluates to default, then no change will be made. For
example:
<a href="#"
tal:attributes="href
python:request.get(’message’, default)">
Link</a>
In this example, I’m using the get function on the request object. If the incoming request to
the page has the message variable, then the first value will be used, which is message. If the
message variable isn’t present, then the second value, default, will be used. Hence, only by
passing the message parameter will a change take place.
120
5.1.3.2.2 tal:condition: Evaluating Conditions
The tal:condition statement allows a condition to be tested before rendering the element. For
example:
<p tal:condition="request/message">
There’s a message
</p>
<p tal:condition="not: request/message">
No message
</p>
Here, the paragraph with the text for a message will be rendered only if the request variable
has an attribute and it resolves to true (ie: the length of message is greater than zero). Being
able to test for a condition is pointless if the opposite condition can’t be tested for; this is
what the not expression allows. The not: prefix inverts the statement, so not: request/message
resolves to true if the request variable message resolves to false (ie: the length of message is
zero). In this case the request variable, message will still need to exist.
In TAL, the following evaluates to false:
• The number zero
• Any float or complex that evaluates to zero (for example, 0.0)
• Strings of zero characters (for example, ”“)
• An empty list or tuple
• An empty dictionary
• Python’s None value
• TALES’s nothing value
The following evaluates to true:
• The default value
• Any number other than zero
• Strings that aren’t empty
• Strings that are just spaces (for example, ” “)
• Anything else
5.1.3.2.3 tal:content: Adding Text
The tal:content statement is probably the most commonly used statement in a page template.
This statement is also one of the simplest, replacing the content of an element with the value
specified. For example:
<i tal:content="context/title_or_id">Some title</i>
121
The example output is as follows:
<i>The title</i>
This will replace the text Some title with the value of the expression context/title_or_id. If the
text to be placed contains HTML elements, those elements will be escaped. By default, the
text to be replaced is HTML escaped; the structure prefix will allow the HTML to be entered
without the elements being escaped. For example:
<i tal:content="structure here/title_or_id">Do not escape HTML</i>
If the element with the tal:content attributes contains other elements, then all those elements
will be replaced. The tal:content and tal:replace tags are mutually exclusive; they can’t both
be placed on the same element, and an error will be raised if this is attempted. If the value is
default, the content is unchanged.
5.1.3.2.4 tal:define: Defining Variables
The tal:define statement allows variables to be created and reused within the template. For
example:
<p tal:define="title here/title_or_id">
... <i tal:content="title">The title</i> ...
</p>
In this example, the variable title is created and assigned the result of here/title_or_id; later the
variable title is used in a tal:content statement. By default the variable is created only locally
within the scope of the current element. So, in the previous example, only elements within
the paragraph tag can use the title variable. You can redefine the variable anywhere within the
statement or reuse it in other elements as many times as needed.
To create a variable to be used globally, you can use the prefix global. This will allow access to
the variable anywhere within the template, not just within the defining element. For example:
<p tal:define="global title string:Foo bar">
... <i tal:content="title">The title</i> ...
</p>
<i tal:content="title">We still have a title</i>
Furthermore, Plone defines a large number of global definitions so that users can easily use
them in their scripts. As with any such definitions, they’re subject to change, so you should
use them carefully. These defines mean a large number of global variables are available. For
example, to get the title of your Plone site, you can just call the following:
<p tal:content="portal_title" />
You can find these defines in the ZMI by clicking portal_skins, clicking plone_templates, and
then clicking global_defines. You can find a full list of all the defines, and an explanation of
them, in Appendix A.
122
5.1.3.2.5 tal:omit-tag: Removing Elements
The tal:omit-tag is rather unusual. It allows the removal of a tag. Because the Zope Page
Templates system requires the use of HTML tags, complicated pages can often need lots of
elements and can result in extra tags being added. For this statement, the tag is removed,
which just leaves the content of the tags. For example:
<p tal:omit-tag="">This is some text</p>
The output is as follows:
This is some text
In this example, the text This is some text will be rendered; however, the tag won’t be rendered.
Optionally, the tal:omit-tag statement can take an expression as an argument. If that expression
evaluates to false, then the tal:omit-tag doesn’t happen. For example, this does nothing:
<p tal:omit-tag="nothing">This is some text</p>
One alternative to using tal:omit-tag is using the tal namespace, as discussed in the ’Useful
Tips” section of Chapter 6.
5.1.3.2.6 tal:on-error: Performing Error Handling
The tal:on-error statement provides a method to handle errors. It acts rather like tal:content
because it causes the content of the tag to replaced, but it’s triggered only when an error
occurs.
The following is an example:
<p tal:content="request/message"
tal:on-error="string: No message">Message</p>
If there’s an error evaluating the request/message expression here, then the on-error attribute
will be activated. This causes the contents of the tag to be replaced with the text No message.
Unfortunately, the on-error statement is rather limited. The tag can’t distinguish between
different errors and allows only one expression to be evaluated and used. This limitation is by
design so that the tag won’t be overused. Error handling should really be handled in the logic
of your application.
Fortunately, for all expressions, you can supply alternatives in the statement if the first part
of the statement evaluates to something other than true or false (in other words, if an error is
raised). Each alternative is separated by the pipe character (|), and multiple alternatives can
appear in a statement. If you’re relying on variables from the incoming request, then always
add a |nothing to the end to ensure that an attribute error isn’t raised.
For example:
123
<p
tal:content="request/message"
tal:condition="request/message|nothing">
There’s a message
</p>
<p tal:condition="not: request/message|nothing">
No message
</p>
This second example is more verbose but desirable for a couple of reasons:
• The designer is able to see the positive and negative condition.
• You can handle a more complicated error condition than just printing a string.
5.1.3.2.7 tal:repeat: Performing Looping
The tal:repeat allows looping through objects and is one of the more complicated statements.
A statement contains the value to be assigned for each iteration of the results, separated by a
space from the results being iterated through.
Here’s an example of looping:
<table>
<tr tal:repeat="row context/portal_catalog">
<td tal:content="row/Title">Title</td>
</tr>
</table>
In this example, the expression here/portal_catalog returns a list of results. Because the repeat
starts on the table’s row tag, for each row in the list of results, a new row in the table will be
created. Rather like tal:define, each iteration of the results is assigned to a local variable (in
this case, row). This example will show one row for every item in the list of results.
You can access some useful variables from the repeat statement, such as the number of the
current iteration. You can access these through the repeat variable, which gets added to the
namespace. For example, to access the current number, you use the following:
<table>
<tr tal:repeat="row context/portal_catalog">
<td tal:content="repeat/row/number">1</td>
<td tal:content="row/Title">Title</td>
</tr>
</table>
The full list of variables available in repeat is as follows:
• index*:* This is the iteration number, starting from zero.
124
– *number*:* This is the iteration number, starting from one.
•
– *even*:* This is true for an even-indexed iteration (for example, 0, 2, 4,
•
...).
– *odd*:* This is true for an odd-indexed iteration (for example, 1, 3, 5,
•
...).
• start*:* This is true for the first iteration.
• end*:* This is true for the last iteration.
– *length*:* This is the total number of iterations.
•
• letter*:* This is the iteration number as a lowercase letter (for ex-
ample, a*–*z, aa*–*az, ba*–*bz, ..., za*–*zz, aaa*–*aaz, and so on),
starting from one.
– *Letter*: This is the uppercase version of letter.*
•
– *roman*: This is the number as a lowercase Roman numeral (*i, ii, iii, iv,
•
v, and so on), starting from one.
Two other values are available in the repeat namespace that are rather unusual and rarely used,
first and last. These two variables allow you to store information about data in the iteration.
By using the value you want to store in an expression, a Boolean value will be returned. For
the variable first, true indicates that this the first time the value has occurred in the iteration.
Likewise, for the variable last, true indicates that this is the last time the value has occurred in
the iteration.
Here’s an example of this:
<ul>
<li tal:repeat="val context/objectValues">
First: <i tal:content="repeat/val/first/meta_type" />,
Last: <i tal:content="repeat/val/last/meta_type" />:
<b tal:content="val/meta_type" />,
<b tal:content="val/title_or_id" />
</li>
</ul>
5.1.3.2.8 tal:replace: Adding Text
The tal:replace statement is similar to tal:content with one difference—it removes the entire
tag.
For example:
<p tal:replace="context/title_or_id">Some title</p>
This will render the result of the expression context/title_or_id but will remove the paragraph
tags from the result. This is equivalent to the following:
125
<p
tal:content="here/title_or_id"
tal:omit-tag="">Some title</p>
If the element with the tal:replace statement contains other elements, then all those elements
will be replaced. You can’t use the tal:replace statement with tal:attributes or tal:content;
they’re mutually exclusive, and an error will be raised if you place both on the same element.
5.1.3.3 Introducing Execution Order
The order that TAL attributes are written isn’t the order in which they’re executed because
they’re really XML elements (and XML doesn’t care about attribute order). The order in which
they’re executed is as follows:
define condition repeat content replace attributes omit-tag You can’t use the content and replace
statements on the same element because they’re mutually exclusive. Using the attributes state-
ment on the same element as a replace or an omit-tag is meaningless since the attributes are
removed. The on-error tag isn’t mentioned because it’ll be used when the first error occurs in
any of the previous elements.
5.1.3.4 Example: Displaying User Information
To illustrate the points you’ve learned so far, you’ll now create a page template that performs
a simple task: displaying information about a user in the system.
In this example, a company is using Plone internally as an intranet. Each employee is registered
in Plone and given a login; however, there’s no simple page that shows employees or how to
contact them. You’ll create a simple user information page that shows a user’s e-mail address,
home page, picture, and when they last logged in.
The first prototype of this page is easily accomplished with TAL, TALES, and a bit of knowledge
of the basic Content Management Framework (CMF) tools. Unfortunately, because the Appli-
cation Programming Interfaces (APIs) are rather convoluted for those tools, some of this code
is a little longer than it should be. At this stage, don’t worry too much about the API of those
tools; these will be covered in Chapter 9. If you just take the API for granted for the moment,
you can concentrate on the TAL.
First, you need to create a page template; click portal_skins, click custom, add a page template,
and give it the ID user_info. Second, you’ll edit it as follows. For a full listing of this page
template, please see Appendix A. Examining the full listing, you’ll see that it starts with HTML
and body tags.
For, convenience you’ll put the main definitions in a div tag:
<div
tal:omit-tag=""
tal:define="
userName request/userName|nothing;
userObj python: here.portal_membership.getMemberById(userName);
126
getPortrait nocall: here/portal_membership/getPersonalPortrait;
getFolder nocall: here/portal_membership/getHomeFolder
">
In this div tag there are four defines: one to get the username passed in through the request
object and another to translate that username into a user object. The last two defines ensure
that you have a valid reference to the methods that give you user pictures and folders; these
again are convenient because they make later code simpler. Making a div tag or other tag such
as this that contains a series of defines is quite a common pattern in the Zope Page Templates
system. It simply makes the code cleaner.
Next, you do two simple conditions to check that you have a user:
<p tal:condition="not: userName">
No username selected.
</p>
<p tal:condition="not: userObj">
That username does not exist.
</p>
If no username is given in the request, then the expression request/username|nothing will result
in a userName that’s nothing and hence fail the simple test. Further, if the username isn’t valid,
the userObj will result in None, and error messages will be printed for both these conditions.
Now you’re ready to actually process the user:
<table tal:condition="userObj">
<tr>
<td>
<img src=""
tal:replace="structure python: getPortrait(userName)" />
</td>
Since you can only show the user if one is found, you’ll ensure that there’s a simple condition on
this table, tal:condition=”userObj“. To show a user’s picture, you’ll use the getPortrait method
defined early. This function returns the entire tag, so the structure tag ensures the whole image
is rendered correctly. Next, you want to show a few properties such as name and email. The
following shows one of these options, getting the home folder:
<li
tal:define="home python: getFolder(userName)"
tal:condition="home">
<a href=""
tal:attributes="href home/absolute_url"
>Home folder</a>
</li>
First, you use a define to get the folder and assign this the variable home. In a Plone site,
creating a home folder for a user is optional, so you have to be sure that if you’re linking to a
127
folder, it exists. Fortunately, because of the TAL execution order, the define comes before the
condition. Following this, you show a link to the folder using the absolute_url attribute of a
folder.
The page template goes through a few more lines of finding other useful and exciting properties
to show the user. As with most things in Plone, the key is finding the correct API calls and then
processing the output accordingly.
Finally, the page ends by closing all the relevant tags. If all goes well, you should able to call the
page by accessing the URL http://yoursite/user_info?userName=[someuser] where someuser is
a username that exists in your Plone site.
At the moment, this page template is pretty limited. Only a user with the manager role can
view this page, it can show only one member at a time, and the information for the user is
rather thin. In Chapter 6, I’ll show how to expand this example and add some component
reusability, as well as the ability to translate the text into other languages.
6 Chapter 6
6.1 Introducing Advanced Plone Templating and Scripting
The previous chapter covered how the Zope Page Templates system works. To understand page
templates, Chapter 5 also covered the object hierarchy, acquisition, and Template Attribute
Language Expression Syntax (TALES). Using the code from the previous chapter, you were
able to generate dynamic Web pages. The chapter also showed an example page template that
plugged the code together, covered the building blocks of the templating system in Plone, and
provided the key information you’ll need in order to use Plone.
It’s now time to move onto some of the more advanced features of page templates and templat-
ing in Plone in general. First, I’ll introduce the Macro Expansion Template Attribute Language
(METAL) and Internationalization (I18N) namespaces. Like the TAL namespace, these pro-
vide functionality to the site developer. For those itching to know exactly how a Plone page is
plugged together, the ’Hooking Into Plone Using METAL section provides many of the answers.
”
Up until now I’ve shown how you can use simple Python expressions in page templates. Of
course, sometimes a one-line Python expression isn’t enough. So in the ’Scripting Plone with
Python” section, I’ll show you can take Python to the next level and increase the power of your
scripting.
Finally, I’ll cover a common example, showing how to put together a form in Plone. This
example demonstrates concepts learned in the previous chapters and ties it all together while
showing you exactly how Plone handles forms.
6.1.1 Understanding Advanced Plone Templating
One of the nice elements of page templates is that different functions are clearly separated into
different namespaces. In the previous chapter, you looked at the TAL namespace. That’s not
the only namespace that page templates provide; two other namespaces are key to Plone.
The first is METAL. As the rather long name suggests, it’s similar to TAL in that it’s an attribute
language and inserts itself into element attributes. However, its primary aim is to ensure that
you can reuse chunks of other page template code. It does this using the slot and macro
functions.
The second is I18N, which allows you to translate the content of page templates. This is used
in Plone to localize the interface of Plone into more than 30 languages and for many users is
one of the key features of Plone. As you’ll see, the ability to localize text is of interest to all
users, even those building a monolingual site. You’ll start with METAL.
128
129
6.1.1.1 Hooking Into Plone Using METAL
So far you’ve seen how to use TAL to dynamically create parts of pages. However, this really
doesn’t let you do a great deal of complex templating. There really isn’t a mechanism to put a
standard header on top of every page, other than using a TAL statement. METAL is a method
of allowing preprocessing of the templates and provides some more powerful functions than
TAL. All METAL functions start with the metal: prefix.
6.1.1.1.1 metal:define-macro
The metal:define-macro command allows you to define an element to reference from another
template. The name of the referenced chunk is the name of the macro. The following is an
example that defines boxA as a piece you want to use elsewhere:
<div metal:define-macro="boxA">
...
</div>
That div element is now a macro that can be referenced from other templates. The macro
refers only to the part of the page referenced by the element, which, in this case, is the div
tag. So, it’s common to use multiple macro:defines in one page and for the page to be a valid
Hypertext Markup Language (HTML) page, like so:
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
i18n:domain="plone">
<body>
<div metal:define-macro="boxA">
...
</div>
<div metal:define-macro="boxB">
...
</div>
</body>
</html>
Corresponding with the earlier goals of page templates, this page is a valid HTML page that
can be edited by a designer. When the macro is called, the HTML outside the div tags will be
discarded.
6.1.1.1.2 metal:use-macro
The metal:use-macro command uses a macro that has been defined using the define-macro.
When a template defines a macro using the define-macro command, it’s accessible to other
templates through a macros property. For example, if you want to pull the portlet macro out of
the portlet_login template, you can do the following:
130
<div metal:use-macro="context/portlet_login/macros/portlet">
The about slot will go here
</div>
This will fetch the macro and insert the result in its place. As shown, the use-macro command
takes a path expression that points to the template and then to the specific macro in the
template.
6.1.1.1.3 Example: Using the use-macro and define-macro Macros
As an example of this, the following is a template called time_template. This template shows
the date and time on the current Plone server. This is quite a useful function to have, so you
can wrap this in a macro to be reused. This is the example page template containing the
define-macro:
<html>
<body>
<div metal:define-macro="time">
<div tal:content="context/ZopeTime">
The time
</div>
</div>
</body>
</html>
If your template is called time_template, then you can reference this macro in another template.
You can now reference this macro in multiple templates. This is an example template:
<html>
<body>
<div metal:use-macro="context/time_template/macros/time">
If there is a message then the macro will display i here.
</div>
</body>
</html>
When this template is rendered, the HTML produced by Plone looks like this:
<html>
<body>
<div>
<div>2004/04/15 17:18:18.312 GMT-7</div>
</div>
</body>
</html>
131
6.1.1.1.4 metal:define-slot
A slot is a section of a macro that the template author expects to be overridden by another
template. You could think of it as a hole in your page template that you’re expecting something
else to fill in. All define-slot commands must be contained within a define-macro. For example:
<div metal:define-macro="master">
<div metal:define-slot="main">
...
</div>
</div>
6.1.1.1.5 metal:fill-slot
This completes a slot that has been defined with the define-slot command. A fill-slot must be
defined with a use-macro command. When the define- macro part is called, the macro will
attempt fill in all the define slots with the appropriate fill-slots. Here’s an example fill-slot:
<div metal:use-macro="master">
<div metal:fill-slot="main">
The main slot will go here
</div>
</div>
6.1.1.1.6 Example: Using Macros and Slots
Returning to the previous example, you’ll now enhance it a little. If you wanted to put a
custom message at the beginning of the time, then you’d add a slot at the beginning of the
time_template, inside the define-macro. The slot is called time and is as follows:
<html>
<body>
<div metal:define-macro="time">
<div metal:define-slot="msg">Time slot</div>
<div tal:content="context/ZopeTime">
The time
</div>
</div>
</body>
</html>
Now, in the calling page template, you can call the fill-slot:
<html>
<body>
<div metal:use-macro="context/time_template/macros/time">
132
<div metal:fill-slot="msg">The time is:</div>
If there is a message then the macro will display i here.
</div>
</body>
</html>
The end result is that you’ll see the time-slot filled in as follows:
<html>
<body>
<div>
<div>The time is: </div>
<div>2004/04/15 17:18:18.312 GMT-7</div>
</div>
</body>
</html>
6.1.1.1.7 How Plone Uses Macros and Slots
Both macros and slots are similar inasmuch as they both extract content from another tem-
plate and insert content, but they do this differently. The difference comes in how they’re
used: Macros are elements of a template that are explicitly called, but slots are like holes in a
template that you expect other templates to fill in for you. For example, in Plone’s case, the
portlets such as the calendar, navigation, and so on are macros that are explicitly called.
In fact, if in the Zope Management Interface (ZMI) you look at the file by clicking portal_skins,
clicking plone_templates, and then clicking main_template, you’ll see that the entire page con-
sists of macros and slots. At this stage, it’s probably a little confusing, but when called, it runs
through a series of macros and pulls everything together. This allows a user to easily change
any part of a Plone site by overriding that macro, as you’ll see in the next chapter. For example:
...
<div metal:use-macro="here/global_siteactions/macros/site_actions">
Site-wide actions (Contact, Sitemap, Help, Style Switcher etc)
</div>
<div metal:use-macro="here/global_searchbox/macros/quick_search">
The quicksearch box, normally placed at the top right
</div>
...
Continuing to scroll down through main_template, you’ll encounter some define slots. Briefly
I’ll recap how a page in Plone is rendered. When an object is shown, a template for that view
of that content is shown. When you view an image, the template image_view is shown, and
that template controls how the image is shown. To do this task, the image template fills the
main slot. If you look in the image_view template, you’ll see the following code defined in that
template:
133
<div metal:fill-slot="main">
...
</div>
If you jump back to the main_template, you’ll see that it contains a define-slot definition for the
slot main:
<metal:bodytext metal:define-slot="main" tal:content="nothing">
Page body text
</metal:bodytext>
Each type of content has a different template, and each template defines how they’ll use the
main slot differently. So, then each content type has its own particular look and feel from the
templates. Just one element is missing in the equation. Somehow when you called image_view,
the template knew it should use main_template. In the image_view template, you use the macro
from the main_template. This is defined in the following HTML:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US"
lang="en-US"
metal:use-macro="here/main_template/macros/master"
i18n:domain="plone">
In this case, the main_template has the main slot filled in by the slot defined as main in the
template being rendered. The following is a timeline of how the page is built for viewing an
image:
image_view main_template image_view main_template define-slot=”main“ fill-slot image_view
fill-slot main_template main_template This allows Plone to be flexible in terms of how each
page is defined. For example, main_template defines just more than that one slot; there’s also
a slot for inserting Cascading Style Sheets (CSS) code:
<metal:cssslot fill-slot="css_slot">
<metal:cssslot define-slot="css_slot" />
</metal:cssslot>
If a view needed a custom set of CSS, you could define this slot in the view, and it’d be filled in
when rendering. Some of the macros in main_template define slots in them as well and then
fill them back in the main_template so that if you really wanted, you could also fill in those
slots. That’s an advanced technique, however, so you’ll want to ensure you have the basics
down before going down that road.
6.1.1.2 Introducing Internationalization
Plone is always striving to maintain a large number of high-quality translations. The fact that
Plone provides an accessible user interface in more than 30 languages is a key selling point for
Plone. This also means that I18N is a key feature of the templates. To facilitate this, the I18N
namespace is an extra namespace such as TAL or METAL that has specific statements.
134
This section details what users need to know in regard to the templates. In a template you can
add an i18n tag to an element that will allow the translation of an attribute or its contents.
Six statements exist: attributes, data, domain, source, target, and translate. The basic pattern
is to wrap the piece of text you want to translate and add the appropriate i18n attributes. For
example, if you want to translate the following:
<i>Some text</i>
it would become like so:
<i i18n:translate="some_text_label">Some text</i>
Each localization provides a translation of Some text, and the translation tool looks up a trans-
lation for a user. When performing the translation, each string to be translated must have
a unique message ID that identifies the item to be translated. For example, a string such as
Search may have a message ID of search_widget_label. The message ID allows the string to be
identified uniquely and the translation to be repeated.
6.1.1.2.1 i18n:translate
This translates the contents of an element, with an optional message ID passed as a statement.
For example, the following will create a message ID of title_string:
<h1 i18n:translate="title_string">This is a title</h1>
This example is for a piece of text that’s static and doesn’t change. However, in some situations,
the piece of text could be taken from a database or an object and is dynamic. By leaving the
translate statement blank, the message ID is composed of the value in the field. In the following
example, if the title returned by the path expression here/title was Alice in Wonderland, then
that title would be passed to the translation tool. If no translation exists, the original value
will be inserted:
<h1
tal:content="here/title"
i18n:translate="">
This is a title.
</h1>
The translation command is probably one of most common i18n tags you’ll use, and you’ll see
it throughout the Plone templates. It not only enables you to translate static parts of your site,
such as form labels, help messages, and descriptions, but also the more dynamic parts of your
site that could change more often, such as page titles.
135
6.1.1.2.2 i18n:domain
This sets the domain for the translation. To prevent conflict, each site can have multiple
domains or groups for translations; for example, there may be one domain for Plone and one
for your custom application. Plone uses the domain plone, which is usually the default domain
in Plone:
<body i18n:domain="plone">
You shouldn’t have to use this tag much; however, if you’re writing a custom application, you
may find it useful to have a domain that doesn’t conflict with other domains.
6.1.1.2.3 Ii8n:source
This sets the source language for the text about to be translated. It isn’t used in Plone:
<p i18n:source="en" i18n:translate="">Some text</p>
6.1.1.2.4 i18n:name
This provides a way of preserving elements in a larger block of text so that the block of text can
be reordered. In many languages, not only are the words changed but also the order. If you
have to translate a whole paragraph or sentence that contains smaller chunks that shouldn’t
be translated, then they can be passed through:
<p i18n:translate="book_message">
The
<span
tal:omit-tag=""
tal:content="book/color"
i18n:name="age">Blue</span>
Book
</p>
This will produce the following message string:
The {color} Book
If the target language required these to be in a different order, these could then be moved
around and still have the dynamic content inserted into the correct place. In French, this
would need to be translated as so:
Le Livre {color}
6.1.1.2.5 i18n:target
This sets the target language for the text about to be translated. It isn’t used in Plone.
136
6.1.1.2.6 i18n:attributes
This allows the translation of attributes within an element, rather than the content. For exam-
ple, an image tag has the alt attribute, which shows an alternate representation of the image:
<img
href="/someimage.jpg"
alt="Some text"
i18n:attributes="alt alternate_image_label" />
Multiple attributes should be separated with a semicolon, just like tal:attributes work.
6.1.1.2.7 i18n:data
This provides a way of translating something other than strings. An example is a DateTime
object. An i18n:data statement requires a matching i18n:translate statement so that a valid
message ID is available. For example:
<span i18n:data="here/currentTime"
i18n:translate="timefmt"
i18n:name="time">2:32 pm</span>... beep!
6.1.1.2.8 Translation Service
Now that I’ve covered the tags, I’ll cover the mechanism for performing the translation. By
default Plone comes with an I18N mechanism. This allows you to internationalize the user
interface so that messages, tabs, and forms can all be translated. At this stage, this doesn’t
cover the actual content that users add. If you add a document in English and view the page
asking for it in French, you’ll get the English document with French text around the outside
(see Figure 6-1).
137
Figure 6-1. Plone.org in French
Plone reads the HTTP headers that a browser sends to the client requesting a language. If your
browser is in English, then you won’t see much.
To change the language settings in Internet Explorer, do the following:
Once you’ve done this, pick your favorite Plone site and visit it in your browser.
The translations for Plone are handled through a tool called Placeless Translation Service
(PTS). You can locate the PTS tool in the Zope control panel; at the bottom of the page you’ll
see an option for Placeless Translation Service. Click this, and it’ll open all the translations
that exist. These translations are read in from the file system; click a translation to see the
138
information about the language, such as the translator, the encoding, and the path to the file.
All the files are actually stored in the i18n directory of the CMFPlone directory.
Translations are handled using two files for a translation, a .po and a .mo file. For example,
plone-de.po contains the translations for German (de is the code for German). The .mo file is
the ’compiled” version of the .po file and is used by Plone for performance. You never need
to look at the .mo file, so you can just ignore it. The .po is the file you can edit to change a
translation. If you open that file in a text editor, you’ll see a series of lines starting with the
text msgid or msgstr. Above the msgid is actually the code where the i18n command occurs, so
you can see which bit of a page you’re translating. For example:
#: from plone_forms/content_status_history.pt
#. <input attributes="tabindex tabindex/next;" value="Apply"
class="context" name="workflow_action_submit" type="submit" />
#.
#: from plone_forms/personalize_form.pt
#. <input attributes="tabindex tabindex/next;" tabindex=""
value="Apply" class="context" type="submit" />
#.
msgid "Apply"
msgstr "Anwenden"
In the two parts of the previous page templates, the word Apply will be translated into An-
wenden for German users. What gets translated is determined by the i18n tags that have been
inserted into the page templates, as you saw earlier. If you want to change that translation or
add your own variation, then merely change the .po file. If no msgstr is found, then the default
English translation is found. Once you’ve made that change, restart Plone. When this happens,
Plone will recompile that file into the .mo version, and your translation will be updated.
For Plone, the default translation is always to use the English translation file if no language is
given or no translation is available. In fact, the plone-en.po file is blank, so no translation will
be available. Therefore, Plone does the final fallback, does no translation, and shows the text
in the page template. The text in all page templates is in English since most developers speak
English. The long and short of this is that there’s no English translation.
Therefore, you can make a new translation by copying the plone.pot file into a new file of
the name plone-xx.po. The value of xx should match the country code of your translation.
You can find a list of language codes at http://www.unicode.org/onlinedat/languages.html.
Once you’ve started the translation, set the values at the top, including the language code,
and start translating away. If you’ve done a new language file, then the Plone I18N
team will happily accept it and help you complete it. The Plone team mailing list is at
http://sourceforge.net/mailarchive/forum.php?forum_id=11647
Translating the content that people add is actually quite a tricky task and something that Plone
is working toward, but currently it hasn’t completely ironed out. The favorite approach at
the moment is to use two products, PloneLanguageTool and i18nLayer, both of which can be
found on SourceForge (http://sf.net/projects/collective). However, both of these are for more
experienced developers to fully understand and integrate; I hope something like this will be in
the next release of the book.
139
6.1.1.3 Example: Displaying Multiple User Information
In Chapter 5 you used simple TAL commands to show a user’s information in more detail. That
template has a few drawbacks; one of them is that it shows only one user at a time. You’ve
seen that a simple tal:repeat can enable you to repeat content, but you’ll now use a macro to
make this page more modular.
You’ll change the user_info page template so that it lists every page member in the site. Instead
of looking for a username being passed in the request, you’ll use the function listMembers,
which returns a list of every member on the site:
<div metal:fill-slot="main">
<tal:block
tal:define="
getPortrait nocall: here/portal_membership/getPersonalPortrait;
getFolder nocall: here/portal_membership/getHomeFolder
">
<table>
<tr tal:repeat="userObj here/portal_membership/listMembers">
<metal:block
metal:use-macro="here/user_section/macros/userSection" />
</tr>
</table>
</tal:block>
</div>
You’ll note that the code for user_info is now a great deal shorter. The member returned
by listMembers is passed in to tal:repeat. For each member, there will be a table row and
then a macro to show information to the user. In that table row, the locally defined variable
userObj now contains the user information. Of course, you now need to make a macro called
userSection in a page template, so you’ll create a page template called user_section as referenced
in the macro. This template contains all the code that was between the table’s row tags. Again,
you can find a full listing for this page template in Appendix B:
<div metal:define-macro="userSection"
tal:define="userName userObj/getUserName">
...
The only real change is that the use-macro in the main template has to be removed and a new
macro defined so that this macro can be defined. Because the username is no longer explicitly
passed, you need to get the username from the user object by using the getUserName method.
To test the resulting page, go to http://yoursite/user_info, and you should see a list of users.
The page now is user-friendly, showing multiple users on one page. The code is more modular,
rendering the user’s information in a separate macro that can be altered independently. This
page is still not perfect but will be improved in later chapters.
140
6.1.1.4 Example: Creating a New Portlet with Google Ads
In Chapter 4 you saw how to easily edit portlets in a Plone site; adding your own portlet isn’t
much harder. To write your own slot, you need to make a new page template with a macro
inside it. Then a TALES expression that points to macro will be added to the list of portlet,
rendering the portlet to the page.
The basic template for a portlet is as follows:
<div metal:define-macro="portlet">
<div class="portlet">
<!-- Enter code here -->
</div>
</div>
All you need to do is insert some suitable code into the portlet. Google set up a text-based
advertising system in 2003 that places text on your site. The ads are based upon what Google
thinks your site is about, based on the search results for your site. The Google system is
available at http://www.google.com/adsense. To display ads (and get paid for them), you’ll
have to register with Google. On the Google Web site, it’ll ask you to pick some colors and
style. Since you’ll put this in a slot, I recommend the ’skyscraper” size—tall and thin. Make a
copy of the JavaScript that the site produces.
Next, you have to create a portlet:
portal_skins/custom googleAds googleBox <!-- Enter code here --> The end result should be
something like Listing 6-1; however, your version will have a valid value for google_ad_client,
rather than yourUniqueValue. That value tells Google which site ordered this ad and who to
pay. Curiously enough, if you don’t have a valid value there, Google will still happily show the
ads but not pay you!
Listing 6-1. Displaying Ads from Google
<div metal:define-macro="portlet">
<div class="portlet">
<script type="text/javascript"><!--
google_ad_client = "yourUniqueValue";
google_ad_width = 120;
google_ad_height = 600;
google_ad_format = "120x600_as";
//--></script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</div>
To then include this on your site, as detailed in Chapter 4, add the following portlet to your
list of portlets:
141
here/googleAds/macros/portlet
6.1.2 Scripting Plone with Python
At least four different levels in Plone exist for creating logic. The simplest level for using
Python in Plone is the Python TALES expression I discussed in the previous chapter. However,
a Python expression allows you to do only one line of code; often you’ll want to do something
more complicated.
Even more common is the problem that you really don’t want to cram all the logic into the
template. Placing logic in your template is a bad idea in general; any time you can move
anything that isn’t explicitly presentation logic out of the template, you’ve saved yourself a
headache. Separating logic and presentation allows you to easily allow different people to
work on different parts of the project, and it improves code reuse. The other layers of adding
scripting Plone happen roughly in the following order:
• Template attribute expressions: These provide expressions and a way of
inserting little snippets of logic or simple paths in many places.
• Script (Python) objects: These are simple scripts that execute in Plone in a
restricted environment.
• External method objects: These are more complicated modules that don’t
execute in restricted environments.
• Python products: This is the key source that the CMF and Plone is written
in; this offers access to everything in Plone. Python products are an advanced
subject and are covered in Chapter 14.
After an expression, the next level of complexity is Script (Python) object. This object allows
for multiple lines of Python code, and you can call it from an expression. When you call a
Script (Python) object, you’re incurring a small amount of extra overhead as Plone makes a
switch into that object. However, that overhead is minimal because there’s a trade-off between
clarity, separation, and performance. My advice is to put as much logic into Python as possible
and keep page templates as simple and as clean as possible. It’s easy to move it back later if
there’s a performance hit, but at least you’ll understand what’s happening later.
6.1.2.1 Using Script (Python) Objects
A Script (Python) object is may you might traditionally think of in Plone as a script. It’s
a snippet of Python that you can write and then call from other templates or through the
Web directly. Plone actually has a large number of these scripts for performing various key
functions. A Script (Python) is halfway between an expression and an external method in
terms of power.
To add a Script (Python) object, go to the ZMI, select Script (Python) from the drop-down
menu, and click Add, as shown in Figure 6-2.
142
Figure 6-2. Adding in a Script (Python) object
Give the script an ID such as test_py and then click Add and Edit. This will open the edit page
for the Script (Python) object, which looks like Figure 6-3.
143
Figure 6-3. Editing a Script (Python) object
You can edit the script directly through the Web. If you make a syntax error, you’ll be told
about it after you’ve clicked Save Changes, as shown in Figure 6-4.
144
Figure 6-4. A deliberate indentation error in the Script (Python) object
If your Script (Python) has no errors, you can click the Test tab to see what the output is. In
this case, the sample is rather boring; it prints the following text:
This is the Script (Python) "test_py" in
http://gloin:8080/Plone/portal_skins/custom
A script also has the following options:
Title: The edit form has a Title option, which is for you to give the script a title.
This will show up in the ZMI, so it’ll be easier to remember what it does.
145
Parameter List: This is a list of parameters that the script takes, such as variableA
or variableB=None. In fact, this is a standard list of parameters you’d expect in
a standard Python function. Some parameters are already defined for you in this
object, however; you can see them by clicking the Bindings tab. In that tab, you’ll
see a list of the variables already bound into the object, which should have familiar
names by now.
The following are the variables bound to the script that are accessible from a Script (Python)
object:
• context: This is the object on which the script is being called.
• container: This is the containing object for this script.
• script: This is the Script (Python) object itself; the equivalent in Zope Page
Templates is template.
• namespace: This is for when this script is called from Document Template
Markup Language (DTML), which is something that doesn’t happen in Plone.
• traverse_subpath: This is the Uniform Resource Locator (URL) path after the
script’s name, which is an advanced feature.
I’ll now show a simple example that ties these topics into the Zope Page Templates system,
using the example I gave of a Python expression in the previous chapter that adds two numbers.
As you saw, you could make a page template for this that looks like the following:
<p>1 + 2 = <em tal:content="python: 1 + 2" /></p>
The equivalent using a Script (Python) object looks like the following. Change the test_py
script to the following line:
return 1+2
As you saw at the beginning of the previous chapter, you call an object by giving its path as an
expression. So, in a page template, you can now do the following:
<p>1 + 2 = <em tal:content="here/test_py" /></p>
The object test_py is acquired in the path expression and called, and it then returns the Python
back to the template and prints. You’ve now called a script from your template! This is obvi-
ously a rather simple example, but my point is that there’s a great deal you can do in a Script
(Python) object that you just can’t do in a page template.
In a Script (Python) object, you can specify the title, parameters, and bindings setting by using
the ## notation at the top of a script. When you save a script with that bit of text at the
top, Plone will remove that line and instead change the appropriate value on the object. This
syntax is used a lot in the Script (Python) object in this book to ensure that you have the right
title and parameters. So, you could rewrite the previous script as follows:
##title=Returns 1+2
##parameters=
return 1+2
146
6.1.2.1.1 Scripting Plone
Scripting Plone is a rather complicated subject because as soon as you’re able to script Plone,
you have to take into account the Application Programming Interface (API) of all the objects
and tools you may want to use. Explaining APIs is beyond the scope of this book; instead, I’ll
demonstrate how to do some simple tasks using Script (Python) objects. Once you’re comfort-
able with them, I’ll describe more API-specific functions.
Page templates can loop through Python dictionaries and lists quite nicely. But often you don’t
have data in one of these convenient formats, so you need to jump into a Script (Python)
object, format the data nicely, and then pass it back to the page template.
The most convenient data format is a list of dictionaries, which lets you combine the power of
a tal:repeat and a path expression in one function. As an example, you’ll see a function that
takes a list of objects. Each of these objects is actually an object in a folder. For each of those
objects, you’ll see the object if it has been updated in the last five days. Listing 6-2 shows a
useful little portlet I put together for a site that wanted to locate this type of information and
then highlight exactly those items.
Listing 6-2. Returning Objects Up to Five Days Old
##title=recentlyChanged
##parameters=objects
from DateTime import DateTime
now = DateTime()
difference = 5 # as in 5 days
result = []
for object in objects:
diff = now - object.bobobase_modification_time()
if diff < difference:
dct = {"object":object,"diff":int(diff)}
result.append(dct)
return result
In this Script (Python) object I’ve introduced a couple of new concepts. First, you import Zope’s
DateTime module using the import function. The DateTime module, covered in Appendix C, is
a module to provide access to dates. It’s pretty simple, but if you make a new DateTime object
with no parameters, then you’ll get the current date and time; this is the now variable. When
you subtract two DateTime objects, you’ll get the number of days. You can compare that to the
difference a user wants to monitor and, if it’s longer, add it to the result list. The result of this
is a list of dictionary objects, which looks like Listing 6-3.
Listing 6-3. The Result of Listing 6-2
[
{
147
’diff’: 1,
’object’: <PloneFolder instance at 02C0C110>
},
{
l ’diff’: 4,
’object’: <PloneFolder instance at 02FE3321>
},
...
So now that you have the results in the right order, you need a page template that will pass in
the list of objects and process the results. An example of this is as follows:
<ul>
<li tal:repeat="updated python: context.updateScript(context.contentValues())">
This template has a tal:repeat call at the top that calls the script (in this case, called update-
Script). Into that function it passes one value, a list of contentValues from the current con-
text. Previously you called the Script (Python) object using a path expression; you could do
that here as context/updateScript. However, you can’t pass parameters through to the script
being called in that syntax, so you make a Python expression instead, which is python: con-
text.updateScript(). The contentValues function returns a list of all content objects in a folder.
Next, look at the code for each iteration:
<a href="#"
tal:attributes="href updated/object/absolute_url"
tal:content="updated/object/title_or_id">
The title of the item</a>
<em tal:content="updated/diff" /> days ago
</li>
</ul>
As shown, you can loop through this list of values, and you can then use path expressions to
access first the repeated value (updated), then the object (object), and then a method of that
object (title_or_id). This is an example of taking complicated logic processing and passing it
off to a Script (Python) object.
6.1.2.1.2 Restricted Python
I’ve mentioned several times that Script (Python) objects and Python TAL expressions all run in
restricted Python mode. Restricted Python is an environment that has some functions removed.
These functions may potentially be dangerous in a Web environment such as Plone. The origi-
nal reasoning is that you may have untrusted (but authenticated) users writing Python on your
site. If you open an account at one of the many free Web hosts for Zope, you’ll find you can do
this. However, if you have given people the right to do that, you don’t want them to get access
to certain things such as the file system.
In restricted Python, some common Python functions have been removed for security reasons—
most notably, dir and open aren’t available. This means that, as with Script (Python) objects,
148
they can’t be introspected, and access is limited to the file system. A few Python modules are
available to the user. Most of these are for experienced developers; for more information, see
the relevant documentation or module code:
• string: This is the Python string module
(http://python.org/doc/current/lib/module-string.html).
• random: This is the Python random module
(http://python.org/doc/current/lib/module-random.html).
• whrandom: This is the Python whrandom module. You should mostly use
random now (http://python.org/doc/current/lib/module-whrandom.html).
• math: This is the Python math module
(http://python.org/doc/current/lib/module-math.html).
• DateTime: This is Zope’s own DateTime module.
• sequence: This is a Zope module for easily sorting sequences.
• ZTUtils: This is a Zope module that provides various utilities.
• AccessControl: This gives access to Zope’s Access module.
• Products.PythonScripts.standard: This gives access to the standard string-
processing functions of DTML such as html_quote, thousands_commas, and so
on.
If you want to import a module that isn’t in the previous list, then you can
find excellent instructions in the PythonScript module. You’ll find them at
Zope/lib/python/Products/PythonScripts/module_access_examples.py. However, a more
simple method is available to you—using an external method.
6.1.2.2 Using External Method Objects
An external method is a Python module written on the file system and then accessed in Plone.
Because it’s written on the file system, it doesn’t run in restricted Python mode, and therefore
it conforms to the standard Plone security settings.
This means you can write a script that does anything you want and then call it from a page
template. Common tasks include opening and closing files, accessing other processes or ex-
ecutables, and performing tasks in Plone or Zope that you simply can’t perform in any other
way. For obvious reasons, when you’re writing a script that can do this, you need to be sure you
aren’t doing anything dangerous, such as reading the password file to your server or deleting
a file you don’t want to delete.
To add an external method, you go to the file system of your instance home and find the
Extensions directory. In that directory, add a new Python script file; for example, Figure 6-5
shows that I added test.py to a directory on my Windows computer.
149
Figure 6-5. A new external method, test.py
You can now open test.py and edit it to your heart’s content, writing any Python code you want.
The only catch is that you must have an entry function that takes at least one argument, self.
This argument is the external method object in Plone that you’ll be adding shortly. The fol-
lowing is an example entry function that reads the README.txt file out of the same Extensions
directory and spits it back to the user (you’ll have to change the path to point to your file):
def readFile(self):
fh = open(r’c:\Program Files\Plone\Data\Extensions\README.txt’, ’rb’)
data = fh.read()
return data
Now that you’ve done that, you need to map an external method to this script. This is a Zope
object, so return to the ZMI, click portal_skins,* and then click custom*. Finally, select External
Method from the Add New Items drop-down list. When you add an external method, you need
to give the name of the module (without the .py) and the entry function, so in this case the
add form looks like Figure 6-6.
150
Figure 6-6. The newly added external method
After clicking Save Changes, you can hit the Test tab to see what happens when it runs. In
this case, you should get a line or two of text. Since you have the External Method module
in Plone, you can access it from a page template in the same way as any other object. A path
expression to here/test_external would do the trick in this case. For example:
<h1>README.txt</h1>
<p tal:content="here/test_external" />
The real power is that you can pass code off to the unrestricted Python mode and from there
to any function you want, without having to worry about security. Although this may seem like
a cool function, external methods aren’t used a great deal in Plone because complicated logic
is usually moved into a Product object, and simple logic is kept in a Script (Python) object. If
you find yourself using External Method objects a lot, consider one of the tools discussed in
Chapter 12.
6.1.3 Useful Tips
Because page templates are valid Extensible Markup Language (XML) and can be used inde-
pendently of Zope or Plone, you have several useful scripts for cleaning up page template code
and performing syntax checks. These are additional tools and checks; Zope actually performs
151
all the necessary checks when you upload a page template. For a project such as Plone, it can
be useful to run automatic checks on your code or verify it locally before committing changes.
To run these checks, you’ll need to be able to edit these tools locally and have Python installed
on your computer. For more information on External Editor, a method for editing remote code
locally, see Chapter 10.
6.1.3.1 Introducing XML Namespaces
Page templates use XML namespaces to generate code. Programmers can use the rules of XML
namespaces to make life easier. At the top of a page template, you’ll see a declaration of the
namespace in the starting tag:
<html xmlns="http://www.w3.org/1999/xhtml"...
This sets the default namespace to Extensible HTML (XHTML). For any containing element,
if no namespace is defined, it uses that default namespace. For example, you know the next
element is XHTML because it has no prefix:
<body>
Normally for TAL and METAL elements and attributes, you have been adding the prefix tal:
and metal: to define the namespace. The following code is something that should be familiar
by now:
<span tal:omit-tag="" tal:content="python: 1+2" />
This will render 3. However, the following is an alternative:
<tal:number content="python: 1+2" />
By using the tal: prefix on the element, you’ve defined the default namespace for this whole
element as tal. If no other prefix is given, the tal namespace is used. In the example, using
span tags, the default namespace is XHTML, so you have to specifically define the tal: prefix
when using the Content tab.
Note that the element name is descriptive and can be anything not already defined
the tal namespace (for example, content or replace). Because tal:number isn’t a
valid XHTML element, the actual tag won’t display, but the content will—making
the omit-tag unnecessary. This technique is used a lot in Plone to make code that’s
smaller, simpler to debug, and more semantic.
152
6.1.3.2 Introducing Tidying Code
HTML Tidy is an excellent tool for testing and cleaning up HTML code that can perform a few
useful tasks. Versions of HTML Tidy exist for all operating systems; you can download it from
http://tidy.sourceforge.net. For Windows users, find the appropriate download for your version
of Windows, unzip the tidy.zip file, and place the tidy.exe in your PATH (usually your Windows
directory, such as C:up WINNT).
HTML Tidy can tell you if there are any XHTML errors in your page template. For these
purposes, one flag can make a difference: -xml. This tells HTML Tidy to process the file as XML
and report any XML errors. Given the example ’bad” template shown in Listing 6-4, you can
see a few errors. Not only is the code not indented, but it’s missing closing elements and has
invalid nesting.
Listing 6-4. An Example Broken Page Template: bad_template.pt
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<p>
<div>
This is bad HTML,
XHTML or XML...<a tal:contents="string: someUrl"></a>
</p>
<img>
Further it isnt indented!
</body>
</html>
If you run Listing 6-4 through HTML Tidy, you’ll see the errors in the template and get nicely
indented code, as shown in Listing 6-5.
Listing 6-5. The Output from HTML Tidy
$ tidy -q -i bad_template.pt
line 11 column 1 - Warning: <img> element not empty or not closed
line 10 column 1 - Warning: missing </div>
line 10 column 39 - Warning: <a> proprietary attribute "tal:contents"
line 11 column 1 - Warning: <img> lacks "alt" attribute
line 11 column 1 - Warning: <img> lacks "src" attribute
line 9 column 1 - Warning: trimming empty <p>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
153
<head>
<meta name="generator" content=
"HTML Tidy for Linux/x86 (vers 1st August 2003), see www.w3.org" />
<title></title>
</head>
<body>
<div>
This is bad HTML, XHTML or XML...<a tal:contents=
"string: someUrl"></a> <img />Further it isnt indented!
</div>
</body>
</html>
The complaints about proprietary attributes can be a little annoying. To check that your page
template is valid XML, pass the -xml flag. The output is less verbose and just points out the
missing tags:
$ tidy -q -xml bad_template.pt
line 15 column 1 - Error: unexpected </body> in <img>
line 16 column 1 - Error: unexpected </html> in <img>
6.1.3.3 Conducting Syntax Checks
When you edit a page template in the ZMI, Zope performs a syntax check on the document
for things such as invalid tags. If a tag is invalid, an error will be shown on the template
while you’re editing it through the Web. If, like me (and as I demonstrate in Chapter 7), you
write most of your page templates on the file system, then a simple syntax check for a page
template is really useful. Listing 6-6 is a Python script that resides on your file system and runs
independently from Zope.
To run this, you must have a Python interpreter, and the Python module PageTemplate must be
importable. To make PageTemplate importable to your Python interpreter, you must add the
Products directory of your Zope installation to your Python path. You have several ways to do
this (covered in Appendix B).
Listing 6-6. Error Checking Page Templates
#!/usr/bin/python
from Products.PageTemplates.PageTemplate import PageTemplate
import sys
def test(file):
raw_data = open(file, ’r’).read()
pt = PageTemplate()
pt.write(raw_data)
if pt._v_errors:
154
print "*** Error in:", file
for error in pt._v_errors[1:]:
print error
if __name__==’__main__’:
if len(sys.argv) < 2:
print "python check.py file [files...]"
sys.exit(1)
else:
for arg in sys.argv[1:]:
test(arg)
For every file passed through to the script, the ZMI will compile the page template and see if
there are any TAL errors. Taking the bad_template.pt file from Listing 6-4, you’ll get an error:
$ python zpt.py /tmp/bad_template.pt
*** Error in: /tmp/bad_template.pt
TAL.TALDefs.TALError: bad TAL attribute: ’contents’, at line 10, column 39
In this case, it has picked up on the incorrect spelling of tal:content as tal:contents. This error
is something HTML Tidy doesn’t catch. Unfortunately, the processing stops at the first syntax
error. If there are multiple errors, only the first is picked up, meaning sometimes you have to
check the syntax several times.
6.1.4 Using Forms
Forms are an integral part of any site, and almost everyone needs to create a method for
creating and altering forms in your Plone site. With the form framework in Plone, you can
change the validation that process forms have, where they take the user to, and so on. This
framework isn’t just specifically designed for stand-alone forms that perform a simple task,
such as request a password, login, and so on. The framework also works for all content types
for tasks such as editing a content type, which I’ll cover later in this book in Chapters 11–13.
All basic forms have at least two components that you’ve already seen so far: a Page Template
object to show the form to the user, and a Script (Python) object to parse the results and
perform some action on the results.
The form controller framework in Plone introduces a few new object types that are equivalent
to the types you’ve seen in this chapter. These are the Controller Page Template object, the
Controller Script (Python) object, and the Controller Validator object. These new objects have
their equivalent objects, as shown in Table 6-1. These new objects have more properties and
act in slightly different ways than the equivalent objects.
Table 6-1. New Object Types That the Controller Provides
Object Type Equivalent Zope Object
Controller Filesystem Page Template Page Template
155
Object Type Equivalent Zope Object
Controller Python Script Python Script
Controller Validator Python Script
To add one of these objects using the ZMI, go to the drop-down box, and select the name.
The form controller framework creates a sequence of events for a form that a user can then
define. The following is the sequence of events when executing a form:
When this sequence of events occurs, a state object is passed around, which contains informa-
tion about the status of the object, the success of any validations, and any message that are to
be passed.
The following sections run through these steps to show how a form can be validated, and then
I’ll show a full example in the ’E-Mail Example: Sending E-Mail to a Webmaster” section.
6.1.4.1 Creating a Sample Form and Associated Scripts
The beginning of this process is a form. Although this is actually a Controller Page Template
object, it’s written using standard TAL code. To add one, select Controller Page Template from
the now-familiar drop-down box and give it an ID of test_cpt.
A form in Plone is actually a rather lengthy piece of code if you want to utilize all the options
available to you. This piece of code is reproduced in full in Appendix B and is the code used in
the later example:
<form method="post"
tal:define="errors options/state/getErrors"
tal:attributes="action template/id;">
...
<input type="hidden" name="form.submitted" value="1" />
</form>
Looking at this code, you should note that to work in the framework, a few minor differences
exist between this and what you may consider a standard form. First, the form is set up to sub-
mit to itself; this isn’t *optional. Second, a special hidden variable exists called *form.submitted.
The Controller Page Template object checks the request variable for the value form.submitted
to see if the form has been submitted. If, instead, it has just been accessed—for example, via
a link—this isn’t *optional. At the beginning of the form, you set the variable errors. The *errors
dictionary comes from the state object that’s passed into the templates. The state object is a
common object to all the templates and scripts in this system.
6.1.4.1.1 Creating Validators
Once the user clicks the Submit button on your form, the data will be run through the validators
and be validated. Validators are optional. Data doesn’t need to be validated, but of course any
156
application should do that as appropriate. The Validator tab for a Controller Page Template
object gives you a link to the possible validators.
A validation script is the same as a normal Script (Python) object that has one extra variable,
state. The state variable is how you can pass results of the validation. Listing 6-7 shows a
simple validation script for checking to see if you’ve been given a number.
Listing 6-7. Validating That a Number Has Been Provided
##title=A validation script to check we have a number
##parameters=
num = context.REQUEST.get(’num’, None)
try:
int(num)
except ValueError:
state.setError("num", "Not a number", new_status="failure")
except TypeError:
state.setError("num", "No number given.", new_status="failure")
if state.getErrors():
state.set(portal_status_message="Please correct the errors.")
return state
This state object contains basic information about what has happened during the validation
chain. The state object stores the errors for each field, the status, and any other values. For
example, if the number given can’t be turned into an integer, you set the status to failure and
give an error message for the field using the setError method. Later this error message will be
shown for the field. At the end of the script, any errors returned so far are retrieved via the
getErrors method.
To add the previous script, click portal_skins, click custom, and select Controller Validator from
the drop-down box. Give it an ID of test_validator. You can now return to the Validation tab
of your Controller Page Template object and add a pointer to this validation script, as shown
in Figure 6-7.
157
Figure 6-7. Adding the test_validator to the Controller Page Template object
You have a couple of choices for a validation. In the example I’ve ignored them since they
aren’t relevant, but the following is a list of the options:
contextType: This is the type of the context object, if any, that template is executed
in. This is a shortcut to the content type of the context object. If you wanted only
this validation to occur on a link, then you could set this value to Link.
button: This is the button, if any, that’s clicked to submit the form. You could have
different buttons on a form (for example, a Submit and a Cancel button). Each of
these buttons could then map to a different action; clicking Cancel would take you
to one place, and clicking Submit would take you to another.
158
validators: This is a comma-separated list of validators, which are Controller Val-
idator objects that the template will acquire. In the previous example, you used the
validator ID of test_validator.
NOTE When writing validation scripts, use Controller Validator objects instead of Script
(Python) objects. Controller Validator objects are just like ordinary Script (Python) objects
with the addition of a ZMI Actions tab.
6.1.4.1.2 Specifying Actions
Actions are the ending actions after the validators have been run, and they depend upon the
status that’s returned by the validators. The Actions tab for a Controller Page Template object
shows all the actions for the page template in question. You can specify actions with the same
kind of specialization options as described previously via a Web form, as shown in Figure 6-8.
Figure 6-8. Adding an action
You have the following four choices for the actual resulting action:
159
redirect_to: This redirects to the URL specified in the argument (a TALES expres-
sion). The URL can be either absolute or relative.
redirect_to_action: This redirect to the action specified in the argument (a TALES
expression) for the current content object (for example, string:view). At this stage
I haven’t covered actions yet, but each content object has actions such as view and
edit. Chapter 11 covers actions for an object.
traverse_to: This traverses to the URL specified in the argument (a TALES expres-
sion). The URL can be either absolute or be relative.
traverse_to_action: This traverses to the action specified in the argument (a TALES
expression) for the current content object (for example, string:view).
One example of this is if the completion of the form is a success, you traverse to a Controller
Python Script object that you’ve written that processes the result of the form. If the page is a
failure, you traverse back to the template and show them the error.
The difference between a redirect and a traversal is that the redirect is an HTTP redirect sent to
the user’s browser. The browser processes it and then sends the user off to the next page. Thus,
the redirect actions lose all the values passed in the original request. If you need to examine
the contents of the original form, then this isn’t the best approach. Instead, I recommend the
traversal to options. The result is the same; it’s just that the traverse option does this all on the
server. Doing this preserves the request variables and allows you to examine this in scripts.
6.1.4.2 E-Mail Example: Sending E-Mail to the Webmaster
You’ll now see a real example and spend the rest of this chapter building it. A common re-
quirement is a custom form that sends e-mail to the Webmaster. You’ll build this type of form
in the following sections. The complete scripts, page template, and assorted code are available
in Appendix B. If you really don’t want to type all this in, you can see this example online at
the book’s Web site; it’s also downloadable as a compressed file from the Plone book Web site
(http://plone-book.agmweb.ca) and the Apress Web site (http://www.apress.com), so you can
just install it and try it. This example has just two fields in the form: the e-mail of the person
submitting the form and some comments from that person. For this form, the e-mail of the
person will be required so you can respond to their comments.
6.1.4.2.1 Building the Form
The form is the largest and most complicated part of this procedure, mostly because there’s
so much work that has to be done to support error handling. This form is a Controller Page
Template object called feedbackForm. To ensure that it’s wrapped in the main template, I’ll
start the form in the standard method:
<html
xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en-US"
lang="en-US"
160
i18n:domain="plone"
metal:use-macro="here/main_template/macros/master">
<body>
<div metal:fill-slot="main"
tal:define="errors options/state/getErrors;">
One addition here is errors options/state/getErrors, which will place any and all errors into the
errors local variable for later use.
Because of the requirement for the form to post back to itself, you set this action in TAL, with
the expression template/id. This path will pull out the ID of the template and insert it into the
action, so this path will always work, even if you rename the template. Note that you’re also
adding the i18n tags you saw earlier to ensure that this form can be localized:
<form method="post"
tal:attributes="action template/id;">
<legend i18n:translate="legend_feedback_form">
Website Feedback
</legend>
The following is the start of the row for the e-mail address. You’ll define a variable here
called error_email_address that’s set to an error string if there’s a suitable string in the errors
dictionaries. That error value will be generated by the validator should there be an error:
<div class="field"
tal:attributes="class python:test(error_email_address,
’field error’, ’field’)">
tal:define="error_email_address errors/email_address|nothing;">
The following is the label for the e-mail address field. In this label you’ll include a div for the
help text. The span element will become the now-familiar red dot next to the label so that the
user knows it’s required:
<label i18n:translate="label_email_address">Your email address</label>
<span class="fieldRequired" title="Required">(Required)</span>
<div class="formHelp"
i18n:translate="label_email_address_help">
Enter your email address.
</div>
Next you’ll add the actual element:
<div tal:condition="error_email_address">
<tal:block i18n:translate=""
content="error_email_address">Error
</tal:block>
</div>
161
<input type="text" name="email_address"
tal:attributes="tabindex tabindex/next;
value request/email_address|nothing" />
</div>
At the top of this block, you test to see if there’s an error. If there is, the class for the element
changed to be the field error* class; this class will show a nice orange box around the field. Next,
if an error has occurred for this field (as you’ve already tested for), the corresponding message
will be displayed. Finally, you’ll show the form element, and if there’s a value for *email_address
already in the request, you’ll populate the form element with that value.
The tabindex is a useful tool in Plone. It contains a sequential number that’s incremented for
each element, and each time it sets a new HTML tabindex value for each element in a form.
This is a nice user interface feature; it means each form element can be safely moved around
without having to worry about remembering the tabindex numbers because that’ll happen
automatically.
That’s a lot of work for one element, but it’s mostly boilerplate code; you can easily copy or
change it. You can find the remainder of the form in Appendix B.
6.1.4.2.2 Creating a Validator
In the example you have only one required element (the e-mail), so it’s a simple piece of
Python called validEmail.vpy that does the work. The contents of this script are as follows:
email = context.REQUEST.get(’email_address’, None)
if not email:
state.setError(’email_address’, ’Email is required’,
new_status=’failure’)
if state.getErrors():
state.set(portal_status_message=’Please correct the errors.’)
return state
If no e-mail address can be found, this script adds an error to the dictionary of errors with the
key of email_address and a message. This key is used in the page template to see if an error
occurred on that particular field.
6.1.4.2.3 Processing the Script
This example has a simple e-mail script that gets the values (which are already validated) and
forms an e-mail out of them. This is a Controller Python Script object; it’s just like a standard
Script (Python) object except that it has a state variable, and, like the Controller Page Template,
you can give it actions for when it succeeds:
mhost = context.MailHost
emailAddress = context.REQUEST.get(’email_address’)
administratorEmailAddress = context.email_from_address
162
comments = context.REQUEST.get(’comments’)
# the message format, %s will be filled in from data
message = """
From: %s
To: %s
Subject: Website Feedback
%s
URL: %s """
# format the message
message = message % (
emailAddress,
administratorEmailAddress,
comments,
context.absolute_url())
mhost.send(message)
You’ve now seen a simple script for sending e-mail. This is a common script that you’ll see
again and again. Basically, the MailHost object in Plone will take an e-mail as a string, as long
as it conforms to the Request for Comment (RFC) specification for e-mail that has From and To
addresses.
In this e-mail, you take the administrator address you specified in the portal setup and send
the e-mail to that person. The only extra part in this script is the addition of setting the state.
This will set a message that provides some feedback to the user:
screenMsg = "Comments sent, thank you."
state.setKwargs( {’portal_status_message’:screenMsg} )
return state
6.1.4.2.4 Binding the Three Parts Together
At the moment, however, three separate entities exist: a form, a validator, and an action script.
These need to be tied together to form the chain, so you’ll return to the Controller Template
object. Click the Validator tab, and enter a new validator that points to the validEmail script.
You’ll also add a success action if the processing is correct to traverse to the sendEmail script
(Expression: ’string:sendEmail’)n the sendEmail script, you can now add another traversal back
to feedbackForm (Expression: ’string:feedBackForm’) so that after sendEmail happens correctly,
the user will be sent back to the original page.
NOTE A much more complete e-mail validation script appears in Plone called vali-
date_emailaddr, which checks that the e-mail is in the right format. If you want to use this
script instead, you can point the validator to this script.
163
That’s it you’re done! You should now be able to test the form on the book’s Web site. To make
it even easier, I made a Feedback tab, which points to the feedbackForm template, and from
there you can now give feedback to me about this book!
7 Chapter 7
7.1 Customizing the Look and Feel of Plone
Over the previous two chapters I’ve covered some of the core components of Plone’s user
interface, including Script (Python) objects and page templates. It’s now time to cover exactly
how you construct the look and feel of a Plone site. This chapter includes the objects from
previous chapters and introduces some new ones.
To start, I’ll cover all the key definitions and Plone elements that comprise a site. I’ll define
terms you may have already heard of, such as skins and layers. Then I’ll cover customizing the
Plone user interface, concentrating on the power that Cascading Style Sheets (CSS) bring to
the site developer. I’ll run through the key variables and show how you can change them. I’ll
then revisit the customization of logos and skin elements, pulling together all the topics I’ve
briefly covered over the past three chapters.
Then, I’ll show how to make a new skin and explain the techniques for developing all this on
the file system.
Finally, I’ll finish this chapter with an example site. Specifically, I’ll show the Maestro site,
which was used by NASA to distribute data about the Mars rovers. This is a high-traffic Web
site built in Plone, and the skin provides an excellent case study for customizing a site. This
real-life example of how you can customize and modify a Plone site will enable you to do the
same on your own.
7.1.1 Introducing Plone Skins
When a document displays in Plone, the content of that document displays in the now-familiar
Plone green and blue interface. A skin determines exactly how that document displays to
the user, including the images and styles surrounding the content. A skin groups elements,
wrapping that piece of content, and presents them in a certain manner.
To generate the representation that a user sees, a skin has many elements, including the static
items, such as images, and the dynamic pieces, such as scripts. In the previous chapter, the
feedback form was an example of adding a few elements to a skin to create new elements of
a skin. That example contained logic in the format of a Python (Script) object and new pages
in the format of page templates. You added those elements to the skin so that a user could
interact with the form.
164
165
You can use as much or as little of an existing skin as you want to assemble a new skin with
Plone. You can make the skin from minor tweaks or major changes, as you can see on com-
munity sites such as http://www.zopezen.org and http://www.zopera.org. Each Plone site must
have at least one skin, which will be used as the default, but it can have as many skins as the
site developer wants. A user may optionally flip between skins, should the site developer want
to allow the user to do so, although I’ve found this is rarely the case.
The default Plone skin is the one you see on a Plone site such as http://www.plone.org, with
the familiar blue and green interface. But Plone doesn’t have to look at all like, or be even
vaguely recognizable as, a Plone site; its look is entirely up to you. Take, for example, the
list of sites available at http://www.plone.org/about/sites; these sites each provide a different
and custom experience for the user. In most cases, these sites can easily flip between skins and
provide different looks for users. Other sites use the power and flexibility of the Plone interface
internally for creating and editing content while providing a totally different look to external
users.
I’ve seen many questions on mailing lists such as, does Plone have to look like a Plone site? Can
it look one way to one user and another way to another user? Can it look like my corporate
site? The answer to all of these questions is ”yes“: the only limit is your imagination (and the
amount of time you can spend on customizing your site).
7.1.1.1 Using Layers Within a Skin
A skin is divided into logical collections of templates and scripts called layers. Altering these
individual collections allows a user to easily add components to a skin or remove them. The
layers are represented in a skin by a hierarchical list of folders. Each layer matches a name of
a folder, and each folder contains the skin elements.
For example, a skin can have the following layers:
custom, gruf, plone_ecmascript, plone_wysiwyg ...
The order of the layers in that list is the key factor to how Plone finds the elements. When an
element, such as logo.jpg, is requested from the skin, the skin looks through the layers to find
the element. The skin starts by looking at the first layer assigned to that skin (in this example,
custom). If the skin can’t find the element in the first layer, it moves to the second layer (in
this example, gruf). It continues looking through the list of layers until it finds the element for
which it’s looking. If it can’t find the element, then a 404 error is raised and returned to the
browser.
A similar concept is the use of the PATH environment variable in most systems. When entering
a command or looking for a program, the operating system looks through the directories on
the file system as specified in the PATH environment variable. A similar approach happens for
layers, where the layers are looked through in order to find that element.
By allowing higher layers to take precedence over lower layers, developers and administrators
now have the ability to customize and manipulate their site through the layers. If you don’t
like a particular element of a Plone skin, then by moving that element up a layer, you can
custom the result. You can sort your skins and layers in Plone with the portal_skins tool, which
I’ll cover next.
166
7.1.1.2 Managing Skins with the portal_skins Tool
You use the portal_skins tool in Plone to define the skin and layers behavior. The portal_skins
tool also provides a service and application programming interface (API) for creating and using
skins.
To access the portal_skins tool, go to the Zope Management Interface (ZMI) and click por-
tal_skins. You’ll see two key screens in the ZMI; the first, the Contents tab, shows all the
folders and file system directory views (FSDVs) located within this tool (see Figure 7-1).
Figure 7-1. The contents of the portal_skins tool in a standard Plone installation
167
All of the folders and file system directory views on the Contents tab aren’t layers by default,
but now you can turn them into layers. Further, the second important screen, the Properties
tab, shows all the skins and layers you’ve defined in your Plone site (see Figure 7-2).
Figure 7-2. The skins and layers in a standard Plone installation
As Figure 7-2 shows, the list of these layers is quite long. Although this may seem intimidating,
this hefty number of layers gives the developer a large degree of flexibility and reuse. Each
skin displays on the left, with a text area to the right displaying all the layers within that skin.
As I mentioned earlier, Plone searches the layers from the top to the bottom to locate elements.
Each layer is the name of a folder or FSDV from the Contents tab. In Figure 7-2, you can see a
plone_ecmascript directory, and in Figure 7-1 you can see the matching FSDV object.
An FSDV is a new object that provides a useful ability in Plone; it allows direct access to skin
elements that are defined on the file system instead of from the Zope object database as usual.
FSDVs make development and customization easier. By reading objects directly from the file
system, it’s much easier for developers to write and edit the code that produces the site. When
you install Plone, the skin is written onto the file system. When you customize an object, you
make a local copy inside your Plone database. Using an FSDV allows you to maintain clean
separation between code you’ve downloaded from the Web and code that has been customized
in your local instance.
168
Plone 2 ships with two skins, Plone Default and Plone Tableless. Plone Default uses tables to
render the main body flanked by two table cells on either side, containing the left and right
slots. For browser compatibility, this is the default setup. However, if you switch to Plone
Tableless, then you’ll get a skin that looks the same, except there are no tables used to produce
the page, which gives you, as the site developer, more flexibility. At the time of writing, the
Plone Tableless skin can be a little problematic on some browsers, such as Internet Explorer. I
hope in the future that the Plone Tableless skin will become the default.
To change the skin, scroll to the bottom of the form where you see the Default Skin value and
select the default skin from the list of choices. If you select the Skin Flexibility option, then
users will be able to choose their own skin from the my preferences section.
Returning to the Contents tab of the portal_skins tool, you can see that some of the folders-
for example, custom-are standard folders that exist in Zope. These have the normal folder
icon. Others-for example, plone_images-are FSDVs that point to areas of the file system. These
have the folder icon with a green lock inside it. This lock indicates that you can’t add or edit
elements in an FSDV through the Web; you can do it only through the file system.
To see where the files for an FSDV reside on your hard drive, click the Properties tab of the
FSDV For example, from the Contents tab of the portal_skins tool, click Properties, and it
.
will list the file system path of CMFPlone/skins/plone_images. This path is the location of
this directory on the file system relative to the instance home you specified in the installation
process. Because you can see files through the Web in the FSDV or on the file system, you can
access them for reading either way. Because viewing files through a file system is generally
more friendly and easy to do, I’ll refer to a file as a path in the file system, which can be
accessed using familiar tools.
7.1.2 Customizing Skins
You’ve seen how skins and layers interact. Now you’ll see how you can customize a Plone
site. I’ll start by returning to the example in Chapter 4, where you learned how to customize
the logo. Using your new knowledge of how skins work, you’ll be able to follow along and
customize the skin. Then I’ll move onto showing the power of the Plone CSS and how you can
customize it. Finally, I’ll cover the main template you saw in earlier chapters and go through
all the elements of it.
7.1.2.1 Customizing the Logo, Revisited
In Chapter 4 you learned how to customize the logo in the top-left corner of a Plone site, but I
skipped over what was actually happening. The section revisits that example.
The logo.jpg image is the image that appears in the top-left corner of every page. You’ll now
look at what happens when a browser tries to render this page. Once Plone receives the request
for that image, it looks through the layers to find logo.jpg. In a default site, this is the item in
plone_images, called logo.jpg. Because this is an FSDV as I mentioned earlier, you’re unable to
,
alter the image through the Web. To guard your site against future changes, you don’t want to
be able to change it on the file system either. Instead, take a closer look at what the Customize
169
button does. If you look at that button again, you can see that there is, to the left of that
button, a drop-down list of folders in the contents of the portal_skins tool.
NOTE The folders listed are ones that exist inside the Zope database. FSDVs aren’t included in
the drop-down list; by default, it shows only folders.
Clicking the Customize button makes a local copy of the item in the folder selected in the drop-
down box. By default that folder is the custom folder, so now you have a copy in the custom
folder. When Plone looks up the item, logo.jpg, it will access the version in the custom folder.
Looking again at the layers for the Plone Default skin, the custom folder is the topmost layer of
the skin. Hence, when logo.jpg is called, it will find the image in the custom layer. This is why
the new logo.jpg is rendered.
Placing customized items into the custom folder is the quickest way to start tweaking your
Plone site. The custom folder is a standard Plone folder, so you can put as many items in there
as you want in order to override earlier elements.
7.1.2.2 Introducing Plone’s Cascading Style Sheets
The visual representation of a Plone site in a browser is put together almost entirely using CSS.
Perhaps the easiest way to see exactly what the CSS does for a Plone site is to compare Figures
7-3 and 7-4. The first shows Plone with style sheets, and the second shows Plone without any
style sheets.
170
Figure 7-3. Plone with style sheets
171
Figure 7-4. Plone without style sheets
TIP If you want to reproduce this, turn off style sheets in your browser. Internet Explorer
doesn’t let you easily do this, but Firefox (http://www.mozilla.org/products/firefox/), the
Mozilla-based open-source browser, lets you easily do this. In Firefox, select Tools - Web Devel-
oper - Disable - Disable Styles. With a large number of CSS and other developer tools, Firefox
is the browser of choice for many Plone developers.
The difference is striking to say the least. CSS provides not only the visual representation of
pages but also the layout. By changing the CSS, you can change this visual representation and
layout on a Plone site (within the constraints of CSS).
Having the presentation of Plone produced by CSS is an impressive achievement
172
used by many talented user interface developers. The following are some of the
benefits of having a CSS layout:
• CSS provides a layer of separation between the presentation and the
templates that generate the presentation.
• You can make a large number of changes without having to touch
the underlying templates. All that’s needed is an experienced CSS
developer.
• CSS makes the site faster by sending smaller files. Each Hypertext
Markup Language (HTML) file is smaller since the layout for the site
isn’t contained in HTML markup but in the CSS, which can then be
cached.
• CSS allows you to customize the look and feel without breaking un-
derlying accessibility work.
7.1.2.2.1 Code Layers
When a Plone page is rendered, at least three layers of code create a page. For the example of
the tabs that appear across the top of a Plone site, this is how they’re assembled:
So rather than asking yourself, how can I customize the tabs? you need to consider exactly
what customization you want to perform. This could mean changing the CSS, the HTML, the
data, or the underlying tabs. The general rules are as follows:
In fact, Plone is so customizable, on so many levels, that it’s easy to worry about which bit to
customize. To make sure that future Plone template changes don’t compromise your applica-
tion’s design, try not to customize the templates. Instead, I recommend you try the CSS or the
actions first. This way, when the templates change in future versions of Plone, there will be
less chance of a problem.
7.1.2.3 Customizing the Font, Colors, and Spacing
The actual style sheet that does most of the work, plone.css, has a number of variables
in it populated using Dynamic HTML (DTML). I do not cover DTML in this book; this is
probably the only use of it in Plone, so if you don’t already know DTML, I recommend
you avoid learning it, if possible! The Zope Page Templates system will give you every-
thing you need. Excellent online references do exist for DTML for Zope, however; refer to
http://zope.org/Documentation/Books/ZopeBook/2_6Edition/DTML.stx.
The DTML syntax for this style sheet is actually pretty simple; each variable relates to a cor-
responding attribute in a property sheet. To access this property sheet, click portal_skins,
plone_styles, and then base_properties. In Figure 7-5, you can see how this file looks in the
ZMI.
173
Figure 7-5. The base properties for the style sheet
For example, dtml-fontColor; locates the variable fontColor and places it in the style sheet, so
the fontColor here will be black. Now you can see where that variable is referenced in the
plone.css file. To access the CSS file, click portal_skins, portal_skins, and then plone.css. In
this file you can see that mainFontColor is referenced in quite a few places; for example, it’s
referenced in the main body of a page, like so:
body {
font: dtml-fontBaseSize; <dtml-var fontFamily>;
background-color: dtml-backgroundColor;;
174
color: dtml-fontColor;;
margin: 0;
padding: 0;
}
You could keep reading through the style sheet, if you really wanted, but changing the variable
is always a quick way to see exactly what it affects.
Returning to the ZMI, click portal_skins, click plone_styles, click base_properties, and then click
the Customize button. As you’ve seen, this will create an object in the ZMI that you can cus-
tomize. This time the customized object is actually a folder that has the properties contained
in the folder. To access the properties you’ve just customized, click portal_skins, click custom,
and then click base_properties. Next, select the Properties tab (see Figure 7-6).
175
Figure 7-6. The properties of the folder
This property list allows you to change the properties of mainColor to something different,
for example, red or #cc9900. Change the value of that property, and click Save Changes.
Returning to the Plone site, you should now see the nice, new color.
In Chapter 4 you saw an example where, to change a tab on the top of a page, users could
change the actions. Although you may type an action with an uppercase first character (such as
Members), it then displays in lowercase letters on the Web page. This because CSS transforms
the text to lowercase because of the textTransform property in the property sheet. To stop this
transformation, change the property for textTransform to none.
In the style sheet, properties are defined for all the colors, spacing, and fonts that are used in
176
a Plone site. Table 7-1 describes all the parameters.
Table 7-1. CSS Properties
Variable Name Description
logoName The filename of the portal logo
fontFamily The font family used for all text that isn’t a header
fontBaseSize The base font size from which everything is calculated
fontColor The main font color
backgroundColor The background color
linkColor The color used on normal links
linkActiveColor The color used on active links
linkVisitedColor The color used on visited links
borderWidth The width of most borders in Plone
borderStyle The style of the border lines (usually solid)
borderStyleAnnotations The style of border lines on comments, and so on
globalBorderColor The border color used on the main tabs, the portlets, and so on
globalBackgroundColor The background color for the selected tabs, portlet headings, and
so on
globalFontColor The color of the font in the tabs and in portlet headings
headingFontFamily The font family for h1, h2, h3, h4, h5, and h6 headlines
headingFontBaseSize The base size used when calculating the different headline sizes
contentViewBorderColor The tabs’ border color on the Contents tab
contentViewBackgroundCole rcontent view tabs’ background color on the Contents tab
Tho
contentViewFontColor The font color used in the tabs on the Contents tab
textTransform Whether to lowercase text in portlets, tabs, and so on
evenRowBackgroundColorhe background color of even rows in listings
T
oddRowBackgroundColor he background color of even rows in listings
T
notifyBorderColor The border color of notification elements eg: status message and
the calendar
notifyBackgroundColor The background color of notification elements
helpBackgroundColor The background color of the calendar pop-up widget
discreetColor The color of the credits, document byline, form help
portalMinWidth The minimum width of the portal
columnOneWidth The width of the left column
columnTwoWidth The width of the right column
7.1.2.4 Customizing the CSS
If you have small customizations, place them in ploneCustom.css. This is a second style sheet
that’s loaded after plone.css. By using the cascading functionality of style sheets, you can apply
any changes to ploneCustom.css to the overall style sheet.
177
For example, to change the byline that appears on the bottom of every page, simply change
ploneCustom.css. Again, access that file through the ZMI, and then click Customize. This will
create a copy of that style sheet in the custom folder. To alter the byline, move it to the left side
of the page and make it bold, as shown in Figure 7-7.
Figure 7-7. The new bold byline on the left
You do this by adding the following:
div.documentByLine {
text-align: left;
font-weight: bold;
}
Here you’ve set two attributes for the byline element: text-align and font-weight. Note that
you haven’t changed any other attributes of the byline element; the remaining attributes are
inherited from the original style sheet. With a few simple lines of CSS, you’ve changed the site
and made sure that other changes to Plone won’t affect your site. Changing ploneCustom.css is
the best bet for small changes.
By using different style sheets, you can use Plone to provide a different look to different clients.
Often Web sites have a Click for Printable Page button that shows a simpler page, without much
formatting. Plone alleviates this problem by providing a separate style sheet; when a browser
prints the page, that style sheet formats the page. All the alternate style sheets are included
at the top of a page; you can find them by clicking portal_skins, clicking plone_templates, and
then clicking header.pt.
NOTE One style sheet that’s a little unusual is the projection style sheet. It’s supported only by
Opera, and when the browser is used in full-screen mode, headings turn into separate pages
and a presentation-like interface displays.
178
7.1.2.5 Customizing the Main Template
As you saw in the previous chapter, to get the Plone look and feel on a page, you need to use
the master macro from the main_template. Every Plone page uses this macro and then fills
in the appropriate slots. By taking a look at the main template in detail, you can see how
a Plone page is constructed in a page template and then see exactly how you can customize
those individual page elements.
If you look at the main Plone page, you’ll see a number of elements in that page. Figure 7-8
shows a Plone page with all the key user interface elements marked. Table 7-2 describes each
of the elements and their purpose. For each element in Figure 7-8, you’ll find a corresponding
number in the table.
179
Figure 7-8. All the main elements in the Plone user interface
Table 7-2. User Interface Elements
180
Name Description
No.
1 Site logo Shows the top logo.
2 Search form Shows the search form.
3 Portal tabs Shows the tabs across the top of the site.
4 Personal bar Shows the personal information for that user such as login
and my folder.
5 Breadcrumbs Shows the location of the current content.
6 Left slot Determines where portlets added to the left_slot property dis-
play.
7 Content tabs Shows the actions with the category content_tabs for that
piece of content.
8 Content drop-down lists Shows some drop-down menus for this content, workflow and
new content types.
9 Document actions Shows the actions for this particular piece of content: printing
or e-mail.
10 Byline Shows a description of the content and its author.
11 Right slo This is where portlets added to the right_slot property display.
12 Footer Shows information at the bottom of the page.
13 Colophon Shows more information below the footer.
I haven’t covered one section of this template: the content. All the text from Welcome to Plone
down to The Plone Team is content added and edited by the users. This is the main slot in the
page template, which is filled in by a the particular content type or page template, as you’ve
seen. Chapter 6 covered using slots; in that chapter, I showed how, by using the main slot, you
can ensure content appears inside a Plone page.
So, given these components of your Plone page, how do you customize a particular part? The
answer is to find the matching part of the main_template, see which part it calls, and then
customize that. For this reason, I’ll cover the main template in detail.
At first glance, the main template looks quite long and complicated, but it’s mostly all macros,
and its main purpose is simply to pull content from other areas. You can find the main template
by clicking portal_skins, clicking* plone_templates*, and then clicking* main_template*.
The philosophy behind the main template is that a user shouldn’t have to alter the actual
configuration of the template, unless there are major changes planned. Because the main
template pulls all the content from other places inside Plone, you can alter the assembled page
by customizing those individual elements. This means you can alter just the sections you’d like
to change rather than altering the whole template.
The main template uses Extensible Markup Language (XML) namespaces heavily to present
the simplest possible metal code. For example:
<metal:headslot define-slot="head_slot" />
<!-- A slot where you can insert elements in the header from a template -->
181
Here, the name of the tag isn’t a standard Extensible HTML (XHTML) element; instead, it uses
the metal: prefix to define a namespace as metal:headslot. This has the following advantages:
• The element headslot is semantic, in that it describes the element. It’s easy to
spot that this is the slot for adding anything you may want to add to the head
of your page.
• Attributes in that element use the namespace in the element if not otherwise
declared; so, instead of metal:fill-slot, you can just use fill-slot.
• The actual tag isn’t a valid XHTML tag, so it won’t display. However, if the
rendering of the tag generates any valid XHTML, that XHTML will display.
When a macro is used, the content in the calling template is removed, so it’s possible to place
comments in the calling template as text inside the macro. For example:
<div metal:use-macro="here/global_searchbox/macros/quick_search">
The quicksearch box, normally placed at the top right
</div>
Because of the comment, it’s easy to determine that this macro refers to the search box in the
top-right corner of a site (element 2 in Figure 7-8). To see the macro, find the script named
global_searchbox and the quick_search macro contained within it. The main template continues
through main macros, pulling information from different templates and scripts, and builds the
page as it goes.
After this section, the main template reaches the main content of the page, which is the object
being rendered. In Chapter 6 I explained the difference between a slot and a macro; recall that
a template defines slots that are then filled by the content. Really there’s only one slot of any
importance for content, and I’ve mentioned it many times: the main slot.
One common pattern in Plone that may be confusing is how to define a slot inside a fill slot.
For example, the following is the definition for the css_slot:
<metal:cssslot fill-slot="css_slot">
<!-- A slot where you can insert CSS from a template -->
<metal:cssslot define-slot="css_slot" />
</metal:cssslot>
This design pattern looks a little odd, but it defines the slot and then re-creates the fill slot. If
you look at the main template carefully, those slots are actually inside the header use-macro,
so the header macro may fill this slot. But you also want the end template to fill the slot, so for
this reason the slot is redefined. This means one slot can now be filled in two places, which is
a useful technique for changing the templates.
Scanning down through the rest of main template, you’ll reach the left and right columns, the
footers, and the colophon. Note that the left column may appear before the main content of
a page (if your language reads from left to right anyway), but the style sheet moves it there.
This ensures that if you visit the site in a text-only browser, the main content appears first, not
after all the navigation options.
182
Table 7-3 describes the macros and slots in the main template.
Table 7-3. Main Template Macros and Slots
183
Name Description Slot or Macro
Cache headers Sets the Hypertext Transfer Protocol Macro: in
cacheheaders
(HTTP) cache headers for the con- global_cache_settings
tent.
Head slot Allows content to add to the head ele- Slot: head_slot
ment of a page.
CSS slot Allows content to add custom CSS for Slot: css_slot
the page.
JavaScript head Allows content to add custom Slot: javascript_head_slot
slot JavaScript to the page.
Macro: in
Site actions The site actions allow you to have a site_actions
series of actions above the search. By global_siteactions
default these allow you to change the
font size.
Quick search The quick search box show in the top- Macro: in
quick_search
right corner. global_searchbox
Portal tabs The (normally blue) portal tabs that Macro: in
portal_tabs
are normally at the top left. The ac- global_sections
tual tabs shown are determined by ac-
tions. This determines how the tabs
are rendered in HTML.
Personal bar The personal bar in the top right: lo- Macro: in
personal_bar
gin, logout, and so on. global_personalbar
Path bar The path breadcrumbs that start with Macro: path_bar in global_pathbar
”You are here.“
Macro: in
Content views The (normally green) tabs across the content_views
top of content. This will show only if global_contentviews
the content is editable by the current
user. The actual tabs shown are de-
termined by actions. This determines
how the tabs are rendered in HTML.
Content actions The little drop-down actions in the Macro: in
content_actions
top-right corner of the context bar. global_contentviews
Portal status A message shown whenever some- Macro: in
portal_message
message thing changes. global_statusmessage
Header The header on a piece of content. Slot: header
Main The main part of a piece of content. Slot: main
Sub The bottom part of a piece of content Slot: sub
where the comments on an object will
appear.
184
Name Description Slot or Macro
Macro: in
Left portlets The slots or portlets show on the left left_column
of a page. There are a few definitions portlets_fetcher
here: column-one-slot is the whole left
column, and portlets-one-slot is then
the slot. If neither of these slots is de-
fined, it calls the macro.
Right portlets The slots or portlets show on the right Macro: in
right_column
of a page. See the left portlets. portlets_fetcher
Footer Copyright and other message. Macro: portal_footer in footer
Colophon Miscellaneous messages for the bot- Macro: colophon in colophon
tom.
Armed with this information, it’s now a matter of customizing the macro or the slot to change
the look and feel of the page. Again, it’s recommended not to actually customize the main
template itself but to instead customize the parts that the main template calls. The next section
shows a few example customizations you can make to Plone.
7.1.2.6 Examining Example Customization Snippets
The following sections show some examples that demonstrate simple customizations you can
do to your Plone site. Some solutions provide one or two different ways of performing the
same task.
7.1.2.7 Removing a Block
One rather neat trick is to be able to easily remove a block from the user interface such as the
path bar or the search box. You have two ways to do this; the most obvious is to customize
the macro that displays the element. For example, to remove the breadcrumbs, you could click
portal_skins, click plone_templates, click global_pathbar, and then turn off the element in the
page template level; for example, you can change the following:
<div metal:define-macro="path_bar"
id="pathBar"
tal:define="breadcrumbs python:here.breadcrumbs(here);
portal_url portal_url|here/portal_url">
To this, add the following line of code:
<div metal:define-macro="path_bar"
id="portal-breadcrumbs"
tal:condition="nothing"
tal:define="breadcrumbs python:here.breadcrumbs(here);
portal_url portal_url|here/portal_url">
185
This means customizing a page template, which isn’t a problem at all and is by now something
with which you should be familiar. The slightly different approach is that you can hide ele-
ments at the CSS level. This still means the item is rendered and HTML is generated, but it’s
then turned off for the client-they can’t see it. Because the generation of the HTML still occurs,
this is a suboptimal solution, but it’s a neat trick.
Most elements in Plone have a unique Document Object Model (DOM) element ID; for exam-
ple, in the case of the breadcrumbs, it’s portal-breadcrumbs, as you can see in the previous
code. To stop showing the portal-breadcrumbs, simply add the following to ploneCustom.css:
#portal-breadcrumbs {
display: none;
}
7.1.2.8 Changing Portal Tabs
I’ve already shown you how you can change the text of the portal tabs if you change the
actions. They’re displayed using the style sheet, not using tables (although users may initially
think so). Looking at Table 7-3, you can see that the code for the portal tabs is portalTabs. To
make the border of the nonselected tabs dotted, you can simply change the ploneCustom style
sheet to the following:
#portal-globalnav li a {
border: 1px dotted;
}
The tabs are a series of HTML list (li) and anchor (a) elements, so by changing the CSS for
these elements, you can change the tabs’ appearance. Later in the ”Case Study: Examining the
NASA Skin“ section I’ll show how to change these tabs into images.
By using CSS you can also move any element’s location with the position attribute. Next, move
your tabs to the top of the screen, above the logo and search box. To do this, you use the
absolute value of the position, which lets you define the position by using the left, right, top,
and bottom attributes. Add the following to your ploneCustom style sheet to place the portal
tabs at the top of your Plone site:
#portal-globalnav {
position: absolute;
top: 0em;
}
This is a powerful technique for moving elements. You have multiple options for positioning
the elements, including relative positioning, but that takes a little work with CSS to get the
positioning just right.
186
7.1.2.9 Moving the Left and Right Slots
I discussed the left and right slots in Chapter 4, and I’ve shown you how to add a new slot
to the list of slots. You may have noticed that the terms left and right slots can be a little
misleading. The default is to show the slots in those positions, but it’s easy to move them.
NOTE This works only when you’re using the Plone Tableless skin. This isn’t the default setting,
so you’ll have to change the skin in the portal_skins tool, as discussed earlier in the ”Managing
Skins with the portal_skins Tool*.
For example, if you wanted to move the left portlets to the right side of the page, then you
could do this by changing ploneCustom.css to the following:
#portal-column-one {
float: right;
}
#portal-column-content {
float: left;
}
This moves the leftmost column to the right and pushes the main section to the left.
7.1.2.10 Hiding Help in Forms
If you wanted to hide the help in all the forms, you couldn’t realistically change all the tem-
plates. But you could employ a similar tactic to hiding the path bar-and just set display: none
for the form elements. The following has the desired effect of not placing the input element
on a new line:
div.formHelp {
display: none;
}
Figure 7-9 shows the feedback page without breadcrumbs, with the help hidden, with dotted
tabs, and with the left slot moved to the right of the page, all changed with only a few lines of
CSS.
187
Figure 7-9. The combined effect of some of the examples
7.1.2.11 How Do You Find Element X?
As I’ve shown, the templates, scripts, and images contained in the skins directory of a Plone
installation create a Plone skin. Many files live in that directory, so going through every file
would be long and counterproductive when those files change. Instead, it’s useful to under-
stand some basic techniques for finding the elements you want to alter.
Bear in mind at which level you want to customize the element. As noted earlier, you have
three levels for rendering an object. If you want to change the visual representation, or its
placement, then the chances are that you can change the CSS and do no further work.
If CSS isn’t sufficient, then your next best bet is to search through the templates. For example,
suppose you want to change the text that appears on the page when a user logs in, or you want
188
to change the entire page. In this example, you’ll alter the page shown in Figure 7-10 to make
it a script that does something unusual.
Figure 7-10. The “You are now logged in” page
Some clues exist to find this template so you can alter it; I’ll run through each of them in turn
now.
189
7.1.2.12 Searching by Using the URL
The Uniform Resource Locator (URL) to a page translates to a series of objects in Plone that
are traversed. In Figure 7-11, I’ve traversed to the login_success page. In this case, the final
part of the URL is login_success, as you can see in the address bar in Figure 7-11. When an
object is loaded into an FSDV the extension is stripped off, so you’re looking for a template or
,
script that starts with login_success.
Figure 7-11. Searching for an ID
In Zope you can perform this search by going to the portal_skins tool and clicking the Find tab.
Once there, enter login_success in the with ids field. Leave all the other settings the same,
190
and click the Find button. Sure enough, you’ll find the login_success template.
You can also conduct this search on the file system, depending upon your operating system
and the tools available. The quickest way to find this file in Linux is to go to your CMFPlone
directory and do the following:
$ cd skins
$ find -name ’login_success*’ -print
./plone_forms/login_success.pt
On Windows, open the CMFPlone folder in Windows Explorer and click the Search tab. Then
enter the name of the file as login_success, and click Search. This should give you a list of
likely files.
This search should provide the result, CMFPlone/plone_forms/login_success.pt. If you per-
form the same search in the ZMI, you click portal_skins, click plone_forms, and then click
login_success.
7.1.2.13 Searching for a Piece of Text
A rather crude approach that’s somewhat successful is to do a full-text search on the code to
find the element that renders the page. For example, looking at the page in Figure 7-12, you
can see that it contains the text Notice that the top. The simplest way to find the bit that renders
that text is to search for it.
191
Figure 7-12. Searching for text
In Zope you can also perform this search by going to the portal_skins tool and clicking the Find
tab. Once there, enter Notice that the top in the containing field. Leave all the other settings
the same, and click the Find button. Sure enough, you’ll find the login_success template.
You can also conduct this search on the file system, depending upon your operating system
and the tools available. The quickest way to find this file in Linux is to go to your CMFPlone
directory and do the following:
$ grep -ri "Notice that the top" *
plone_forms/login_success.pt: Notice that the top
192
On Windows, open the CMFPlone folder in Windows Explorer and click the Search* tab. Then
enter the contents of the file as **Notice that the top*, and click Search. This should give you a list
of likely files. Using this rather crude technique, you’ve been given a template, login_success,
that renders the message back to the user.
This technique has the following issues:
• Beware of lowercasing the content in CSS; always make your searches case
insensitive (the default in Windows). It’s annoying to search for home when
it’s Home in the template and it’s lowercased in CSS.
• If you’re trying to do this in a language other than English, the content may
have been localized, causing the search to fail.
• Occasionally there may not be searchable text that will match up; in this case,
looking up via URL is the recommended approach.
7.1.3 Making New Skins and Layers
So far I’ve talked about customizing the existing skin. The process for making a whole new
skin or a new layer isn’t actually that different. I’ll cover one key point, putting your templates
and scripts on the file system.
Making templates and scripts on the file system and creating new skins and layers are def-
initely the best way to go for long-term maintainability and flexibility. Not only is creating
skin elements much easier in familiar tools on the file system, but it also allows you to easily
redistribute your code. Writing on the file system is the style of choice for almost all Plone
developers, with minor modifications in the custom directory if needed.
7.1.3.1 Making a New Skin
As you’ve seen, a skin is actually nothing more than a collection of layers. For my new skin, I
wanted to place all my custom code in one place, so I went to the portal_skins tool, added a
new folder, and gave it the ID of custom_chrome.
Then, to add a new skin, you have to click portal_skins, select the Properties tab, and add a
new skin under the text Add a new skin. You’ll need to enter a series of layers that you want to
set up for this skin. In this example, I added a new skin called Custom Chrome and a series of
layers, as shown in Figure 7-13.
193
Figure 7-13. Adding the Custom Chrome skin
Then I added the layers for the skin. In this case, the skin didn’t have a layer called
custom in it; instead it had a folder called custom_chrome. You now have two skins
that use two layers and two folders. Any objects added to the custom_chrome folder
will affect that skin, not the Plone Default skin.
7.1.3.2 Using Multiple Skins
As mentioned, a standard Plone site has two skins, Plone Default and Plone Tableless. In the
previous section, I added a new skin, Custom Chrome. As I discussed in Chapter 4, you can
194
set the default skin using the Plone interface. Click plone setup, and then click the Portal Skin
button. This mirrors the choices available in the ZMI after clicking portal_skins, selecting the
Properties tab, and scrolling to the bottom of the page.
You have one more option, though: REQUEST *variable name*. This is the request variable
that will contain the user’s skin information. This is plone_skin by default, which is the cookie
name. But it can also be passed through other request variables such as the query string. It’s
available only through the ZMI.
You can also set skins programatically. This allows developers to show different skins to dif-
ferent users depending upon certain business or site logic. For example, if a user is writing
content for a site, they may see the standard Plone skin. If they’re an anonymous user, then
they can see a totally different skin. Rather than letting the user choose, the site is making that
decision. If you really want, you could base the skin on the folder they’re accessing; however,
that approach can lead to confusion, so I don’t recommend it.
To change the skin, add a Script (Python) object called setSkin in the root of your Plone site.
Then add the following code:
##title=Skin changing script
##parameters=
req = context.REQUEST
if req[’SERVER_URL’].find(’internal.somesite.org’) > -1:
context.changeSkin("Plone Default")
context.changeSkin("Custom Chrome")
The actual logic for determining the skin will depend upon the site’s business rules. In this case
anyone accessing http://internal.somesite.org will get the Plone Default skin, and anyone ac-
cessing http://external.somesite.org will get the Custom Chrome skin. Unfortunately, one catch
is that you can’t determine the skin on the security level of the user (for example, authenti-
cated users see one skin, and managers see another). This rather obvious need isn’t possible
at the time of writing, without severely hacking a Plone site.
NOTE Basing the skin on untrusted client information is common practice but not completely
secure because you’re trusting the information from the client. Making sure this is secure
depends on your particular network settings. In most cases, you can handle this easily at the
firewall or using a proxy server such as Apache, which could be configured to block all external
requests to http://internal.somesite.org. I discuss integration with Apache in Chapter 10.
To activate this code, assign an access rule to this object. This means that each time this Plone
site is accessed, this Script (Python) object will be executed. Each time the script is run, the
skin will be set according to the script. To assign a rule to this script, select Set Access Rule
from the drop-down menu and then enter the name of your Python (Script) object. Now test
by visiting your site, and see what skin you get.
You do have to be careful with access rules because they occur on every invocation of that
folder (or Plone site); you have to ensure that they’re correct and that nothing bad can happen
in them. If you’ve accidentally written a bad or incorrect Script (Python) object and can’t even
get access back into the ZMI to fix it, then you can turn off access rules by restarting Plone
with the following environment variable:
195
SUPPRESS_ACCESSRULE = 1
Appendix B explains how to set environment variables if you’re unfamiliar with this process.
7.1.3.3 Making a New Skin on the File System
Throughout these chapters I’ve been using the ZMI. But what most Plone developers use for
any serious work is the file system. Making a skin on the file system is actually easy.
Go to the instance home directory of your Plone installation. Inside the Products directory,
make a new directory; the name of this directory is the name of the product, so the conven-
tion is something short, with no spaces or underscores and mixed case. PloneBookExample,
CMFPlone, and PloneSilverCity are all examples. Inside that folder, make a new file called
__init__.py and a directory called skins. In the __init__.py file, you need to add the following
two lines:
from Products.CMFCore import DirectoryView
DirectoryView.registerDirectory(’skins’, globals())
Next, restart Plone, and then click portal_skins to add an FSDV*.* This will open a list of
the registered directories. Scroll down until you find the one that matches the directory you
registered; this will be the name of the directory with /skins on the end. Enter an ID that
makes sense, and click Add. You now have an empty directory where you can go to add layers
of your skin.
7.1.3.4 Debugging Skins
Another reason I’ve repeatedly been using the ZMI with you, rather than the file system, is that
it gives feedback about errors and gets you comfortable with placing objects inside others. A
further positive feature about using the ZMI is that changes are instantaneous. If you change
an object and then refresh, you see the change immediately (assuming you have no cache).
This isn’t the case with the file system. If you change something in the file system, it isn’t
updated in Plone. This is for performance reasons. Plone has no way of knowing you made
that change, so it must update the Zope cached copy of that object. Without getting into file
system notification trickery, a Plone site has two states: production and debug mode. When
Plone is in debug mode, it checks all the directories, finds files that have changed, and then
updates Plone. This means you can make a change, and it will appear immediately. When run
in production mode, however, your changes will not happen until you refresh the skin (see
Chapter 11) or you restart Zope.
For obvious reasons, if you’re developing skins in Plone, then running in debug mode is the way
to go. Chapter 2 showed you how to change the configuration for Plone so that it runs in debug
mode. As a quick recap, open the zope.conf file inside the etc directory of your installation and
ensure the debug-mode directive is set to on.
196
7.1.3.5 Using File System Objects
The FSDVs allow the mapping of only those Zope objects that have been specifically configured
to be used in this manner. It determines the Zope object based on the extension of the filename.
The contents of that file are the contents of one attribute of the object-usually the main content,
such as the binary contents of an image, or the text contents of the template.
To create an object in your empty FSDV just go to the skins directory and start adding files that
,
match the objects you want to make. Once the file is loaded into Zope as a Zope object, that
extension is stripped off. For example, some_template.pt becomes a file system page template
with the ID some_template. Table 7-4 describes the extensions.
Table 7-4. Extensions
Extensions Object Type Equivalent Zope Ob-
ject
.pt, .zpt, .html, .htm Filesystem Page Template Page Template
Controller Filesystem Page Tem- Controller Page Tem-
.cpt
plate plate
Filesystem Script (Python) Script (Python)
.py
Controller Python Script Controller Python Script
.cpy
Controller Validator Controller Validator
.vpy
.doc, .pdf, .swf, .jar Filesystem File File
.cab, .ico, .js, .css Filesystem File File
.gif, .jpg, .jpeg, .png Filesystem Image Image
Filesystem Properties Object Folder with Properties
.props
Filesystem Z SQL Method ZSQL Method
.zsql
Filesystem DTML Method DTML Method
.dtml
So, to get an image in your directory view, dump in a .gif or .jpeg file. If you want a Script
(Python) object, then add a file ending in .py.
7.1.3.6 Setting File System Object Metadata
Extra content for an object such as title, security, or cache is stored in a separate file. That file
is given the same filename as the original file, with .metadata added to the end. If the original
file is logo.jpg, for example, then the metadata will be contained in logo.jpg.metadata.
The metadata file is in the Windows .ini format of key = value pairs. This format has been
extended to contain information about forms for the Form Controller object, which you’ll see
in the next section. All the choices, even the presence of this file, are optional. The following
is a sample file:
[default]
title = Test object
197
cache = RAMCache
proxy = Manager
[security]
Access contents information = 1:Manager,Anonymous
The following are the values you can set in that file:
title: This is the title that’s applied to the object in the ZMI and in Plone; this will
show up in the Plone templates.
cache: This is the ID of the cache object in which you’d like the object to be cached.
By default Plone comes with two cache objects: a RAM Cache Manager and a HTTP
Cache Manager. Chapter 14 discusses the function of these two objects.
proxy: This is the proxy role you’d like to apply to this object. See Chapter 9 for
more information.
security: This is the security area, which allows multiple lines of security settings.
The key contains the name of the permission. The right side contains the acqui-
sition setting, followed by the roles delimited by commas. For example, View =
0:Manager means only users with the member and manager role can see an object,
and security settings aren’t acquired for that permission.
7.1.3.7 Using Validators in the File System
To specify validators on the file system, add the validator to the .metadata file. The validator
section of the .metadata file would look like this:
[validators]
validators = validate_script1, validate_script2
This will run the two validation scripts: validate_script1 and validate_script2, in that order. A
validation script will examine the data and add errors to the form controller state if there’s a
problem.
The contextType and button options need a slightly different syntax. Validations are run on the
context being executed-for example, a document or image. You could have a different validator
execute for the document and for the image. For example, to have a different validator script
run when this is invoked as a document, add the following line:
validators.Document = validate_script2
You can vary the validator depending on the button clicked on the form by appending the
name of the button in the form to the left side of the validator. The name of the button must
begin with form.button. For example:
<input type="submit" name="form.button.button1" value="First" />
198
The metadata file would then look like the following:
validators..button1 = validate_script1
The .. is a space for the context type, so if, as previously, you wanted this to occur for button1
on a document, then the metadata file would look like the following:
validators.Document.button1 = validate_script5
7.1.3.8 Using Actions in the File System
Like validators, you can specify actions in the .metadata file. The syntax for the actions section
of your file would look like this:
[actions]
action.success = traverse_to:string:script1
In the previous example, when the form is submitted and the validation scripts return a status
of success, the traverse to action is called with the argument string:script1. That argument is
actually an expression. The default action for the failure status is to reload the current form.
The form will have access to all the error messages via the state object in its options.
Again, you can specify a particular action on a particular context; for example, to specify an
action for success when on a document, you can do the following:
action.success.Documnent = traverse_to:string:document_script
Again, you can specify the action for the following button:
<input type="submit" name="form.button.button1" value="Button" />
by adding the following to the .metadata file:
action.success..button1 = traverse_to:string:script1
This example has no explicit context given, so it’s valid for any type of context.
*start sidebar*
7.1.3.8.1 Finding Example File System Skins and the Book Examples
All the examples in this book have been collected in a skin for you to install. You can find this
on the Plone book Web site at http://plone-book.agmweb.ca/Software/PloneBookExamples and
on the Apress book Web site at http://www.apress.com. It’s available as a .zip file of the skin;
after you’ve downloaded and unzipped it, you’ll find there’s a file structure similar to the one
mentioned earlier.
199
You have an __init__.py file and skins directory. In the skins directory you’ll find a series of
page templates, Controller Validator objects, and all the matching metadata files. If you want
to install this, then copy the PloneBookExamples folder into the Products directory of your
instance home. Restart Plone, and then click plone setup. Select Add/Remove Products,* and
you’ll see an entry for *PloneBookExamples; check it, and then click install. You’ve now installed
the templates and can go to feedbackForm and get the page template you saw in the previous
chapter.
What the install procedure did was automate the process of adding an FSDV and then added
a layer to each skin. If you click portal_skins and then select the Properties tab, you’ll see that
the new layer plone_book_examples has been added.
*end sidebar*
7.1.4 Case Study: Examining the NASA Skin
In January 2004 two NASA probes landed on Mars: Spirit and Opportunity. These remote-
controlled robots scoured the surface of Mars, returning pictures and analysis of the surface.
The probes were a great success, returning stunning pictures of the surface of Mars that thrilled
the world.
One small part of this cog was a Web site at http://mars.telascience.org. This site published a
program called Maestro. To quote the Web site, its purpose was the following:
You can download a scaled-down version of the program that NASA scientists use to
operate Spirit and Opportunity. Updates are also available for Maestro that contain
real data from Mars that you can add to your copy of Maestro.
Turning to Plone, the group responsible for this site developed a site that looks great quickly
and easily. In this case, a large number of community members and volunteers helped the
members of the Maestro team develop the site. Figure 7-14 shows the working Plone site.
200
Figure 7-14. The Maestro site
You’ll probably recognize some similar signs of a Plone site: the tabs across the top, the per-
sonal bar in the top-right corner, and the usual breadcrumbs. Other than that, the site looks
quite different from the standard Plone site. In the following sections, I’ll step through exactly
how this was done. Well, actually, it’s quite simple because most of the look and feel was put
together using CSS. There were little to no changes other than changes to the custom style
sheet and some new images.
You’ll first look at the non-CSS changes to the site, which are changes to some of the templates
and properties.
7.1.4.1 Removing the Portlets and Some of the Main Elements
The site has no portlets. These have been removed because in this site there aren’t any that
are relevant. Instead, news appears on the home page. To remove these portlets from your
site, go to the root of the Plone site and click Properties. In the form fields next to left_slots
and right_slots, delete all the values.
In the Maestro site, a few elements have been removed. Sometimes I’ve found that this the
best thing to do for features that just aren’t needed in a site. It can be a little hard to squeeze
every user interface element into a Plone site, but really you don’t always have to do that;
201
instead, just remove the elements you don’t need. A few elements here have been removed:
the site actions, the search box, the footer, and the colophon.
To accomplish this, those templates that produced the code were customized and then altered
so they rendered nothing. For example, to remove the search box, in the ZMI click portal_skins,
click plone_templates, and then click global_searchbox. Next, click the Customize button. Then
change the template to the following:
<html
xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en" lang="en"
i18n:domain="plone">
<body>
<div id="portal-searchbox"
metal:define-macro="quick_search"
tal:condition="nothing">
Nothing to see here.
</div>
</body>
</html>
This is the technique I showed earlier for removing elements; just set tal:condition on the
macro element to ensure that the condition is false.
7.1.4.2 Customizing the Colors
You set the base colors for the site in the base_properties object. This object has been cus-
tomized, and the colors changed to the following colors (unless mentioned, all the other items
are the same):
linkColor: #776a44
globalBorderColor: #776a44
globalBackgroundColor: #e0d3ad
globalFontColor: #776a44
The color change I most noticed is globalBackgroundColor, which affects the colors of the
personal bar and has changed from blue to brownish. These minor colors changes will alter
the base style sheet so that it matches the images and overall look and feel nicely.
7.1.4.3 Creating the Style Sheet
The big part of this site is the style sheet, which is reproduced in full in Appendix B. Here I’ll
highlight some of the main parts of the style sheet. This style sheet is based on ploneCustom.css,
which was customized in the custom folder. Then, some of the elements of the Web page were
overridden in the new ploneCustom.css file.
First, the entire background for the body is set to the color #343434.
202
body {
background: #343434;
}
Second, the actual content of a Plone page, the part that you can edit, is contained in one class
called documentContent. Because the background color of the documentContent element is set
to white in the main plone.css file, the background of the text is white and produces the white
area in the middle of the screen.
Next, the image of the satellite and robot at the top of the Web site is one large image. You
place it at the top using CSS. The code for this is as follows:
#portal-top {
background: url("http://mars.telascience.org/header.jpg") transparent no-repeat;
padding: 162px 0 0 0;
position: relative;
}
This CSS code sets the parameters for the element that has the ID of portal-top. If you look at
the HTML code for a Plone site, you’ll see the portal-top element at the top of the page, just
below the body element. By setting the background for that image to the URL of the image
in question, you can have the image appear. The image is 162 pixels high, which is why the
padding for the top of the #portal-top element is set to 162px. If you don’t do this, then all the
items below will be pushed up, overriding the image.
The header image is 677 pixels wide, and you’ll note that the text in the page fits cleanly
underneath the image, rather than spilling out to the left or right. You can do this by setting
the value for the element to 680px. The visual-portal-wrapper HTML element is actually just
below the body, and it sets the width for the entire page body. The code for this is as follows:
#visual-portal-wrapper {
width: 680px;
margin: 1em auto 0 auto;
}
This sets the width for all the pages to be a fixed width, which is fine as long as you make
sure the width is smaller than the industry-standard 800-pixel width. No matter how big the
user makes the browser window, the main part of the page will never grow beyond those 680
pixels, ensuring it matches nicely with the image.
Probably the other obvious changes are the tabs at the top of the page, which are now images
instead of just the standard Plone boxes. Three images make up the tabs at the top of the page:
a spacer between tab, the left part of the tab, and the right part of the tab. By putting these
three images together, you get the effect of the tab. Figure 7-15 shows these three images.
203
Figure 7-15. The three images that combine to make the tab
For editing the CSS, remember that each of the tabs is really a list item containing a link
inside an element with the ID portal-globalnav. To set up the background spacer between each
tab, the skin first sets the background for the entire element. Again, note that by setting the
height of the image to 21 pixels, the same size as the image, you’ve ensured that there’s the
appropriate space for the image. The code is as follows:
#portal-globalnav {
background: url("http://mars.telascience.org/listspacer.gif") transparent;
padding: 0;
height: 21px;
border: 0;
margin: 0 0 1px 6px;
clear: both;
}
To set the image at the left end of the tab, you use the start image. You set the start image by
setting the value on the li element, rather than the anchor element, like so:
#portal-globalnav li {
display: block;
float: left;
height: 21px;
background: url("/liststart.gif") transparent no-repeat;
padding: 0 0 0 33px;
margin: 0 0.5em 0 0;
}
Finally, you set the right part of the tab by adding an image to the anchor element. You do this
by altering the anchor element inside the tab. The following code shows where you set the
background image to be the right part:
#portal-globalnav li a {
display: block;
float: left;
height: 21px;
background: url("/listitem.gif") transparent right top;
padding: 0 33px 0 0;
border: 0;
line-height: 2em;
color: black;
font-size: 90%;
margin: 0;
}
Now, you’ve replaced the rather standard-looking Plone tabs with great-looking buttons.
204
7.1.4.4 Creating the Splash Page
This page has one other key element. The front page of the site is a splash page, which shows
a nice graphic and invites the user to enter. You can add this by going into the ZMI and
removing the index_html object that’s normally there. Once removed, create a new file called
index_html. In that file, create custom code to make the home page, including a custom CSS.
The main element of that is one image, placed there by the following CSS:
div {
background: url(/splash.jpg) transparent no-repeat;
width: 260px;
height: 335px;
position: absolute;
...
}
The remaining CSS handles the placing of the text and links within that image. This page has
no Plone elements at all; it’s static HTML.
7.1.4.5 Conclusion
This looks like a reasonably complex site, with relatively simple CSS that does most of the hard
work. By using CSS you’ve changed Plone’s look and feel without having to know a great deal
about Plone beyond the HTML. Also, by ensuring that the images are placed using CSS, you
maintained key accessibility functions.
Thank you to NASA and all the people involved from the Plone community for helping with
this site and case study. These include but are not limited to John Graham, Alma Ong, Joe
Geldart, Michael Zeltner, and Tom Croucher.
8 Chapter 8
8.1 Managing Workflow
One of Plone’s many strengths is the workflow component. Workflow fits into one of the core
themes of content management, which is the separation of logic, content, and presentation.
This chapter therefore covers Plone’s workflow in detail.
The chapter starts by covering some key definitions related to workflow, as well as the key tools
involved, so that you can begin to conceptualize workflows. Once these concepts are clear, I
then discuss how to add and edit your own workflows.
Throughout this chapter, I reference simple changes you can make to the workflow that comes
straight out of the box with Plone. I also provide a series of examples to help you perform tasks
such as creating notifications, moving content, and so on. Finally, I show some of the more
advanced features of workflow development and some of the useful tools that are available.
8.1.1 What Is Workflow?
Workflow is a chain of actions or events that occurs on something to achieve an objective.
Workflow often expresses business rules that may exist. Every business has different rules
and policies about tasks that must happen within that company. Examples of this include the
following:
• Before an employee’s time sheet is approved, it must be viewed and acknowl-
edged by a supervisor.
• In a widget factory, for each widget assembled, users must be notified of the
order and any change in the state of the widget as it passes through the factory.
• Before a Web page is published on a Web site, it must be approved by market-
ing, approved by the Webmaster, and translated by a linguist.
Workflow separates the logic of these business rules and standardizes the concept of thinking
about these changes. By having separate logic, it’s now easy for businesses to change the
application to fit their business and their business rules. Often applications try to enforce a
workflow on a business because the workflow is hard-coded into the application.
205
206
8.1.2 Understanding Workflow in Plone
Plone’s workflow tool provides certain features and limitations that are key to understanding
workflow in Plone. The workflow product used in Plone is DCWorkflow, which is an open-
source product released by Zope Corporation. Other workflow systems are available, and
some of them are being incorporated into Plone, such as OpenFlow (http://www.openflow.it).
However, for the moment, DCWorkflow is powerful and simple enough to provide all the
functionality most users will need.
DCWorkflow assumes there’s one object in the system that’s the target of the workflow-for
example, one piece of content or one widget. It further assumes that all objects of the same
type go through the same workflow. By repurposing content (see Chapter 11 for more on this),
you can have similar content use different workflows.
Since the DCWorkflow system is included in Plone, there’s nothing extra to install. It’s repre-
sented in the Zope Management Interface (ZMI) by the portal_workflow object.
8.1.2.1 Conceptualizing a Workflow
Before explaining a workflow, I’ll explain a few simple pieces of terminology: states and tran-
sitions.
A state is information about an item of content at a particular moment in time. Examples of
states are private, public, pending, and draft. All workflows have at least one starting state
in which all the content starts. The workflow will then move the content through a series of
states, either by user interaction or by some automation process. When the content reaches an
end state, it’ll remain in that state for a long time (usually forever). Content may reach one or
more different end states in the process of a workflow.
For that piece of content to move from one state to another, a transition is needed. A transition
connects a starting state and an ending state. A transition can have lots of different features
associated with it, as you’ll see later, but for the moment, you just need to know that a tran-
sition moves content between two states. Usually a transition is triggered by some external
force, such as a user clicking a button on a Web page or a script interacting with a page.
Visualizing a workflow, especially when talking about something as nebulous as content, can
be a little confusing. Thinking about an everyday occurrence will help. In this case, the
following example shows the workflow of my credit card bill, which I have the joy of getting
every month:
1. The credit card company prepares a bill and mails it to me.
2. I get the bill and put it on my desk. Sometimes the bill sits on my desk for
quite a while as I wait for the end of month. Occasionally I have to query
people about certain expenditures, such as “What were those clothes you
bought?”
3. Any serious queries or questions then go back to the credit card company,
perhaps causing a new bill to be created (although this happens quite
rarely).
207
4. Usually at the end of the month, when I do all the accounting, I then pay
the bill.
From this, then, you can come up with some states. Looking at the previous steps, you’ll
see you really have no need to create different states for receiving the bill, which includes
opening it and putting it on my desk. Similarly, you don’t need to bother with every review
that happens. Although these are all valid steps that take place, trying to make a workflow
for every state would be too cumbersome. Instead, you can summarize the workflow with the
following states:
• Draft: The credit card bill has been prepared and sent to me.
• Review: The credit card bill has been received and is on my desk, being re-
viewed.
• Paid: The credit card bill has been paid, put in my filing cabinet, and forgotten
about forever.
Now that you’ve come up with the states, you can think of the changes that need to occur. For
each of these states, you’ll have at least one transition that occurs to move the bill from one
state to another:
• Post: The bank sends the credit card bill.
• Pay: I pay the credit card bill.
• Reject: Something is wrong on the bill, and it isn’t approved.
Figure 8-1 shows this set of transitions and states. In the figure, boxes represent states, with
the state written in them. Arrows represent the transitions from one state to the next, with the
name of the transition in italics.
Figure 8-1. A simple state machine for paying credit card bills
You’ve now extracted this business process of paying a credit card bill into a workflow. The next
step is to think about roles and security for this credit card bill. This workflow now contains
the business logic for an application for processing credit cards.
8.1.2.2 Understanding Roles and Security in Workflow
In any complicated system, you’ll have users of all roles and groups. These roles give Plone a
large amount of flexibility with security, but they also can make it more complicated. Chapter
208
9 covers security, local roles, and groups, but this section covers some key points about how
these topics relate to workflow.
When a piece of content moves from one workflow state to another, the workflow process can
change the security settings on that content. The security settings determine what user can
perform what action on what piece of content. By manipulating the security settings through
workflow, you can cause the security to change on a piece of content through its life cycle.
Users from static systems or Zope often get confused because in Zope, all pieces of content
have the same security settings throughout their life cycle.
Returning to the credit card example, you can infer the security settings for the credit card bill.
One way to represent this is to produce a table that expands the security in general terms for
the transitions that can occur at each of the various states, as shown in Table 8-1.
Table 8-1. The Transitions and Entities That Can Make Them
State Me Bank
Draft Post
Review Pay Reject
Paid
At this stage in Table 8-1, you’ve seen the transitions and who can make them. You haven’t
thought about the access that each user has to perform an action on an object at each point.
For example, at which point can someone edit the bill, and when can it be viewed? These are
called actions in Plone terminology, as shown in Table 8-2. I hope that only I have access to my
own credit card statements! Likewise, at any stage, the bank is able to view the credit card bill
and answer queries on it.
Table 8-2. The Actions and Entities That Can Make Them
State Me Bank
Draft View, Edit
Review View View
Paid View View
Actually, as it turns out, I can’t edit my credit card bill; only the bank can. I can send back my
credit bill by rejecting it, but the bank is unlikely to want my edits. In this situation, assume
the bank is the owner of the credit card bill. This demonstrates a concept called ownership. I
may have several credit card bills from several banks, and in each case you can think of the
bank as the owner. Each bank owns its own credit card bills, but Bank A isn’t the owner of
Bank B’s bill. Table 8-3 combines the transitions and actions, changing the terms Me and Bank
to Payee and Owner, respectively.
Table 8-3. The Transitions and Actions Combined, plus the Roles of People
State Payee Owner
Draft Post, View, Edit
209
State Payee Owner
Review Pay, Reject, View View
Paid View View
Of course, this is a rather contrived example, but it illustrates how you can apply
workflow to basic states. More transitions can occur here-for instance, I’d be more
than happy for someone else to pay my credit card bill for me-but that’s so unlikely
that you shouldn’t add it to the workflow or security.
Before showing how to create and edit workflows, I’ll now show you the default workflows
that ship with Plone.
8.1.2.3 Introducing Plone Workflows
Plone ships with a set of default workflows for your Plone site. These workflows provide a
logical way of moving content through a Plone site. A standard Plone site ships with two
workflows: the default workflow and the folder workflow. The following sections present each
of these in turn.
8.1.2.3.1 Default Workflow
Chapter 3 covered the default workflow and the default settings when publishing content. I
discussed the security and settings for each state in the workflow. However, a picture is worth
a thousand words, so Figure 8-2 shows the workflow state.
210
Figure 8-2. The default workflow for content that comes with Plone
Figure 8-2 shows the main states and the transitions. This figure has a gray dotted line that
represents a sort of security divider. To the left of the line is where owners of the content
usually interact with the content. To the right of the line is where reviewers usually interact
with the content.
NOTE The owner of the content is the person who created the content originally.
An owner is one particular member of a Plone site. Although many members exist
in a Plone site, only one person can be the owner of a piece of content in a Plone
site. Because the owner role is calculated when an object is created, the owner role
is special.
Just like with the credit card example, an associated set of permissions exists for the default
workflow. Table 8-4 outlines all the permissions and the states.
Table 8-4. The Default Workflow Permissions
State Anonymous Authenticated Owner Manager Reviewer
Pending View View View Edit Edit
Private Edit Edit View
211
State Anonymous Authenticated Owner Manager Reviewer
Published View View View Edit View
Visible View View Edit Edit View
View refers to the following permissions: Access Contents Information and View
Edit refers to the following permission: Modify Portal Content
As you can see from Table 8-4, by default only when content is in the private state is it truly
hidden from everyone else. When content is in the published state, only the manager can edit
it. Later in the “Editing Permissions” section, I’ll show you how to change these permissions
easily through the Web.
8.1.2.3.2 Folder Workflow
I also discussed the folder workflow in Chapter 3, when I covered publishing content with you.
However, as I noted in that chapter, no pending state exists for folders. Instead, you have a
slightly simpler workflow, as shown in Figure 8-3.
Figure 8-3. The folder workflow for content that comes with Plone
212
8.1.2.3.3 Other Workflows
Numerous workflows are available to a Plone site, including private workflow, community
workflow, one-step publication workflow, and so on. ZopeZen comes with a workflow, and
PloneCollectorNG also comes with a workflow. DCWorkflow comes with four workflows.
Currently, two workflows come in the PloneWorkflows product in the collective project on
SourceForge (http://sf.net/projects/collective): the community workflow and one-step publica-
tion workflow. The community workflow is similar to the Plone workflow, with a few changes.
The one-step publication workflow has two states: private and published.
At the moment, you have no easy way to install and uninstall workflows, and you have no
real easy way to transition content between one state and another. For example, if you install
the one-step publication workflow into an existing state, you also need to fix the states for all
objects and move them into one of the new states. In this case, it’s probably simple-everything
in a published state should stay as it is, and everything else should move into the private state.
8.1.3 Adding and Editing Workflow
Now that I’ve discussed the default workflow, I come to the key point that’s probably most on
your mind: How can you change the defaults? Well, as with most of Plone, you can add, edit,
and delete all workflow through the ZMI. The tool that controls workflow is portal_workflow.
In the following sections, I cover how workflows are assigned and then go through all the
settings for a workflow in detail.
8.1.3.1 Setting Workflows to a Content Type
After clicking portal_workflow, you’ll see a list of workflow assignments. A feature of DCWork-
flow is that each content type has one and only one workflow assigned to it; Figure 8-4 shows
these assignments.
213
Figure 8-4. The list of workflow by type
On this page you’ll see a list of each content type and the workflow that has been applied
to it. If a workflow isn’t specified (in other words, the value is blank), then no workflow is
applied. As an example, the d
First Edition
16th May 2005
1
Table of Contents
1 Chapter 1
1.1 Introducing Plone
1.1.1 What Is a Content Management System?
1.1.2 Do You Want a Content Management System?
1.1.3 Introducing Plone’s Features
1.1.4 Getting Involved in Plone’s Development
2 Chapter 2
2.1 Installing Plone
2.1.1 Installing Plone on Windows
2.1.2 Installing Plone on Mac OS X, Unix, and Linux
2.1.3 Installing from Source
2.1.4 Configuring the Web Server
3 Chapter 3
3.1 Adding and Editing Content
3.1.1 Joining a Site
3.1.2 Enabling Cookies
3.1.3 Setting Up Your Folder and Preferences
3.1.4 Adding and Editing Documents
3.1.5 Adding and Editing Other Types of Content
3.1.6 Organizing Content
3.1.7 Discussing and Finding Content
3.1.8 Example: Creating the Plone Book Web Site
4 Chapter 4
4.1 Making Simple Customizations
4.1.1 Administering Sites
4.1.2 Using the ZMI
4.1.3 Customizing Plone’s Look and Feel
5 Chapter 5
5.1 Introducing Basic Plone Templating
5.1.1 Understanding the Underlying Templating Machinery
5.1.2 Using the Zope Page Templates System
5.1.3 Understanding the Page Template Basic Syntax
6 Chapter 6
6.1 Introducing Advanced Plone Templating and Scripting
2
6.1.1 Understanding Advanced Plone Templating
6.1.2 Scripting Plone with Python
6.1.3 Useful Tips
6.1.4 Using Forms
7 Chapter 7
7.1 Customizing the Look and Feel of Plone
7.1.1 Introducing Plone Skins
7.1.2 Customizing Skins
7.1.3 Making New Skins and Layers
7.1.4 Case Study: Examining the NASA Skin
8 Chapter 8
8.1 Managing Workflow
8.1.1 What Is Workflow?
8.1.2 Understanding Workflow in Plone
8.1.3 Adding and Editing Workflow
8.1.4 Common Tasks and Examples
9 Chapter 9
9.1 Setting Up Security and Users
9.1.1 Administering Users
9.1.2 User Registration Tools
9.1.3 Setting Permissions
9.1.4 Scripting Users
9.1.5 Integrating Plone with Other Services
10 Chapter 10
10.1 Integrating with Other Systems
10.1.1 Open-Source Licensing
10.1.2 Installing Plone Products
10.1.3 Using a Different Web Server
10.1.4 Integrating Plone with the File System
11 Chapter 11
11.1 Manipulating and Categorizing Content Types
11.1.1 An Overview of Content Types
11.1.2 Searching and Categorizing Content
12 Chapter 12
12.1 Writing a Product in Python
3
12.1.1 Writing a Custom Content Type
12.1.2 Writing a Custom Tool
12.1.3 Adding Some User Interface Elements
13 Chapter 13
13.1 Developing with Archetypes
13.1.1 Introducing Archetypes
13.1.2 Putting Together the Rest of the Content Type
13.1.3 Archetypes based Development
13.1.4 Setting Up a Content Type
13.1.5 Setting Up a Transformation on Windows
13.1.6 Testing the Content Type
13.1.7 Making a Content Type Persist in a Relational Database
14 Chapter 14
14.1 Administering and Scaling Plone
14.1.1 Administering a Plone Site
14.1.2 Improving Plone Performance
14.1.3 Using Zope Enterprise Objects
1 Chapter 1
1.1 Introducing Plone
A company without a Web site is unthinkable—and most companies and organizations have
more than one site. Whether it’s an external site for communicating with clients, an intranet
for employees to use, or a site for direct client communication and feedback, all Web sites have
a common problem—how to manage the content on them. This is a challenge that can often
cost organizations large amounts of time and effort. Producing a powerful yet flexible system
for these sites that meets ever-changing requirements while growing to meet your company’s
emerging needs isn’t easy.
No matter what the requirements for your Web site are or the amount of content or users,
Plone is a user-friendly, powerful solution that lets you easily add and edit any type of content
through the Web, produce navigation and searches for that content, and apply security and
workflow to that content.
Plone enables you to put together almost any Web site and easily update it. This lets you
build content-rich sites quickly so you can gain a competitive advantage. Finally, probably the
best things about this system are that it’s free and it’s open source. With its large and impres-
sive feature set, it’s comparable, if not better than, many closed-source content management
systems that cost hundreds of thousands of dollars.
Mike Sugarbaker says the following when reporting on the Open Source
Content Management Conference (OSCOM) in 2002 for the Mindjack site
(http://www.mindjack.com/events/oscom.html):
“I won’t do the complete rundown of all the ’competing’ open-source content management
frameworks. I’ll cut to the chase: The winner is Plone. This ’productized’ take on the six-year-
old web application framework Zope was the package with the most tools, the most profes-
sionalism, the most traction, and, above all, the most buzz.”
You can find the Plone Web site at http://www.plone.org, as shown in Figure 1-1. To try Plone
easily, a demonstration site is available at http://demo.plone.org. There you can quickly and
easily add and edit content through the Web. Specifically, you can add events, upload pictures,
add documents, and process them all through the framework that Plone provides.
4
5
Figure 1-1. The Plone Web site
1.1.1 What Is a Content Management System?
One simple definition for a Content Management System (CMS) is that it’s a system for man-
aging content. This is a rather unhelpful definition, so I’ll break it down into smaller parts for
a fuller explanation. I’ll start with a broad definition of content: Content is a unit of data with
some extra information attached to it. That piece of data could be a Web page, information
about an upcoming event, a Microsoft Word document, an image, a movie clip, or any piece of
data that has meaning to the organization deploying the system.
6
All these items are called content, and they all share similar attributes, such as the need to be
added or edited by certain users and be published in various ways. A system called workflow
controls these attributes. Workflow is logic defined by the organization’s business rules, and it
describes a system for managing the content.
Historically there has been a difference between document management systems and CMSs,
but mostly these two systems have converged. The essential difference is the items being
managed; it’s often considered that content is any unit of information, and a document refers to
something that’s created and edited by humans using software such as Microsoft Office. Take,
for example, a book: A book contains many units of data and may require management slightly
different from that required by content. However, in most cases, this is a small difference, and
products such as Plone are able to manage the small units of a larger piece of content and
reassemble them.
With the ubiquitousness of the Web, many CMSs are now classified as Web CMSs, either be-
cause they have a Web-based interface or because they focus on a Web-based delivery system
over the Internet or an intranet. Plone provides a Web management interface and Web-based
delivery system.
The following is one definition of a CMS (http://www.contentmanager.eu.com/history.htm):
A CMS is a tool that enables a variety of (centralized) technical and (decentralized)
nontechnical staff to create, edit, manage and finally publish a variety of content
(such as text, graphics, video, and so on) whilst being constrained by a centralized
set of rules, process, and workflows that ensure a coherent, validated Web site
appearance.
1.1.2 Do You Want a Content Management System?
Although not the only advantage of a CMS, the most obvious benefit of a CMS is coordinating
a Web site easily. Take a situation where one person, a Webmaster, coordinates a Web site,
either an intranet or an external site. Content comes from users in a variety of formats, and
the Webmaster turns these into usable Web pages by converting them to Hypertext Markup
Language (HTML). If a user has to change those pages, then the user sends the changes to the
Webmaster, who changes the pages, and so on.
This presents many problems for the organization, the biggest being that all content
is flowing through one person - an obvious bottleneck. That one person can do
only so much work, and if that person is sick or leaves the company, a great deal of
productivity is lost in finding a replacement. The publishing process can be quite
frustrating as e-mails fly between the Webmaster and the user trying to get content
published.
What’s needed is a system that does the following:
Separates the content of a page from the presentation: If the actual content is
separate from the presentation method, then the content author doesn’t need to
know any HTML or how the page is delivered. In fact, one piece of content could
7
have many different templates applied to it, including formats other than HTML,
such as Portable Document Format (PDF), or Scalable Vector Graphics (SVG). When
you want to change the look and feel of the site, you have to change only that one
template rather than all the content.
Allows certain users to add and edit content: If specified users can add and edit
content easily, then there’s no need to send content to the Webmaster or Web team.
Instead, the user who wants to create a page can do so and edit it as much as
necessary.
Applies rules to whom can publish what and when: Your business rules might
not want just anybody publishing content on your Web site; for example, people in
marketing would be able to publish to the press release part of the site and not to
the engineering section.
Can apply business rules to content: If a person from marketing creates a press
release, somebody in legal might need to review that document. In this case, the
document will be passed through a review process that ensures it won’t go live until
these reviews are done.
Can search and index information intelligently: Since the CMS can keep track
of structured information about the content (such as author’s name, publication
date, modification dates, categories, and so on), it can produce listings of content
by author, recent content, and so on. It can also provide searching capabilities that
are much smarter and more useful than just a simple textual search.
Although this example portrays paybacks that are more significant for large organizations,
organizations of all levels benefit from this approach. In fact, typically small organizations
that don’t employ a full-time Webmaster can be one of the key beneficiaries of such a system.
By installing a CMS, you can resolve all these issues and more.
The key factor of any CMS is that it provides a clear separation of the key elements in it:
security, workflow, templates, and so on. For example, the templates presenting an item are
separate from the content. This allows you to easily modify the presentation.
1.1.3 Introducing Plone’s Features
Plone is open source, licensed under the General Public License (GPL), which is a common
open-source license that allows anyone to use the source for free. For more information about
the GPL, go to the Free Software Foundation Web site at http://ww.gnu.org. You can examine
any aspect of Plone’s code and alter it to fit your application. There are no licensing fees to
pay, there’s no license that will expire, and all the code is visible. This open-source philosophy
means that Plone already has a large user base and legion of developers, usability experts,
translators, technical writers, and graphic designers who are able to work on Plone. By choos-
ing Plone, you’re not locked into one company; rather, nearly a dozen companies offer different
Plone services.
8
1.1.3.1 Packaging
Plone maintains easy installers for Windows, Linux, and Mac. Other third-party products and
add-ons also come with the installers. Maintaining quality releases of these products makes
installation and management easy. Also, each new release maintains migration paths and
updates so that your Plone site will keep working and stay up-to-date.
1.1.3.2 Internationalization
The whole Plone user interface is translated into more than 20 languages, including Korean,
Japanese, French, Spanish, and German. Inserting your own translation is easy (see Chapter
4).
1.1.3.3 Usability
Plone offers an excellent user experience that provides high levels of usability and accessibility.
This isn’t just a matter of presenting pretty HTML but instead goes to the core of Plone. Plone
provides an interface that’s compatible with the industry and government standard WAI-AAA
and U.S. Section 508. This allows sites built with Plone to be used by people with vision
disabilities. In addition, this provides the unexpected but related benefit that your page may
index better in search engines such as Google.
1.1.3.4 Skinnable
Plone separates the content from the actual templates used to present the content, often called
skins. The skins are written in the excellent HTML templating system, Zope Page Templates,
and a large amount of Cascading Style Sheets (CSS). With little knowledge of Plone, you can
apply multiple skins, achieve multiple looks, and totally customize your Web site’s appearance.
1.1.3.5 Registration and Personalization
Plone features a complete user registration system. Users register with a Plone site using their
own username, password, and any other information you might want to add about the user.
You can then personalize the whole user interface for that user. In addition, with add-ons, you
can use information you already have about users, coming from many places, such as relational
databases, Lightweight Directory Access Protocol (LDAP), Active Directory, and more. Chapter
8 covers how to register and configure users.
1.1.3.6 Workflow and Security
Workflow controls the logic of processing content through the site. You can configure this logic
through the Web using graphical tools. Site administrators can make sites as complex or as
simple as they’d like; for example, you can add notification tools such as sending e-mails or
instant messages to users. Chapter 8 covers workflow in great detail.
9
For every item of content in a Plone site, you can set up access control lists to decide who has
access to that item and how they’ll be able to interact with it. Will they be able to edit it, view
it, or comment on it? All this is configurable through the Web (see Chapter 9).
1.1.3.7 Extensible
Since Plone is open source, it can be easily altered. You can change and configure almost any
aspect of Plone to suit your needs. Countless packages and tools for Plone provide a wide ar-
ray of options for smaller sites and for large-scale enterprises. Repositories of free add-ons for
Plone are available at http://www.plone.org. With development tools such as Archetypes (cov-
ered in Chapter 13), you can generate and alter Plone code easily through the Web or using
Unified Modeling Language (UML) tools. Chapter 10 covers integration of Plone with enter-
prise solutions such as LDAP Apache, Microsoft Internet Information Services (IIS), Macrome-
,
dia Dreamweaver, and so on.
1.1.3.8 Content Customization
Users of a Plone site can add all manner of content, but the data added isn’t limited or con-
strained. Plone developers can create their own content types so that almost any type of
content can be managed; the only limit is your own imagination. In Chapters 11 and 12,
I’ll discuss how to customize the content types. Chapter 13 will introduce Archetypes, which
is a very powerful system for generating content types that don’t require programming; for
instance, you can generate new types of content from UML tools.
1.1.3.9 Documentation
The Plone project maintains documentation, including this book, which is published under the
Creative Commons license. The best starting place for the community documentation is at
http://www.plone.org/documentation.
1.1.3.10 Community
One of the best things about Plone is the community of developers and companies that sup-
ports and develops Plone. With more than 60 developers involved to some degree in the project
around the world, it’s almost always possible to find a Plone developer online who is willing
and able to help you. Alan Runyan, Alexander Limi, and Vidar Andersen started Plone; how-
ever, it quickly grew into a thriving open-source project as more developers became involved.
The contributions from these developers form the Plone product that’s now available.
1.1.3.10.1 Example Plone Sites
Many Plone sites exist; some are obvious because of their looks, and some aren’t. The following
is just a small sample of the more diverse sites:
10
Plone (*http://www.plone.org*) Plone Demo Site (*http://demo.plone.org*) Zope.org
(*http://www.zope.org*) Liquidnet (*http://www.liquidnet.com*) Design Science Toys
(*http://www.dstoys.com*) Give Kids the World (*http://www.gktw.org*) Propane
(*http://www.usepropane.com*) Maestro Headquarters (*http://mars.telascience.org*)
More Plone sites are available at http://www.plone.org/about/sites, including sites that provide
a quite different user interface. Without knowing about the development of these sites, it
would in fact be hard to tell that these sites use Plone.
1.1.4 Getting Involved in Plone’s Development
Although Plone has an impressive list of features, its list of ’wants’ even more impressive. For
this reason, the project is always on the lookout for new people willing to contribute time for
the project.
Fortunately, because Plone is focused on the end user, there’s a need for a very broad spectrum
of disciplines. Volunteers in a range of areas, rather than just coders or Web developers,
are welcomed. Plone needs user interface developers, usability experts, graphic designers,
translators, writers, and testers. You can find the current development status on the Plone Web
site at http://plone.org/development, and the best way to get involved is to join the mailing
lists or join the developers on an Internet Relay Chat (IRC) channel.
Plone is built on top of Zope and the Content Management Framework (CMF). To understand
Plone, you have to understand Zope and the CMF as the underlying architecture. For this
reason, I’ll explain these two items and how they integrate with Plone in this section.
Zope is a powerful and flexible open-source Web application server developed
by Zope Corporation (http://www.zope.org). Originally, Zope was developed as
a stand-alone CMS, but over time it didn’t satisfy the needs of its users. Then
Zope Corporation developed the CMF an open-source project. The CMF provides
developers with the tools necessary to create complex CMSs; it enables workflow,
provides site skinning, and offers other functions.
The CMF is a framework for a system; in other words, it provides the tools for developers to
build a product, rather than just providing an out-of-the-box system that users can use imme-
diately. Plone takes this and many other features and improves upon them to provide the user
with a high-quality product. Plone is a layer on top of the CMF, which is an application running
on top of Zope. Understanding the CMF is key to understanding Plone. Most administration
functions require the use of Zope’s administration interface, and developing Plone requires an
understanding of Zope and its objects.
This book doesn’t go into depth about Zope; rather, it gives you enough information
to complete tasks in Plone. Just reading this book will give you enough information
to customize and modify almost anything you want in Plone. For more information on
Zope, I recommend The Zope Book. Originally published by New Riders, it has since
been placed online and is updated by community members. It’s available free online at
http://www.zope.org/Documentation/Books/ZopeBook/2_6Edition.
Both Zope and the CMF are key technologies that Plone needs; without them, Plone wouldn’t
exist. The Plone team owes a great deal of thanks to everyone at Zope Corporation for having
11
the vision to create and then offer both Zope and the CMF as open source. The list of people
I’d like to thank there and in the CMF communities is long. Thank you, everyone involved.
Zope is written in Python, a powerful object-oriented, open-source programming language
comparable to Perl or Tcl. Knowledge of Python isn’t required to use Plone or even to do some
basic administration; however, customizing products and scripting Plone does require some
Python.
Tommy Burnette, a senior technical director at Industrial Light Magic, says this about Python
(http://www.python.org/Quotes.html):
Python plays a key role in our production pipeline. Without it a project the size of
Star Wars: Episode II
If you plan to do anything sophisticated with Plone, take a day or two to learn the basics of
Python. Not only will this allow you to customize Plone substantially, but it’ll also familiarize
you with objects and how they interact in the Plone environment. Teaching you Python is
outside the scope of this book; instead, I assume you have a basic knowledge of Python. That
fundamental knowledge of Python will be enough to get you through this book and allow you
to customize the Plone installation easily.
Fortunately, Python is an easy programming language to learn; on average, it takes an experi-
enced programmer a day to become productive in it. New programmers take a little longer. If
you’re installing Plone using the Windows or Mac installers, then the correct version of Python
will be included. To download Python as separate product, for almost any operating system,
go to http://www.python.org.
The best way to master Python is to try it from the command Python interpreter. If you have a
Windows installation of Plone, there’s a link for the Pythonwin, a Python Integrated Develop-
ment Environment (IDE) already in the Start menu; go to Start - Programs - Plone - Pythonwin
(see Figure 1-2).
12
Figure 1-2. The Python prompt on Windows
On Linux and Mac OS X, usually typing python will start the Python interpreter:
$ python
Pyython 2.3.2 (#1, Oct 6 2003, 10:07:16)
[GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
Since Python is an interpreted language, instead of the whole Python script being compiled
and run, you can just send lines of code to the interpreter as you write them. This makes the
interpreter an amazingly useful place for testing and debugging code. In the interpreter, each
line waiting for input is prefixed with >>>.
For example, the simplest ’Hello, World” program is as follows:
>>> print "Hello, world!"
Hello, world!
>>>
To exit the interpreter, press Ctrl+D (press the D key while holding down Ctrl) on Linux
or press Ctrl+Z on Windows. (You’ll also use this later for more advanced Zope and Plone
interaction.) You can execute normal Python scripts by passing them to the interpreter; for
example, given the following script called hello.py:
13
print "Hello, world!"
you can run this using the following command:
$ python hello.py
Hello, world!
The Python Web site at http://www.python.org has excellent documentation, especially the
tutorial. Also, the following books provide a good overview of Python:
• *Dive Into Python* (Apress,2004): Based on Mark Pilgrim’s popular Web-
based tutorial, this books treats readers to a fast-paced introduction to the
Python language. This is a great book geared toward experienced program-
mers.
• *Learning Python*, Second Edition (O’Reilly, 2003): This book covers ver-
sion 2.3 of Python and provides a good overview of Python and all the new
features. This is good for relatively new programmers.
• *Practical Python* (Apress, August 2002): This highly practical introduc-
tion to Python offers insight into the language’s array of features. The reader
can immediately put this knowledge into practice, following along with the
creation of ten interesting projects, including a Web-based bulletin board and
a Graphical User Interface–based file-sharing application.
• *Python Essential Reference*, Second Edition* *(Sams, 2001): A refer-
ence book that provides a great overview of all the key libraries and functions.
This is an excellent book for experienced programmers.
This book uses the following conventions:
• Italics: New terms are italicized. (Appendix C contains an extensive glossary
that defines all acronyms.) Also, links that appear in the user interface are
italicized.
• Bold: If there are instructions within the text that include something you
should type on your keyboard, these words are in bold.
• Code font: A monospaced font indicates filenames, folder paths, code, vari-
ables, and Uniform Resource Locators (URLs).
This book contains lots of screen shots of Zope, Python, and Plone. Since Plone is a rapidly
developing product, the screen shots might vary slightly from the version of software you’re
using; these changes should be minor and shouldn’t affect your understanding of the system.
For this book, the following versions of software are used; although this book is specifically
written with these versions in mind, all the software should work on these and later versions
for some time to come.
Plone 2.0 was the most recently released version of Plone at the time of writing. This is the
second major release of the software, and it provides many new features over 1.0, including
group user management, a new interface, and an improved Zope distribution. It’s strongly
14
suggested you start any new projects in 2.0 or later, rather than using the earlier versions of
Plone.
Version 2.0 of Plone has the following dependencies: Zope 2.7, CMF 1.4.2, and Python 2.3.3.
All of the code examples in this book have been specifically designed not to be dependent upon
these versions or a particular operating system. However, there may be situations where this
isn’t the case; I apologize for any inconvenience.
Originally, a group of Plone users keen to produce quality documentation came up with the
idea of this book. We released the first version of that book on the Plone Web site as an open-
source documentation project. All the content added to the Plone Web site was under the open
publication license.
Growing interest in Plone made a commercial book more feasible, and in the summer of
2003, Apress and I started this book. I used some of the material from the old book with
the original owners’ permissions. With the change to Plone 2, I added a large amount of
new material. This book is now published under the Creative Commons license, which al-
lows for the reuse of this work as long as the original author is attributed. However, you may
not use this work for commercial purposes. For more information, see the license online at
http://creativecommons.org/licenses/by-nc-sa/1.0/.
2 Chapter 2
2.1 Installing Plone
This chapter explains how to install Plone on a variety of platforms and set the basic configu-
ration options for Plone. If you want to try Plone really quickly, then your best bet is to go to
the live demonstration site at http://demo.plone.org; you can try adding and editing content
immediately without installing anything.
Unlike the other chapters, reading this chapter from one end to the other may not make the
most sense. Instead, I’ve broken this chapter down by operating system, so you can read only
the sections you need to in order to install Plone. Plone will install on any of the platforms
that Zope supports: Windows, Mac OS X, Linux, most Unix platforms, and Solaris.
For a Plone server, a high-performance computer will obviously make Plone perform better.
Plone is a complicated system that requires processing power and memory. In general, it’s
recommended you don’t go into production with a machine slower than 2GHz with less than
1GB of Random Access Memory (RAM) if you’re serving a large Web site. It works fine with
setups as low as 500MHz and 64MB of memory for more modest sites, however. For advanced
information about the performance, caching, and acceleration of Plone, see Chapter 14. For
a base installation of Plone, you’ll need about 50MB of hard drive space. If you already have
installations of Zope or Python, then this will be a great deal less; you’ll need about 2MB. You
must also account for the Plone object database, which can grow to almost any size depending
upon the amount of data you store.
To use Plone, you need a Web browser that can access the server. If users want to log into
your site, then they must have cookies enabled. JavaScript isn’t required but will provide a
richer user experience. Because of the large amount of Cascading Style Sheets (CSS) in Plone,
modern browsers will see the correct Plone interface in a richer, more attractive way; however,
it should be quite functional in any reasonable browser.
I recommend any of the following browsers:
• Microsoft Internet Explorer 5.5 and later
• Netscape 7.0 and later
• Mozilla 1.0 and later
• Opera 7.0 and later
• Konqueror 3.0 and later
• Safari 1.0 and later
15
16
Plone also is fully functional in the following browsers but may look different from the original
Plone:
• Netscape 4.*x*
• Microsoft Internet Explorer 5.0
• Microsoft Internet Explorer 4.0
• Konqueror 2.*x*
• Lynx (text-based)
• w3m (text-based)
• AWeb
• Links (text-based, with optional graphics)
• Any browser that handles a basic set of Hypertext Markup Language (HTML)
and form input cookies, including most mobile/Personal Digital Assistant
(PDA) browsers
2.1.1 Installing Plone on Windows
By far the easiest way to install Plone is to use the Plone Windows installer, which automates
the installation of Plone on Windows. The installation includes extra packages and options,
a Hypertext Transfer Protocol (HTTP)–loaded database, the setup of services, and Python for
Windows packages. You can download this installer from http://www.plone.org/download.
2.1.1.1 Using the Installer
The installer has been tested on Windows 9*x*, ME, NT 3.51+, 2000, and XP but it should
,
also work on other Windows versions. It’s recommended you have administrator access on the
computer you want to install on since the installer will try to set up as a service and install
settings into the Windows Registry. If you already have Zope or Python installed, you may
want to install the source separately to save hard drive space.
Before installing Plone, you should make note of any current Web servers you have running.
For example, later versions of Windows automatically install and start Microsoft Internet Infor-
mation Services (IIS), which listens to port 80. The installer starts Plone on ports 80 and 8080.
To test if something is using port 80 already, the easiest way to test is by opening a browser
to http://127.0.0.1/ and seeing if it finds a page. You can either disable that Web server or
change the ports for Plone; see ’Configuring the Web Server” later in this chapter. If you want
to run Plone behind IIS or run both Plone and IIS on the same server at the same time, then
see Chapter 14 for more information. At the moment, however, it’s easiest to just disable that
Web server.
Once you’ve downloaded the installer, double-click the installer to begin (see Figure 2-1).
17
Figure 2-1. The start of the Plone installer
The installer goes through the usual steps for installing software; click Next to
continue the setup or Cancel to exit. The Plone installer lets you choose a location
to install the software; the default is c:Program FilesPlone 2 (see Figure 2-2).
Figure 2-2. Selecting a directory
When you get to the password screen, as shown in Figure 2-3, you must enter a username and
a password. This will create a user for you, and this will make the Plone site in that user’s
name. Often people create one user called admin or similar for this role. You’ll need this
18
username and password later, so remember it; however, if you do lose this password, you can
enter a new one later.
Figure 2-3. Entering a username and password
The installation takes about five minutes, depending upon the speed of your computer. The
installation performs a few tasks at the end of the installation, such as compiling all the Python
files and setting up the database. When the installation has finished, a message displays to let
you know that it’s done (see Figure 2-4).
Figure 2-4. Final setup screen
19
To start Plone, access the Plone controller by going to Start - Programs TRA Plone TRA Plone.
The controller is an application that provides a nice user interface for starting and stopping
Plone. It begins with the Status page, which lets you easily start or stop your Plone installation
(see Figure 2-5).
Figure 2-5. Plone isn’t running.
As shown in Figure 2-5, the screen displays the status of your Plone installation. Plone doesn’t
start automatically; you’ll have to click Start to start Plone. Once you’ve clicked this, you may
have to wait a minute as it completes the startup process (see Figure 2-6).
20
Figure 2-6. Plone is now running.
When Plone has started, you can access the Plone site by clicking the View Plone button. This
starts a browser and accesses the Plone site; you should then see the Plone welcome page.
Note that the address in the browser is http://localhost/; this is the address to access your
Plone site. Clicking the Zope Management Interface button starts a browser and accesses the
management interface; the address in the browser for this is http://localhost::8080/manage,
which gives you access to the underlying application server. When you click the Manage button
and access Plone, it’ll ask you for your username and password. This is the username and
password you added in the installer.
The controller will know whether you’ve installed Plone as a service or not as a service. If
Plone has been installed as a Windows service, then you can stop and start Plone using the
standard service management screens and commands. If it hasn’t been installed as Windows
service, you’ll see a little icon appear in the task bar. At this point if you want to edit content,
move on to Chapter 3.
2.1.1.2 Configuring the Server on Windows
The configuration for Plone is contained in a text file that you can edit to configure your Plone
instance. You can change the ports Plone listens to, the log files used, and a whole host of
options. On Windows some of the key features are available through the controller and the
Graphical User Interface (GUI). If you want to alter some of the other configuration options,
refer to Appendix A for a full list of configuration options. To access the controller, select Start
- Programs - Plone - Plone; this will start the controller.
As discussed earlier, the first page you’ll see is the Status page, which allows you to stop or
start Plone. On the left of the controller are a few other screens that I’ll now discuss.
2.1.1.2.1 Changing the Ports
The Ports choice, as shown in Figure 2-7, allows you to specify the ports that Plone listens to for
incoming connections such as HTTP File Transfer Protocol (FTP), and Web-based Distributed
,
Authoring and Versioning (WebDAV).
21
Figure 2-7. The Ports page displays the ports on which Plone is running
As mentioned when installing Plone, you’ll want to ensure that no other server is listening to
the same port as Plone servers such as II:S, Apache, and Personal Web Server (PWS) could
be listening to port 80. At the time of writing, only the Plone HTTP and Zope Management
HTTP ports are enabled; to enable them, you have to go into a text file to configure them. The
following are the four fields on the Ports page:
Plone HTTP: This field specifies the port to access Plone for the user. The default
is port 80, the standard default for a Web server. Although this port isn’t required,
without it you won’t be able to access Plone with a Web browser. If this port is
enabled and Plone is running, the View Plone button is enabled on the Status page.
Zope Management HTTP: This field specifies the port to access Plone as the man-
ager. The default is port 8080. This port gives you access to Zope Management
Interface (ZMI) for the root of Zope. You can still get to this through the HTTP
port; however, it’s easier and more convenient to have a separate port. If this port
is enabled and Plone is running, the Manage Plone button is enabled on the Status
page.
FTP Access: This field specifies the port to access Plone via FTP The default is
.
blank, meaning that this isn’t enabled; if you want to enable this, the usual port is
21. You can use FTP to transfer large files to and from Plone.
WebDAV Source: This field specifies the port to access Plone via WebDAV The .
default is blank, meaning that this isn’t enabled; if you want to enable this, the
usual port is 8081. (WebDAV is a protocol for remotely authoring content in Plone.
With WebDAV you’re able to perform tasks such as mapping your Plone server to a
,
Windows drive letter.)
22
2.1.1.2.2 Using the Emergency User Page
Chapter 9 covers the Emergency User page, but, put briefly, it allows you to get emergency
access to your system should you forget your username or password.
2.1.1.2.3 Starting Plone in Debug Mode
Up to this point, you’ve started and stopped Plone in production mode. This is the fastest way
to run Plone and is recommended. For developing add-ons in Plone or debugging problems,
you’ll need to start Plone in debug mode. This mode is the recommended way of running Plone
when you’re developing products and skins, as you’ll do in later chapters. This method isn’t
the default because you’ll note that Plone is about ten times slower than normal.
To start Plone in debug mode, select Start - Programs - Plone - Plone (Debug Mode), and
a command prompt will appear; all the log information will be printed to this window (see
Figure 2-8).
Figure 2-8. Running Plone from the command line
To test that Plone is running, start a browser and go to http://localhost/; if Plone is installed
successfully, you’ll see the Plone welcome screen.
2.1.2 Installing Plone on Mac OS X, Unix, and Linux
The installations for Mac OS X, Unix, and Linux are different, but the configurations are similar.
Specific packages exist for different operating systems, including Mac OS X, Debian, Gentoo,
FreeBSD, OpenBSD, and RPM Package Managers (RPMs) for Red Hat, SuSE, and Mandrake.
In the following sections, I’ll cover some of the more popular: Mac OS X, Red Hat, and Debian.
For information about your specific operating system, consult the installation instructions for
the specific installation system.
23
2.1.2.1 Installing on Mac OS X
The installer automates the installation of Plone on Mac OS X and has been tested on ver-
sion 10.2.3 and later. You’ll need administrator access on the computer on which you want
to install. You can download this installer from http://ww.plone.org/download. Once you’ve
downloaded the installer, double-click the installer to decompress the archive, and double-click
the resulting installer package to begin the install. You should see the screen shown in Figure
2-9.
Figure 2-9. Authorizing the installation using your Mac OS X password
Enter your Mac OS X account password to authorize the installation; your account must have
administrator privileges to do this. If your account doesn’t have administrator privileges, log
out and log back in as someone who does and then relaunch the installer. You may want to
move the installer package to /Users/Shared before you log out so you can access it from the
other account. Once the installation is authorized, you’ll see the screen shown in Figure 2-10.
24
Figure 2-10. Welcome to the installer.
The installer goes through the usual steps for installing software. Click the Continue and Go
Back buttons at the bottom as necessary; most of the steps are self-explanatory. However,
when presented with the choice of volumes to install Plone on, you must choose the partition
on which Mac OS X is installed (see Figure 2-11).
::image:: img/3294f0211.png
Figure 2-11. Choosing the boot volume
The installation takes about five minutes, depending upon the speed of your computer. When
the installation has finished, Plone isn’t started by default. The ReadMe.rtf file in /Applica-
tions/Plone contains a lot of useful information about running and managing your Plone in-
stallation, including how to start Plone. For example, running the following command will
start Plone:
sudo /Library/StartupItems/Plone/Plone start
To test whether Plone has worked, use a browser to go to http://localhost:9090/; you should
see the Plone welcome page. Also in that ReadMe file you’ll find the username and password
that Plone has set up for you to access the server.
25
2.1.2.2 Installing Using an RPM
RPMs are available for the Red Hat, Mandrake, and SuSE distributions. You can download
the latest packages from http://www.plone.org/download. The RPM requires that Python 2.3 is
installed. To find out which version of Python you have, run the following command in a shell:
$ python -V
Python 2.3.2
In this case, Python 2.3.2 is installed; if you don’t have this, RPMs for Python are available
from the Python Web site at http://www.python.org. After downloading the files, install us-
ing the standard rpm command; fortunately, the Plone installation prints some really useful
information. For example:
[root@lappi i386]# rpm -ivh Plone2-2.0.0rh-2.i386.rpm
Preparing... ###########################################
[100%]
Making group plone (not altered if already exists).
Making user plone.
~ 1:Plone2 ###########################################
[100%]
Creating initial ’main’ instance...
Instance created. Listening on 127.0.0.1:8080, initial user: ’plone’
with password: ’plone’.
Setup of initial database in ’main’ instance...
/usr/lib/plone2/lib/python/AccessControl/Owned.py:79:
DeprecationWarning: Owned.getOwner(1) is deprecated; please use
getOwnerTuple() instead.
~ DeprecationWarning)
Created initial database content.
look at /etc/plone2/main/zope.conf.
Run then "/etc/rc.d/init.d/plone2 start" to start Plone2.
you may create new Plone instances with mkploneinstance.
As shown in the previous output, to start Plone, run the following:
/etc/rc.d/init.d/plone2 start
To test that Plone is working, use a browser to go to http://localhost:8080/; you should see
the Plone welcome page. The username plone* and the password *plone have been created for
you*.*
2.1.2.3 Installing on Debian Linux
Plone is a standard package in Debian and moves through the standard release process, so
you’ll want to either get the stable or get the unstable version of Plone, depending upon how
your Debian installation is configured. To install Plone, simple use Debian’s apt system to get
the package. This is an example installation:
26
agmweb:/home/andy# apt-get install plone
Reading Package Lists... Done
Building Dependency Tree... Done
The following extra packages will be installed:
zope zope-cmf zope-cmfcalendar zope-cmfcore zope-cmfdefault
zope-cmfplone zope-cmftopic zope-cmfworkflow
zope-formulator zopectl
Suggested packages:
zope-cmfwiki python-unit zope-devguide zope-book
Recommended packages:
zope-cmfforum zope-localizer
The following NEW packages will be installed:
plone zope zope-cmf zope-cmfcalendar zope-cmfcore zope-cmfdefault
zope-cmfplone zope-cmftopic zope-cmfworkflow
zope-formulator zopectl
0 upgraded, 11 newly installed, 0 to remove and 49 not upgraded.
Need to get 4743kB of archives.
After unpacking 24.9MB of additional disk space will be used.
Do you want to continue? [Y/n]
Press Y to continue and install all the required packages. To start and stop Zope, an install
script has been created in the init.d directory, called zope. To start Plone, run the following:
/etc/init.d/zope start
The Debian installer starts Zope on the nonstandard port 9673. Since the Debian installer
is quite unusual, it’s recommended that you read the documentation for the packages at
/usr/share/doc/zope and /usr/share/doc/zope-cmfplone.
2.1.3 Installing from Source
As an alternative to using an installer or package, you can install from the source tarball. If
you’re familiar with installing from source, it’s actually rather simple but does require famil-
iarity with basic tools such as tar. The following sections demonstrate how to install it on
Linux.
This installation assumes you’re familiar with basic operations such as ’untarring” files and
moving files. This requires a working Zope installation.
NOTE To install Zope, see the Zope installation instructions in the
doc/INSTALL.txt file of your Zope download. For more information, see
http://zope.org/Documentation/Books/ZopeBook/2_6Edition/InstallingZope.stx).
Follow these steps to install Plone:
1. Download Plone 2 from http://www.plone.org/download, and select the
tarball file.
27
2. Unzip the archive using the following: tar xzf CMFPlone2.0.tar.gz
3. You’ll find that a directory has been created called CMFPlone-xxx, where
xxx is the version (for example, CMFPlone-2.0).
4. Move the contents of that directory into your Zope installation’s Product
directory. For example, if the Zope Products directory is in /var/zope,
then do this: mv CMFPlone2.0/ /var/zope/Products*
After completing this installation, restart Zope. Once Zope has restarted, access Zope by open-
ing a browser and going to http://localhost:8080/manage. You’ll need a username and pass-
word for this (for example, the username and password you gave during the Zope installation).
In the ZMI, there’s a drop-down list of products you can add located in the top-right corner.
Ensure that Plone Site is one of the options. If so, your installation is complete (see Figure
2-12).
Figure 2-12. Plone Site in the drop-down list
2.1.3.1 Installing from CVS
Concurrent Versioning System (CVS) access is recommended only for experi-
enced users and developers. You can find current CVS access information at
http://ww.plone.org/development/cvs. The current CVS checkout command is as follows:
cvs -d:pserver:anonymous@cvs.sf.net:/cvsroot/plone login
cvs -d:pserver:anonymous@cvs.sf.net:/cvsroot/plone co CMFPlone
Plone 2 has a whole host of other dependencies (such as DCWorkflow, Formulator, Group User
Folder, and so on) that aren’t in Plone CVS, meaning users will have to go and find all these
dependencies. When you start Plone, it’ll print any errors regarding packages not found. For
example:
28
2003-11-21T12:23:11 ERROR(200) Plone Dependency
CMFActionIcons not found. Please download it from http://cvs.zope.org/Products/
2.1.3.2 Adding a Plone Site
Once you’ve installed Plone from source, you need to create an instance of Plone. To do
this, you’ll need to log into the ZMI and add a Plone site. You can access this by going
to the Uniform Resource Locator (URL) for the management interface, which is normally
http://localhost:8080/manage (this port will change depending upon your installation). You’ll
need a manager’s username and password for access to the ZMI; this password is created
during the Zope installation.
NOTE If you’ve forgotten the password for Plone that was created in the installa-
tion, don’t panic. You can create a new one; see Chapter 9 for more information.
You add all objects via the drop-down list in the top-right corner, as shown in Figure 2-12.
Scroll down the list until you find Plone Site, and click Add.
After selecting the option Plone Site, a form will display that prompts for some more informa-
tion (see Figure 2-13):
• Id: This is the unique ID of the Plone site (for example, enter Plone
or Site).
• Title: This is the title of the Plone site (for example, enter My Por-
tal).
• Membership source: For the moment, leave this as the default op-
tion, Create a New User Folder in the Portal. This allows you to have
user authentication somewhere other than the portal (see Chapter 9
for more information).
• Description: This is a description of the portal that members will
see in e-mails, (for example, enter A site all about the exciting
new widget technology). Don’t worry too much about this; you
can always change this later in the portal’s properties.
29
Figure 2-13. Adding a Plone site
After clicking Add Plone Site, a Plone site will be created. This may take a minute or two on
slower machines because a great deal of processing occurs. The screen will then redirect you
to the Plone welcome page.
2.1.4 Configuring the Web Server
Once Plone has been installed, you may want to configure the Plone site so that it runs on a
different port, has FTP capabilities, logs to a different file, and so on. This section covers these
basic setup issues. Note that you aren’t configuring the Plone sites themselves; you’re altering
the configuration of the underlying Web server.
NOTE If you’ve installed on Windows using the Windows installer, then most of this
configuration is provided through a nice user interface program; see ’Configuring
the Server on Windows” earlier in this chapter.
NOTE If you’ve installed using Mac OS X or the Windows installer, then you’ll find
one extra file (plone.conf), which contains port definitions used in the main Zope
configuration file.
30
Zope 2.7 creates a configuration file inside each instance installed. All the configuration for the
server is located in that one file. A full list of the configuration options is available in Appendix
A. To find the configuration file, look for a file called zope.conf located in the etc folder of your
Plone installation. Some installers (Windows and Mac OS X, for example) create a second
configuration file called plone.conf that contains Plone-specific configuration options. If your
installation contains a plone.conf file, then use that configuration file to make changes; they’ll
be included in the main configuration file.
The configuration file is extremely verbose and contains a great deal of useful comments and
examples. If you’re familiar with Unix configuration files such as Apache, then you’ll find the
Zope configuration file familiar. To alter Zope configuration, open the configuration in a text
editor, and change the lines as needed; after altering the configuration, you’ll need to restart
Zope.
It’s possible to run Plone 2.0 with a version of Zope prior to 2.7; however, Zope 2.7 offers
increased stability and new features, including easier configuration. If you’re using a version
of Zope prior to 2.7, you’ll need to read the documentation on how to change the configuration.
2.1.4.1 Adjusting the Ports
To change a port, add the address lines for that port. For example, to run Plone on port 80
instead of the default, change the following bold line in zope.conf:
<http-server>
# valid keys are "address" and "force-connection-close"
address 8080
# force-connection-close on
</http-server>
to the following:
<http-server>
# valid keys are "address" and "force-connection-close"
address 80
# force-connection-close on
</http-server>
If you used the Windows or Mac OS X installer, then you’ll find these port definitions in
plone.conf. These values are then imported into the main configuration file. So, on a Mac
to change the port, you’ll edit plone.conf from this:
## PLONE_WEBSERVER_PORT
## --------------------
## This is the port you will access your Plone site from. Set this to a port
## number above 1024 not used for any other server on your computer.
%define PLONE_WEBSERVER_PORT 8080
to the following:
%define PLONE_WEBSERVER_PORT 80
31
2.1.4.2 Using the Debug Mode
By default in Zope 2.7 debug mode is enabled. Note that Plone runs significantly slower in
debug mode, approximately 10-20 times slower. To turn this off, add the following line to the
configuration file:
debug-mode off
To make the out-of-the-box experience more impressive for Windows users (debug mode slows
Plone down on Windows even more than on Linux), it ships with debug mode off already.
If you have a Plone site running and want to know if debug mode is running, go to por-
tal_migration in the ZMI and look at the variables listed there; this will tell you if debug mode
is enabled.
2.1.4.3 Using Logs
By default there are two logs in Plone: an access log that you can produce site statistics from
and an event log that contains debug information about Plone products. The event log is the
place to find errors and messages in Plone. The default configuration looks like the following:
<eventlog>
level all
<logfile>
path $INSTANCE/log/event.log
level INFO
</logfile>
</eventlog>
<logger access>
level WARN
<logfile>
path $INSTANCE/log/Z2.log
format %(message)s
</logfile>
</logger>
This is where you can change the path to the file by defining a new file. The values that are
logged are based upon a level sent with error messages; more serious messages are sent with
higher levels. By default, only information and the previous message are sent to the log, but
that value could be one of the following: CRITICAL, ERROR, WARN, INFO, DEBUG, and ALL. If
you wanted to log only errors, then you’d change level INFO to level ERROR.
3 Chapter 3
3.1 Adding and Editing Content
Adding and editing content is a simplification of the sheer power that Plone has available to
leverage. Creating content-rich and feature-rich Web pages with Plone is an absolute breeze.
If you have Plone installed locally, then this chapter shows you how Plone works straight out
of the box. However, if you don’t have Plone installed, then don’t worry; you can try Plone
online by going to http://demo.plone.org.
Before you can alter or edit a Plone site, you need to log into a Plone site. If you installed
Plone, you should have the username and password that came with the installation. This user
has the role of an administrator user, which allows you to log in and alter any content. Most
users of a Plone site will join the site and log in through the login process described in the
’Joining a Site’ section. Users can, of course, view a Plone site without logging in, but they
won’t be able to add or edit content.
In this chapter, I’ll go through the steps a user takes to create content on a Plone site. First, I’ll
cover how to join the site and log in. Once you’ve accomplished this, I’ll cover how to create
and then edit a document. Finally, I’ll show how you can search and publish this content. In
short, this chapter covers how to use Plone.
3.1.1 Joining a Site
When you join a Plone site, you create an account on the server. That account gives you the
right as a member to add content such as images, documents, and so forth. To join a site, click
the join link in the top-right corner of the Web site (see Figure 3-1).
Figure 3-1. Clicking the join link in the top-right corner of the page
This will take you to a registration form that you’ll have to complete (see Figure 3-2). Because
this is the first Plone form you’ve encountered, take note of the following:
• Some fields are required; a little red box next to the text indicates
the required fields.
32
33
• For most fields, some grayed-out help text beneath the field name
indicates what you should enter.
Figure 3-2. The registration page
NOTE Because many of the Plone pages are quite large, the figures in this book
have been cropped to show only the key parts (in this case, the form) and not the
Plone logo or the footer. These elements are still there, but they’re superfluous.
34
To complete the form, complete the fields that are reasonably obvious. The values of the fields
are as follows:
Full Name: Enter your full name. This field is optional.
User Name: Enter the username you want to use. Most people choose an alphanu-
meric value without spaces, such as bob or jane97. This username will be used
throughout the Web site to refer to you. This field is required.
E-mail: A valid e-mail address is required. This will allow the site administrator
to contact you and to send a password to you. You can change this e-mail address
later by editing your member preferences. This field is required.
Password and Confirm Password: This is the password you want to use; it must
be more than four characters and can contain letters, numbers, and the underscore
(_) character. Passwords are case sensitive (in other words, SomePassword isn’t the
same as somepassword). These fields are required.
Send a mail with the password: Check this box if you’d like your password sent
to the e-mail address you provided. This field is optional.
Once you’ve completed this form, click Register to submit your information. If you’ve made
any errors on this form, then you’ll see a message at the top and the key fields that have an
error highlighted. In Figure 3-3 I entered a password but didn’t enter a value for the Confirm
Password field. Again, this is the standard way that Plone forms will show errors to you.
35
Figure 3-3. Errors on a form
If you’ve completed the form correctly, then you’ll be given the option of logging in immedi-
ately. Click the Log In button to log in. You’ll see the page shown in Figure 3-4.
36
Figure 3-4. After registering
If you already have a username and password or are returning to a site you’ve previously
joined, then you can enter your name and password in the boxes in the left column of the site
and click the Log In button.
3.1.2 Enabling Cookies
To log into a Plone site, you must have cookies enabled. If you access a Plone site and try to log
in with cookies disabled, you’ll get a friendly message telling you that cookies must be enabled
with a link to more information. To enable cookies, perform the following steps, depending on
your browser.
Internet Explorer 6.x 1. Select Tools > Internet Options.
2. Click the Privacy tab at the top of the screen.
3. Move the slider to Medium, and click OK.
Internet Explorer 5.x 1. Select Tools > Internet Options.
2. Click the Security tab at the top of the screen.
37
3. Click Custom Level, and scroll down to the Cookies section.
4. Set Allow Per-Session Cookies to Enable, and click OK.
Internet Explorer 4.x 1. Select View > Internet Options.
2. Click the Security tab at the top of the screen.
3. Click Custom Level, and scroll down to the Cookies section.
4. Select Always Accept Cookies or Prompt Before Accepting Cookies, and click
OK.
Mozilla 1.x 1. Select Edit > Preferences.
2. Find Privacy & Security in the menu on the left. If there’s a plus sign (+) to
the left of Privacy & Security, click it.
3. Select Cookies under Advanced.
4. Select Enable Cookies for the Originating Web Site Only or Enable All Cookies,
and click OK.
Opera 1. Press F12.
2. Click Enable Cookies.
Netscape Navigator 6.x 1. Select Edit > Preferences.
2. Find Privacy & Security in the menu on the left. If there’s a triangle pointing
to the right next to Privacy & Security, click it.
3. Select Cookies under Privacy & Security.
4. Select Enable Cookies for the Originating Web Site Only or Enable All Cookies,
and click OK.
If you forget your password at some point, you can get it sent to the e-mail address provided
when you registered with the Plone site. To have your password mailed to you, click the
Forgot your password? link located in the left column of the Web site. This will bring up the
forgotten password form, as shown in Figure 3-5; enter your login name, and a password will
be e-mailed to you.
38
Figure 3-5. Getting a forgotten password
Unfortunately, if you have no longer access to that e-mail account, or you can’t even remember
the username, you’ll have to contact a site administrator. Using the techniques discussed in
Chapter 9, the administrator can change your e-mail and find your user account. Once logged
in to the Plone site, you’ll see a log out link in the upper-right corner. When your work is
finished, it’s good practice to log out of Plone site, especially if you’re accessing it from a
computer that’s likely to be used by other people.
3.1.3 Setting Up Your Folder and Preferences
After you’ve logged in, the member bar in the top-right corner will change to represent the
options available to you as a member of the site (see Figure 3-6).
39
Figure 3-6. Your personal choices in the top-right corner have changed.
One of these options is that each member has a folder created for them when they join a site.
This folder is set up with particular security so only that member (and administrators) can add
and edit the content in that folder. To access your personal folder, click the my folder link in the
personal bar in the upper-right corner of the site. In the upper-right corner you’ll also see a my
preferences link; clicking this will open a list of personalization options. You’ll see two choices
at the moment; you can change your password, or you can go to the personal preferences and
change key preferences in your site.
The change password form allows you to change your password. To complete the form, give
your current password and the new password twice. After you’ve changed your password, the
change will be immediate. You don’t have to log in again; just remember your new password
when you return.
The personal preferences form allows you to set a number of preferences that change how you
see the site. These preferences are stored on the server so they’re retained between uses of the
site (see Figure 3-7).
40
Figure 3-7. Changing preferences
The options are as follows:
Full name: This is the full name you gave when you registered with the site.
E-mail: This is the e-mail address associated with your membership and is used
a number of places in a Plone site. Most important, if you lose or forget your
password, this is the address to which the system will send it.
41
Content editor: When editing complex content, you may want the help of an
editor. If your site administrator has made one available, you can select it here. It
will then be used when you click the edit tab of an object. If you’re unsure, leave
this as the default.
Listed status: This property specifies whether your profile will show up on the
members tab and when someone searches the members listing.
Editing of Short Names: Objects have an ID or Short Name property that’s used for
the internal representation of the content object. This also shows up in the item’s
Web address and the item’s Uniform Resource Locator (URL). By default these look
something like News_Item.2002-11-16.4102, but you could make it much simpler,
such as november_news, by changing the Short Name value.
NOTE When you change an object’s name value, anything that references the older
name will no longer be valid and will result in the page not being found. It’s best
not to change the name value after you submit an object for review or link to it
from elsewhere. For this reason, I recommend setting this option to No.
Portrait: In larger organizations and in community Web sites, it’s useful to see
pictures of other members. The Portrait field allows you to upload a picture of
yourself. The picture should be 75 by 100 pixels.
Once you’ve made the desired changes, click the Save button to commit the changes. Now that
you’ve logged in, it’s time to start adding and editing content.
3.1.4 Adding and Editing Documents
As mentioned, now that you’re a site member, a folder has been created for you where you
can store content. Of course, you can add content to any folder that the site administrator has
given you the right to do so, but by default every user can add content to their member folder.
Each type of content you can add is distinct, and you can edit and view it in different ways.
For this reason, Plone references each type of content differently; for example, you can add
images, links, documents, and so on. Out of the box, Plone provides the following content
types:
• Document: This is an item that presents some static information to the user.
This is the most common type of content added and most closely represents a
typical Web page.
• News item: This is a document that’s to be shown under the news tab (for
example, a press release).
• Link: This is a link to another item, which may be internal or external to
another Web site.
• Image: This is an image, such as a .gif or .jpeg file.
• Event: This is an upcoming event, meeting, conference, or other event.
• Folder: This is like a folder on a hard drive; this is a folder for putting content
into so that it’s easy to find later.
42
• Topic: This is a grouping of other content. This is essentially a saved search
criteria that you can reuse later. Only privileged site users can add topics.
• File: This is another piece of content such as a movie, sound clip, text file,
spreadsheet, compressed file, or anything else you’d like to upload.
I’ll go through each of these items using the document as an example, showing in detail how
to add and edit documents easily and quickly. Using these basic content types, I’ll show you
how to build a dynamic site through a browser, without doing any programming.
Actually, you have many ways of adding and editing content in a Plone site than just through a
Web browser. Access via File Transfer Protocol (FTP), via Web-based Distributed Authoring and
Versioning (WebDAV), or via scripts is all possible. I’ll discuss how to set these up in Chapter
10 and just deal with the Web browser interface for now. In Chapters 11 to 13 of this book, I’ll
discuss how to make new custom content types that you can tailor to a particular site’s needs.
3.1.4.1 Understanding the Document Content Types
Rather than detailing how to add and edit all the different types of content available, I’ll
cover adding one type of content, a document, in detail. After adding and editing a few of
these documents, the approach to adding content should be second nature, and editing other
content will be easy.
A document is a page of content, usually a self-contained piece of text. Although all items
added to Plone are accessible as Web pages, if there’s one content type you could think of as
a Web page, this is it. The default home page for a Plone site that you’ve seen already - the
now-famous Welcome to Plone page - is one example of a document (see Figure 3-8).
43
Figure 3-8. Welcome to Plone, a simple document
3.1.4.2 Adding a Document
You have two ways to add any piece of content using a Web browser. First, ensure you’re
logged in, because only logged-in users can add content. Second, select the my folder link from
the top-right navigation bar. This will take you to your home folder, an area that you control.
44
If you’re able to add content to a folder, then the folder will show up with the green border
around the top (see Figure 3-9).
Figure 3-9. My content
If the green border doesn’t appear, then you won’t be able to add content; this border contains
the actions you can perform in the current location. In Figure 3-9, you can see that the page
shows the contents of the folder, because that’s the highlighted tab. Other tabs appear such as
view, sharing, and properties for more advanced options. In the top-right corner of the green
border, you’ll see an Add New Item drop-down menu and a State drop-down menu. Click the
Add New Item menu to open a drop-down list of items to add (see Figure 3-10).
45
Figure 3-10. Adding a document from the green drop-down menu
To add a new document, select Document. Alternatively, if you look in the body of the page,
you can see another Add New Item drop-down box. Again, click the down arrow to open a list
of items that can be added and then select the item you’d like to add (see Figure 3-11).
Figure 3-11. Adding a document from the main folder’s content menu
Using the Add New Item list from the green border is a handy menu since it’s available most
of the time.
NOTE **If you’re familiar with Zope, you should never, never, never add con-
tent from the Zope Management Interface (ZMI). Depending upon how you’ve
installed Plone, you may have already seen the ZMI and used it for customiz-
ing and developing Plone through the Web. However, adding content through
the ZMI will create content items that are incomplete and don’t ***Begin Side-
bar*
3.1.4.3 Understanding Where to Add Content
The easiest place to add content at the beginning is in the user’s member folder, accessi-
ble by clicking the my folder link. Although this is useful, it’s probably not the best ap-
proach for a long-term solution. Most noticeably it creates long URLs (for example, /Mem-
bers/andy/Docum....). It also means your content isn’t accurately reflected in the navigation
tree.
As you’ll see later, a few solutions exist for this; the most common solution is to make a folder
and give certain users the right to access it. For example, that folder may be Help or News. The
’Using Folder” section later in this chapter discusses adding folders, and Chapter 9 discusses
using group workspaces and security.
3.1.4.4 Editing a Document
Once you’ve clicked to add a document, you’ll be taken directly to the edit page with a mes-
sage telling you that the document has been created. If this doesn’t happen, you can click a
document and then click the edit tab. Again, you’ll see that the edit tab becomes highlighted
in green (see Figure 3-12).
46
Figure 3-12. Editing a document
Now you can edit the document in your Web browser, using the form provided. If you look at
the URL in the address bar of your browser, you’ll note that a short name for the object has
been created for you, something such as Document.2003-12-29.43787. The following is a list
of the fields and their meanings:
Short name: The short name will become part of the document’s URL, so keep the
name short and descriptive, preferably without spaces. Keeping to these rules will
make URLs easier to read. For example, use something such as audit-report-2003.
If you don’t provide a name, Plone will create one for you.
47
NOTE This field won’t appear if you selected No for the short names in your pref-
erences page.
Title: This is the title for the item, and it’ll be shown throughout the site (for
example, at the top of the page, in the search interface, in the title of the browser,
and so on). This field is required.
Description: This is a short lead-in to the document, usually about 20 words to
introduce the document and provide a teaser for the remainder of the document.
This is useful for pages that show summaries of documents, such as search results
and folder contents.
Body text: This contains the body of the document. The format for the content is
set using the Format field (described next).
Format: You have three choices for the format of body content: Structured Text,
HTML, and Plain Text. These types of text are discussed in the ’Choosing a Text
Format” sidebar; if you’re unsure, leave this field alone and type the body text as
usual.
Upload document: If you do have your document as a file on your computer, you
can upload it instead of typing the content into the Body Text field. Use the Upload
button at the bottom of the page to select a file. The contents of an uploaded file
will replace any content in the Body Text field.
Once you’ve finished editing your document, click the Save button to commit your changes.
You’ll be returned to the view tab where you can see how the document will be shown to users
(see Figure 3-13); to edit it again, click the edit tab.
48
Figure 3-13. Saving the content will take you to the view tab.
If you don’t provide the correct input on the edit form, when you save the document you’ll
be returned to the edit page, and your errors will be highlighted. At this point your changes
haven’t been applied—you must correct the mistakes and click Save again before the changes
will be committed. The view tab shown in Figure 3-13 shows the document you’ve created.
You’ll see that the title, description, and content are all shown in slightly different styles. At
the bottom of the page is a byline that contains information about the author of the document,
including the date the page was created.
You’ll note that if you go back to folder contents after saving your changes, you’ll see two
documents in your folder: the existing one that’s created for you and the new one you’ve just
added. You can edit either of these documents by clicking them to open the view tab, which
allows you to select the edit tab.
49
3.1.4.5 Choosing a Text Format
As mentioned previously, you can edit the document content in at least three formats: struc-
tured text, HTML, and plain text. This rather confusing state of affairs is brought about by
trying to produce easy systems for users to write rich marked-up content in plain text without
having to use fancy editors.
Unfortunately, in most cases, this really doesn’t work; training is required to understand the
formatting. Structured text requires quite a bit of understanding in itself because it has a frus-
trating syntax and doesn’t internationalize well. If I had to pick one format that I’d recommend
over all the others, I’d pick HTML because it’s widely understood, and you can use What You
See Is What You Get (WYSIWYG) editors such as Epoz to produce it.
*HTML* HTML is the most standard format; if a document is entered as HTML, it will be
rendered in the same format. This HTML shouldn’t be a complete page but rather a snippet.
For example:
<p>Here is a sample in <i>HTML</i> for a demonstration.</p>
Ideally the HTML should also be valid Extensible HTML (XHTML) to comply with the rest of
the Plone system; if it’s not, your pages don’t comply with Web standards. Entering text as
XHTML isn’t for the faint of heart, so in Chapter 9, you’ll see how into integrate rich-editing
tools into Plone that allow users to easily write content in XHTML. The following screen shot
shows Plone using Epoz so users don’t have to understand HTML:
*Plain Text* Plain text is simple. It does no major conversion or manipulation of the text
entered; it’s just plain text. The only modification made is that new lines are converted into
HTML when rendered so that new lines appear in the Web browser. No other altering happens.
For example:
Here is a sample in plain text for a demonstration
*Structured Text* Structured text is a system for writing plain-text documents in a particular
format, which can then be interpreted in different ways. For example, if a piece of text needs
to be highlighted, then it can written as italics; this will then be shown as italics. This series of
rules means that a user can write a page that contains formatting information easily. For a full
list of structured text rules and examples, please see Appendix A. The following is a sample of
structured text:
Here is a sample in *structured text* for a demonstration
50
3.1.4.6 Setting Document Metadata
Any piece of content can have any number of properties assigned to it. These properties are
known as metadata and provide information such as keywords, copyrights, and contributors of
an item.
This entire set of properties is optional and is usually used only if there are special requirements
for this piece of content, especially since this information isn’t normally shown to the person
viewing the content. So the main reason for entering this data is to add information for tasks
such as searching or categorizing the content.
You can access properties on an object by selecting the green properties tab. This properties
form has the following fields, which are common to all content types:
Allow Discussion: This lets this document be discussed by users who have the
right to do so. If the value is left as default, it’ll use the sitewide policy for that
content type.
Keywords: Each item can have keywords assigned to it to enable grouping and
sorting of the items. For example, an article about recent events in politics may
have the keywords politics and prime minister. Keywords are flexible, and you can
use any keyword from the given list. By default there are no keywords in the Plone
system; site administrators may add new keywords so that other users can select
them.
Effective Date: The effective date is the first day a piece of content should be
available. You can specify this date by entering the values in the form or clicking
the little calendar icon, which opens a calendar, and picking a date (see Figure
3-14).
Figure 3-14. Entering an effective date
Expiration Date: The expiration date is the last day a piece of content should be
available. Usually the Effective Date and Expiration Date fields are left blank.
Format: This is the Multipurpose Internet Mail Extensions (MIME) type of the
item. The term MIME type refers to a computer definition of the type of content
51
(for example, application/msword or image/jpeg). This is set at a default value; if
you’re unsure about this field, just ignore it.
Language: This is the language in which the document is written; the default is
English.
Copyright: This is the copyright information for the content, which is usually
blank.
Contributors: This includes the names of the people outside the Plone system who
contributed to the object. Each person’s name should be on its own line.
After completing the values for this form, click Save to commit the changes. As stated, usually
you won’t need to edit the values on this tab. Editing the contents of this tab is usually based
upon the requirements for your site and the type of site you’re building.
3.1.4.7 What Are Effective and Expiration Dates?
Any item in the Plone system can have effective and expiration dates if the person editing the
content wants. Both of these are optional, and leaving the fields blank will ensure that these
values aren’t set.
One example of an item that may have an effective date is a press release. In the ideal world,
the news item is crafted, prepared, and reviewed in Plone. However, suppose the news item
has to go live on the Web site at midnight, but that’s exactly when you plan to be sleeping.
Not a problem—give the press release an effective date and a time of midnight. Up until the
effective date, it won’t be visible in the calendar, in navigation, in searches, or in pages that
use a search as the listing under the news tab. However, anybody who knows about the press
release will be able to access the page directly. Once the effective date has passed, the item
will appear in all the aforementioned places and be live to the world.
The effect is similar with expiration dates. If you have a special offer that stops being effective
on a particular day, then you could set an expiration date of that day. After that, date it
wouldn’t appear in calendar, navigation, searches, and so on.
The effective and expiration dates don’t actually change the state of the item in workflow (see
Chapter 7 for more information on workflow); rather, they just change where it displays. You
can also set effective and expiration dates on the state tab, which you’ll learn about in the next
section.
3.1.4.8 Publishing Your Document
When a document is created, it’s given an initial state, called visible. By default, content isn’t
automatically published and available to the world; instead, others can view your content, but
it doesn’t show up in searches or the navigation tree. This is a useful state because you can
point other users to this content, but because it won’t show up in navigation or searches, it’s
not visible unless users know about it.
At any point in time, each item of content in your Plone site is in a particular state. This state
describes its permissions and roles within the Plone site. By having items in different states, it’s
52
possible to apply different security to each item of content. For example, sometimes an item
may take a week or two to prepare and involve multiple revisions. Eventually you’ll want to
publish the content so that it’s visible for all users and shows up in the navigation and search.
You can publish the content using the State drop-down menu located at the top right of the
main navigation (see Figure 3-15).
Figure 3-15. State drop-down menu
To publish an object, select Submit from the drop-down menu. By default you can’t directly
publish content, but you can submit it for review. When an item is submitted for review, it
moves into the review state. This is an intermediary state between visible and published. It
allows for the review of content by users of your site with the reviewer role, before it goes live
for the entire world to see. After you’ve submitted the content, you’ll notice that the content is
now in the review state by looking at the box in the top-right corner. You’ll also notice that in
Figure 3-16, there’s no longer an edit tab.
Figure 3-16. The content has been submitted for review, the state has changed to pending, and
the edit tab is no longer an option.
NOTE If you’re logged in as a manager, then you’ll note there will be one extra option in the
drop-down publishing list called Publish. This lets you put content straight into the published
state with no intermediate step.
In the workflow drop-down list in the top-right corner, there’s also an option for Advanced,
which opens the state form for changing the status of an object. This form is the same as
clicking the state tab. It has the following fields:
Effective Date: This is the same as the Effective Date field in the properties (see
the ’Setting Document Metadata’ action).
Expiration Date: This is the same as the Expiration Date field in the properties
(see the ’Setting Document Metadata’ section).
Comments: This includes any comments you want to make for this change in state
that will be recorded in history. For example, you could enter First draft; Bob,
please see second paragraph.
53
Change State: These mirror the choices available in the drop-down menu. For ex-
ample, the options are Publish, Submit, and so on. One further option, No Change,
is available if no change is necessary.
Select the change of state you’d like to occur, and click Save to commit the changes.
3.1.4.8.1 What Are the Workflow States?
At this point you may be asking yourself what this workflow thing is and what the states mean.
Workflow, as discussed in Chapter 7, is the ability to apply different states to the content. The
following are the default states:
Visible: Content is created in the visible state. All users can find visible content
through the search function and can access it directly by visiting the object URL.
Visible content doesn’t show up in the navigation tree. Visible content is editable
by their owners and site managers.
Pending: Pending content includes items that have been submitted for publishing
by site members. From a user standpoint, pending content behaves like content in
the visible state. The difference between the two types is that pending items are
flagged for review; site reviewers are prompted to publish or reject pending items.
Pending items are editable only by managers and reviewers.
Published: Published items are visible to all site visitors. They appear in search
results and the navigation tree. They may also appear in other areas specific to
that type (news items, for example, also appear when you click the news tab).
Published items are editable only by managers, but owners can retract them for
editing (retracting reverts an item to the public draft state).
Private: Items in the private state are visible and editable only by their owners and
others with manager access to the folder in which they exist. They won’t appear in
search results or on the navigation tree for other users. Private items are editable
by managers.
3.1.4.8.2 How Does Content Get Reviewed?
If you’re a reviewer, then in the right column of the home page you’ll see a new review list
when you first log in. This is a list of the items that have been submitted for review and need
reviewing by you or another reviewer (see Figure 3-17).
54
Figure 3-17. The review list
The review list will appear on the right when you log in as a user with the review role and
there are items to review. In my case, I logged in as admin, which was the user created
during my install process. You can tell you’re logged in because your name will appear in the
member bar. The review list gives a list of items to review - in this case, you need to review
the test document. Click the document to open the item. At this point you essentially have the
following choices for this item:
Reject it: Reject it by selecting Reject from the drop-down choices. This will move
the content back into the visible state and assumes that as a reviewer you’re un-
happy with it. Usually you may want to click the Advanced option to open the
comments form and add some comments stating why you’re rejecting it.
Approve it: Approve it by selecting Publish; this will change the content into the
published state. This will make the content publicly available.
Do nothing: Leave it by doing nothing. This leaves the content in limbo but some-
times happens when you need to check information or talk to others. Eventually
you should return to do something with this content because it’ll continue to show
up in your list of items until you make one of the previous actions.
Edit it: Edit it, and then perform one of the previous actions. As the reviewer, you
can make any change you’d like to do, so feel free to change the content by using
the edit tab.
Once you’ve moved content out of the review state by publishing or retracting it, it will no
longer show up in the review list. Of course, this assumes you do have someone as a reviewer
for your site; this usually (although not necessarily) is also the user who created the Plone site
as an administrator. In Chapter 8, I’ll discuss how to add and edit users and give some users
the review role.
3.1.4.8.3 How Do You Edit a Published Document?
Once a document has been published, it must be retracted to be edited. To do this, select
Retract from the workflow drop-down menu, which will move the item back into the visible
state. Once it has returned to the visible state, you can reedit it and placed it back into the
review queue.
This step, although a little annoying, is necessary to ensure that all content goes through a
review step. For example, you have to ensure that any edits made to a page are appropriate by
reviewing the content. Users with the manager role can edit the content at any time, so they
can quickly go in and fix a typo without having to go through the review step. It’s assumed
that users with a manager role are trustworthy! If you’re a manager, as defined in Chapter 9,
you can go to any piece of content and will see the edit tab. At that point, click Edit to alter
the document and make your changes.
55
3.1.4.9 Sharing Your Document
This allows you to assign more rights to other users or groups of users of the system to your
document. This is an advanced feature and is covered in more detail in Chapter 9.
3.1.5 Adding and Editing Other Types of Content
I’ve just covered how to add and edit documents in detail. All the other content types are
similar. They all have the same or similar actions to edit; it’s just the forms and the data in
them that change. In the following sections, I’ll cover some of these other types of content. All
the following types of content use the same workflow process, so they need to be published in
the same manner as documents.
3.1.5.1 Adding and Editing Images
Images are graphical pieces of content; you add them by selecting Image from the drop-down
list. When you add an image, the name of the content changes to the file of the image. So, if
you upload an image called photo.gif, it’ll be accessible in Plone as photo.gif. When adding or
uploading a new image, you can select the image from your hard drive by clicking the Browse
button and selecting the file (see Figure 3-18).
56
Figure 3-18. Uploading an image
It’s common for image filenames to end with an extension such as .gif, .jpg, .jpeg, .png, or
.pict. You can display images inside Plone on a Web page without having to download them
to the local computer if the type of the image uploaded is viewable in the user’s Web browser.
The most common image types are .gif, .jpg, and .png, which are visible on almost computer
system. Figure 3-19 shows an image of the Plone logo.
57
Figure 3-19. Viewing the image
You can’t edit images directly; instead, you can edit the image on your hard drive using any
program, such as Adobe Photoshop or GNU Image Manipulation Program (GIMP). Once com-
plete, clicking the edit tab allows you upload your new image into Plone. If you do a lot of
image manipulation, you can refer to Chapter 10, which covers External Editor, a tool that lets
you edit images using a program without having to upload and download them.
3.1.5.2 Adding and Editing Files
A file is any arbitrary file that can be uploaded from your hard drive. To add a file, select File*
from the drop-down list. On the edit tab, you’ll see an Upload button that lets you pick the file
from your hard drive. This could be any sort of item, including a plain-text file, a Microsoft Word
document, a Microsoft Excel spreadsheet, an executable program, an Adobe Acrobat document,
and so on. When you add a file, the name of the item in Plone changes to the name of the file
uploaded. So, if you upload a file called *book.pdf, it’ll be accessible in Plone as book.pdf. Figure
3-20 shows a plain-text file.
58
Figure 3-20. Adding a plain-text file
If the file is recognized as being text, then the contents of the file are shown in the Web page
and are editable through the edit tab. Otherwise, the file is downloadable, and users must
download it to their local hard drive and edit it there. Afterward they can upload it to the
system. You’ll note that a file object also has an extra download tab that lets you directly
download the file.
59
3.1.5.3 Adding and Editing Events
An event can be something that will happen in the future or something that happened in past.
You can add events to Plone, and they show up on the calendar. To add an event, select Event
from the drop-down list. An event has more information than most Plone objects; however,
most of it is self-explanatory (see Figure 3-21).
Figure 3-21. Adding an event
As usual, the only required field is Title; however, if you want the event to show up in the
calendar, then you must provide a start and end time. Events can span multiple days or be in
60
the past—as long as the start date is before the end date. To enter a date, select the appropriate
dates from the drop-down menu, or click the little date icon to open a graphical date picker.
Once the event is published, it’ll show up in the calendar. Moving a mouse over the item in the
calendar will show the start and end dates for the event, as well as the event’s title (see Figure
3-22).
Figure 3-22. Viewing events in the calendar
3.1.5.4 Adding and Editing Links
Link content types are the primary way for users to share links. These URLs can be resources
on the Internet or an intranet, an internal resource, or anything to which the users have access.
To add a link, select Link from the drop-down menu.
If you’re going to link to an Internet resource, you should preface your link with the suitable
protocol (for example, http://). For instance, if I was visiting an interesting page on the BBC ’s
Web site and wanted to share this, I could add a link. The value of the URL will be the text in
the address bar (for example, http://news.bbc.co.uk), as shown in Figure 3-23.
61
Figure 3-23. Adding a link
3.1.5.5 Adding and News Items
News items are commonly used in Web sites to display news that’s of interest to the reader.
Actually, a news item contains the same information as a document. The only real difference is
that a news item will show up when a visitor clicks the news tab (once the item is published),
as shown in Figure 3-24.
62
Figure 3-24. A list of news items
If I wanted to write a Web page that was going to be relevant for a long period of time, such
as directions to my company’s office, I’d use a document. If I wanted a page that detailed my
exciting new product and drew attention to it, I’d use a news item. That news item would be
visible under the news tab, and as new things happened, it’d slowly move down the page.
63
3.1.6 Organizing Content
So far you’ve seen how to add and edit content in a Plone site, but without clear organization,
this can become a mess quite quickly. You have two main ways of organizing content: folders
and topics. A folder is the simplest and most powerful mechanism for organizing content and
works just like a folder or a directory on a computer’s hard drive. A folder can contain any
item of content; content can be copied and pasted between folders, and of course folders can
contain other folders.
To organize content that’s spread all over a site, a more sophisticated and less-used tool called
a topic is available. A topic searches your Plone site and finds all objects that match a certain
criteria, allowing you to group lots of disparate content.
3.1.6.1 Using Folders
A folder is just like a folder or a directory on a hard drive, except that the folder and its
contents exist inside Plone. You use a folder the same way; when you need to categorize
content or make things a little clearer, you can group items and place them in a folder. To add
a folder to your site, select Folder from the drop-down list. This will add a folder and take you
to the edit properties page for that form. A folder has just three rather simple attributes that a
user can edit: Name, Title, and Description. I’ve discussed all these attributes for documents,
and nothing is different for folders.
Folders have two green tabs that represent slightly different views: contents and view. Actually,
you may have already noticed that there’s a contents tab accessible from any piece of content
that you’ve added to the site; for example, when you were editing a document, the contents
tab was there. That contents tab will always take you to the contents for that folder.
3.1.6.1.1 Viewing the Contents of a Folder
The folder has the concept of a default page, which is a page that will be shown to the user
when they view a folder. It’s a concept taken from Web sites where viewing a folder on a
Web site shows a default page if one is present; often that default page’s name is index.htm
or index.html. If a folder has a default page, then clicking the view tab will show that default
page. If the folder doesn’t have a default page, then it’ll show a folder listing of all the content
in that folder. When looking for a default page to display, Plone looks through the folder for
content with a certain name and shows this item. The page name is usually index.html or
index_html; however, the site administrator can add or alter these names.
This contents view of a folder allows a user to perform a variety of tasks, such as move content,
rename it, delete it, publish it, and change the order it’s listed. As shown in Figure 3-25, you’ll
also see a simple table of the folder contents. Each row of the table shows the title of the
content (plus an icon), the type, its size, when it was last modified, its current workflow
status, and order selectors. On the left is a checkbox to select the items you want to change
and a series of options across the bottom: Rename, Cut, Copy, Delete, and Change Status.
These functions are all pretty self-explanatory, and you can apply them to multiple objects at
once by clicking several checkboxes.
64
Figure 3-25. Contents of a folder after I’ve added some of the content types described in this
chapter
For example, to quickly rename a piece of content, click that item’s checkbox and then click
Rename. This will open the rename form and allow you to rename the title of each item in that
list. Click Save to have the changes take effect. The Cut and Copy buttons allow you to copy
or move content between different folders. The Delete button allows you to delete the item
from Plone. Just like on your hard drive, if you copy, move, or delete a folder, all the contents
of the folder will also be moved, copied, or deleted.
A new feature in Plone 2 is the ability to change the default order of items in a folder. By
default, items in a folder will display in the order the items were added. If one item is more
important and needs to be moved to the top, use the arrows on the right side of the table to
move the item. The following features will appear in the folder contents only when certain
things happen:
• If the content has an expiration date set and it has expired, you’ll see the word
expired appear in red next to the item.
• If the server has External Editor installed, you can click the pencil to edit in
External Editor (this is covered in Chapter 10).
• If the content is locked, you’ll see a lock icon appear next to the content.
65
3.1.6.1.2 Publishing a Folder
Folders have a much simpler workflow than documents. Earlier in this chapter you saw how to
publish content to make it publicly visible because this allows users to create and edit content
as much as possible before pushing it live. However, folders are a little different because they
contain content but don’t have any content of their own. For this reason, folders have no
review state. Anyone can directly publish or make private folders, so there are three states:
private, visible, and published.
After adding a folder, select Publish from the drop-down list. Then it’ll show up in the naviga-
tion. As per the earlier rules for workflow, if you don’t publish a folder, it won’t show up in the
navigation.
3.1.6.2 Using Topics
A topic allows you to collect content from disparate places throughout a Plone site and provide
it in one location. Topics work by creating a criterion that’s common to all the objects you’d like
to gather. This criterion could be all images or all news items with Plone in the text. Because
topics are a rather complicated type of content, only managers can add them initially. If you
can’t see Topic in the list of items to add, then you don’t have the permission to do so.
To add a topic, select Topic from the drop-down list. After adding the topic, you can create key
criteria on the criteria tab. The list of criteria and types is available in the drop-down box at the
bottom of the page. This is a rather confusing list; I won’t try to discuss it here. Unfortunately,
what those terms are and what they mean is based heavily on the underlying technology, such
as catalog indexes and object attributes. For this reason, Chapter 11 covers this in detail.
For example, to create a topic that shows all the images, you need to add a criterion that
searches for content based on portal_type. For this, select a field name of portal_type, select
String Criterion, and then click Add. These criteria will be added to the top of the page; in the
field to the right of portal_type, enter Image, and then click Save. You now have criteria for
your topic that will show all content that’s an image. Returning to the view tab, you can now
see all the images on a site.
As stated, topics are quite complicated, have a rather unfriendly interface, and are recom-
mended for only advanced users. Many people have found topics useful, which is why they’re
still available in Plone; however, a more user-friendly system will be developed in the future.
3.1.7 Discussing and Finding Content
Adding and editing content in Plone is much more useful if people can find and then discuss the
content. The primary ways users find content is through searching and navigation. Fortunately,
Plone automatically sets up searching and navigation for the users, so it’s easy to find the
content you’ve added.
66
3.1.7.1 Adding Comments to Content
Feedback from users is a vital part of any Web site. By allowing users to add comments, you
ensure that users can give feedback, correct typographical errors, or otherwise discuss the
content. You can discuss almost any piece of content in Plone; folders and topics are the only
exceptions.
You can enable discussions in one of two ways. First, the owner of the content (otherwise
known as the person who created it) turns on the discussion feature by clicking the properties
tab of the object and selecting Enabled under the Allow Discussion on This Item header, as
shown in Figure 3-26. Second, the default option applies the policy for that type of content
as defined by the site administrator; setting this option is described for the administrator in
Chapter 10.
Figure 3-26. Enabling discussions
Once discussions are enabled, click the Add Comment button to discuss the content, which
opens a form for adding the comment (see Figure 3-27).
67
Figure 3-27. Adding a comment to a piece of content
Enter the subject of the comment and the text of the comment. The text is entered as plain
text, so just type away as usual. Comments don’t go through any workflow, so comments show
up as soon as they’ve been added. Once a comment has been entered, it can be replied to,
forming a threaded list of comments on an item. Further, comments will be entered in the
catalog so they can be searched.
NOTE Administrators logged in as managers can remove any replies or entire threads. Dis-
abling replies doesn’t remove the comments; it just stops them being shown, so reenabling
comments will show the existing comments again.
3.1.7.2 Searching for Content
Plone contains a powerful search engine system based on Zope’s ZCatalog. This search engine
allows content to be cataloged in multiple ways and to be queried efficiently and quickly.
Chapter 10 covers the internals of how this works and how it can be queried.
When you’re searching for content, the content will be shown to a user if it’s one of the two
states: published or visible. At the top of your Plone site a search box provides an easy way to
do simple textual searches in the same way as a search engine (see Figure 3-28). For example,
enter Tuesday to find all content that contains the word Tuesday. A result of all matching
content will display; click the title to get to the content.
68
Figure 3-28. A search for Tuesday on Plone.org
The search provides a quite sophisticated search, with features similar to most search engines.
You can make this simple query quite complex. For example, you can use the following options:
Globbing: You can use an asterisk to signify any letters. For example, entering
Tues* matches Tuesday and Tuesdays. You can’t use the asterisk at the beginning of
a word, though.
Single wildcards: You can use a question mark anywhere to signify one letter.
For example, entering ro?e matches rope, rote, role, and so on. You can’t use the
question mark at the beginning of a word, though.
And: You can use the word and to signify that both terms on either side of the and
must exist. For example, entering Rome and Tuesday will return a result of when
both those words are in the content.
Or: You can use the word or to signify that either terms can exist. For example,
entering Rome or Tuesday will return a result of when either of those words are
in the content.
Not: You can use the word not to return results where the word isn’t present; a
prefix of and is required. For example, entering welcome and not page would
return matches for pages that contained welcome, but not page.
Phrases: Phrases are grouped with double quotes (’ ”) and signify several words
one after the other. For example, entering ’welcome page” matches This welcome
page is used to introduce you to the Plone Content Management System, but not
Welcome to the front page of my Web site.
Not phrase: You can specify a phrase with a minus (-) prefix. For example, entering
welcome -’welcome page” matches all pages with welcome in them, but not ones
that match the phrase welcome page.
69
NOTE All searches are case insensitive.
Large sites may have a lot of results, so only 20 results display at a time. To page through the
results, navigation bars will appear at the top and the bottom of the search result pages. The
values on an object used in a search are its title, description, and body text (if the content type
has any—for example, news items and documents).
3.1.7.3 Performing an Advanced Search
You can narrow down the search results by using an advanced search, which is accessible via
the search results of a standard search. In old Plone sites, a search tab brought users to this
page; you can reenable this tab if you want, as covered in Chapter 4. The advanced search
form enables a user to query content using a number of attributes, including title, keywords,
description, review state, creation date, content type, and even author, as well as the search
text (as used in the quick search available from the top-right corner), as shown in Figure 3-29.
Figure 3-29. Advanced search
Although the search text field searches both the title and description, you may want to search
the description or title only. For that reason, these fields are presented on the advanced search
form. You can’t search the title and description using the wildcards, globbing, or any of the
advanced search options. Any search result will match the input (if given) of all the fields; the
results will be an intersection of all the terms.
70
3.1.8 Example: Creating the Plone Book Web Site
To give an example Plone site and provide a series of examples, I set up a Web site for this
book. This is a Plone site with a few minor modifications. As I go through the book, I’ll
make references to the site and add new features as they’re covered in the book, including
new templates, skins, and so on. The Web site for this book is at http://plone-book.agmweb.ca;
initially I set this site up on Windows server, as described in Chapter 2. However, I later
transferred it to Linux.
The site serves the following purposes:
• It gives people a place to get information about the book and buy it.
• It allows easy access to the software used in the book.
• It gives code examples and allows users to interact with samples in the book.
• It contains any errata or issues found after publication.
Once I set up a Plone site, I created the following basic folder and page structure:
Home
|_ Software
|_ Chapters
|_ Chapter 1
|_ Chapter 2
...
To do this, I logged in as the user who was created with the installer—in my case, the admin
user. After logging in, I went to the home page, clicked the edit tab, and wrote some text for
the home page. I created links to the Chapters and Software folders. Then I clicked the contents
tab and added two folders, as shown in Figure 3-30.
Figure 3-30. The folder contents with my home page and the new folders
Next, I went to the Chapters folder and started adding a folder for each chapter. Because
I haven’t made a default page, Plone will happily create a listing of all the chapters. The
71
description for each chapter is the name of the chapter (for example, Introducing to Plone and
This Book), and the short name is the chapter number—this will keep my URLs nice and short
(for example, /Chapters/3). I’ve left everything in the visible state, so after this, it’s just a
matter of adding content.
4 Chapter 4
4.1 Making Simple Customizations
After you’ve figured out how to add and edit content, you’ll want to start customizing your
site. This chapter explains how to perform simple customizations in Plone using the options
available to administrators. Performing the customizations in this chapter requires a user to be
logged in with the manager role, as discussed in Chapter 2.
These customizations are all configuration options you can make through the Web. Rather than
explain all these parts in detail, this chapter gives an overview of many subjects and explains
how to accomplish certain tasks while showing some of the under-the-hood machinery. These
topics are then expanded and explained in later chapters throughout the book.
The first and most useful place to look is the Plone control panel, which offers a variety of
options for the site administrator. All the parts of a Plone site are designed to be easily changed
and customized; the blue tabs you can see across the top of the page are easy to add and
remove. Other examples are the boxes in the left and right columns, which are called portlets.
Plone comes with several portlets, and you can easily choose where to display certain portlets.
Finally, this chapter shows how to customize Cascading Style Sheets (CSS) and images in
Plone. CSS affects everything in a Plone site. In fact, as you’ll see in this chapter, CSS deter-
mines all the colors, all the positioning, and a lot of the images you see. If you have the ability
to change the CSS code, then you can change almost the entire look and feel of your Plone
site. All the options covered in this chapter show you the large degree of control you have over
your Plone site.
4.1.1 Administering Sites
The first place site administrators should visit is the Plone control panel. This is the way to
access some of the administration functions of a site, including the name and description of
your Plone site, user and group administration, and any errors that occur within your site.
The term control panel is common, so don’t confuse it with the Zope Management Interface
(ZMI) control panel that shows the low-level ZMI options. The Plone control panel is an
ongoing attempt to provide a more user-friendly interface for the functions provided in the
ZMI. Since the project is ongoing, it’s hard to predict what functionality will be available in the
future. Instead, I recommend you go to the control panel and see what functions are currently
available; if you can’t complete your task there, then you’ll have to go to the ZMI.
72
73
To access the control panel, log into Plone as a user with the manager role. If you don’t have
a user with that role and are an administrator of the site, see Chapter 9 for more information
on how to do this. If you aren’t the administrator of a site and want this level of access, ask
your site administrator. To access the control panel, click plone setup at the top of the page (see
Figure 4-1).
Figure 4-1. Accessing the control panel
This opens the control panel (see Figure 4-2).
Figure 4-2. Plone control panel
The following functions are available in the control panel:
74
• Add/Remove Products: Clicking this link allows you to automate the instal-
lation products (covered in detail in Chapter 10).
• Error Log: Clicking this link accesses the log of errors that have occurred in
the Plone site.
• Mail Settings: Clicking this link allows you to alter the Simple Mail Transfer
Protocol (SMTP) server Plone uses to send e-mail.
• Portal Settings: Clicking this link allows you to alter portal settings (discussed
in the ’Changing the Title, Description, and E-Mail Addresses” section of this
chapter).
• Skins: Clicking this link allows you to set the current skin (discussed in Chap-
ter 7).
• Users and Groups Administration: Clicking this link allows you to alter users
and groups (discussed in Chapter 8).
• Zope Management Interface: Clicking this link takes you to the ZMI.
Throughout the rest of the book, I reference the Plone control panel if the current feature is
accessible from there; however, the remainder of book uses the ZMI for altering properties.
4.1.2 Using the ZMI
The ZMI is the basic interface that gives you access to Plone’s underlying Zope interface. Before
Plone existed, the ZMI was the main way to access, edit, and manage a Zope site and its
content. This was originally the Web interface for the content management system. Of course,
nowadays Zope isn’t really an out-of-the-box content management system but instead is an
application that sits under a system such as Plone. After quickly playing with the ZMI, you’ll
see why it isn’t suited as an interface to a content management system.
One thing the ZMI does provide is a simple interface to the underlying Plone and Zope infras-
tructure. You can find many of the basic features mentioned in this chapter through Plone, but
you’ll need to use the ZMI eventually. If you haven’t gone to the ZMI before, then you’ll find
that there are a few simple ways to get there; the easiest way is to log in as a user with the
manager role, click plone setup, and then click Zope Management Interface. You’ll note that the
address of the ZMI is the uniform resource locator (URL) of your Plone site with /manage on
the end of it. The ZMI for your Plone site should look like this:
75
You may have a problem with virtual hosting, which occurs with the Windows and Mac in-
stallers. Virtual hosting is the ability to have the Plone site as the root object rather than the
root of your Zope instance. For more information on virtual hosting, see Chapter 10. So, to
get to the root, you need to access the manage port. On Windows, select Start - Plone - Plone
- Manage Root. You’ll note that this sets the address to http://localhost:8080/manage. For
information on virtual hosting with your installation, see the specific documentation.
You’ll need to get to the root of your Zope installation for two reasons. First, you’ll need to
get to the Zope control panel. Second, you’ll need to get to the root of your Plone site to
make, rename, and copy Plone sites. The Zope control panel gives you database information
and access to products and other add-ons (you’ll need access to this for Chapter 10), as shown
here:
TIP When dealing with the ZMI, I find having two different browsers open helpful. For exam-
ple, I use Mozilla and Firefox. Besides, as a site administrator, it’s always a good idea to have
two different browsers to test that your changes work in more than one browser.
76
4.1.2.1 Changing the Title, Description, and E-Mail Addresses
The title, description, and e-mail addresses are stored in a Plone site as properties on an object
inside Plone. You can access these fields by clicking the Portal Settings link in the Plone control
panel (see Figure 4-3).
Figure 4-3. Portal options
The portal settings are as follows:
• Portal title: This is the title of the site that will appear in the title for browsers,
breadcrumbs, navigation, e-mails, and so on. The default is Portal.
77
• Portal description: This is the description for the portal, which is currently
used only in syndication.
• Portal From’ name: This field is related to various functions, such as a lost
password or the ’send-to-a-friend” function. Plone sends the e-mail messages
with this name attached. The default is Portal Administrator.
• Portal From’ address: This is the address used for the e-mails that Plone
sends. The default is postmaster@localhost.
• Default Language: This is the default language that’s given in the properties
of an object.
• Password policy: New users have two options; they can either enter a pass-
word or enter a password that’s then e-mailed to them. Although in both cases
they have to enter an e-mail address, the latter choice ensures that the e-mail
address they enter is valid.
• Enable External Editor Feature: This will turn on External Editor, which is
an advanced editing tool. It requires that External Editor be installed on the
user’s computer. Chapter 10 covers this in more detail.
After selecting the options you want, click Save to commit the changes. All the changes on this
form will occur immediately.
4.1.2.2 Setting Up a Mail Server
Plone will send e-mail using the MailHost object, which provides an interface to an SMTP
server and allows the developer to write forms and tools that send e-mails. The ’send-to-a-
friend” function and the mailing of a forgotten password use the settings configured here.
The default configuration is for a mail server on the localhost at port 25. If the SMTP server is
located elsewhere in the network, then you can access the form by clicking plone setup and then
clicking Mail Settings. Then change the mail server and the port to suit your configuration. On
my network, the mail server is at monty.clearwind.ca on port 1025, so I set the server as shown
in Figure 4-4; however, in most cases, you won’t need to change this.
78
Figure 4-4. Setting up the mail server
NOTE The MailHost object is a Zope object accessible in the ZMI. This object doesn’t currently
allow for authentication with the server. If this is needed, change the settings on the server.
4.1.2.3 Logging Errors
The error log catches errors that may occur in a Plone site; these are features such as Page Not
Found (404) errors, unauthorized errors, and so on. This isn’t designed to trap errors from
forms. For instance, if somebody doesn’t enter a required field in a form, then this won’t be
reported; this isn’t an error since it’s captured by the validation framework. This error log is
designed to catch internal server errors that may occur.
From the Plone interface, click plone setup and then click Error Log to see the errors reported
by the Plone site. Click the exception in the list (if there’s one reported) to see the error. Figure
4-5 shows an error that occurred when incorrectly filling out the mail settings form. It’s a long
page that includes a complete Python traceback and the incoming request.
79
Figure 4-5. An example error
On the error log form you’ll see the following settings:
• Number of exceptions to keep: These are the exceptions to keep in the active
log on the screen. The default is 20.
• Copy exceptions to the event log: This copies each exception to the file-
based log file. Not doing this means that no permanent record will be kept for
exceptions. The default is that this is selected.
• Ignored exception types: This is a list (one per line) of exception types to
ignore. The default is Unauthorized, NotFound, and Redirect.
80
You can log each exception and view it on the screen. This means if a user is visiting your
site and an error occurs, then you can go to the error log and see what occurred. The three
components of an error are the error type (which is the type of the error), the error value
(which is a string explaining when an error occurs), and the traceback. These first two items
are shown to a user on a standard error page (see Figure 4-6).
81
Figure 4-6. An example error message
So, when a user reports an error, the report will often include a message with the error name
and a value in it. If user isn’t allowed to do something and an Unauthorized error is raised
or a Page Not Found (404) is triggered, then you’ll get a custom error page rather than the
82
standard page shown in Figure 4-6. The following standard error types occur:
• Unauthorized: This occurs when a user doesn’t have the right to perform a
function.
• NotFound: This occurs when the item a user is trying to access doesn’t exist.
• Redirect: This is an error that can raise a Hypertext Transfer Protocol (HTTP)
redirect.
• AttributeError: When an object doesn’t have this attribute, this error is raised.
• ValueError: This occurs when a value given is incorrect and isn’t caught cor-
rectly by the validation or other framework.
4.1.3 Customizing Plone’s Look and Feel
The following sections describe the other customizations you can make to a site; almost of all
these require access to the ZMI.
4.1.3.1 Understanding Portlets
On a Plone site, you’ll see three columns to a page by default: the left, middle, and right
columns. The middle column contains the content for the object being currently viewed. This
is where most of the user functionality is for adding and editing content, completing forms,
and so on. The left and right columns contain a series of boxes that display information. Each
of these boxes is called a portlet. A variable determines which portlets display at a particular
point in time. The best way to understand these portlets is to look at the default portlets that
ship with a Plone site. You can find the parameters for the portlets in the portal object. To
access this, go to the ZMI, ensure you’re on the root Plone site, and click the properties tab.
This opens a list of properties, including left_slots, right_slots, and document_action_slots (see
Figure 4-7).
NOTE In earlier versions of Plone, the portlets were called slots. This is a common
term that conflicts with the page template term slot, so it was changed to portlets
for version 2. In certain places in the code and in the text, you may see the term
slot used. In these contexts, the words slot and portlet are synonymous.
83
Figure 4-7. The default portlet properties
The left_slots properties refer to portlets shown on the left of the page, and the right_slots
properties list the portlets shown on the right of the page. The portlets are shown in the order,
from top to bottom, that they’re listed in the properties; notice that each portlet is on a new
line. However, most portlets have some code to ensure that the portlet displays only if it makes
sense. For example, a login portlet is pointless to show if the user is already logged in. In this
case, the login portlet is included in the list of portlet but will show up only when needed.
Each portlet value is actually a special value called a Template Attribute Languages Expression
Syntax (TALES) path expression (Chapter 5 covers this in detail). Site developers can add their
own portlets to a site by creating simple macros and page templates. The default portlets are
as follows:
84
left_slots: This includes the navigation, login, and related portlets.
right_slots: This includes the review, news, events, recently published, and calen-
dar portlets. All the available portlets aren’t configured in Plone by default. The
following sections describe portlet slot in Plone. Each section describes the portlet
and shows what it looks like. Then I give the path expression that you need to add
to the slots property so it’ll show up in your Plone site.
For example, to show the calendar portlet on left side, enter
here/portlet_calendar/macros/portlet in the left_slots property, and click Save Changes. If
you want to remove it from the right_slots property, then you could remove the same line from
the right_slots property and click Save Changes.
4.1.3.1.1 Calendar
The calendar portlet is one of the default portlets that displays the calendar on the right of
the Plone page. This portlet shows published events for that month in a little calendar. The
calendar portlet will appear regardless if there are events to show in the calendar. You can
further configure the calendar using the portal_calendar tool in the ZMI (see Figure 4-8).
Figure 4-8. Calendar portlet
The expression to add is here/portlet_calendar/macros/portlet.
4.1.3.1.2 Events
The events portlet displays a list of the upcoming published events. If you have this item in
the portlet list, it won’t show up unless there are some published events to display (see Figure
4-9).
Figure 4-9. Events portlet
The expression to add is here/portlet_events/macros/portlet.
85
4.1.3.1.3 Favorites
In the top-right corner of a Plone document, you’ll see a Plone icon. A user can click this logo
to add a favorite. A favorite is similar to the concept of a bookmark or link to the page to which
you want to return; however, this favorite is stored on the Plone site. Figure 4-10 shows the
icon to add a favorite.
Figure 4-10. The icon to add in a favorite
The favorites are added to the user’s home folder, and they display in the favorites portlet along
with a link to organize them (see Figure 4-11). The favorites that are shown are particular to
the favorites that user has saved, so even if you have this item in the portlet list, it won’t show
up unless the user has some favorites.
Figure 4-11. Favorites portlet
The expression to add is here/portlet_favorites/macros/portlet.
4.1.3.1.4 Login
The login portlet displays the login form so a user can log in using their username and pass-
word. If they’ve forgotten their password, they have an option to get their password e-mailed
to them. Even if you have this item in the portlet list, however, it won’t show if the user is
already logged in (see Figure 4-12).
86
Figure 4-12. Login portlet
The expression to add is here/portlet_login/macros/portlet.
4.1.3.1.5 Navigation
The navigation portlet shows a simple tree of the folders in the current position in the form of
a tree. It provides a powerful and simple navigation tool. The navigation portlet is extremely
customizable; you can alter it by clicking portal_properties and then clicking navtree_properties
inside the ZMI, which is covered in the ’Altering the Navigation Portlet“ section (see Figure
4-13).
Figure 4-13. Navigation portlet
The expression to add is here/portlet_navigation/macros/portlet.
4.1.3.1.6 News
The news portlet lists all the recent news items, with links to them (see Figure 4-14). Even
if you have this item in the portlet list, it won’t show up unless there are some news items
published. The news items on a site are also available by clicking the news tab.
87
Figure 4-14. News portlet
The expression to add is here/portlet_news/macros/portlet.
4.1.3.1.7 Recent
The recent portlet lists the recently published items on the site since the last time you logged
in (see Figure 4-15). If there are no such items, it’ll still display.
Figure 4-15. Recent portlet
The expression to add is here/portlet_recent/macros/portlet.
4.1.3.1.8 Related
The related portlet shows a list of items that are related to the item you’re currently viewing,
as determined by the keywords on that item. If a related item is a link to another Web site,
88
then it’ll show up in a separate list of external resources. Even if you have this item in the list
of portlets, it won’t display unless there are some related items (see Figure 4-16).
Figure 4-16. Related portlet
The expression to add is here/portlet_related/macros/portlet.
4.1.3.1.9 Review
The review portlet displays a list of items that are in the review state and are waiting to be
reviewed. This is shown only if the user logged in has the reviewer role and there are items
awaiting review (see Figure 4-17).
Figure 4-17. Review portlet
The expression to add is here/portlet_review/macros/portlet.
4.1.3.1.10 Book Web Site: Altering Slots
For the Plone book site, most of the slots on the right side made no sense. This book has no
events, so the calendar and event slots weren’t needed. I expect new things to be added to
the site, but really they’ll be minimal once the site is complete. So I decided to remove all the
right slots for my site. I did this by going to the portal root object in the ZMI and clicking the
Properties object. Then I deleted right slots. The navigation, login, and related portlets, which
normally occur on the left side, are all useful to me, so I kept those.
Here’s how the portlet properties for the Plone book site look at this point:
89
4.1.3.1.11 Having Different Portlets in Different Parts of Your Site
The underlying Zope database of Plone provides a feature called acquisition. In its simplest
form, this means when looking up an item, such as right_slots, Plone finds the closest object
that contains the property. So, when looking for what portlets to show in the right column,
normally Plone finds the root object and lists those.
That’s why you can change the properties in the root portal object to change the whole site.
You may notice that if you click the my folder link and go to your personal folder, there’s no
calendar. If you click Members and then click Properties in the ZMI, you’ll see that there’s an
entry for right_slots. The entry for that folder is an empty list. When the Plone site goes
looking for a value to show for the right column’s portlets, it moves up the folder hierarchy
until it reaches the Members folder. There it finds the right_slots value and uses that. Since the
value of right_slot in the Members folder is empty whenever you’re viewing content located in
Members, the right slot will be empty.
By adding and removing properties from folders through the ZMI, site administrators can cus-
tomize exactly what portlets appear on their sites. The properties tab is fortunately reasonably
straightforward. You just select the item in the ZMI and then click Properties. To add a left or
right slots property, use the add form at the bottom of the form and ensure that the property
type is list.
4.1.3.2 Altering the Navigation Portlet
Of all the portlets covered, probably the most useful and the most asked about is the navigation
portlet. Specifically, how can you alter the navigation portlet and the way that it’s displayed?
The navigation portlet is list of current folders and documents in the navigation slot. You can
alter the navigation slot by changing the code; however, you can make many changes through
the ZMI. The biggest thing to remember is that only published objects will be shown in the
navigation tree to members and anonymous users. To alter the navigation tree properties,
click portal_properties and then click navtree_properties in the ZMI.
The following is an abridged list of the options available:
• showMyUserFolderOnly: This displays only the user folder of the user logged
in. So when the Members folder is being shown in the navigation, it won’t
show every Members folder. This is selected by default.
90
• showFolderishSiblingsOnly: Only folders in parent folders will display if this
is selected; otherwise, it will show all content. This is selected by default.
• showFolderishChildrenOnly: When this is enabled on a folder, it shows only
folders in the same folder rather than all the other types of content. Select-
ing this option effectively shows you all the contents of the folder currently
viewed. This is selected by default.
• roleSeeUnpublishedContent: As mentioned, content is shown only if pub-
lished to members and anonymous users. Add more roles to this list, with
each role on a new line, to have nonpublished content display. This is unde-
sirable if the user doesn’t actually have access anyway.
• croppingLength: This determines how many characters of the names to show
in the navigation tree. The default is 256.
• idsnotToList: This is the item IDs not to show. Place each ID on a separate
line. The default is None.
Make any changes to that form, and then click Save Changes. The order of the items in the
navigation tree is determined by the order of the items in the folder contents form. As shown
in Chapter 3, using the up and down arrows, users can change this order to suit their own
needs.
4.1.3.3 Book Web Site: Altering the Navigation Tree
For most Web sites I prefer a fuller navigation tree than the one provided by default. So, I went
to the navigation tree options and deselected showFolderishChildrenOnly and showFolderishSi-
blingsOnly. This made the contents show up nicely; for example, here’s the software folder
with just a few items selected:
4.1.3.4 Altering the Date Formats
Throughout the portlets and the whole site, Plone presents dates in a consistent format that
are editable using formats set internally. Whenever a date is shown in Plone, it calls one of
two formats. You can find these formats by accessing the ZMI, clicking portal_properties, and
clicking site_properties. These are the formats:
91
• localTimeFormat: This is the time format to use for dates that should appear
in a short format in Plone.
• localLongTimeFormat: This is the time format to use for dates that should
appear in a long format in Plone, showing seconds.
The format for the date is based on Python’s time format module. The reference for the formats
are at http://www.python.org/doc/current/lib/module-time.html. For the short date, the default
value is %Y-%m-%d, which means year-month-day as decimal numbers (for example, 2003-10-
26). For long date, the default value is %Y-%m-%d %I:%M %p, which means year-month-day
hours:minutes am/pm (for example, 2003-10-26 07:32 PM).
The following is a quick summary of the options available:
%a: Locale’s abbreviated weekday name (for example, Mon)
•
%A: Locale’s full weekday name (for example, Monday)
•
%b: Locale’s abbreviated month name (for example, Jan)
•
%B: Locale’s full month name (for example, January)
•
%d: Day of the month as a decimal number
•
%H: Hour (24-hour clock) as a decimal number
•
%I: Hour (12-hour clock) as a decimal number
•
%m: Month as a decimal number
•
%M: Minute as a decimal number
•
%S: Second as a decimal number
•
%y: Year without century as a decimal number
•
%Y: Year with century as a decimal number
•
If you want to include the day name in the short date, it’s a simple matter of
changing the short date format to read %A, %b. %d, %y. This produces Thursday,
Oct. 24, 02. These dates are used in the boxes on the left and right of the screen,
in the search results, in the content byline, and so on.
4.1.3.5 Adding Keywords and Event Types
One of the tools in Plone, portal_metadata, allows the site administrator to define some of the
metadata elements. Plone uses the metadata defined in the portal_metadata tool in several
places.
For example, when you add an event, you’re given a list of possible types of events. You can
add to this list by clicking portal_metadata, clicking elements, and then clicking subject in the
ZMI. You’ll see a vocabulary for events that lists the subjects for that content type. It’s a simple
matter of adding or editing that list, one item per line, to have the relevant event types. These
event types will then appear in the forms for adding and editing events.
Another use of portal_metadata is the selection of keywords available on a site. On the form
at portal_metadata/elements/subject, you’ll also see a vocabulary form for a content type of
<default>. If you add items to the Vocabulary field of that page and click Update, you’ll add
these to the list of available keywords for every content type.
92
If you want keywords to appear for, say, only documents, then use the add form at the bottom
of the page. Select a content type, and add some vocabulary, one value for each line. These
will then become keywords that users can select for that piece of content only.
If you’re logged in as a user with the manager or reviewer role, clicking the properties tab of
an object in the Plone interface will display a New Keywords box for the addition of ad-hoc
keywords. These keywords won’t appear in the portal_metadata vocabulary but will appear on
all types of content for other users to enter.
4.1.3.6 Changing the Default Page
As discussed in Chapter 3, when a user is viewing a folder, the default page for that folder is
shown if present. In the old versions of Zope and Plone, the name for that default page was
index_html. You’ll see these a lot in Plone sites, where Web site addresses often have index_html
on the end. If you made this filename an extension that’s more commonly recognized as
index.html, then it’d be easier to edit using editing programs and Web site tools.
In Plone you can define a list of pages that will be looked up to be rendered as the default page
(see Figure 4-18). The default pages are index_html, index.html, index.htm, and FrontPage.
You set the list of pages in portal_properties/site_properties/default_page property, one name
per line. When the default page is looked for, Plone will look for each page in that list, starting
with the first until it finds one that matches. Further, if you’d like to change the value for a
folder only, you can access the folder through the ZMI, click the properties tab, and then add a
new list property called default_page.
93
Figure 4-18. Making index.asp the first default page
4.1.3.7 How Can You Make the News Items List the Default Page?
Exactly how this works involves knowing the underlying machinery a little too much. For now,
go to your portal root and click Properties. Then you need to go to the bottom of the page,
complete the add new property form with the following information, and then click the Add
button:
For the Name field: default_page For the Value field: news For the Type field:
lines
Now return to your Plone site. Instead of the standard home page, you’ll see the news page.
The news tab will still show you the news, as well, but in the following sections I’ll show how
to remove that.
4.1.3.8 Altering the Site Tabs
In a Plone site various tabs refer to different sections or parts of a site. Using tabs is a familiar
concept in Web site design and is common in sites such as Amazon, MSN, and Plone sites.
94
Two main types of tabs exist: portal tabs and content tabs. The portal tabs are blue and appear
at the top of the Plone site. The default ones are home, news, and members. The following
sections show how to customize these. The content tabs are green and appear when an item
can be edited. The content tabs, as the name suggests, are related to content. Chapter 11
covers how to alter these tabs. The tabs you see in a Plone site are formed by a collection
of actions, so to understand how to modify these tabs, you’ll take a quick look at actions in
general.
4.1.3.8.1 Introducing Actions
In Plone certain people can perform certain tasks at different times in different parts of the
site. These various tasks are called actions. Plone translates them into tabs, links, and other
elements. They’re a highly configurable way of providing navigational elements for a site.
Each action has the following properties that can be configured in the ZMI. Exactly where
you configure them depends upon where the action is stored. The following is a list of the
properties for a default action:
Name: This is a user-friendly name for the action. This name is often used in the
user interface. For example, when the action is used as a tab, this value is the text
in the tab.
Id: This is a unique ID for the action.
Actions: This is the action that’s to be performed. For example, when the action is
used as a tab, this action is used as the link. This field is a TALES expression (see
Chapter 5 for more information).
Condition: This is a condition that has to occur in order for the action to be used.
For example, when used as tab, if this condition is met, the tab will appear. This
field is a TALES expression (see Chapter 5 for more information).
Permission: This is the permission the user has to have in order to have this action.
This permission has to be met in order for the action to be used (see Chapter 9 for
more information on security).
Category: This categorizes the actions. In Plone this distinguishes the actions so
they’re used in different sections of the user interface. For portal tabs, the category
value is portal_tabs.
Visible: This indicates if the category is active. Since actions usually relate to visual
elements, the term visible is used.
4.1.3.8.2 Introducing the Top Tabs
In the following sections, you’ll alter the portal tabs in two different ways as an example. You’ll
change the home tab to say welcome, and you’ll move the members tab to the left of the news
tab. The actions for the portal tabs are stored in the portal_actions tool, so to alter these, click
portal_actions in the ZMI. As shown in Figure 4-19, this will open a large list of portal actions
present by default. Some of these actions will seem familiar in that they represent parts of the
Plone site.
95
Figure 4-19. The portal actions for your Plone site
Scroll through the actions until you find the Home item, and change the Name field to wel-
come. Then scroll down to the bottom of the page, and click Save. Returning to the Plone
interface, you’ll now notice that it says welcome on the tab.
The order of the tabs from left to right on the page is set by the order from top to bottom in
the list of actions. So, to move the tabs, it’s a matter of checking the tab and then scrolling
to the bottom of the page to the Move Up and Move Down buttons. It’s a little tedious, but
by repeatedly checking the actions and then using the up and down buttons, you can alter the
order. Do this, and you’ll note that the tabs now appear in a different order on your Plone site.
96
4.1.3.8.3 Why Is the Text in Lowercase?
Plone changes the case for many features, such as tabs, to lowercase in the style sheet. To
turn off this option, you can alter the style sheet, which is discussed later in this chapter in the
’Changing Images and CSS” section.
4.1.3.8.3.1 Book Web Site: Adding a New Tab
A nice navigation helper is to add a tab or remove one in the portal tabs. So, in this sidebar,
you’ll add a tab that points to the Software folder, and you’ll remove the news and members
tabs (which in my site is pointless). Return to the ZMI, and click portal_actions. Scroll to the
bottom of the form to the add form. I filled out the form with the following values:
• name: Software
• id: software_tab
• action: string:$portal_url/Software
• permission: View
• category: portal_tabs
• visible: selected
Further, I found the actions for news and members and deselected Visible (not forgetting to
hit Save, of course). Returning to the Plone interface, you’ll now see those new tabs. The key
value here is Action, which is a TALES expression. These are discussed fully in Chapter 5. The
Action value points to the URL of the folder you’re pointing to; in my case, it’s Software and is
in the root of my Plone site. Hence, the expression is string:$portal_url/Software.
4.1.3.8.4 Altering the Icons for a Document
If you’re looking at a list of links or options in a Plone site, then chances are that a series of
actions are producing that list. If it’s not actions, then it’s code, but many of the key features
of the Plone interface are generated dynamically from settings in the ZMI. Two other examples
of actions are the document actions and site actions.
The site actions appear in the top-right corner and are links to change the size of the text.
These links could be anything but just happen to reference some client-side script functions.
These links are again configured in portal_actions and are just actions that have a different
category. If you look through the actions in portal_actions, you’ll see these three actions at the
bottom of the page. They have the category site_actions. If you want to remove them, just
uncheck the Visible option. The icons come from the portal_actionicons tool, which is another
simple tool that maps the icon to the action. Looking in portal_actionicons, you’ll see a match
for normal_text of site_actions that matches up an icon (see Figure 4-20).
97
Figure 4-20. Site actions
Similarly, document actions are in portal_actions and have the category document_actions. You
can again edit the order, icons, and text and add or remove icons from the interface all through
editing those actions.
4.1.3.9 Changing Images and CSS
The look and feel of a Plone site is a big subject that takes three chapters of its own, Chapters
5-7. The following sections cover the basics and, rather than trying to explain everything, just
show how to quickly make a few changes.
A skin is a series of CSS, images, templates, and scripts that come together to form a look and
feel for the user. The idea of a skin is that you can change the skin and hence change the look
and feel of a site without having to change the content.
4.1.3.9.1 Changing the Skin
You can change the default skin for a site using the portal skin form, which is accessible from
the control panel. You can represent a Plone site in a few different ways by applying different
colors, style sheets, and templates to a site.
98
The portal skin form provides the following three choices:
• Default skin: This is the default skin to show to a user when they access the
site. There is only one skin that’s given by default, which is Plone Default.
• Skin flexibility: This sets whether you’re going to allow users the choice of
choosing their skin. If this is enabled, a user can go to their preferences and
choose a new skin. This is enabled by default.
• Skin cookie persistence: If a user can select a skin, then select this to have
the cookie last indefinitely. This means that a user will always see this skin
when logging into a site. This is disabled by default.
Select the changes you’d like to make, and click Save to commit the changes. To improve
performance sites, use image and style sheet caching. To ensure that you’re seeing the new
skin as it should be, clear your browser’s cache (on Internet Explorer, pressing Ctrl+F5 will do
this).
4.1.3.9.2 Setting a Different Logo
Changing the logo of a Plone site from the Plone logo is a simple operation, but the steps can
get a little confusing, so you should following them carefully.
First, access the ZMI, click portal_skins, click plone_images, and then click logo.jpg. This will
open the page for that object. It should look something like Figure 4-21.
99
Figure 4-21. The default logo
This object represents the logo as it’s used in Zope. In Figure 4-21 you can clearly see infor-
mation about the image, its size, its type, and its location on the file system. In the middle of
the page is the Customize button; click it. This will create a copy of the object called logo.jpg
in the custom folder (see Figure 4-22).
NOTE If at this point you get an error message about a bad request, return to por-
tal_skins/custom, and you’ll see an object called logo.jpg. Click that object. There can be
only one object called logo.jpg in the custom folder, and the error is warning you that this
procedure has been performed already. If you want to customize the original object (in other
words, repeat these steps), you’ll have to delete the object inside custom.
Figure 4-22. The customized image
This page may look similar to the previous page shown in Figure 4-21, but there are a couple of
differences. First, if you look in the top-left corner of the page, you’ll see that the meta_type and
location of this object has changed. No longer are you in portal_skins/plone_images/logo.jpg;
rather, you’re in portal_skins/custom/logo.jpg. Second, you’ll now see a Browse button that lets
you select an image and upload it, meaning you can change this image. Click that button to
find your new image, and click Save to commit the changes. In Figure 4-23, I’ll add a Canadian
Plone logo as an example.
100
Figure 4-23. The Canadian Plone logo
Now return to the Plone interface, and you’ll see that the image has changed. To ensure you’re
seeing the new image, clear your browser’s cache (on Internet Explorer, pressing Ctrl+F5 will
do this).
4.1.3.9.3 What If Your New Image Isn’t in JPG Format?
Zope doesn’t base the Multipurpose Internet Mail Extensions (MIME) type on the extension
but rather on the content. So, you can upload a GIF image into logo.jpg, and it’ll still work
since the correct MIME type of image/gif is applied. However, you may want to rename the
image to logo.gif or logo.png to be less confusing.
4.1.3.9.4 Changing the CSS Code
CSS determines the majority of the look and feel of your site, including the tabs, the images,
the boxes, and the overall layout. The fact that Plone’s CSS is totally customizable means that
from a few style sheets users can completely customize many aspects of a site.
101
Again, Chapter 7 covers what all the elements do; in this section, I’ll quickly show you how to
change the CSS code for a Plone site. First, access the ZMI, click portal_skins, click plone_styles,
and then click ploneCustom.css. This opens the page for that object. This style sheet is actu-
ally straightforward; in fact, it’s empty. Plone is using the cascading property of CSS. Because
the Hypertext Markup Language (HTML) for Plone first imports plone.css and then ploneCus-
tom.css, any changes to the latter overrides the standard style sheet. Why is this a good thing?
It means you can make small incremental changes to ploneCustom.css without breaking or
altering the core style sheet.
So, to customize the ploneCustom.css object, click portal_skins, click plone_styles, and then click
ploneCustom.css. Next, click the Customize button. Again, this object has been customized,
and instead of being at portal_skins/plone_styles/ploneCustom.css, you’ll notice you’re now at
portal_skins/custom/ploneCustom.css. Because file objects can now be edited through the Web,
you can directly edit the style sheet through the Web.
As an example, make the background have an image in the middle of it (this isn’t necessarily
the best user interface, but it’s a clear example of how to customize the CSS code). First, you
need to upload an image to Plone. To do this, click portal_skins, click custom, click the Add
button, and then select Image, as shown in Figure 4-24.
Figure 4-24. Adding the new image
For the file I chose an image I found on the Web (which is also available on the Plone book
Web site), but you could choose any image you have. Make sure that the ID of the image is
background.gif, as shown in Figure 4-25.
102
Figure 4-25. Checking the new image
Second, you need to change the CSS code to point to the new image. You’ve already cus-
tomized the CSS code, so return to portal_skins/custom/ploneCustom.css and change the text
from this:
/* DELETE THIS LINE AND PUT YOUR CUSTOM STUFF HERE */
to the following:
body {
background-image: url(background.jpg);
background-repeat: no-repeat;
background-position: center;
}
Click Save Changes to commit changes to this file. Then return to the Plone interface. If all
went well, you should see the new image (see Figure 4-26).
103
Figure 4-26. The new background image
5 Chapter 5
5.1 Introducing Basic Plone Templating
Plone puts together three layers of technology to create a page. Python and page templates
create some Hypertext Markup Language (HTML), which is sent to the browser. There, some
Cascading Style Sheets (CSS) render the nice page with which you’re now familiar. Those two
first elements, the Python code and the page templates, are the main areas of discussion in this
chapter and Chapter 6.
To understand how to generate and then edit a Plone template, you have to first learn about
some key underlying concepts. Some of these concepts are particularly unique to Plone, and
although they provide great advantages, it does take some time to get used to them.
In this chapter, I’ll start by covering object publishing. I’ll explain how to interact with objects
inside Plone. Then I’ll explain how to build expressions. Once you’re familiar with those two
concepts, I’ll cover how Plone pages are actually put together. At the end of the chapter, you’ll
create a new page inside your Plone site that uses the techniques you’ve learned so far.
This chapter will make more sense if you’re familiar with Python. However, at each stage
I’ll explain the concept behind the code, so even if you don’t understand Python, you should
be fine. The rest of the book references the Template Attribute Language Expression Syntax
(TALES) and Script (Python) objects, so you should take the time to get familiar with them in
this chapter. You should have a head start: I already introduced TALES in the previous chapter
because it’s used for generating portlets and actions.
5.1.1 Understanding the Underlying Templating Machinery
Diving straight into how Plone templating works would likely leave you confused, so I’ll start
by going through the underlying templating machinery. In an ideal world, this is something
you shouldn’t have to worry about, but in practice I’ve found that it’s the first block people hit
when trying to learn to use Plone.
Plone is rather unique in that everything in Plone is an object. If you’re unfamiliar with the
concept of an object, there isn’t much to know; an object is just a ’thing” that encapsulates some
behavior. Each object has methods that you can call on the object. One example is a computer
mouse. A computer mouse could have methods such as move, click, and right-click.
In Plone, a document is an object of a particular type. All this means is that the document
isn’t just a static bit of text; instead, it’s something a little more complicated and far more
104
105
useful. A document in Plone has a description method, for example, that will give you the
description that the user added. When using the templating system, you’ll see in more detail
that everything is an object. You’ll look first at some of the basic principles of object publishing.
5.1.1.1 Introducing Object Publishing
In Plone, you’re actually publishing objects that are located in Zope; most of them are objects
that are persisted in the object database. The concept is more complicated than standard
Common Gateway Interface (CGI) environments, where a script is executed and passed a
series of request variables. Everything in Plone is an object, everything in Zope is an object,
and everything in Python is an object. Until now I’ve tried to avoid using the word object;
instead I’ve used words such as template, script, and item, but all of these are really objects—
just ones that behave differently.
When you request a Uniform Resource Locator (URL) from Plone, an object in the
environment is called. Plone does this by translating the URL into a path. So, if the
URL is /Plone/login_form, what Plone is going to do is break that URL down into
a path and look up each of those objects in the database. It’s going find the Plone
object and then a login_form object inside the Plone object. Looking up this path is
called traversal; essentially, Zope traverses across those objects and then calls the
last one in the path.
When Zope calls the login_form object, the object is executed in its context. The term context
is something you’ll hear a lot of in Plone. It’s merely the current context of the object being
executed. In this case, it’s /Plone. The context changes a lot as you move through a Plone
site. If you called the URL /Plone/Members/login_form in a browser, then the context would
be /Plone/Members.
As mentioned, traversal is how you can programmatically access objects in Plone in the same
manner as you do in a URL. This is similar to accessing items in a file system—if you wanted to
access a picture in My Documents on Windows, you’d enter a directory such as c:Documents
and SettingsandymMy DocumentsMy Portrait.jpg. You could access an object in Plone by
entering Members/andy/My Portrait.jpg. This would work if you had a series of folders and
objects that looked like the following:
Members
|_ andy
|_ My Portrait.jpg
In the file system version, you go through the computer’s hard drive directory by directory. In
Plone, the same thing happens; it’s just that Members and andy are objects.
One catch is that Zope is case sensitive. In Windows, you can type My Portrait.jpg or my
portrait.jpg. That won’t work in Plone, however; you have to provide the same case as the
object ID. For this reason, it’s recommended that you try to keep all URLs lowercase so your
users have less chance of making a mistake.
Plone and Zope have added a twist, called acquisition, to this whole publishing system. The
concept behind acquisition is one of containment: Objects are situated inside other objects
106
called containers. In the previous example, the andy object is a container inside the Mem-
bers container inside the Plone site container (which in turn is inside the Zope application
container).
In a standard object-oriented environment, an object inherits behavior from its parent. In
Plone and Zope, an object also inherits behavior from its container. An object goes through a
container hierarchy to figure out how to get these behaviors.
So, take the example of accessing Members/andy/My Portrait.jpg. What if the object Some
Image.jpg didn’t exist in the andy folder but instead exists higher up in the hierarchy? Well,
acquisition would find it for you. Take the following hierarchy:
Members
|_ andy
|_ My Portrait.jpg
In this case, if you executed the URL, Plone would traverse to andy and then try to find My
Portrait.jpg*—but, sure enough, it doesn’t exist in the container. So, it’d look in the containment
hierarchy, which is the *Members folder, and it finds and returns My Portrait.jpg. The result is
that the user will see the image, just like usual.
However, if you compare this to the earlier example where the image was contained in the
andy folder, you’d find that the following key differences exist:
• First, the context is the same, even though the object is in a different place.
Context is based on the location from where the object is called.
• Second, the container is different, and the container of My Portrait.jpg is now
different. It’s Members, not andy.
So, what’s the point of all this? Well, you can now put an object in the root of a Plone site, and
any object can get to it because it’s looked up through acquisition.
Although this probably makes sense, acquisition can be quite complicated, especially look-
ing through the context hierarchy (which can occur). If you want to learn more about
it, you can read Zope lead developer Jim Fulton’s excellent discussion of acquisition at
http://www.zope.org/Members/jim/Info/IPC8/AcquisitionAlgebra/index.html.
5.1.1.2 Introducing Template Expressions
Before diving into the Zope Page Templates system, you must understand TALES. Often in an
application you need to write expressions that can be evaluated dynamically. These aren’t
scripts; rather, they’re one liners simple expressions that can do something simple and easy in
one line of code.
An expression is evaluated with a series of local variables passed into it. These variables are
determined by what’s calling the expression. Workflow passes one set of variables in, and
the Zope Page Templates system passes another. For the moment, I’ll use examples that have
context. Remember, as discussed, the context is the context in which an object is requested.
107
So far you’ve seen some TALES expressions, such as string:${portal_url}/Software. However,
this is merely one example of a wide range of expressions. The main use of TALES is in Zope
Page Templates, the HTML generation system for Plone. Although its name may suggest it’s
suitable only in templates, many tools in Plone use this syntax to provide simple expressions,
such as actions, workflow, and security. Different kinds of expressions exist, and I’ll run through
them one by one.
5.1.1.2.1 Using Path Expressions
The path expression is the default and most commonly used expression. Unlike all the other
expressions, it doesn’t require a prefix to denote the expression type. The expression comprises
one or more paths. Each path is separated by the pipe symbol (|). Each path is a series of
variables separated by forward slashes (/). The following are some simple examples:
context/message
context/folderA/title
context/Members/andy/My Portrait.jpg
When the expression is evaluated, the path is split on the forward slashes. It then starts at the
leftmost value and traverses to find that object, method, or value. It then places that object
on the current stack and moves onto the next value; it repeats that process until it reaches the
end of the expression or can’t find a matching value. If the object it finds is a Python dictionary
or mapping object, it’ll call that value of the dictionary. One nice feature of a path expression
in that the only restricted character is /, so names can contain spaces and periods and still be
evaluated.
When the end is reached, it’ll call that object (if it can be called). If it’s a noncallable object,
it’ll get the object’s string value, and this is what will be returned. If at any time there’s an
error in this lookup (the most common being that the requested attribute doesn’t exist), then
it’ll move onto the alternate expression, if there is one. You can specify an alternate expression
by separating it with a pipe symbol.
For example:
context/folderA/title|context/folderB/title
The previous example will render folderA’s title if it exists or folderB’s title if the first one doesn’t
exist. It’ll repeat this process for each expression, until there are no more expressions or until
one of them evaluates successfully.
5.1.1.2.2 Using Not Expressions
A not expression has the prefix not: at the beginning and simply inverses the evaluation of
the TALES expression that follows the prefix. Because the Zope Page Templates system doesn’t
have an if statement, you can use this to test for the opposite of a previous condition.
For example:
not: context/message|nothing
108
5.1.1.2.3 Using Nocall Expression
By default, when a path expression reaches the last item in the path sequence, it calls the item,
if possible. The nocall: prefix prevents this from happening. A nocall expression is rarely used
in Plone, but it does have occasional uses. For example, you can use it to reference another
object but not render it. Here’s an example:
nocall: context/someImage
5.1.1.2.4 Using String Expressions
String expressions allow you to mix up text and variables into one expression. All string
expressions start with the string: prefix. This is a useful function, and you’ll see it used quite a
bit. The text can contain anything that’s legally allowed inside an attribute, which essentially
includes alphanumeric characters plus spaces. Contained inside the text can be variables,
prefixed with a dollar sign ($). Here are some examples:
string: This is some long string
string: This is the $title
In the latter example, the variable $title is evaluated. The variable can actually be any path
expression. If the variable contains /, then the variable has to be wrapped with {} to signify
the start and end of the expression.
For example:
string: This is the ${context/someImage/title}.
If a dollar sign in the text needs to be escaped, use another dollar sign immediately before the
dollar sign you need to escape.
For example:
string: In $$US it costs ${context/myThing/cost}.
5.1.1.2.5 Using Python Expressions
Python expressions evaluate a line of Python code. All Python expressions start with a python:
prefix and contain one line of Python.
For example:
python: 1 + 2
The Python code is evaluated using the same security model that a Script (Python) object uses,
as discussed in Chapter 6. For these reasons, Python should be simple and limited to presenta-
tion functionality, such as formatting strings and numbers or performing simple conditions.
Further, almost all the other TALES expressions mentioned can be wrapped in Python and
called. The following are the expressions:
109
• path(string): Evaluates a path expression
• string(string): Evaluates a string expression
• exists(string): Evaluates a string expression
• nocall(string): Evaluates a nocall expression
For example, the following code:
python: path(’context/Members’)
is equivalent to the following:
context/Members
A few convenience functions have also been added to assist developers. The test function takes
three parameters: a statement to evaluate and the true and false conditions. The statement is
evaluated, and the appropriate value is returned. For example:
python: test(1 - 1, 0, 1)
The same_type function takes two variables and compares if they’re the same. For example:
python: same_type(something, ’’)
Some developers discourage using Python inside the Zope Page Templates system because it
means adding logic in the presentation templates. Often, as a developer, for each piece of
Python added, it can be useful to ask yourself if that piece of code would be better factored
out and placed in a separate Script (Python) object. This doesn’t mean you should move every
piece of Python out—just think about it before adding anything.
5.1.1.2.6 Book Web Site: Revisiting Actions
In Chapter 4, you added an action for pointing to the software part of the site so it appeared as
a portal tab. In that action, you added in the string expression string: ${portal_url}/Software.
This may make a bit more sense now that I’ve explained the variable portal_url. This is the
URL to your portal, which may vary depending upon if you’re using virtual hosting. It does
this by using acquisition to acquire the portal_url object and insert the resulting value into the
string. The result is that you’ll always get an absolute link to the Software folder.
5.1.1.2.7 Gotcha: Mixing Python and Strings
I’ve seen newcomers mixing up Python and strings a few times. All the expressions are dif-
ferent. In other words, you can’t place path-like expressions inside a Python expression. For
example, the expression python: here/Members/absolute_url + ”/danae“ doesn’t make sense.
The entire expression is interpreted as Python, so Plone will try to divide one thing by another,
and you’ll get errors. This is an ideal situation to use a string expression (which lets you do
variable substitution), so the variable contain a path expression. So, you could use string:
${here/Members/absolute_url}/danae.
110
5.1.2 Using the Zope Page Templates System
Now that you understand object publishing and expressions, you can get into the real meat of
the system, Zope Page Templates. This is the templating system that Plone uses for generating
HTML.
Many HTML generation systems are available, and some of the better known are JavaServer
Pages, Active Server Pages, and PHP To users of the other systems, the Zope Page Templates
.
system at first looks rather odd, but quickly you’ll see it’s an extremely powerful system.
The simplest template looks something like the following:
<p tal:content="here/message">The title</p>
If the value of message resolved to Hello, World! then the following would be output when the
template was rendered:
<p>Hello, World!</p>
For a moment I’ll gloss over a few of the finer points and show what has happened here.
A standard paragraph was written in HTML, yet the content of that paragraph isn’t the text
shown in the output. To the opening paragraph tag, a tal:content attribute was added, and
the here/message expression was written for that attribute. The content of the paragraph was
output, however, as the value of the message variable (in this case, Hello, World!).
At run time, the template is evaluated, and the tal:content attribute is called. The tal part stands
for Template Attribute Language and has a range of commands, including content. You’ll see all
these commands later; with them, you can do almost anything you want to do the HTML tags.
You can create loops, alter tags, alter attributes, remove tags, and so on. Before the template
runs, this will show up as valid Extensible HTML (XHTML) and will show up in an editor as a
paragraph with that text.
All these page templates are valid XHTML. This is a standard for HTML and is valid Extensible
Markup Language (XML) code. This means you must follow these rules:
• All tags must be lowercase.
• Attributes must always be quoted (for example, <input type=”checkbox“
checked=”1“ />).
• Empty elements must be terminated (for example, <br />, not <br>).
To define a page as XHTML, you must give a DOCTYPE declaration and use the XML namespace
set in the html tag. Plone uses the following declaration at the top of every page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
For more information on the XHTML specification, go to
http://www.w3.org/TR/xhtml1/#xhtml.
111
5.1.2.1 Another HTML Generation System?
In the first few years of the Web, programmers were the prime creators of HTML. Programmers
rapidly threw together systems to generate HTML programmatically so they could get on with
their real jobs. With tools such as Perl’s CGI modules, programmers could write complicated
server-side code for content.
However, soon everybody was generating content, and the process had to be made easier. This
brought about the wave of escape coding languages. These languages used a special kind of
HTML markup that was processed to produce output. As mentioned, some of the most popular
are Active Server Pages, JavaServer Pages, and even whole languages based on the concept,
such as PHP Zope followed this trend with Document Template Markup Language (DTML).
.
These systems take HTML and intersperse it with custom tags such as <% .. %> or <dtml-...
/>. This system was popular because it was easy to understand, and users who already knew
basic HTML could grasp the idea of a few more tags. Designers could ignore the content of
these tags and let the programmers deal with them. Programmers could alter the relevant code
parts without upsetting the content.
However, these systems have the following problems:
• The HTML templates can be hard to scale as more and more content gets
added to the script. Pages quickly become huge and hard to manage.
• Logic and content aren’t neatly separated. They can be separated with some
of these systems; however, the ability to intersperse any HTML with a piece of
programming code is too easy. Often, content, presentation, and logic become
one large, entangled mess.
• Pages can’t be easily edited. Often pages or templates come with the note ”just
leave these bits alone...“cause editing them would break the code. What You
See Is What You Get (WYSIWYG) editors can be set to not alter some tags, but
they can easily break others. In large organizations, users with different roles
all have to edit the same page.
• It can be hard to see a default result. Take, for example, a database query
that shows the result in a table. How can a designer see how that would look
without actually running the code?
For these reasons, the Zope Page Templates system was created. Page templates present a novel
approach; instead of providing another method of escape coding, code is added to existing tag
attributes. Not only is the Zope Page Templates system free and open source, it doesn’t require
Zope. Currently, versions of the system exist in Python, Perl, and Java.
5.1.2.2 Introducing Page Templates and Content
As you’re now aware, Plone is a content management system where users add content to a
Plone site through the Web. Those content objects are stored inside Plone and then rendered
back to the world using page templates.
Returning to the earlier example of accessing /Members/andy/My Portrait.jpg, I’ll now discuss
what actually happens to the content in Plone. First, Plone finds and calls the My Portrait.jpg
112
object; it’s called because there’s no specific method being called on the object. When a content
type is called, a certain template is located and rendered. The context for that template will
be the image you want to access, and the template will be the one for that image.
If a different action was being called on the image, such as /Members/andy/My Por-
trait/image_edit, then the action image_edit would be looked up for that object, and the corre-
sponding template would be returned. Chapter 11 discusses how this works in more detail.
So, in all the templates in Plone, you’ll see a referral to here or context. This is the context of
the content being accessed. In a template, you can now say context/something or other, and
this will be the something or other looked up relative to the piece of content, not the template.
You’ll now create your first template in Plone.
5.1.2.3 Creating Your First Page Template
The standard way to create a page template is through the Zope Management Interface (ZMI).
Unfortunately, because it means editing the template through a text area in a Web browser, the
ZMI is also the most painful to use as a developer. The text area provides limited functionality
compared with most editors; it’s lacking features such as line numbers, syntax highlighting,
and so on. In Chapter 9, I show you how to use External Editor to edit content; this allows
you to edit Web site content in local editors such as Macromedia Dreamweaver or Emacs. In
Chapter 6, I show you how to make Plone read page templates off a hard drive as files, and
then you can use any tool you’d like.
To create a template, go to the ZMI, click portal_skins, click custom, and then select Page
Template from the drop-down box (see Figure 5-1). Click Add, and you’ll see the page shown
in Figure 5-2.
Figure 5-1. Selecting the Page Template option
113
Figure 5-2. Adding a page template
Enter test for the page template’s ID. Then click the Add and Edit button, which takes you
to management screen (see Figure 5-3). You can then edit this template through the Web by
using the text area and clicking Save Changes to commit your changes.
Figure 5-3. Editing a page template
NOTE Before Plone 2, all the page templates passed through the variable here, which is equiv-
alent to context. If you see here in any code in a page template, it means context. The new
context variable was added to be clearer and bring the page templates in line with Script
(Python) objects.
After clicking Save Changes, the page template will be compiled. If you’ve made any errors in
the template, you’ll see them highlighted at the top of the page. Figure 5-4 shows an error with
an h1 tag that isn’t closed. (As previously mentioned, page templates must be valid XHTML.)
114
Figure 5-4. Page template error
Once you’ve saved the page template successfully, you can click the Test tab to see the rendered
value of the template. In Figure 5-5, you’ll see that the heading has been replaced with the ID
of the template, and the main paragraph now includes the ID of the template.
Figure 5-5. Generating the page
The management screen for a page template also has the following important features:
Title: This is the title for this template, and it’s optional. If you change this in
the previous example, for instance, after clicking Test, you’ll note that the resulting
HTML has changed.
Content-Type: This is the content type for this template; it’s usually text/html.
Browse HTML source: This will render the template unprocessed as HTML. This
is how the template would appear if it were loaded into an HTML editor.
Test: This will process and render the template.
Expand macros when editing: This checkbox will try to expand macros. I recom-
mend leaving this unchecked most of the time. Macros are an advanced feature
and are discussed in Chapter 6.
115
Now that you’ve created a page template, you’ll make a few modifications to it. This will
demonstrate the topics covered so far in this chapter. For example, if you want your page
template to demonstrate 1+2, you could add the following line to your page template:
<p>1+2 = <em tal:content="python: 1+2" /></p>
Then click the Test tab to see if it works. You should see the following:
1+2 = 3
To see an example of a path traversal, print the logo of your Plone site. You can include an
expression in the logo of your Plone site by adding the following to your page template:
<p tal:replace="structure context/logo.jpg" />
This will create the appropriate HTML for the image and show it on the page.
5.1.3 Understanding the Page Template Basic Syntax
Now that you’ve seen how to make a page template, I’ll explain the basic syntax of it. You
can break the syntax of page templates into a few different components, which I’ll cover in the
following sections.
5.1.3.1 Introducing Built-in Variables
You’ve seen the expression syntax, so now you’ll learn about the variables that are passed to it
when you render a page template. All of the following happen in the context of accessing the
image Some Image.jpg in the Members/andy folder, called with the URL /Members/andy/Some
Image.jpg:
container: This is the container in which the template is located. With Plone
this is usually the portal_skins folder. You should avoid using a container because
portal_skins can do unexpected things to the meaning of container (for example, a
reference to the andy folder).
context: This is the context in which the template is being executed. In Plone this
is the object being viewed if you’re viewing a portal object (for example, a reference
to the Some Image.jpg object).
default: Some statements have particular default behavior. This is noted in each
of the statements, and this variable is a pointer to that behavior.
here: This is equivalent to context.
loop: This is equivalent to repeat.
modules: This is a container for imported modules. For example, mod-
ules/string/atoi is the atoi function of the Python string module. This includes
116
all the modules that are safe to import into the Zope Page Templates system. For
more information, see ’Scripting Plone with Python” in Chapter 6.
nothing: This is the equivalent of Python’s None.
options: These are the options passed to a template, which occurs when the tem-
plate is called from a script or other method, not through the Web.
repeat: This is the repeated element; see the tal:repeat element in the ’Introducing
TAL Statement Syntax” section of this chapter.
request: This is the incoming request from the client (all the values from the in-
coming request are visible using the following test context script). All the GET and
POST parameters are marshaled into a dictionary for easy access. Here are some
examples:
request/HTTP_USER_AGENT: the users browser
request/REMOTE_ADDRR: the users browser
request/someMessage: the value of some message, in the query string
root: This is the root Zope object. For example, root/Control_Panel gives you the
control panel for Zope.
template: This is this template being called. For example, template/id is the ID of
the template being rendered.
traverse_subpath: This contains a list of the elements still to be traversed. This is
an advanced variable, and it’s recommend you understand traversal and acquisition
before using this.
user: This is the current user object. For example, user/getUserName is the user-
name of the current user.
CONTEXTS: This is a list of most of these values.
NOTE: With the exception of CONTEXTS, any of these variables can be redefined in a tal:define
statement if the user wants. However, this can be confusing for anyone using the code and
isn’t recommended.
The test_context page template shows all the values of these variables, plus the locations of
some of the objects (see Listing 5-1). It can be useful for debugging and explaining the vari-
ables. Add it as a page template called test_context, and then click Test to see the results.
Listing 5-1. test_context
<html>
<head />
<body>
<h1>Debug information</h1>
<h2>CONTEXTS</h2>
<ul>
<tal:block
tal:repeat="item CONTEXTS">
<li
117
tal:condition="python: item != ’request’"
tal:define="context CONTEXTS;">
<b tal:content="item" />
<span tal:replace="python: context[item]" />
</li>
</tal:block>
</ul>
<h2>REQUEST</h2>
<p tal:replace="structure request" />
</body>
</html>
The test_context page template will produce the output shown in Figure 5-6.
118
Figure 5-6. An example of all the default variables in a script
5.1.3.2 Introducing TAL Statement Syntax
The Template Attribute Language (TAL) provides all the basic building blocks for dynamic
presentation. TAL defines eight statements: attributes, condition, content, define, omit-tag, on-
error, repeat, and replace.
Since page templates are valid XML, all TAL attributes must be lowercase. Further, each ele-
ment can have each statement only once. In the following examples, I’ve inserted new lines in
119
the elements to increase legibility; this is perfectly valid code and quite common in the Plone
source. However, this is optional and isn’t required.
5.1.3.2.1 tal:attributes: Changing an Element’s Attributes
The tal:attributes allows you to replace one or more attribute of an element. A statement
contains the attribute to be changed, separated by a space from the statement. For example:
<a href="#"
tal:attributes="href context/absolute_url">
Link to here
</a>
This will change the href attribute of the link to the result of here/absolute_url. The href at-
tribute has already been defined on this element, so if a designer opens this page, the designer
will see a valid element (although the link may not make sense until the page is processed).
Some example output is as follows:
<a href="http://plone.org/Members/andy/book">Link to here</a>
Since each element can have multiple attributes, tal:attributes allows you to alter one or more
attributes simultaneously by having multiple statements. To change multiple attributes at once,
separate statements with a semicolon (;). If the attribute or statement contains a semicolon,
you can escape this with another semicolon immediately after it appears (;;). For example, to
change both the href and title element, do the following:
<a href="#"
tal:attributes="href context/absolute_url;
title context/title_or_id">Link</a>
The example output is as follows:
<a href="http://plone.org/Members/andy/book" title="Plone Book">Link</a>
The tal:attributes and tal:replace tag are mutually exclusive since replace eliminates the ele-
ment. If the Zope Page Templates system detects this, it’ll raise a warning, and it’ll ignore the
tal:attributes tag. If the expression evaluates to default, then no change will be made. For
example:
<a href="#"
tal:attributes="href
python:request.get(’message’, default)">
Link</a>
In this example, I’m using the get function on the request object. If the incoming request to
the page has the message variable, then the first value will be used, which is message. If the
message variable isn’t present, then the second value, default, will be used. Hence, only by
passing the message parameter will a change take place.
120
5.1.3.2.2 tal:condition: Evaluating Conditions
The tal:condition statement allows a condition to be tested before rendering the element. For
example:
<p tal:condition="request/message">
There’s a message
</p>
<p tal:condition="not: request/message">
No message
</p>
Here, the paragraph with the text for a message will be rendered only if the request variable
has an attribute and it resolves to true (ie: the length of message is greater than zero). Being
able to test for a condition is pointless if the opposite condition can’t be tested for; this is
what the not expression allows. The not: prefix inverts the statement, so not: request/message
resolves to true if the request variable message resolves to false (ie: the length of message is
zero). In this case the request variable, message will still need to exist.
In TAL, the following evaluates to false:
• The number zero
• Any float or complex that evaluates to zero (for example, 0.0)
• Strings of zero characters (for example, ”“)
• An empty list or tuple
• An empty dictionary
• Python’s None value
• TALES’s nothing value
The following evaluates to true:
• The default value
• Any number other than zero
• Strings that aren’t empty
• Strings that are just spaces (for example, ” “)
• Anything else
5.1.3.2.3 tal:content: Adding Text
The tal:content statement is probably the most commonly used statement in a page template.
This statement is also one of the simplest, replacing the content of an element with the value
specified. For example:
<i tal:content="context/title_or_id">Some title</i>
121
The example output is as follows:
<i>The title</i>
This will replace the text Some title with the value of the expression context/title_or_id. If the
text to be placed contains HTML elements, those elements will be escaped. By default, the
text to be replaced is HTML escaped; the structure prefix will allow the HTML to be entered
without the elements being escaped. For example:
<i tal:content="structure here/title_or_id">Do not escape HTML</i>
If the element with the tal:content attributes contains other elements, then all those elements
will be replaced. The tal:content and tal:replace tags are mutually exclusive; they can’t both
be placed on the same element, and an error will be raised if this is attempted. If the value is
default, the content is unchanged.
5.1.3.2.4 tal:define: Defining Variables
The tal:define statement allows variables to be created and reused within the template. For
example:
<p tal:define="title here/title_or_id">
... <i tal:content="title">The title</i> ...
</p>
In this example, the variable title is created and assigned the result of here/title_or_id; later the
variable title is used in a tal:content statement. By default the variable is created only locally
within the scope of the current element. So, in the previous example, only elements within
the paragraph tag can use the title variable. You can redefine the variable anywhere within the
statement or reuse it in other elements as many times as needed.
To create a variable to be used globally, you can use the prefix global. This will allow access to
the variable anywhere within the template, not just within the defining element. For example:
<p tal:define="global title string:Foo bar">
... <i tal:content="title">The title</i> ...
</p>
<i tal:content="title">We still have a title</i>
Furthermore, Plone defines a large number of global definitions so that users can easily use
them in their scripts. As with any such definitions, they’re subject to change, so you should
use them carefully. These defines mean a large number of global variables are available. For
example, to get the title of your Plone site, you can just call the following:
<p tal:content="portal_title" />
You can find these defines in the ZMI by clicking portal_skins, clicking plone_templates, and
then clicking global_defines. You can find a full list of all the defines, and an explanation of
them, in Appendix A.
122
5.1.3.2.5 tal:omit-tag: Removing Elements
The tal:omit-tag is rather unusual. It allows the removal of a tag. Because the Zope Page
Templates system requires the use of HTML tags, complicated pages can often need lots of
elements and can result in extra tags being added. For this statement, the tag is removed,
which just leaves the content of the tags. For example:
<p tal:omit-tag="">This is some text</p>
The output is as follows:
This is some text
In this example, the text This is some text will be rendered; however, the tag won’t be rendered.
Optionally, the tal:omit-tag statement can take an expression as an argument. If that expression
evaluates to false, then the tal:omit-tag doesn’t happen. For example, this does nothing:
<p tal:omit-tag="nothing">This is some text</p>
One alternative to using tal:omit-tag is using the tal namespace, as discussed in the ’Useful
Tips” section of Chapter 6.
5.1.3.2.6 tal:on-error: Performing Error Handling
The tal:on-error statement provides a method to handle errors. It acts rather like tal:content
because it causes the content of the tag to replaced, but it’s triggered only when an error
occurs.
The following is an example:
<p tal:content="request/message"
tal:on-error="string: No message">Message</p>
If there’s an error evaluating the request/message expression here, then the on-error attribute
will be activated. This causes the contents of the tag to be replaced with the text No message.
Unfortunately, the on-error statement is rather limited. The tag can’t distinguish between
different errors and allows only one expression to be evaluated and used. This limitation is by
design so that the tag won’t be overused. Error handling should really be handled in the logic
of your application.
Fortunately, for all expressions, you can supply alternatives in the statement if the first part
of the statement evaluates to something other than true or false (in other words, if an error is
raised). Each alternative is separated by the pipe character (|), and multiple alternatives can
appear in a statement. If you’re relying on variables from the incoming request, then always
add a |nothing to the end to ensure that an attribute error isn’t raised.
For example:
123
<p
tal:content="request/message"
tal:condition="request/message|nothing">
There’s a message
</p>
<p tal:condition="not: request/message|nothing">
No message
</p>
This second example is more verbose but desirable for a couple of reasons:
• The designer is able to see the positive and negative condition.
• You can handle a more complicated error condition than just printing a string.
5.1.3.2.7 tal:repeat: Performing Looping
The tal:repeat allows looping through objects and is one of the more complicated statements.
A statement contains the value to be assigned for each iteration of the results, separated by a
space from the results being iterated through.
Here’s an example of looping:
<table>
<tr tal:repeat="row context/portal_catalog">
<td tal:content="row/Title">Title</td>
</tr>
</table>
In this example, the expression here/portal_catalog returns a list of results. Because the repeat
starts on the table’s row tag, for each row in the list of results, a new row in the table will be
created. Rather like tal:define, each iteration of the results is assigned to a local variable (in
this case, row). This example will show one row for every item in the list of results.
You can access some useful variables from the repeat statement, such as the number of the
current iteration. You can access these through the repeat variable, which gets added to the
namespace. For example, to access the current number, you use the following:
<table>
<tr tal:repeat="row context/portal_catalog">
<td tal:content="repeat/row/number">1</td>
<td tal:content="row/Title">Title</td>
</tr>
</table>
The full list of variables available in repeat is as follows:
• index*:* This is the iteration number, starting from zero.
124
– *number*:* This is the iteration number, starting from one.
•
– *even*:* This is true for an even-indexed iteration (for example, 0, 2, 4,
•
...).
– *odd*:* This is true for an odd-indexed iteration (for example, 1, 3, 5,
•
...).
• start*:* This is true for the first iteration.
• end*:* This is true for the last iteration.
– *length*:* This is the total number of iterations.
•
• letter*:* This is the iteration number as a lowercase letter (for ex-
ample, a*–*z, aa*–*az, ba*–*bz, ..., za*–*zz, aaa*–*aaz, and so on),
starting from one.
– *Letter*: This is the uppercase version of letter.*
•
– *roman*: This is the number as a lowercase Roman numeral (*i, ii, iii, iv,
•
v, and so on), starting from one.
Two other values are available in the repeat namespace that are rather unusual and rarely used,
first and last. These two variables allow you to store information about data in the iteration.
By using the value you want to store in an expression, a Boolean value will be returned. For
the variable first, true indicates that this the first time the value has occurred in the iteration.
Likewise, for the variable last, true indicates that this is the last time the value has occurred in
the iteration.
Here’s an example of this:
<ul>
<li tal:repeat="val context/objectValues">
First: <i tal:content="repeat/val/first/meta_type" />,
Last: <i tal:content="repeat/val/last/meta_type" />:
<b tal:content="val/meta_type" />,
<b tal:content="val/title_or_id" />
</li>
</ul>
5.1.3.2.8 tal:replace: Adding Text
The tal:replace statement is similar to tal:content with one difference—it removes the entire
tag.
For example:
<p tal:replace="context/title_or_id">Some title</p>
This will render the result of the expression context/title_or_id but will remove the paragraph
tags from the result. This is equivalent to the following:
125
<p
tal:content="here/title_or_id"
tal:omit-tag="">Some title</p>
If the element with the tal:replace statement contains other elements, then all those elements
will be replaced. You can’t use the tal:replace statement with tal:attributes or tal:content;
they’re mutually exclusive, and an error will be raised if you place both on the same element.
5.1.3.3 Introducing Execution Order
The order that TAL attributes are written isn’t the order in which they’re executed because
they’re really XML elements (and XML doesn’t care about attribute order). The order in which
they’re executed is as follows:
define condition repeat content replace attributes omit-tag You can’t use the content and replace
statements on the same element because they’re mutually exclusive. Using the attributes state-
ment on the same element as a replace or an omit-tag is meaningless since the attributes are
removed. The on-error tag isn’t mentioned because it’ll be used when the first error occurs in
any of the previous elements.
5.1.3.4 Example: Displaying User Information
To illustrate the points you’ve learned so far, you’ll now create a page template that performs
a simple task: displaying information about a user in the system.
In this example, a company is using Plone internally as an intranet. Each employee is registered
in Plone and given a login; however, there’s no simple page that shows employees or how to
contact them. You’ll create a simple user information page that shows a user’s e-mail address,
home page, picture, and when they last logged in.
The first prototype of this page is easily accomplished with TAL, TALES, and a bit of knowledge
of the basic Content Management Framework (CMF) tools. Unfortunately, because the Appli-
cation Programming Interfaces (APIs) are rather convoluted for those tools, some of this code
is a little longer than it should be. At this stage, don’t worry too much about the API of those
tools; these will be covered in Chapter 9. If you just take the API for granted for the moment,
you can concentrate on the TAL.
First, you need to create a page template; click portal_skins, click custom, add a page template,
and give it the ID user_info. Second, you’ll edit it as follows. For a full listing of this page
template, please see Appendix A. Examining the full listing, you’ll see that it starts with HTML
and body tags.
For, convenience you’ll put the main definitions in a div tag:
<div
tal:omit-tag=""
tal:define="
userName request/userName|nothing;
userObj python: here.portal_membership.getMemberById(userName);
126
getPortrait nocall: here/portal_membership/getPersonalPortrait;
getFolder nocall: here/portal_membership/getHomeFolder
">
In this div tag there are four defines: one to get the username passed in through the request
object and another to translate that username into a user object. The last two defines ensure
that you have a valid reference to the methods that give you user pictures and folders; these
again are convenient because they make later code simpler. Making a div tag or other tag such
as this that contains a series of defines is quite a common pattern in the Zope Page Templates
system. It simply makes the code cleaner.
Next, you do two simple conditions to check that you have a user:
<p tal:condition="not: userName">
No username selected.
</p>
<p tal:condition="not: userObj">
That username does not exist.
</p>
If no username is given in the request, then the expression request/username|nothing will result
in a userName that’s nothing and hence fail the simple test. Further, if the username isn’t valid,
the userObj will result in None, and error messages will be printed for both these conditions.
Now you’re ready to actually process the user:
<table tal:condition="userObj">
<tr>
<td>
<img src=""
tal:replace="structure python: getPortrait(userName)" />
</td>
Since you can only show the user if one is found, you’ll ensure that there’s a simple condition on
this table, tal:condition=”userObj“. To show a user’s picture, you’ll use the getPortrait method
defined early. This function returns the entire tag, so the structure tag ensures the whole image
is rendered correctly. Next, you want to show a few properties such as name and email. The
following shows one of these options, getting the home folder:
<li
tal:define="home python: getFolder(userName)"
tal:condition="home">
<a href=""
tal:attributes="href home/absolute_url"
>Home folder</a>
</li>
First, you use a define to get the folder and assign this the variable home. In a Plone site,
creating a home folder for a user is optional, so you have to be sure that if you’re linking to a
127
folder, it exists. Fortunately, because of the TAL execution order, the define comes before the
condition. Following this, you show a link to the folder using the absolute_url attribute of a
folder.
The page template goes through a few more lines of finding other useful and exciting properties
to show the user. As with most things in Plone, the key is finding the correct API calls and then
processing the output accordingly.
Finally, the page ends by closing all the relevant tags. If all goes well, you should able to call the
page by accessing the URL http://yoursite/user_info?userName=[someuser] where someuser is
a username that exists in your Plone site.
At the moment, this page template is pretty limited. Only a user with the manager role can
view this page, it can show only one member at a time, and the information for the user is
rather thin. In Chapter 6, I’ll show how to expand this example and add some component
reusability, as well as the ability to translate the text into other languages.
6 Chapter 6
6.1 Introducing Advanced Plone Templating and Scripting
The previous chapter covered how the Zope Page Templates system works. To understand page
templates, Chapter 5 also covered the object hierarchy, acquisition, and Template Attribute
Language Expression Syntax (TALES). Using the code from the previous chapter, you were
able to generate dynamic Web pages. The chapter also showed an example page template that
plugged the code together, covered the building blocks of the templating system in Plone, and
provided the key information you’ll need in order to use Plone.
It’s now time to move onto some of the more advanced features of page templates and templat-
ing in Plone in general. First, I’ll introduce the Macro Expansion Template Attribute Language
(METAL) and Internationalization (I18N) namespaces. Like the TAL namespace, these pro-
vide functionality to the site developer. For those itching to know exactly how a Plone page is
plugged together, the ’Hooking Into Plone Using METAL section provides many of the answers.
”
Up until now I’ve shown how you can use simple Python expressions in page templates. Of
course, sometimes a one-line Python expression isn’t enough. So in the ’Scripting Plone with
Python” section, I’ll show you can take Python to the next level and increase the power of your
scripting.
Finally, I’ll cover a common example, showing how to put together a form in Plone. This
example demonstrates concepts learned in the previous chapters and ties it all together while
showing you exactly how Plone handles forms.
6.1.1 Understanding Advanced Plone Templating
One of the nice elements of page templates is that different functions are clearly separated into
different namespaces. In the previous chapter, you looked at the TAL namespace. That’s not
the only namespace that page templates provide; two other namespaces are key to Plone.
The first is METAL. As the rather long name suggests, it’s similar to TAL in that it’s an attribute
language and inserts itself into element attributes. However, its primary aim is to ensure that
you can reuse chunks of other page template code. It does this using the slot and macro
functions.
The second is I18N, which allows you to translate the content of page templates. This is used
in Plone to localize the interface of Plone into more than 30 languages and for many users is
one of the key features of Plone. As you’ll see, the ability to localize text is of interest to all
users, even those building a monolingual site. You’ll start with METAL.
128
129
6.1.1.1 Hooking Into Plone Using METAL
So far you’ve seen how to use TAL to dynamically create parts of pages. However, this really
doesn’t let you do a great deal of complex templating. There really isn’t a mechanism to put a
standard header on top of every page, other than using a TAL statement. METAL is a method
of allowing preprocessing of the templates and provides some more powerful functions than
TAL. All METAL functions start with the metal: prefix.
6.1.1.1.1 metal:define-macro
The metal:define-macro command allows you to define an element to reference from another
template. The name of the referenced chunk is the name of the macro. The following is an
example that defines boxA as a piece you want to use elsewhere:
<div metal:define-macro="boxA">
...
</div>
That div element is now a macro that can be referenced from other templates. The macro
refers only to the part of the page referenced by the element, which, in this case, is the div
tag. So, it’s common to use multiple macro:defines in one page and for the page to be a valid
Hypertext Markup Language (HTML) page, like so:
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
i18n:domain="plone">
<body>
<div metal:define-macro="boxA">
...
</div>
<div metal:define-macro="boxB">
...
</div>
</body>
</html>
Corresponding with the earlier goals of page templates, this page is a valid HTML page that
can be edited by a designer. When the macro is called, the HTML outside the div tags will be
discarded.
6.1.1.1.2 metal:use-macro
The metal:use-macro command uses a macro that has been defined using the define-macro.
When a template defines a macro using the define-macro command, it’s accessible to other
templates through a macros property. For example, if you want to pull the portlet macro out of
the portlet_login template, you can do the following:
130
<div metal:use-macro="context/portlet_login/macros/portlet">
The about slot will go here
</div>
This will fetch the macro and insert the result in its place. As shown, the use-macro command
takes a path expression that points to the template and then to the specific macro in the
template.
6.1.1.1.3 Example: Using the use-macro and define-macro Macros
As an example of this, the following is a template called time_template. This template shows
the date and time on the current Plone server. This is quite a useful function to have, so you
can wrap this in a macro to be reused. This is the example page template containing the
define-macro:
<html>
<body>
<div metal:define-macro="time">
<div tal:content="context/ZopeTime">
The time
</div>
</div>
</body>
</html>
If your template is called time_template, then you can reference this macro in another template.
You can now reference this macro in multiple templates. This is an example template:
<html>
<body>
<div metal:use-macro="context/time_template/macros/time">
If there is a message then the macro will display i here.
</div>
</body>
</html>
When this template is rendered, the HTML produced by Plone looks like this:
<html>
<body>
<div>
<div>2004/04/15 17:18:18.312 GMT-7</div>
</div>
</body>
</html>
131
6.1.1.1.4 metal:define-slot
A slot is a section of a macro that the template author expects to be overridden by another
template. You could think of it as a hole in your page template that you’re expecting something
else to fill in. All define-slot commands must be contained within a define-macro. For example:
<div metal:define-macro="master">
<div metal:define-slot="main">
...
</div>
</div>
6.1.1.1.5 metal:fill-slot
This completes a slot that has been defined with the define-slot command. A fill-slot must be
defined with a use-macro command. When the define- macro part is called, the macro will
attempt fill in all the define slots with the appropriate fill-slots. Here’s an example fill-slot:
<div metal:use-macro="master">
<div metal:fill-slot="main">
The main slot will go here
</div>
</div>
6.1.1.1.6 Example: Using Macros and Slots
Returning to the previous example, you’ll now enhance it a little. If you wanted to put a
custom message at the beginning of the time, then you’d add a slot at the beginning of the
time_template, inside the define-macro. The slot is called time and is as follows:
<html>
<body>
<div metal:define-macro="time">
<div metal:define-slot="msg">Time slot</div>
<div tal:content="context/ZopeTime">
The time
</div>
</div>
</body>
</html>
Now, in the calling page template, you can call the fill-slot:
<html>
<body>
<div metal:use-macro="context/time_template/macros/time">
132
<div metal:fill-slot="msg">The time is:</div>
If there is a message then the macro will display i here.
</div>
</body>
</html>
The end result is that you’ll see the time-slot filled in as follows:
<html>
<body>
<div>
<div>The time is: </div>
<div>2004/04/15 17:18:18.312 GMT-7</div>
</div>
</body>
</html>
6.1.1.1.7 How Plone Uses Macros and Slots
Both macros and slots are similar inasmuch as they both extract content from another tem-
plate and insert content, but they do this differently. The difference comes in how they’re
used: Macros are elements of a template that are explicitly called, but slots are like holes in a
template that you expect other templates to fill in for you. For example, in Plone’s case, the
portlets such as the calendar, navigation, and so on are macros that are explicitly called.
In fact, if in the Zope Management Interface (ZMI) you look at the file by clicking portal_skins,
clicking plone_templates, and then clicking main_template, you’ll see that the entire page con-
sists of macros and slots. At this stage, it’s probably a little confusing, but when called, it runs
through a series of macros and pulls everything together. This allows a user to easily change
any part of a Plone site by overriding that macro, as you’ll see in the next chapter. For example:
...
<div metal:use-macro="here/global_siteactions/macros/site_actions">
Site-wide actions (Contact, Sitemap, Help, Style Switcher etc)
</div>
<div metal:use-macro="here/global_searchbox/macros/quick_search">
The quicksearch box, normally placed at the top right
</div>
...
Continuing to scroll down through main_template, you’ll encounter some define slots. Briefly
I’ll recap how a page in Plone is rendered. When an object is shown, a template for that view
of that content is shown. When you view an image, the template image_view is shown, and
that template controls how the image is shown. To do this task, the image template fills the
main slot. If you look in the image_view template, you’ll see the following code defined in that
template:
133
<div metal:fill-slot="main">
...
</div>
If you jump back to the main_template, you’ll see that it contains a define-slot definition for the
slot main:
<metal:bodytext metal:define-slot="main" tal:content="nothing">
Page body text
</metal:bodytext>
Each type of content has a different template, and each template defines how they’ll use the
main slot differently. So, then each content type has its own particular look and feel from the
templates. Just one element is missing in the equation. Somehow when you called image_view,
the template knew it should use main_template. In the image_view template, you use the macro
from the main_template. This is defined in the following HTML:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US"
lang="en-US"
metal:use-macro="here/main_template/macros/master"
i18n:domain="plone">
In this case, the main_template has the main slot filled in by the slot defined as main in the
template being rendered. The following is a timeline of how the page is built for viewing an
image:
image_view main_template image_view main_template define-slot=”main“ fill-slot image_view
fill-slot main_template main_template This allows Plone to be flexible in terms of how each
page is defined. For example, main_template defines just more than that one slot; there’s also
a slot for inserting Cascading Style Sheets (CSS) code:
<metal:cssslot fill-slot="css_slot">
<metal:cssslot define-slot="css_slot" />
</metal:cssslot>
If a view needed a custom set of CSS, you could define this slot in the view, and it’d be filled in
when rendering. Some of the macros in main_template define slots in them as well and then
fill them back in the main_template so that if you really wanted, you could also fill in those
slots. That’s an advanced technique, however, so you’ll want to ensure you have the basics
down before going down that road.
6.1.1.2 Introducing Internationalization
Plone is always striving to maintain a large number of high-quality translations. The fact that
Plone provides an accessible user interface in more than 30 languages is a key selling point for
Plone. This also means that I18N is a key feature of the templates. To facilitate this, the I18N
namespace is an extra namespace such as TAL or METAL that has specific statements.
134
This section details what users need to know in regard to the templates. In a template you can
add an i18n tag to an element that will allow the translation of an attribute or its contents.
Six statements exist: attributes, data, domain, source, target, and translate. The basic pattern
is to wrap the piece of text you want to translate and add the appropriate i18n attributes. For
example, if you want to translate the following:
<i>Some text</i>
it would become like so:
<i i18n:translate="some_text_label">Some text</i>
Each localization provides a translation of Some text, and the translation tool looks up a trans-
lation for a user. When performing the translation, each string to be translated must have
a unique message ID that identifies the item to be translated. For example, a string such as
Search may have a message ID of search_widget_label. The message ID allows the string to be
identified uniquely and the translation to be repeated.
6.1.1.2.1 i18n:translate
This translates the contents of an element, with an optional message ID passed as a statement.
For example, the following will create a message ID of title_string:
<h1 i18n:translate="title_string">This is a title</h1>
This example is for a piece of text that’s static and doesn’t change. However, in some situations,
the piece of text could be taken from a database or an object and is dynamic. By leaving the
translate statement blank, the message ID is composed of the value in the field. In the following
example, if the title returned by the path expression here/title was Alice in Wonderland, then
that title would be passed to the translation tool. If no translation exists, the original value
will be inserted:
<h1
tal:content="here/title"
i18n:translate="">
This is a title.
</h1>
The translation command is probably one of most common i18n tags you’ll use, and you’ll see
it throughout the Plone templates. It not only enables you to translate static parts of your site,
such as form labels, help messages, and descriptions, but also the more dynamic parts of your
site that could change more often, such as page titles.
135
6.1.1.2.2 i18n:domain
This sets the domain for the translation. To prevent conflict, each site can have multiple
domains or groups for translations; for example, there may be one domain for Plone and one
for your custom application. Plone uses the domain plone, which is usually the default domain
in Plone:
<body i18n:domain="plone">
You shouldn’t have to use this tag much; however, if you’re writing a custom application, you
may find it useful to have a domain that doesn’t conflict with other domains.
6.1.1.2.3 Ii8n:source
This sets the source language for the text about to be translated. It isn’t used in Plone:
<p i18n:source="en" i18n:translate="">Some text</p>
6.1.1.2.4 i18n:name
This provides a way of preserving elements in a larger block of text so that the block of text can
be reordered. In many languages, not only are the words changed but also the order. If you
have to translate a whole paragraph or sentence that contains smaller chunks that shouldn’t
be translated, then they can be passed through:
<p i18n:translate="book_message">
The
<span
tal:omit-tag=""
tal:content="book/color"
i18n:name="age">Blue</span>
Book
</p>
This will produce the following message string:
The {color} Book
If the target language required these to be in a different order, these could then be moved
around and still have the dynamic content inserted into the correct place. In French, this
would need to be translated as so:
Le Livre {color}
6.1.1.2.5 i18n:target
This sets the target language for the text about to be translated. It isn’t used in Plone.
136
6.1.1.2.6 i18n:attributes
This allows the translation of attributes within an element, rather than the content. For exam-
ple, an image tag has the alt attribute, which shows an alternate representation of the image:
<img
href="/someimage.jpg"
alt="Some text"
i18n:attributes="alt alternate_image_label" />
Multiple attributes should be separated with a semicolon, just like tal:attributes work.
6.1.1.2.7 i18n:data
This provides a way of translating something other than strings. An example is a DateTime
object. An i18n:data statement requires a matching i18n:translate statement so that a valid
message ID is available. For example:
<span i18n:data="here/currentTime"
i18n:translate="timefmt"
i18n:name="time">2:32 pm</span>... beep!
6.1.1.2.8 Translation Service
Now that I’ve covered the tags, I’ll cover the mechanism for performing the translation. By
default Plone comes with an I18N mechanism. This allows you to internationalize the user
interface so that messages, tabs, and forms can all be translated. At this stage, this doesn’t
cover the actual content that users add. If you add a document in English and view the page
asking for it in French, you’ll get the English document with French text around the outside
(see Figure 6-1).
137
Figure 6-1. Plone.org in French
Plone reads the HTTP headers that a browser sends to the client requesting a language. If your
browser is in English, then you won’t see much.
To change the language settings in Internet Explorer, do the following:
Once you’ve done this, pick your favorite Plone site and visit it in your browser.
The translations for Plone are handled through a tool called Placeless Translation Service
(PTS). You can locate the PTS tool in the Zope control panel; at the bottom of the page you’ll
see an option for Placeless Translation Service. Click this, and it’ll open all the translations
that exist. These translations are read in from the file system; click a translation to see the
138
information about the language, such as the translator, the encoding, and the path to the file.
All the files are actually stored in the i18n directory of the CMFPlone directory.
Translations are handled using two files for a translation, a .po and a .mo file. For example,
plone-de.po contains the translations for German (de is the code for German). The .mo file is
the ’compiled” version of the .po file and is used by Plone for performance. You never need
to look at the .mo file, so you can just ignore it. The .po is the file you can edit to change a
translation. If you open that file in a text editor, you’ll see a series of lines starting with the
text msgid or msgstr. Above the msgid is actually the code where the i18n command occurs, so
you can see which bit of a page you’re translating. For example:
#: from plone_forms/content_status_history.pt
#. <input attributes="tabindex tabindex/next;" value="Apply"
class="context" name="workflow_action_submit" type="submit" />
#.
#: from plone_forms/personalize_form.pt
#. <input attributes="tabindex tabindex/next;" tabindex=""
value="Apply" class="context" type="submit" />
#.
msgid "Apply"
msgstr "Anwenden"
In the two parts of the previous page templates, the word Apply will be translated into An-
wenden for German users. What gets translated is determined by the i18n tags that have been
inserted into the page templates, as you saw earlier. If you want to change that translation or
add your own variation, then merely change the .po file. If no msgstr is found, then the default
English translation is found. Once you’ve made that change, restart Plone. When this happens,
Plone will recompile that file into the .mo version, and your translation will be updated.
For Plone, the default translation is always to use the English translation file if no language is
given or no translation is available. In fact, the plone-en.po file is blank, so no translation will
be available. Therefore, Plone does the final fallback, does no translation, and shows the text
in the page template. The text in all page templates is in English since most developers speak
English. The long and short of this is that there’s no English translation.
Therefore, you can make a new translation by copying the plone.pot file into a new file of
the name plone-xx.po. The value of xx should match the country code of your translation.
You can find a list of language codes at http://www.unicode.org/onlinedat/languages.html.
Once you’ve started the translation, set the values at the top, including the language code,
and start translating away. If you’ve done a new language file, then the Plone I18N
team will happily accept it and help you complete it. The Plone team mailing list is at
http://sourceforge.net/mailarchive/forum.php?forum_id=11647
Translating the content that people add is actually quite a tricky task and something that Plone
is working toward, but currently it hasn’t completely ironed out. The favorite approach at
the moment is to use two products, PloneLanguageTool and i18nLayer, both of which can be
found on SourceForge (http://sf.net/projects/collective). However, both of these are for more
experienced developers to fully understand and integrate; I hope something like this will be in
the next release of the book.
139
6.1.1.3 Example: Displaying Multiple User Information
In Chapter 5 you used simple TAL commands to show a user’s information in more detail. That
template has a few drawbacks; one of them is that it shows only one user at a time. You’ve
seen that a simple tal:repeat can enable you to repeat content, but you’ll now use a macro to
make this page more modular.
You’ll change the user_info page template so that it lists every page member in the site. Instead
of looking for a username being passed in the request, you’ll use the function listMembers,
which returns a list of every member on the site:
<div metal:fill-slot="main">
<tal:block
tal:define="
getPortrait nocall: here/portal_membership/getPersonalPortrait;
getFolder nocall: here/portal_membership/getHomeFolder
">
<table>
<tr tal:repeat="userObj here/portal_membership/listMembers">
<metal:block
metal:use-macro="here/user_section/macros/userSection" />
</tr>
</table>
</tal:block>
</div>
You’ll note that the code for user_info is now a great deal shorter. The member returned
by listMembers is passed in to tal:repeat. For each member, there will be a table row and
then a macro to show information to the user. In that table row, the locally defined variable
userObj now contains the user information. Of course, you now need to make a macro called
userSection in a page template, so you’ll create a page template called user_section as referenced
in the macro. This template contains all the code that was between the table’s row tags. Again,
you can find a full listing for this page template in Appendix B:
<div metal:define-macro="userSection"
tal:define="userName userObj/getUserName">
...
The only real change is that the use-macro in the main template has to be removed and a new
macro defined so that this macro can be defined. Because the username is no longer explicitly
passed, you need to get the username from the user object by using the getUserName method.
To test the resulting page, go to http://yoursite/user_info, and you should see a list of users.
The page now is user-friendly, showing multiple users on one page. The code is more modular,
rendering the user’s information in a separate macro that can be altered independently. This
page is still not perfect but will be improved in later chapters.
140
6.1.1.4 Example: Creating a New Portlet with Google Ads
In Chapter 4 you saw how to easily edit portlets in a Plone site; adding your own portlet isn’t
much harder. To write your own slot, you need to make a new page template with a macro
inside it. Then a TALES expression that points to macro will be added to the list of portlet,
rendering the portlet to the page.
The basic template for a portlet is as follows:
<div metal:define-macro="portlet">
<div class="portlet">
<!-- Enter code here -->
</div>
</div>
All you need to do is insert some suitable code into the portlet. Google set up a text-based
advertising system in 2003 that places text on your site. The ads are based upon what Google
thinks your site is about, based on the search results for your site. The Google system is
available at http://www.google.com/adsense. To display ads (and get paid for them), you’ll
have to register with Google. On the Google Web site, it’ll ask you to pick some colors and
style. Since you’ll put this in a slot, I recommend the ’skyscraper” size—tall and thin. Make a
copy of the JavaScript that the site produces.
Next, you have to create a portlet:
portal_skins/custom googleAds googleBox <!-- Enter code here --> The end result should be
something like Listing 6-1; however, your version will have a valid value for google_ad_client,
rather than yourUniqueValue. That value tells Google which site ordered this ad and who to
pay. Curiously enough, if you don’t have a valid value there, Google will still happily show the
ads but not pay you!
Listing 6-1. Displaying Ads from Google
<div metal:define-macro="portlet">
<div class="portlet">
<script type="text/javascript"><!--
google_ad_client = "yourUniqueValue";
google_ad_width = 120;
google_ad_height = 600;
google_ad_format = "120x600_as";
//--></script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</div>
To then include this on your site, as detailed in Chapter 4, add the following portlet to your
list of portlets:
141
here/googleAds/macros/portlet
6.1.2 Scripting Plone with Python
At least four different levels in Plone exist for creating logic. The simplest level for using
Python in Plone is the Python TALES expression I discussed in the previous chapter. However,
a Python expression allows you to do only one line of code; often you’ll want to do something
more complicated.
Even more common is the problem that you really don’t want to cram all the logic into the
template. Placing logic in your template is a bad idea in general; any time you can move
anything that isn’t explicitly presentation logic out of the template, you’ve saved yourself a
headache. Separating logic and presentation allows you to easily allow different people to
work on different parts of the project, and it improves code reuse. The other layers of adding
scripting Plone happen roughly in the following order:
• Template attribute expressions: These provide expressions and a way of
inserting little snippets of logic or simple paths in many places.
• Script (Python) objects: These are simple scripts that execute in Plone in a
restricted environment.
• External method objects: These are more complicated modules that don’t
execute in restricted environments.
• Python products: This is the key source that the CMF and Plone is written
in; this offers access to everything in Plone. Python products are an advanced
subject and are covered in Chapter 14.
After an expression, the next level of complexity is Script (Python) object. This object allows
for multiple lines of Python code, and you can call it from an expression. When you call a
Script (Python) object, you’re incurring a small amount of extra overhead as Plone makes a
switch into that object. However, that overhead is minimal because there’s a trade-off between
clarity, separation, and performance. My advice is to put as much logic into Python as possible
and keep page templates as simple and as clean as possible. It’s easy to move it back later if
there’s a performance hit, but at least you’ll understand what’s happening later.
6.1.2.1 Using Script (Python) Objects
A Script (Python) object is may you might traditionally think of in Plone as a script. It’s
a snippet of Python that you can write and then call from other templates or through the
Web directly. Plone actually has a large number of these scripts for performing various key
functions. A Script (Python) is halfway between an expression and an external method in
terms of power.
To add a Script (Python) object, go to the ZMI, select Script (Python) from the drop-down
menu, and click Add, as shown in Figure 6-2.
142
Figure 6-2. Adding in a Script (Python) object
Give the script an ID such as test_py and then click Add and Edit. This will open the edit page
for the Script (Python) object, which looks like Figure 6-3.
143
Figure 6-3. Editing a Script (Python) object
You can edit the script directly through the Web. If you make a syntax error, you’ll be told
about it after you’ve clicked Save Changes, as shown in Figure 6-4.
144
Figure 6-4. A deliberate indentation error in the Script (Python) object
If your Script (Python) has no errors, you can click the Test tab to see what the output is. In
this case, the sample is rather boring; it prints the following text:
This is the Script (Python) "test_py" in
http://gloin:8080/Plone/portal_skins/custom
A script also has the following options:
Title: The edit form has a Title option, which is for you to give the script a title.
This will show up in the ZMI, so it’ll be easier to remember what it does.
145
Parameter List: This is a list of parameters that the script takes, such as variableA
or variableB=None. In fact, this is a standard list of parameters you’d expect in
a standard Python function. Some parameters are already defined for you in this
object, however; you can see them by clicking the Bindings tab. In that tab, you’ll
see a list of the variables already bound into the object, which should have familiar
names by now.
The following are the variables bound to the script that are accessible from a Script (Python)
object:
• context: This is the object on which the script is being called.
• container: This is the containing object for this script.
• script: This is the Script (Python) object itself; the equivalent in Zope Page
Templates is template.
• namespace: This is for when this script is called from Document Template
Markup Language (DTML), which is something that doesn’t happen in Plone.
• traverse_subpath: This is the Uniform Resource Locator (URL) path after the
script’s name, which is an advanced feature.
I’ll now show a simple example that ties these topics into the Zope Page Templates system,
using the example I gave of a Python expression in the previous chapter that adds two numbers.
As you saw, you could make a page template for this that looks like the following:
<p>1 + 2 = <em tal:content="python: 1 + 2" /></p>
The equivalent using a Script (Python) object looks like the following. Change the test_py
script to the following line:
return 1+2
As you saw at the beginning of the previous chapter, you call an object by giving its path as an
expression. So, in a page template, you can now do the following:
<p>1 + 2 = <em tal:content="here/test_py" /></p>
The object test_py is acquired in the path expression and called, and it then returns the Python
back to the template and prints. You’ve now called a script from your template! This is obvi-
ously a rather simple example, but my point is that there’s a great deal you can do in a Script
(Python) object that you just can’t do in a page template.
In a Script (Python) object, you can specify the title, parameters, and bindings setting by using
the ## notation at the top of a script. When you save a script with that bit of text at the
top, Plone will remove that line and instead change the appropriate value on the object. This
syntax is used a lot in the Script (Python) object in this book to ensure that you have the right
title and parameters. So, you could rewrite the previous script as follows:
##title=Returns 1+2
##parameters=
return 1+2
146
6.1.2.1.1 Scripting Plone
Scripting Plone is a rather complicated subject because as soon as you’re able to script Plone,
you have to take into account the Application Programming Interface (API) of all the objects
and tools you may want to use. Explaining APIs is beyond the scope of this book; instead, I’ll
demonstrate how to do some simple tasks using Script (Python) objects. Once you’re comfort-
able with them, I’ll describe more API-specific functions.
Page templates can loop through Python dictionaries and lists quite nicely. But often you don’t
have data in one of these convenient formats, so you need to jump into a Script (Python)
object, format the data nicely, and then pass it back to the page template.
The most convenient data format is a list of dictionaries, which lets you combine the power of
a tal:repeat and a path expression in one function. As an example, you’ll see a function that
takes a list of objects. Each of these objects is actually an object in a folder. For each of those
objects, you’ll see the object if it has been updated in the last five days. Listing 6-2 shows a
useful little portlet I put together for a site that wanted to locate this type of information and
then highlight exactly those items.
Listing 6-2. Returning Objects Up to Five Days Old
##title=recentlyChanged
##parameters=objects
from DateTime import DateTime
now = DateTime()
difference = 5 # as in 5 days
result = []
for object in objects:
diff = now - object.bobobase_modification_time()
if diff < difference:
dct = {"object":object,"diff":int(diff)}
result.append(dct)
return result
In this Script (Python) object I’ve introduced a couple of new concepts. First, you import Zope’s
DateTime module using the import function. The DateTime module, covered in Appendix C, is
a module to provide access to dates. It’s pretty simple, but if you make a new DateTime object
with no parameters, then you’ll get the current date and time; this is the now variable. When
you subtract two DateTime objects, you’ll get the number of days. You can compare that to the
difference a user wants to monitor and, if it’s longer, add it to the result list. The result of this
is a list of dictionary objects, which looks like Listing 6-3.
Listing 6-3. The Result of Listing 6-2
[
{
147
’diff’: 1,
’object’: <PloneFolder instance at 02C0C110>
},
{
l ’diff’: 4,
’object’: <PloneFolder instance at 02FE3321>
},
...
So now that you have the results in the right order, you need a page template that will pass in
the list of objects and process the results. An example of this is as follows:
<ul>
<li tal:repeat="updated python: context.updateScript(context.contentValues())">
This template has a tal:repeat call at the top that calls the script (in this case, called update-
Script). Into that function it passes one value, a list of contentValues from the current con-
text. Previously you called the Script (Python) object using a path expression; you could do
that here as context/updateScript. However, you can’t pass parameters through to the script
being called in that syntax, so you make a Python expression instead, which is python: con-
text.updateScript(). The contentValues function returns a list of all content objects in a folder.
Next, look at the code for each iteration:
<a href="#"
tal:attributes="href updated/object/absolute_url"
tal:content="updated/object/title_or_id">
The title of the item</a>
<em tal:content="updated/diff" /> days ago
</li>
</ul>
As shown, you can loop through this list of values, and you can then use path expressions to
access first the repeated value (updated), then the object (object), and then a method of that
object (title_or_id). This is an example of taking complicated logic processing and passing it
off to a Script (Python) object.
6.1.2.1.2 Restricted Python
I’ve mentioned several times that Script (Python) objects and Python TAL expressions all run in
restricted Python mode. Restricted Python is an environment that has some functions removed.
These functions may potentially be dangerous in a Web environment such as Plone. The origi-
nal reasoning is that you may have untrusted (but authenticated) users writing Python on your
site. If you open an account at one of the many free Web hosts for Zope, you’ll find you can do
this. However, if you have given people the right to do that, you don’t want them to get access
to certain things such as the file system.
In restricted Python, some common Python functions have been removed for security reasons—
most notably, dir and open aren’t available. This means that, as with Script (Python) objects,
148
they can’t be introspected, and access is limited to the file system. A few Python modules are
available to the user. Most of these are for experienced developers; for more information, see
the relevant documentation or module code:
• string: This is the Python string module
(http://python.org/doc/current/lib/module-string.html).
• random: This is the Python random module
(http://python.org/doc/current/lib/module-random.html).
• whrandom: This is the Python whrandom module. You should mostly use
random now (http://python.org/doc/current/lib/module-whrandom.html).
• math: This is the Python math module
(http://python.org/doc/current/lib/module-math.html).
• DateTime: This is Zope’s own DateTime module.
• sequence: This is a Zope module for easily sorting sequences.
• ZTUtils: This is a Zope module that provides various utilities.
• AccessControl: This gives access to Zope’s Access module.
• Products.PythonScripts.standard: This gives access to the standard string-
processing functions of DTML such as html_quote, thousands_commas, and so
on.
If you want to import a module that isn’t in the previous list, then you can
find excellent instructions in the PythonScript module. You’ll find them at
Zope/lib/python/Products/PythonScripts/module_access_examples.py. However, a more
simple method is available to you—using an external method.
6.1.2.2 Using External Method Objects
An external method is a Python module written on the file system and then accessed in Plone.
Because it’s written on the file system, it doesn’t run in restricted Python mode, and therefore
it conforms to the standard Plone security settings.
This means you can write a script that does anything you want and then call it from a page
template. Common tasks include opening and closing files, accessing other processes or ex-
ecutables, and performing tasks in Plone or Zope that you simply can’t perform in any other
way. For obvious reasons, when you’re writing a script that can do this, you need to be sure you
aren’t doing anything dangerous, such as reading the password file to your server or deleting
a file you don’t want to delete.
To add an external method, you go to the file system of your instance home and find the
Extensions directory. In that directory, add a new Python script file; for example, Figure 6-5
shows that I added test.py to a directory on my Windows computer.
149
Figure 6-5. A new external method, test.py
You can now open test.py and edit it to your heart’s content, writing any Python code you want.
The only catch is that you must have an entry function that takes at least one argument, self.
This argument is the external method object in Plone that you’ll be adding shortly. The fol-
lowing is an example entry function that reads the README.txt file out of the same Extensions
directory and spits it back to the user (you’ll have to change the path to point to your file):
def readFile(self):
fh = open(r’c:\Program Files\Plone\Data\Extensions\README.txt’, ’rb’)
data = fh.read()
return data
Now that you’ve done that, you need to map an external method to this script. This is a Zope
object, so return to the ZMI, click portal_skins,* and then click custom*. Finally, select External
Method from the Add New Items drop-down list. When you add an external method, you need
to give the name of the module (without the .py) and the entry function, so in this case the
add form looks like Figure 6-6.
150
Figure 6-6. The newly added external method
After clicking Save Changes, you can hit the Test tab to see what happens when it runs. In
this case, you should get a line or two of text. Since you have the External Method module
in Plone, you can access it from a page template in the same way as any other object. A path
expression to here/test_external would do the trick in this case. For example:
<h1>README.txt</h1>
<p tal:content="here/test_external" />
The real power is that you can pass code off to the unrestricted Python mode and from there
to any function you want, without having to worry about security. Although this may seem like
a cool function, external methods aren’t used a great deal in Plone because complicated logic
is usually moved into a Product object, and simple logic is kept in a Script (Python) object. If
you find yourself using External Method objects a lot, consider one of the tools discussed in
Chapter 12.
6.1.3 Useful Tips
Because page templates are valid Extensible Markup Language (XML) and can be used inde-
pendently of Zope or Plone, you have several useful scripts for cleaning up page template code
and performing syntax checks. These are additional tools and checks; Zope actually performs
151
all the necessary checks when you upload a page template. For a project such as Plone, it can
be useful to run automatic checks on your code or verify it locally before committing changes.
To run these checks, you’ll need to be able to edit these tools locally and have Python installed
on your computer. For more information on External Editor, a method for editing remote code
locally, see Chapter 10.
6.1.3.1 Introducing XML Namespaces
Page templates use XML namespaces to generate code. Programmers can use the rules of XML
namespaces to make life easier. At the top of a page template, you’ll see a declaration of the
namespace in the starting tag:
<html xmlns="http://www.w3.org/1999/xhtml"...
This sets the default namespace to Extensible HTML (XHTML). For any containing element,
if no namespace is defined, it uses that default namespace. For example, you know the next
element is XHTML because it has no prefix:
<body>
Normally for TAL and METAL elements and attributes, you have been adding the prefix tal:
and metal: to define the namespace. The following code is something that should be familiar
by now:
<span tal:omit-tag="" tal:content="python: 1+2" />
This will render 3. However, the following is an alternative:
<tal:number content="python: 1+2" />
By using the tal: prefix on the element, you’ve defined the default namespace for this whole
element as tal. If no other prefix is given, the tal namespace is used. In the example, using
span tags, the default namespace is XHTML, so you have to specifically define the tal: prefix
when using the Content tab.
Note that the element name is descriptive and can be anything not already defined
the tal namespace (for example, content or replace). Because tal:number isn’t a
valid XHTML element, the actual tag won’t display, but the content will—making
the omit-tag unnecessary. This technique is used a lot in Plone to make code that’s
smaller, simpler to debug, and more semantic.
152
6.1.3.2 Introducing Tidying Code
HTML Tidy is an excellent tool for testing and cleaning up HTML code that can perform a few
useful tasks. Versions of HTML Tidy exist for all operating systems; you can download it from
http://tidy.sourceforge.net. For Windows users, find the appropriate download for your version
of Windows, unzip the tidy.zip file, and place the tidy.exe in your PATH (usually your Windows
directory, such as C:up WINNT).
HTML Tidy can tell you if there are any XHTML errors in your page template. For these
purposes, one flag can make a difference: -xml. This tells HTML Tidy to process the file as XML
and report any XML errors. Given the example ’bad” template shown in Listing 6-4, you can
see a few errors. Not only is the code not indented, but it’s missing closing elements and has
invalid nesting.
Listing 6-4. An Example Broken Page Template: bad_template.pt
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<p>
<div>
This is bad HTML,
XHTML or XML...<a tal:contents="string: someUrl"></a>
</p>
<img>
Further it isnt indented!
</body>
</html>
If you run Listing 6-4 through HTML Tidy, you’ll see the errors in the template and get nicely
indented code, as shown in Listing 6-5.
Listing 6-5. The Output from HTML Tidy
$ tidy -q -i bad_template.pt
line 11 column 1 - Warning: <img> element not empty or not closed
line 10 column 1 - Warning: missing </div>
line 10 column 39 - Warning: <a> proprietary attribute "tal:contents"
line 11 column 1 - Warning: <img> lacks "alt" attribute
line 11 column 1 - Warning: <img> lacks "src" attribute
line 9 column 1 - Warning: trimming empty <p>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
153
<head>
<meta name="generator" content=
"HTML Tidy for Linux/x86 (vers 1st August 2003), see www.w3.org" />
<title></title>
</head>
<body>
<div>
This is bad HTML, XHTML or XML...<a tal:contents=
"string: someUrl"></a> <img />Further it isnt indented!
</div>
</body>
</html>
The complaints about proprietary attributes can be a little annoying. To check that your page
template is valid XML, pass the -xml flag. The output is less verbose and just points out the
missing tags:
$ tidy -q -xml bad_template.pt
line 15 column 1 - Error: unexpected </body> in <img>
line 16 column 1 - Error: unexpected </html> in <img>
6.1.3.3 Conducting Syntax Checks
When you edit a page template in the ZMI, Zope performs a syntax check on the document
for things such as invalid tags. If a tag is invalid, an error will be shown on the template
while you’re editing it through the Web. If, like me (and as I demonstrate in Chapter 7), you
write most of your page templates on the file system, then a simple syntax check for a page
template is really useful. Listing 6-6 is a Python script that resides on your file system and runs
independently from Zope.
To run this, you must have a Python interpreter, and the Python module PageTemplate must be
importable. To make PageTemplate importable to your Python interpreter, you must add the
Products directory of your Zope installation to your Python path. You have several ways to do
this (covered in Appendix B).
Listing 6-6. Error Checking Page Templates
#!/usr/bin/python
from Products.PageTemplates.PageTemplate import PageTemplate
import sys
def test(file):
raw_data = open(file, ’r’).read()
pt = PageTemplate()
pt.write(raw_data)
if pt._v_errors:
154
print "*** Error in:", file
for error in pt._v_errors[1:]:
print error
if __name__==’__main__’:
if len(sys.argv) < 2:
print "python check.py file [files...]"
sys.exit(1)
else:
for arg in sys.argv[1:]:
test(arg)
For every file passed through to the script, the ZMI will compile the page template and see if
there are any TAL errors. Taking the bad_template.pt file from Listing 6-4, you’ll get an error:
$ python zpt.py /tmp/bad_template.pt
*** Error in: /tmp/bad_template.pt
TAL.TALDefs.TALError: bad TAL attribute: ’contents’, at line 10, column 39
In this case, it has picked up on the incorrect spelling of tal:content as tal:contents. This error
is something HTML Tidy doesn’t catch. Unfortunately, the processing stops at the first syntax
error. If there are multiple errors, only the first is picked up, meaning sometimes you have to
check the syntax several times.
6.1.4 Using Forms
Forms are an integral part of any site, and almost everyone needs to create a method for
creating and altering forms in your Plone site. With the form framework in Plone, you can
change the validation that process forms have, where they take the user to, and so on. This
framework isn’t just specifically designed for stand-alone forms that perform a simple task,
such as request a password, login, and so on. The framework also works for all content types
for tasks such as editing a content type, which I’ll cover later in this book in Chapters 11–13.
All basic forms have at least two components that you’ve already seen so far: a Page Template
object to show the form to the user, and a Script (Python) object to parse the results and
perform some action on the results.
The form controller framework in Plone introduces a few new object types that are equivalent
to the types you’ve seen in this chapter. These are the Controller Page Template object, the
Controller Script (Python) object, and the Controller Validator object. These new objects have
their equivalent objects, as shown in Table 6-1. These new objects have more properties and
act in slightly different ways than the equivalent objects.
Table 6-1. New Object Types That the Controller Provides
Object Type Equivalent Zope Object
Controller Filesystem Page Template Page Template
155
Object Type Equivalent Zope Object
Controller Python Script Python Script
Controller Validator Python Script
To add one of these objects using the ZMI, go to the drop-down box, and select the name.
The form controller framework creates a sequence of events for a form that a user can then
define. The following is the sequence of events when executing a form:
When this sequence of events occurs, a state object is passed around, which contains informa-
tion about the status of the object, the success of any validations, and any message that are to
be passed.
The following sections run through these steps to show how a form can be validated, and then
I’ll show a full example in the ’E-Mail Example: Sending E-Mail to a Webmaster” section.
6.1.4.1 Creating a Sample Form and Associated Scripts
The beginning of this process is a form. Although this is actually a Controller Page Template
object, it’s written using standard TAL code. To add one, select Controller Page Template from
the now-familiar drop-down box and give it an ID of test_cpt.
A form in Plone is actually a rather lengthy piece of code if you want to utilize all the options
available to you. This piece of code is reproduced in full in Appendix B and is the code used in
the later example:
<form method="post"
tal:define="errors options/state/getErrors"
tal:attributes="action template/id;">
...
<input type="hidden" name="form.submitted" value="1" />
</form>
Looking at this code, you should note that to work in the framework, a few minor differences
exist between this and what you may consider a standard form. First, the form is set up to sub-
mit to itself; this isn’t *optional. Second, a special hidden variable exists called *form.submitted.
The Controller Page Template object checks the request variable for the value form.submitted
to see if the form has been submitted. If, instead, it has just been accessed—for example, via
a link—this isn’t *optional. At the beginning of the form, you set the variable errors. The *errors
dictionary comes from the state object that’s passed into the templates. The state object is a
common object to all the templates and scripts in this system.
6.1.4.1.1 Creating Validators
Once the user clicks the Submit button on your form, the data will be run through the validators
and be validated. Validators are optional. Data doesn’t need to be validated, but of course any
156
application should do that as appropriate. The Validator tab for a Controller Page Template
object gives you a link to the possible validators.
A validation script is the same as a normal Script (Python) object that has one extra variable,
state. The state variable is how you can pass results of the validation. Listing 6-7 shows a
simple validation script for checking to see if you’ve been given a number.
Listing 6-7. Validating That a Number Has Been Provided
##title=A validation script to check we have a number
##parameters=
num = context.REQUEST.get(’num’, None)
try:
int(num)
except ValueError:
state.setError("num", "Not a number", new_status="failure")
except TypeError:
state.setError("num", "No number given.", new_status="failure")
if state.getErrors():
state.set(portal_status_message="Please correct the errors.")
return state
This state object contains basic information about what has happened during the validation
chain. The state object stores the errors for each field, the status, and any other values. For
example, if the number given can’t be turned into an integer, you set the status to failure and
give an error message for the field using the setError method. Later this error message will be
shown for the field. At the end of the script, any errors returned so far are retrieved via the
getErrors method.
To add the previous script, click portal_skins, click custom, and select Controller Validator from
the drop-down box. Give it an ID of test_validator. You can now return to the Validation tab
of your Controller Page Template object and add a pointer to this validation script, as shown
in Figure 6-7.
157
Figure 6-7. Adding the test_validator to the Controller Page Template object
You have a couple of choices for a validation. In the example I’ve ignored them since they
aren’t relevant, but the following is a list of the options:
contextType: This is the type of the context object, if any, that template is executed
in. This is a shortcut to the content type of the context object. If you wanted only
this validation to occur on a link, then you could set this value to Link.
button: This is the button, if any, that’s clicked to submit the form. You could have
different buttons on a form (for example, a Submit and a Cancel button). Each of
these buttons could then map to a different action; clicking Cancel would take you
to one place, and clicking Submit would take you to another.
158
validators: This is a comma-separated list of validators, which are Controller Val-
idator objects that the template will acquire. In the previous example, you used the
validator ID of test_validator.
NOTE When writing validation scripts, use Controller Validator objects instead of Script
(Python) objects. Controller Validator objects are just like ordinary Script (Python) objects
with the addition of a ZMI Actions tab.
6.1.4.1.2 Specifying Actions
Actions are the ending actions after the validators have been run, and they depend upon the
status that’s returned by the validators. The Actions tab for a Controller Page Template object
shows all the actions for the page template in question. You can specify actions with the same
kind of specialization options as described previously via a Web form, as shown in Figure 6-8.
Figure 6-8. Adding an action
You have the following four choices for the actual resulting action:
159
redirect_to: This redirects to the URL specified in the argument (a TALES expres-
sion). The URL can be either absolute or relative.
redirect_to_action: This redirect to the action specified in the argument (a TALES
expression) for the current content object (for example, string:view). At this stage
I haven’t covered actions yet, but each content object has actions such as view and
edit. Chapter 11 covers actions for an object.
traverse_to: This traverses to the URL specified in the argument (a TALES expres-
sion). The URL can be either absolute or be relative.
traverse_to_action: This traverses to the action specified in the argument (a TALES
expression) for the current content object (for example, string:view).
One example of this is if the completion of the form is a success, you traverse to a Controller
Python Script object that you’ve written that processes the result of the form. If the page is a
failure, you traverse back to the template and show them the error.
The difference between a redirect and a traversal is that the redirect is an HTTP redirect sent to
the user’s browser. The browser processes it and then sends the user off to the next page. Thus,
the redirect actions lose all the values passed in the original request. If you need to examine
the contents of the original form, then this isn’t the best approach. Instead, I recommend the
traversal to options. The result is the same; it’s just that the traverse option does this all on the
server. Doing this preserves the request variables and allows you to examine this in scripts.
6.1.4.2 E-Mail Example: Sending E-Mail to the Webmaster
You’ll now see a real example and spend the rest of this chapter building it. A common re-
quirement is a custom form that sends e-mail to the Webmaster. You’ll build this type of form
in the following sections. The complete scripts, page template, and assorted code are available
in Appendix B. If you really don’t want to type all this in, you can see this example online at
the book’s Web site; it’s also downloadable as a compressed file from the Plone book Web site
(http://plone-book.agmweb.ca) and the Apress Web site (http://www.apress.com), so you can
just install it and try it. This example has just two fields in the form: the e-mail of the person
submitting the form and some comments from that person. For this form, the e-mail of the
person will be required so you can respond to their comments.
6.1.4.2.1 Building the Form
The form is the largest and most complicated part of this procedure, mostly because there’s
so much work that has to be done to support error handling. This form is a Controller Page
Template object called feedbackForm. To ensure that it’s wrapped in the main template, I’ll
start the form in the standard method:
<html
xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en-US"
lang="en-US"
160
i18n:domain="plone"
metal:use-macro="here/main_template/macros/master">
<body>
<div metal:fill-slot="main"
tal:define="errors options/state/getErrors;">
One addition here is errors options/state/getErrors, which will place any and all errors into the
errors local variable for later use.
Because of the requirement for the form to post back to itself, you set this action in TAL, with
the expression template/id. This path will pull out the ID of the template and insert it into the
action, so this path will always work, even if you rename the template. Note that you’re also
adding the i18n tags you saw earlier to ensure that this form can be localized:
<form method="post"
tal:attributes="action template/id;">
<legend i18n:translate="legend_feedback_form">
Website Feedback
</legend>
The following is the start of the row for the e-mail address. You’ll define a variable here
called error_email_address that’s set to an error string if there’s a suitable string in the errors
dictionaries. That error value will be generated by the validator should there be an error:
<div class="field"
tal:attributes="class python:test(error_email_address,
’field error’, ’field’)">
tal:define="error_email_address errors/email_address|nothing;">
The following is the label for the e-mail address field. In this label you’ll include a div for the
help text. The span element will become the now-familiar red dot next to the label so that the
user knows it’s required:
<label i18n:translate="label_email_address">Your email address</label>
<span class="fieldRequired" title="Required">(Required)</span>
<div class="formHelp"
i18n:translate="label_email_address_help">
Enter your email address.
</div>
Next you’ll add the actual element:
<div tal:condition="error_email_address">
<tal:block i18n:translate=""
content="error_email_address">Error
</tal:block>
</div>
161
<input type="text" name="email_address"
tal:attributes="tabindex tabindex/next;
value request/email_address|nothing" />
</div>
At the top of this block, you test to see if there’s an error. If there is, the class for the element
changed to be the field error* class; this class will show a nice orange box around the field. Next,
if an error has occurred for this field (as you’ve already tested for), the corresponding message
will be displayed. Finally, you’ll show the form element, and if there’s a value for *email_address
already in the request, you’ll populate the form element with that value.
The tabindex is a useful tool in Plone. It contains a sequential number that’s incremented for
each element, and each time it sets a new HTML tabindex value for each element in a form.
This is a nice user interface feature; it means each form element can be safely moved around
without having to worry about remembering the tabindex numbers because that’ll happen
automatically.
That’s a lot of work for one element, but it’s mostly boilerplate code; you can easily copy or
change it. You can find the remainder of the form in Appendix B.
6.1.4.2.2 Creating a Validator
In the example you have only one required element (the e-mail), so it’s a simple piece of
Python called validEmail.vpy that does the work. The contents of this script are as follows:
email = context.REQUEST.get(’email_address’, None)
if not email:
state.setError(’email_address’, ’Email is required’,
new_status=’failure’)
if state.getErrors():
state.set(portal_status_message=’Please correct the errors.’)
return state
If no e-mail address can be found, this script adds an error to the dictionary of errors with the
key of email_address and a message. This key is used in the page template to see if an error
occurred on that particular field.
6.1.4.2.3 Processing the Script
This example has a simple e-mail script that gets the values (which are already validated) and
forms an e-mail out of them. This is a Controller Python Script object; it’s just like a standard
Script (Python) object except that it has a state variable, and, like the Controller Page Template,
you can give it actions for when it succeeds:
mhost = context.MailHost
emailAddress = context.REQUEST.get(’email_address’)
administratorEmailAddress = context.email_from_address
162
comments = context.REQUEST.get(’comments’)
# the message format, %s will be filled in from data
message = """
From: %s
To: %s
Subject: Website Feedback
%s
URL: %s """
# format the message
message = message % (
emailAddress,
administratorEmailAddress,
comments,
context.absolute_url())
mhost.send(message)
You’ve now seen a simple script for sending e-mail. This is a common script that you’ll see
again and again. Basically, the MailHost object in Plone will take an e-mail as a string, as long
as it conforms to the Request for Comment (RFC) specification for e-mail that has From and To
addresses.
In this e-mail, you take the administrator address you specified in the portal setup and send
the e-mail to that person. The only extra part in this script is the addition of setting the state.
This will set a message that provides some feedback to the user:
screenMsg = "Comments sent, thank you."
state.setKwargs( {’portal_status_message’:screenMsg} )
return state
6.1.4.2.4 Binding the Three Parts Together
At the moment, however, three separate entities exist: a form, a validator, and an action script.
These need to be tied together to form the chain, so you’ll return to the Controller Template
object. Click the Validator tab, and enter a new validator that points to the validEmail script.
You’ll also add a success action if the processing is correct to traverse to the sendEmail script
(Expression: ’string:sendEmail’)n the sendEmail script, you can now add another traversal back
to feedbackForm (Expression: ’string:feedBackForm’) so that after sendEmail happens correctly,
the user will be sent back to the original page.
NOTE A much more complete e-mail validation script appears in Plone called vali-
date_emailaddr, which checks that the e-mail is in the right format. If you want to use this
script instead, you can point the validator to this script.
163
That’s it you’re done! You should now be able to test the form on the book’s Web site. To make
it even easier, I made a Feedback tab, which points to the feedbackForm template, and from
there you can now give feedback to me about this book!
7 Chapter 7
7.1 Customizing the Look and Feel of Plone
Over the previous two chapters I’ve covered some of the core components of Plone’s user
interface, including Script (Python) objects and page templates. It’s now time to cover exactly
how you construct the look and feel of a Plone site. This chapter includes the objects from
previous chapters and introduces some new ones.
To start, I’ll cover all the key definitions and Plone elements that comprise a site. I’ll define
terms you may have already heard of, such as skins and layers. Then I’ll cover customizing the
Plone user interface, concentrating on the power that Cascading Style Sheets (CSS) bring to
the site developer. I’ll run through the key variables and show how you can change them. I’ll
then revisit the customization of logos and skin elements, pulling together all the topics I’ve
briefly covered over the past three chapters.
Then, I’ll show how to make a new skin and explain the techniques for developing all this on
the file system.
Finally, I’ll finish this chapter with an example site. Specifically, I’ll show the Maestro site,
which was used by NASA to distribute data about the Mars rovers. This is a high-traffic Web
site built in Plone, and the skin provides an excellent case study for customizing a site. This
real-life example of how you can customize and modify a Plone site will enable you to do the
same on your own.
7.1.1 Introducing Plone Skins
When a document displays in Plone, the content of that document displays in the now-familiar
Plone green and blue interface. A skin determines exactly how that document displays to
the user, including the images and styles surrounding the content. A skin groups elements,
wrapping that piece of content, and presents them in a certain manner.
To generate the representation that a user sees, a skin has many elements, including the static
items, such as images, and the dynamic pieces, such as scripts. In the previous chapter, the
feedback form was an example of adding a few elements to a skin to create new elements of
a skin. That example contained logic in the format of a Python (Script) object and new pages
in the format of page templates. You added those elements to the skin so that a user could
interact with the form.
164
165
You can use as much or as little of an existing skin as you want to assemble a new skin with
Plone. You can make the skin from minor tweaks or major changes, as you can see on com-
munity sites such as http://www.zopezen.org and http://www.zopera.org. Each Plone site must
have at least one skin, which will be used as the default, but it can have as many skins as the
site developer wants. A user may optionally flip between skins, should the site developer want
to allow the user to do so, although I’ve found this is rarely the case.
The default Plone skin is the one you see on a Plone site such as http://www.plone.org, with
the familiar blue and green interface. But Plone doesn’t have to look at all like, or be even
vaguely recognizable as, a Plone site; its look is entirely up to you. Take, for example, the
list of sites available at http://www.plone.org/about/sites; these sites each provide a different
and custom experience for the user. In most cases, these sites can easily flip between skins and
provide different looks for users. Other sites use the power and flexibility of the Plone interface
internally for creating and editing content while providing a totally different look to external
users.
I’ve seen many questions on mailing lists such as, does Plone have to look like a Plone site? Can
it look one way to one user and another way to another user? Can it look like my corporate
site? The answer to all of these questions is ”yes“: the only limit is your imagination (and the
amount of time you can spend on customizing your site).
7.1.1.1 Using Layers Within a Skin
A skin is divided into logical collections of templates and scripts called layers. Altering these
individual collections allows a user to easily add components to a skin or remove them. The
layers are represented in a skin by a hierarchical list of folders. Each layer matches a name of
a folder, and each folder contains the skin elements.
For example, a skin can have the following layers:
custom, gruf, plone_ecmascript, plone_wysiwyg ...
The order of the layers in that list is the key factor to how Plone finds the elements. When an
element, such as logo.jpg, is requested from the skin, the skin looks through the layers to find
the element. The skin starts by looking at the first layer assigned to that skin (in this example,
custom). If the skin can’t find the element in the first layer, it moves to the second layer (in
this example, gruf). It continues looking through the list of layers until it finds the element for
which it’s looking. If it can’t find the element, then a 404 error is raised and returned to the
browser.
A similar concept is the use of the PATH environment variable in most systems. When entering
a command or looking for a program, the operating system looks through the directories on
the file system as specified in the PATH environment variable. A similar approach happens for
layers, where the layers are looked through in order to find that element.
By allowing higher layers to take precedence over lower layers, developers and administrators
now have the ability to customize and manipulate their site through the layers. If you don’t
like a particular element of a Plone skin, then by moving that element up a layer, you can
custom the result. You can sort your skins and layers in Plone with the portal_skins tool, which
I’ll cover next.
166
7.1.1.2 Managing Skins with the portal_skins Tool
You use the portal_skins tool in Plone to define the skin and layers behavior. The portal_skins
tool also provides a service and application programming interface (API) for creating and using
skins.
To access the portal_skins tool, go to the Zope Management Interface (ZMI) and click por-
tal_skins. You’ll see two key screens in the ZMI; the first, the Contents tab, shows all the
folders and file system directory views (FSDVs) located within this tool (see Figure 7-1).
Figure 7-1. The contents of the portal_skins tool in a standard Plone installation
167
All of the folders and file system directory views on the Contents tab aren’t layers by default,
but now you can turn them into layers. Further, the second important screen, the Properties
tab, shows all the skins and layers you’ve defined in your Plone site (see Figure 7-2).
Figure 7-2. The skins and layers in a standard Plone installation
As Figure 7-2 shows, the list of these layers is quite long. Although this may seem intimidating,
this hefty number of layers gives the developer a large degree of flexibility and reuse. Each
skin displays on the left, with a text area to the right displaying all the layers within that skin.
As I mentioned earlier, Plone searches the layers from the top to the bottom to locate elements.
Each layer is the name of a folder or FSDV from the Contents tab. In Figure 7-2, you can see a
plone_ecmascript directory, and in Figure 7-1 you can see the matching FSDV object.
An FSDV is a new object that provides a useful ability in Plone; it allows direct access to skin
elements that are defined on the file system instead of from the Zope object database as usual.
FSDVs make development and customization easier. By reading objects directly from the file
system, it’s much easier for developers to write and edit the code that produces the site. When
you install Plone, the skin is written onto the file system. When you customize an object, you
make a local copy inside your Plone database. Using an FSDV allows you to maintain clean
separation between code you’ve downloaded from the Web and code that has been customized
in your local instance.
168
Plone 2 ships with two skins, Plone Default and Plone Tableless. Plone Default uses tables to
render the main body flanked by two table cells on either side, containing the left and right
slots. For browser compatibility, this is the default setup. However, if you switch to Plone
Tableless, then you’ll get a skin that looks the same, except there are no tables used to produce
the page, which gives you, as the site developer, more flexibility. At the time of writing, the
Plone Tableless skin can be a little problematic on some browsers, such as Internet Explorer. I
hope in the future that the Plone Tableless skin will become the default.
To change the skin, scroll to the bottom of the form where you see the Default Skin value and
select the default skin from the list of choices. If you select the Skin Flexibility option, then
users will be able to choose their own skin from the my preferences section.
Returning to the Contents tab of the portal_skins tool, you can see that some of the folders-
for example, custom-are standard folders that exist in Zope. These have the normal folder
icon. Others-for example, plone_images-are FSDVs that point to areas of the file system. These
have the folder icon with a green lock inside it. This lock indicates that you can’t add or edit
elements in an FSDV through the Web; you can do it only through the file system.
To see where the files for an FSDV reside on your hard drive, click the Properties tab of the
FSDV For example, from the Contents tab of the portal_skins tool, click Properties, and it
.
will list the file system path of CMFPlone/skins/plone_images. This path is the location of
this directory on the file system relative to the instance home you specified in the installation
process. Because you can see files through the Web in the FSDV or on the file system, you can
access them for reading either way. Because viewing files through a file system is generally
more friendly and easy to do, I’ll refer to a file as a path in the file system, which can be
accessed using familiar tools.
7.1.2 Customizing Skins
You’ve seen how skins and layers interact. Now you’ll see how you can customize a Plone
site. I’ll start by returning to the example in Chapter 4, where you learned how to customize
the logo. Using your new knowledge of how skins work, you’ll be able to follow along and
customize the skin. Then I’ll move onto showing the power of the Plone CSS and how you can
customize it. Finally, I’ll cover the main template you saw in earlier chapters and go through
all the elements of it.
7.1.2.1 Customizing the Logo, Revisited
In Chapter 4 you learned how to customize the logo in the top-left corner of a Plone site, but I
skipped over what was actually happening. The section revisits that example.
The logo.jpg image is the image that appears in the top-left corner of every page. You’ll now
look at what happens when a browser tries to render this page. Once Plone receives the request
for that image, it looks through the layers to find logo.jpg. In a default site, this is the item in
plone_images, called logo.jpg. Because this is an FSDV as I mentioned earlier, you’re unable to
,
alter the image through the Web. To guard your site against future changes, you don’t want to
be able to change it on the file system either. Instead, take a closer look at what the Customize
169
button does. If you look at that button again, you can see that there is, to the left of that
button, a drop-down list of folders in the contents of the portal_skins tool.
NOTE The folders listed are ones that exist inside the Zope database. FSDVs aren’t included in
the drop-down list; by default, it shows only folders.
Clicking the Customize button makes a local copy of the item in the folder selected in the drop-
down box. By default that folder is the custom folder, so now you have a copy in the custom
folder. When Plone looks up the item, logo.jpg, it will access the version in the custom folder.
Looking again at the layers for the Plone Default skin, the custom folder is the topmost layer of
the skin. Hence, when logo.jpg is called, it will find the image in the custom layer. This is why
the new logo.jpg is rendered.
Placing customized items into the custom folder is the quickest way to start tweaking your
Plone site. The custom folder is a standard Plone folder, so you can put as many items in there
as you want in order to override earlier elements.
7.1.2.2 Introducing Plone’s Cascading Style Sheets
The visual representation of a Plone site in a browser is put together almost entirely using CSS.
Perhaps the easiest way to see exactly what the CSS does for a Plone site is to compare Figures
7-3 and 7-4. The first shows Plone with style sheets, and the second shows Plone without any
style sheets.
170
Figure 7-3. Plone with style sheets
171
Figure 7-4. Plone without style sheets
TIP If you want to reproduce this, turn off style sheets in your browser. Internet Explorer
doesn’t let you easily do this, but Firefox (http://www.mozilla.org/products/firefox/), the
Mozilla-based open-source browser, lets you easily do this. In Firefox, select Tools - Web Devel-
oper - Disable - Disable Styles. With a large number of CSS and other developer tools, Firefox
is the browser of choice for many Plone developers.
The difference is striking to say the least. CSS provides not only the visual representation of
pages but also the layout. By changing the CSS, you can change this visual representation and
layout on a Plone site (within the constraints of CSS).
Having the presentation of Plone produced by CSS is an impressive achievement
172
used by many talented user interface developers. The following are some of the
benefits of having a CSS layout:
• CSS provides a layer of separation between the presentation and the
templates that generate the presentation.
• You can make a large number of changes without having to touch
the underlying templates. All that’s needed is an experienced CSS
developer.
• CSS makes the site faster by sending smaller files. Each Hypertext
Markup Language (HTML) file is smaller since the layout for the site
isn’t contained in HTML markup but in the CSS, which can then be
cached.
• CSS allows you to customize the look and feel without breaking un-
derlying accessibility work.
7.1.2.2.1 Code Layers
When a Plone page is rendered, at least three layers of code create a page. For the example of
the tabs that appear across the top of a Plone site, this is how they’re assembled:
So rather than asking yourself, how can I customize the tabs? you need to consider exactly
what customization you want to perform. This could mean changing the CSS, the HTML, the
data, or the underlying tabs. The general rules are as follows:
In fact, Plone is so customizable, on so many levels, that it’s easy to worry about which bit to
customize. To make sure that future Plone template changes don’t compromise your applica-
tion’s design, try not to customize the templates. Instead, I recommend you try the CSS or the
actions first. This way, when the templates change in future versions of Plone, there will be
less chance of a problem.
7.1.2.3 Customizing the Font, Colors, and Spacing
The actual style sheet that does most of the work, plone.css, has a number of variables
in it populated using Dynamic HTML (DTML). I do not cover DTML in this book; this is
probably the only use of it in Plone, so if you don’t already know DTML, I recommend
you avoid learning it, if possible! The Zope Page Templates system will give you every-
thing you need. Excellent online references do exist for DTML for Zope, however; refer to
http://zope.org/Documentation/Books/ZopeBook/2_6Edition/DTML.stx.
The DTML syntax for this style sheet is actually pretty simple; each variable relates to a cor-
responding attribute in a property sheet. To access this property sheet, click portal_skins,
plone_styles, and then base_properties. In Figure 7-5, you can see how this file looks in the
ZMI.
173
Figure 7-5. The base properties for the style sheet
For example, dtml-fontColor; locates the variable fontColor and places it in the style sheet, so
the fontColor here will be black. Now you can see where that variable is referenced in the
plone.css file. To access the CSS file, click portal_skins, portal_skins, and then plone.css. In
this file you can see that mainFontColor is referenced in quite a few places; for example, it’s
referenced in the main body of a page, like so:
body {
font: dtml-fontBaseSize; <dtml-var fontFamily>;
background-color: dtml-backgroundColor;;
174
color: dtml-fontColor;;
margin: 0;
padding: 0;
}
You could keep reading through the style sheet, if you really wanted, but changing the variable
is always a quick way to see exactly what it affects.
Returning to the ZMI, click portal_skins, click plone_styles, click base_properties, and then click
the Customize button. As you’ve seen, this will create an object in the ZMI that you can cus-
tomize. This time the customized object is actually a folder that has the properties contained
in the folder. To access the properties you’ve just customized, click portal_skins, click custom,
and then click base_properties. Next, select the Properties tab (see Figure 7-6).
175
Figure 7-6. The properties of the folder
This property list allows you to change the properties of mainColor to something different,
for example, red or #cc9900. Change the value of that property, and click Save Changes.
Returning to the Plone site, you should now see the nice, new color.
In Chapter 4 you saw an example where, to change a tab on the top of a page, users could
change the actions. Although you may type an action with an uppercase first character (such as
Members), it then displays in lowercase letters on the Web page. This because CSS transforms
the text to lowercase because of the textTransform property in the property sheet. To stop this
transformation, change the property for textTransform to none.
In the style sheet, properties are defined for all the colors, spacing, and fonts that are used in
176
a Plone site. Table 7-1 describes all the parameters.
Table 7-1. CSS Properties
Variable Name Description
logoName The filename of the portal logo
fontFamily The font family used for all text that isn’t a header
fontBaseSize The base font size from which everything is calculated
fontColor The main font color
backgroundColor The background color
linkColor The color used on normal links
linkActiveColor The color used on active links
linkVisitedColor The color used on visited links
borderWidth The width of most borders in Plone
borderStyle The style of the border lines (usually solid)
borderStyleAnnotations The style of border lines on comments, and so on
globalBorderColor The border color used on the main tabs, the portlets, and so on
globalBackgroundColor The background color for the selected tabs, portlet headings, and
so on
globalFontColor The color of the font in the tabs and in portlet headings
headingFontFamily The font family for h1, h2, h3, h4, h5, and h6 headlines
headingFontBaseSize The base size used when calculating the different headline sizes
contentViewBorderColor The tabs’ border color on the Contents tab
contentViewBackgroundCole rcontent view tabs’ background color on the Contents tab
Tho
contentViewFontColor The font color used in the tabs on the Contents tab
textTransform Whether to lowercase text in portlets, tabs, and so on
evenRowBackgroundColorhe background color of even rows in listings
T
oddRowBackgroundColor he background color of even rows in listings
T
notifyBorderColor The border color of notification elements eg: status message and
the calendar
notifyBackgroundColor The background color of notification elements
helpBackgroundColor The background color of the calendar pop-up widget
discreetColor The color of the credits, document byline, form help
portalMinWidth The minimum width of the portal
columnOneWidth The width of the left column
columnTwoWidth The width of the right column
7.1.2.4 Customizing the CSS
If you have small customizations, place them in ploneCustom.css. This is a second style sheet
that’s loaded after plone.css. By using the cascading functionality of style sheets, you can apply
any changes to ploneCustom.css to the overall style sheet.
177
For example, to change the byline that appears on the bottom of every page, simply change
ploneCustom.css. Again, access that file through the ZMI, and then click Customize. This will
create a copy of that style sheet in the custom folder. To alter the byline, move it to the left side
of the page and make it bold, as shown in Figure 7-7.
Figure 7-7. The new bold byline on the left
You do this by adding the following:
div.documentByLine {
text-align: left;
font-weight: bold;
}
Here you’ve set two attributes for the byline element: text-align and font-weight. Note that
you haven’t changed any other attributes of the byline element; the remaining attributes are
inherited from the original style sheet. With a few simple lines of CSS, you’ve changed the site
and made sure that other changes to Plone won’t affect your site. Changing ploneCustom.css is
the best bet for small changes.
By using different style sheets, you can use Plone to provide a different look to different clients.
Often Web sites have a Click for Printable Page button that shows a simpler page, without much
formatting. Plone alleviates this problem by providing a separate style sheet; when a browser
prints the page, that style sheet formats the page. All the alternate style sheets are included
at the top of a page; you can find them by clicking portal_skins, clicking plone_templates, and
then clicking header.pt.
NOTE One style sheet that’s a little unusual is the projection style sheet. It’s supported only by
Opera, and when the browser is used in full-screen mode, headings turn into separate pages
and a presentation-like interface displays.
178
7.1.2.5 Customizing the Main Template
As you saw in the previous chapter, to get the Plone look and feel on a page, you need to use
the master macro from the main_template. Every Plone page uses this macro and then fills
in the appropriate slots. By taking a look at the main template in detail, you can see how
a Plone page is constructed in a page template and then see exactly how you can customize
those individual page elements.
If you look at the main Plone page, you’ll see a number of elements in that page. Figure 7-8
shows a Plone page with all the key user interface elements marked. Table 7-2 describes each
of the elements and their purpose. For each element in Figure 7-8, you’ll find a corresponding
number in the table.
179
Figure 7-8. All the main elements in the Plone user interface
Table 7-2. User Interface Elements
180
Name Description
No.
1 Site logo Shows the top logo.
2 Search form Shows the search form.
3 Portal tabs Shows the tabs across the top of the site.
4 Personal bar Shows the personal information for that user such as login
and my folder.
5 Breadcrumbs Shows the location of the current content.
6 Left slot Determines where portlets added to the left_slot property dis-
play.
7 Content tabs Shows the actions with the category content_tabs for that
piece of content.
8 Content drop-down lists Shows some drop-down menus for this content, workflow and
new content types.
9 Document actions Shows the actions for this particular piece of content: printing
or e-mail.
10 Byline Shows a description of the content and its author.
11 Right slo This is where portlets added to the right_slot property display.
12 Footer Shows information at the bottom of the page.
13 Colophon Shows more information below the footer.
I haven’t covered one section of this template: the content. All the text from Welcome to Plone
down to The Plone Team is content added and edited by the users. This is the main slot in the
page template, which is filled in by a the particular content type or page template, as you’ve
seen. Chapter 6 covered using slots; in that chapter, I showed how, by using the main slot, you
can ensure content appears inside a Plone page.
So, given these components of your Plone page, how do you customize a particular part? The
answer is to find the matching part of the main_template, see which part it calls, and then
customize that. For this reason, I’ll cover the main template in detail.
At first glance, the main template looks quite long and complicated, but it’s mostly all macros,
and its main purpose is simply to pull content from other areas. You can find the main template
by clicking portal_skins, clicking* plone_templates*, and then clicking* main_template*.
The philosophy behind the main template is that a user shouldn’t have to alter the actual
configuration of the template, unless there are major changes planned. Because the main
template pulls all the content from other places inside Plone, you can alter the assembled page
by customizing those individual elements. This means you can alter just the sections you’d like
to change rather than altering the whole template.
The main template uses Extensible Markup Language (XML) namespaces heavily to present
the simplest possible metal code. For example:
<metal:headslot define-slot="head_slot" />
<!-- A slot where you can insert elements in the header from a template -->
181
Here, the name of the tag isn’t a standard Extensible HTML (XHTML) element; instead, it uses
the metal: prefix to define a namespace as metal:headslot. This has the following advantages:
• The element headslot is semantic, in that it describes the element. It’s easy to
spot that this is the slot for adding anything you may want to add to the head
of your page.
• Attributes in that element use the namespace in the element if not otherwise
declared; so, instead of metal:fill-slot, you can just use fill-slot.
• The actual tag isn’t a valid XHTML tag, so it won’t display. However, if the
rendering of the tag generates any valid XHTML, that XHTML will display.
When a macro is used, the content in the calling template is removed, so it’s possible to place
comments in the calling template as text inside the macro. For example:
<div metal:use-macro="here/global_searchbox/macros/quick_search">
The quicksearch box, normally placed at the top right
</div>
Because of the comment, it’s easy to determine that this macro refers to the search box in the
top-right corner of a site (element 2 in Figure 7-8). To see the macro, find the script named
global_searchbox and the quick_search macro contained within it. The main template continues
through main macros, pulling information from different templates and scripts, and builds the
page as it goes.
After this section, the main template reaches the main content of the page, which is the object
being rendered. In Chapter 6 I explained the difference between a slot and a macro; recall that
a template defines slots that are then filled by the content. Really there’s only one slot of any
importance for content, and I’ve mentioned it many times: the main slot.
One common pattern in Plone that may be confusing is how to define a slot inside a fill slot.
For example, the following is the definition for the css_slot:
<metal:cssslot fill-slot="css_slot">
<!-- A slot where you can insert CSS from a template -->
<metal:cssslot define-slot="css_slot" />
</metal:cssslot>
This design pattern looks a little odd, but it defines the slot and then re-creates the fill slot. If
you look at the main template carefully, those slots are actually inside the header use-macro,
so the header macro may fill this slot. But you also want the end template to fill the slot, so for
this reason the slot is redefined. This means one slot can now be filled in two places, which is
a useful technique for changing the templates.
Scanning down through the rest of main template, you’ll reach the left and right columns, the
footers, and the colophon. Note that the left column may appear before the main content of
a page (if your language reads from left to right anyway), but the style sheet moves it there.
This ensures that if you visit the site in a text-only browser, the main content appears first, not
after all the navigation options.
182
Table 7-3 describes the macros and slots in the main template.
Table 7-3. Main Template Macros and Slots
183
Name Description Slot or Macro
Cache headers Sets the Hypertext Transfer Protocol Macro: in
cacheheaders
(HTTP) cache headers for the con- global_cache_settings
tent.
Head slot Allows content to add to the head ele- Slot: head_slot
ment of a page.
CSS slot Allows content to add custom CSS for Slot: css_slot
the page.
JavaScript head Allows content to add custom Slot: javascript_head_slot
slot JavaScript to the page.
Macro: in
Site actions The site actions allow you to have a site_actions
series of actions above the search. By global_siteactions
default these allow you to change the
font size.
Quick search The quick search box show in the top- Macro: in
quick_search
right corner. global_searchbox
Portal tabs The (normally blue) portal tabs that Macro: in
portal_tabs
are normally at the top left. The ac- global_sections
tual tabs shown are determined by ac-
tions. This determines how the tabs
are rendered in HTML.
Personal bar The personal bar in the top right: lo- Macro: in
personal_bar
gin, logout, and so on. global_personalbar
Path bar The path breadcrumbs that start with Macro: path_bar in global_pathbar
”You are here.“
Macro: in
Content views The (normally green) tabs across the content_views
top of content. This will show only if global_contentviews
the content is editable by the current
user. The actual tabs shown are de-
termined by actions. This determines
how the tabs are rendered in HTML.
Content actions The little drop-down actions in the Macro: in
content_actions
top-right corner of the context bar. global_contentviews
Portal status A message shown whenever some- Macro: in
portal_message
message thing changes. global_statusmessage
Header The header on a piece of content. Slot: header
Main The main part of a piece of content. Slot: main
Sub The bottom part of a piece of content Slot: sub
where the comments on an object will
appear.
184
Name Description Slot or Macro
Macro: in
Left portlets The slots or portlets show on the left left_column
of a page. There are a few definitions portlets_fetcher
here: column-one-slot is the whole left
column, and portlets-one-slot is then
the slot. If neither of these slots is de-
fined, it calls the macro.
Right portlets The slots or portlets show on the right Macro: in
right_column
of a page. See the left portlets. portlets_fetcher
Footer Copyright and other message. Macro: portal_footer in footer
Colophon Miscellaneous messages for the bot- Macro: colophon in colophon
tom.
Armed with this information, it’s now a matter of customizing the macro or the slot to change
the look and feel of the page. Again, it’s recommended not to actually customize the main
template itself but to instead customize the parts that the main template calls. The next section
shows a few example customizations you can make to Plone.
7.1.2.6 Examining Example Customization Snippets
The following sections show some examples that demonstrate simple customizations you can
do to your Plone site. Some solutions provide one or two different ways of performing the
same task.
7.1.2.7 Removing a Block
One rather neat trick is to be able to easily remove a block from the user interface such as the
path bar or the search box. You have two ways to do this; the most obvious is to customize
the macro that displays the element. For example, to remove the breadcrumbs, you could click
portal_skins, click plone_templates, click global_pathbar, and then turn off the element in the
page template level; for example, you can change the following:
<div metal:define-macro="path_bar"
id="pathBar"
tal:define="breadcrumbs python:here.breadcrumbs(here);
portal_url portal_url|here/portal_url">
To this, add the following line of code:
<div metal:define-macro="path_bar"
id="portal-breadcrumbs"
tal:condition="nothing"
tal:define="breadcrumbs python:here.breadcrumbs(here);
portal_url portal_url|here/portal_url">
185
This means customizing a page template, which isn’t a problem at all and is by now something
with which you should be familiar. The slightly different approach is that you can hide ele-
ments at the CSS level. This still means the item is rendered and HTML is generated, but it’s
then turned off for the client-they can’t see it. Because the generation of the HTML still occurs,
this is a suboptimal solution, but it’s a neat trick.
Most elements in Plone have a unique Document Object Model (DOM) element ID; for exam-
ple, in the case of the breadcrumbs, it’s portal-breadcrumbs, as you can see in the previous
code. To stop showing the portal-breadcrumbs, simply add the following to ploneCustom.css:
#portal-breadcrumbs {
display: none;
}
7.1.2.8 Changing Portal Tabs
I’ve already shown you how you can change the text of the portal tabs if you change the
actions. They’re displayed using the style sheet, not using tables (although users may initially
think so). Looking at Table 7-3, you can see that the code for the portal tabs is portalTabs. To
make the border of the nonselected tabs dotted, you can simply change the ploneCustom style
sheet to the following:
#portal-globalnav li a {
border: 1px dotted;
}
The tabs are a series of HTML list (li) and anchor (a) elements, so by changing the CSS for
these elements, you can change the tabs’ appearance. Later in the ”Case Study: Examining the
NASA Skin“ section I’ll show how to change these tabs into images.
By using CSS you can also move any element’s location with the position attribute. Next, move
your tabs to the top of the screen, above the logo and search box. To do this, you use the
absolute value of the position, which lets you define the position by using the left, right, top,
and bottom attributes. Add the following to your ploneCustom style sheet to place the portal
tabs at the top of your Plone site:
#portal-globalnav {
position: absolute;
top: 0em;
}
This is a powerful technique for moving elements. You have multiple options for positioning
the elements, including relative positioning, but that takes a little work with CSS to get the
positioning just right.
186
7.1.2.9 Moving the Left and Right Slots
I discussed the left and right slots in Chapter 4, and I’ve shown you how to add a new slot
to the list of slots. You may have noticed that the terms left and right slots can be a little
misleading. The default is to show the slots in those positions, but it’s easy to move them.
NOTE This works only when you’re using the Plone Tableless skin. This isn’t the default setting,
so you’ll have to change the skin in the portal_skins tool, as discussed earlier in the ”Managing
Skins with the portal_skins Tool*.
For example, if you wanted to move the left portlets to the right side of the page, then you
could do this by changing ploneCustom.css to the following:
#portal-column-one {
float: right;
}
#portal-column-content {
float: left;
}
This moves the leftmost column to the right and pushes the main section to the left.
7.1.2.10 Hiding Help in Forms
If you wanted to hide the help in all the forms, you couldn’t realistically change all the tem-
plates. But you could employ a similar tactic to hiding the path bar-and just set display: none
for the form elements. The following has the desired effect of not placing the input element
on a new line:
div.formHelp {
display: none;
}
Figure 7-9 shows the feedback page without breadcrumbs, with the help hidden, with dotted
tabs, and with the left slot moved to the right of the page, all changed with only a few lines of
CSS.
187
Figure 7-9. The combined effect of some of the examples
7.1.2.11 How Do You Find Element X?
As I’ve shown, the templates, scripts, and images contained in the skins directory of a Plone
installation create a Plone skin. Many files live in that directory, so going through every file
would be long and counterproductive when those files change. Instead, it’s useful to under-
stand some basic techniques for finding the elements you want to alter.
Bear in mind at which level you want to customize the element. As noted earlier, you have
three levels for rendering an object. If you want to change the visual representation, or its
placement, then the chances are that you can change the CSS and do no further work.
If CSS isn’t sufficient, then your next best bet is to search through the templates. For example,
suppose you want to change the text that appears on the page when a user logs in, or you want
188
to change the entire page. In this example, you’ll alter the page shown in Figure 7-10 to make
it a script that does something unusual.
Figure 7-10. The “You are now logged in” page
Some clues exist to find this template so you can alter it; I’ll run through each of them in turn
now.
189
7.1.2.12 Searching by Using the URL
The Uniform Resource Locator (URL) to a page translates to a series of objects in Plone that
are traversed. In Figure 7-11, I’ve traversed to the login_success page. In this case, the final
part of the URL is login_success, as you can see in the address bar in Figure 7-11. When an
object is loaded into an FSDV the extension is stripped off, so you’re looking for a template or
,
script that starts with login_success.
Figure 7-11. Searching for an ID
In Zope you can perform this search by going to the portal_skins tool and clicking the Find tab.
Once there, enter login_success in the with ids field. Leave all the other settings the same,
190
and click the Find button. Sure enough, you’ll find the login_success template.
You can also conduct this search on the file system, depending upon your operating system
and the tools available. The quickest way to find this file in Linux is to go to your CMFPlone
directory and do the following:
$ cd skins
$ find -name ’login_success*’ -print
./plone_forms/login_success.pt
On Windows, open the CMFPlone folder in Windows Explorer and click the Search tab. Then
enter the name of the file as login_success, and click Search. This should give you a list of
likely files.
This search should provide the result, CMFPlone/plone_forms/login_success.pt. If you per-
form the same search in the ZMI, you click portal_skins, click plone_forms, and then click
login_success.
7.1.2.13 Searching for a Piece of Text
A rather crude approach that’s somewhat successful is to do a full-text search on the code to
find the element that renders the page. For example, looking at the page in Figure 7-12, you
can see that it contains the text Notice that the top. The simplest way to find the bit that renders
that text is to search for it.
191
Figure 7-12. Searching for text
In Zope you can also perform this search by going to the portal_skins tool and clicking the Find
tab. Once there, enter Notice that the top in the containing field. Leave all the other settings
the same, and click the Find button. Sure enough, you’ll find the login_success template.
You can also conduct this search on the file system, depending upon your operating system
and the tools available. The quickest way to find this file in Linux is to go to your CMFPlone
directory and do the following:
$ grep -ri "Notice that the top" *
plone_forms/login_success.pt: Notice that the top
192
On Windows, open the CMFPlone folder in Windows Explorer and click the Search* tab. Then
enter the contents of the file as **Notice that the top*, and click Search. This should give you a list
of likely files. Using this rather crude technique, you’ve been given a template, login_success,
that renders the message back to the user.
This technique has the following issues:
• Beware of lowercasing the content in CSS; always make your searches case
insensitive (the default in Windows). It’s annoying to search for home when
it’s Home in the template and it’s lowercased in CSS.
• If you’re trying to do this in a language other than English, the content may
have been localized, causing the search to fail.
• Occasionally there may not be searchable text that will match up; in this case,
looking up via URL is the recommended approach.
7.1.3 Making New Skins and Layers
So far I’ve talked about customizing the existing skin. The process for making a whole new
skin or a new layer isn’t actually that different. I’ll cover one key point, putting your templates
and scripts on the file system.
Making templates and scripts on the file system and creating new skins and layers are def-
initely the best way to go for long-term maintainability and flexibility. Not only is creating
skin elements much easier in familiar tools on the file system, but it also allows you to easily
redistribute your code. Writing on the file system is the style of choice for almost all Plone
developers, with minor modifications in the custom directory if needed.
7.1.3.1 Making a New Skin
As you’ve seen, a skin is actually nothing more than a collection of layers. For my new skin, I
wanted to place all my custom code in one place, so I went to the portal_skins tool, added a
new folder, and gave it the ID of custom_chrome.
Then, to add a new skin, you have to click portal_skins, select the Properties tab, and add a
new skin under the text Add a new skin. You’ll need to enter a series of layers that you want to
set up for this skin. In this example, I added a new skin called Custom Chrome and a series of
layers, as shown in Figure 7-13.
193
Figure 7-13. Adding the Custom Chrome skin
Then I added the layers for the skin. In this case, the skin didn’t have a layer called
custom in it; instead it had a folder called custom_chrome. You now have two skins
that use two layers and two folders. Any objects added to the custom_chrome folder
will affect that skin, not the Plone Default skin.
7.1.3.2 Using Multiple Skins
As mentioned, a standard Plone site has two skins, Plone Default and Plone Tableless. In the
previous section, I added a new skin, Custom Chrome. As I discussed in Chapter 4, you can
194
set the default skin using the Plone interface. Click plone setup, and then click the Portal Skin
button. This mirrors the choices available in the ZMI after clicking portal_skins, selecting the
Properties tab, and scrolling to the bottom of the page.
You have one more option, though: REQUEST *variable name*. This is the request variable
that will contain the user’s skin information. This is plone_skin by default, which is the cookie
name. But it can also be passed through other request variables such as the query string. It’s
available only through the ZMI.
You can also set skins programatically. This allows developers to show different skins to dif-
ferent users depending upon certain business or site logic. For example, if a user is writing
content for a site, they may see the standard Plone skin. If they’re an anonymous user, then
they can see a totally different skin. Rather than letting the user choose, the site is making that
decision. If you really want, you could base the skin on the folder they’re accessing; however,
that approach can lead to confusion, so I don’t recommend it.
To change the skin, add a Script (Python) object called setSkin in the root of your Plone site.
Then add the following code:
##title=Skin changing script
##parameters=
req = context.REQUEST
if req[’SERVER_URL’].find(’internal.somesite.org’) > -1:
context.changeSkin("Plone Default")
context.changeSkin("Custom Chrome")
The actual logic for determining the skin will depend upon the site’s business rules. In this case
anyone accessing http://internal.somesite.org will get the Plone Default skin, and anyone ac-
cessing http://external.somesite.org will get the Custom Chrome skin. Unfortunately, one catch
is that you can’t determine the skin on the security level of the user (for example, authenti-
cated users see one skin, and managers see another). This rather obvious need isn’t possible
at the time of writing, without severely hacking a Plone site.
NOTE Basing the skin on untrusted client information is common practice but not completely
secure because you’re trusting the information from the client. Making sure this is secure
depends on your particular network settings. In most cases, you can handle this easily at the
firewall or using a proxy server such as Apache, which could be configured to block all external
requests to http://internal.somesite.org. I discuss integration with Apache in Chapter 10.
To activate this code, assign an access rule to this object. This means that each time this Plone
site is accessed, this Script (Python) object will be executed. Each time the script is run, the
skin will be set according to the script. To assign a rule to this script, select Set Access Rule
from the drop-down menu and then enter the name of your Python (Script) object. Now test
by visiting your site, and see what skin you get.
You do have to be careful with access rules because they occur on every invocation of that
folder (or Plone site); you have to ensure that they’re correct and that nothing bad can happen
in them. If you’ve accidentally written a bad or incorrect Script (Python) object and can’t even
get access back into the ZMI to fix it, then you can turn off access rules by restarting Plone
with the following environment variable:
195
SUPPRESS_ACCESSRULE = 1
Appendix B explains how to set environment variables if you’re unfamiliar with this process.
7.1.3.3 Making a New Skin on the File System
Throughout these chapters I’ve been using the ZMI. But what most Plone developers use for
any serious work is the file system. Making a skin on the file system is actually easy.
Go to the instance home directory of your Plone installation. Inside the Products directory,
make a new directory; the name of this directory is the name of the product, so the conven-
tion is something short, with no spaces or underscores and mixed case. PloneBookExample,
CMFPlone, and PloneSilverCity are all examples. Inside that folder, make a new file called
__init__.py and a directory called skins. In the __init__.py file, you need to add the following
two lines:
from Products.CMFCore import DirectoryView
DirectoryView.registerDirectory(’skins’, globals())
Next, restart Plone, and then click portal_skins to add an FSDV*.* This will open a list of
the registered directories. Scroll down until you find the one that matches the directory you
registered; this will be the name of the directory with /skins on the end. Enter an ID that
makes sense, and click Add. You now have an empty directory where you can go to add layers
of your skin.
7.1.3.4 Debugging Skins
Another reason I’ve repeatedly been using the ZMI with you, rather than the file system, is that
it gives feedback about errors and gets you comfortable with placing objects inside others. A
further positive feature about using the ZMI is that changes are instantaneous. If you change
an object and then refresh, you see the change immediately (assuming you have no cache).
This isn’t the case with the file system. If you change something in the file system, it isn’t
updated in Plone. This is for performance reasons. Plone has no way of knowing you made
that change, so it must update the Zope cached copy of that object. Without getting into file
system notification trickery, a Plone site has two states: production and debug mode. When
Plone is in debug mode, it checks all the directories, finds files that have changed, and then
updates Plone. This means you can make a change, and it will appear immediately. When run
in production mode, however, your changes will not happen until you refresh the skin (see
Chapter 11) or you restart Zope.
For obvious reasons, if you’re developing skins in Plone, then running in debug mode is the way
to go. Chapter 2 showed you how to change the configuration for Plone so that it runs in debug
mode. As a quick recap, open the zope.conf file inside the etc directory of your installation and
ensure the debug-mode directive is set to on.
196
7.1.3.5 Using File System Objects
The FSDVs allow the mapping of only those Zope objects that have been specifically configured
to be used in this manner. It determines the Zope object based on the extension of the filename.
The contents of that file are the contents of one attribute of the object-usually the main content,
such as the binary contents of an image, or the text contents of the template.
To create an object in your empty FSDV just go to the skins directory and start adding files that
,
match the objects you want to make. Once the file is loaded into Zope as a Zope object, that
extension is stripped off. For example, some_template.pt becomes a file system page template
with the ID some_template. Table 7-4 describes the extensions.
Table 7-4. Extensions
Extensions Object Type Equivalent Zope Ob-
ject
.pt, .zpt, .html, .htm Filesystem Page Template Page Template
Controller Filesystem Page Tem- Controller Page Tem-
.cpt
plate plate
Filesystem Script (Python) Script (Python)
.py
Controller Python Script Controller Python Script
.cpy
Controller Validator Controller Validator
.vpy
.doc, .pdf, .swf, .jar Filesystem File File
.cab, .ico, .js, .css Filesystem File File
.gif, .jpg, .jpeg, .png Filesystem Image Image
Filesystem Properties Object Folder with Properties
.props
Filesystem Z SQL Method ZSQL Method
.zsql
Filesystem DTML Method DTML Method
.dtml
So, to get an image in your directory view, dump in a .gif or .jpeg file. If you want a Script
(Python) object, then add a file ending in .py.
7.1.3.6 Setting File System Object Metadata
Extra content for an object such as title, security, or cache is stored in a separate file. That file
is given the same filename as the original file, with .metadata added to the end. If the original
file is logo.jpg, for example, then the metadata will be contained in logo.jpg.metadata.
The metadata file is in the Windows .ini format of key = value pairs. This format has been
extended to contain information about forms for the Form Controller object, which you’ll see
in the next section. All the choices, even the presence of this file, are optional. The following
is a sample file:
[default]
title = Test object
197
cache = RAMCache
proxy = Manager
[security]
Access contents information = 1:Manager,Anonymous
The following are the values you can set in that file:
title: This is the title that’s applied to the object in the ZMI and in Plone; this will
show up in the Plone templates.
cache: This is the ID of the cache object in which you’d like the object to be cached.
By default Plone comes with two cache objects: a RAM Cache Manager and a HTTP
Cache Manager. Chapter 14 discusses the function of these two objects.
proxy: This is the proxy role you’d like to apply to this object. See Chapter 9 for
more information.
security: This is the security area, which allows multiple lines of security settings.
The key contains the name of the permission. The right side contains the acqui-
sition setting, followed by the roles delimited by commas. For example, View =
0:Manager means only users with the member and manager role can see an object,
and security settings aren’t acquired for that permission.
7.1.3.7 Using Validators in the File System
To specify validators on the file system, add the validator to the .metadata file. The validator
section of the .metadata file would look like this:
[validators]
validators = validate_script1, validate_script2
This will run the two validation scripts: validate_script1 and validate_script2, in that order. A
validation script will examine the data and add errors to the form controller state if there’s a
problem.
The contextType and button options need a slightly different syntax. Validations are run on the
context being executed-for example, a document or image. You could have a different validator
execute for the document and for the image. For example, to have a different validator script
run when this is invoked as a document, add the following line:
validators.Document = validate_script2
You can vary the validator depending on the button clicked on the form by appending the
name of the button in the form to the left side of the validator. The name of the button must
begin with form.button. For example:
<input type="submit" name="form.button.button1" value="First" />
198
The metadata file would then look like the following:
validators..button1 = validate_script1
The .. is a space for the context type, so if, as previously, you wanted this to occur for button1
on a document, then the metadata file would look like the following:
validators.Document.button1 = validate_script5
7.1.3.8 Using Actions in the File System
Like validators, you can specify actions in the .metadata file. The syntax for the actions section
of your file would look like this:
[actions]
action.success = traverse_to:string:script1
In the previous example, when the form is submitted and the validation scripts return a status
of success, the traverse to action is called with the argument string:script1. That argument is
actually an expression. The default action for the failure status is to reload the current form.
The form will have access to all the error messages via the state object in its options.
Again, you can specify a particular action on a particular context; for example, to specify an
action for success when on a document, you can do the following:
action.success.Documnent = traverse_to:string:document_script
Again, you can specify the action for the following button:
<input type="submit" name="form.button.button1" value="Button" />
by adding the following to the .metadata file:
action.success..button1 = traverse_to:string:script1
This example has no explicit context given, so it’s valid for any type of context.
*start sidebar*
7.1.3.8.1 Finding Example File System Skins and the Book Examples
All the examples in this book have been collected in a skin for you to install. You can find this
on the Plone book Web site at http://plone-book.agmweb.ca/Software/PloneBookExamples and
on the Apress book Web site at http://www.apress.com. It’s available as a .zip file of the skin;
after you’ve downloaded and unzipped it, you’ll find there’s a file structure similar to the one
mentioned earlier.
199
You have an __init__.py file and skins directory. In the skins directory you’ll find a series of
page templates, Controller Validator objects, and all the matching metadata files. If you want
to install this, then copy the PloneBookExamples folder into the Products directory of your
instance home. Restart Plone, and then click plone setup. Select Add/Remove Products,* and
you’ll see an entry for *PloneBookExamples; check it, and then click install. You’ve now installed
the templates and can go to feedbackForm and get the page template you saw in the previous
chapter.
What the install procedure did was automate the process of adding an FSDV and then added
a layer to each skin. If you click portal_skins and then select the Properties tab, you’ll see that
the new layer plone_book_examples has been added.
*end sidebar*
7.1.4 Case Study: Examining the NASA Skin
In January 2004 two NASA probes landed on Mars: Spirit and Opportunity. These remote-
controlled robots scoured the surface of Mars, returning pictures and analysis of the surface.
The probes were a great success, returning stunning pictures of the surface of Mars that thrilled
the world.
One small part of this cog was a Web site at http://mars.telascience.org. This site published a
program called Maestro. To quote the Web site, its purpose was the following:
You can download a scaled-down version of the program that NASA scientists use to
operate Spirit and Opportunity. Updates are also available for Maestro that contain
real data from Mars that you can add to your copy of Maestro.
Turning to Plone, the group responsible for this site developed a site that looks great quickly
and easily. In this case, a large number of community members and volunteers helped the
members of the Maestro team develop the site. Figure 7-14 shows the working Plone site.
200
Figure 7-14. The Maestro site
You’ll probably recognize some similar signs of a Plone site: the tabs across the top, the per-
sonal bar in the top-right corner, and the usual breadcrumbs. Other than that, the site looks
quite different from the standard Plone site. In the following sections, I’ll step through exactly
how this was done. Well, actually, it’s quite simple because most of the look and feel was put
together using CSS. There were little to no changes other than changes to the custom style
sheet and some new images.
You’ll first look at the non-CSS changes to the site, which are changes to some of the templates
and properties.
7.1.4.1 Removing the Portlets and Some of the Main Elements
The site has no portlets. These have been removed because in this site there aren’t any that
are relevant. Instead, news appears on the home page. To remove these portlets from your
site, go to the root of the Plone site and click Properties. In the form fields next to left_slots
and right_slots, delete all the values.
In the Maestro site, a few elements have been removed. Sometimes I’ve found that this the
best thing to do for features that just aren’t needed in a site. It can be a little hard to squeeze
every user interface element into a Plone site, but really you don’t always have to do that;
201
instead, just remove the elements you don’t need. A few elements here have been removed:
the site actions, the search box, the footer, and the colophon.
To accomplish this, those templates that produced the code were customized and then altered
so they rendered nothing. For example, to remove the search box, in the ZMI click portal_skins,
click plone_templates, and then click global_searchbox. Next, click the Customize button. Then
change the template to the following:
<html
xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en" lang="en"
i18n:domain="plone">
<body>
<div id="portal-searchbox"
metal:define-macro="quick_search"
tal:condition="nothing">
Nothing to see here.
</div>
</body>
</html>
This is the technique I showed earlier for removing elements; just set tal:condition on the
macro element to ensure that the condition is false.
7.1.4.2 Customizing the Colors
You set the base colors for the site in the base_properties object. This object has been cus-
tomized, and the colors changed to the following colors (unless mentioned, all the other items
are the same):
linkColor: #776a44
globalBorderColor: #776a44
globalBackgroundColor: #e0d3ad
globalFontColor: #776a44
The color change I most noticed is globalBackgroundColor, which affects the colors of the
personal bar and has changed from blue to brownish. These minor colors changes will alter
the base style sheet so that it matches the images and overall look and feel nicely.
7.1.4.3 Creating the Style Sheet
The big part of this site is the style sheet, which is reproduced in full in Appendix B. Here I’ll
highlight some of the main parts of the style sheet. This style sheet is based on ploneCustom.css,
which was customized in the custom folder. Then, some of the elements of the Web page were
overridden in the new ploneCustom.css file.
First, the entire background for the body is set to the color #343434.
202
body {
background: #343434;
}
Second, the actual content of a Plone page, the part that you can edit, is contained in one class
called documentContent. Because the background color of the documentContent element is set
to white in the main plone.css file, the background of the text is white and produces the white
area in the middle of the screen.
Next, the image of the satellite and robot at the top of the Web site is one large image. You
place it at the top using CSS. The code for this is as follows:
#portal-top {
background: url("http://mars.telascience.org/header.jpg") transparent no-repeat;
padding: 162px 0 0 0;
position: relative;
}
This CSS code sets the parameters for the element that has the ID of portal-top. If you look at
the HTML code for a Plone site, you’ll see the portal-top element at the top of the page, just
below the body element. By setting the background for that image to the URL of the image
in question, you can have the image appear. The image is 162 pixels high, which is why the
padding for the top of the #portal-top element is set to 162px. If you don’t do this, then all the
items below will be pushed up, overriding the image.
The header image is 677 pixels wide, and you’ll note that the text in the page fits cleanly
underneath the image, rather than spilling out to the left or right. You can do this by setting
the value for the element to 680px. The visual-portal-wrapper HTML element is actually just
below the body, and it sets the width for the entire page body. The code for this is as follows:
#visual-portal-wrapper {
width: 680px;
margin: 1em auto 0 auto;
}
This sets the width for all the pages to be a fixed width, which is fine as long as you make
sure the width is smaller than the industry-standard 800-pixel width. No matter how big the
user makes the browser window, the main part of the page will never grow beyond those 680
pixels, ensuring it matches nicely with the image.
Probably the other obvious changes are the tabs at the top of the page, which are now images
instead of just the standard Plone boxes. Three images make up the tabs at the top of the page:
a spacer between tab, the left part of the tab, and the right part of the tab. By putting these
three images together, you get the effect of the tab. Figure 7-15 shows these three images.
203
Figure 7-15. The three images that combine to make the tab
For editing the CSS, remember that each of the tabs is really a list item containing a link
inside an element with the ID portal-globalnav. To set up the background spacer between each
tab, the skin first sets the background for the entire element. Again, note that by setting the
height of the image to 21 pixels, the same size as the image, you’ve ensured that there’s the
appropriate space for the image. The code is as follows:
#portal-globalnav {
background: url("http://mars.telascience.org/listspacer.gif") transparent;
padding: 0;
height: 21px;
border: 0;
margin: 0 0 1px 6px;
clear: both;
}
To set the image at the left end of the tab, you use the start image. You set the start image by
setting the value on the li element, rather than the anchor element, like so:
#portal-globalnav li {
display: block;
float: left;
height: 21px;
background: url("/liststart.gif") transparent no-repeat;
padding: 0 0 0 33px;
margin: 0 0.5em 0 0;
}
Finally, you set the right part of the tab by adding an image to the anchor element. You do this
by altering the anchor element inside the tab. The following code shows where you set the
background image to be the right part:
#portal-globalnav li a {
display: block;
float: left;
height: 21px;
background: url("/listitem.gif") transparent right top;
padding: 0 33px 0 0;
border: 0;
line-height: 2em;
color: black;
font-size: 90%;
margin: 0;
}
Now, you’ve replaced the rather standard-looking Plone tabs with great-looking buttons.
204
7.1.4.4 Creating the Splash Page
This page has one other key element. The front page of the site is a splash page, which shows
a nice graphic and invites the user to enter. You can add this by going into the ZMI and
removing the index_html object that’s normally there. Once removed, create a new file called
index_html. In that file, create custom code to make the home page, including a custom CSS.
The main element of that is one image, placed there by the following CSS:
div {
background: url(/splash.jpg) transparent no-repeat;
width: 260px;
height: 335px;
position: absolute;
...
}
The remaining CSS handles the placing of the text and links within that image. This page has
no Plone elements at all; it’s static HTML.
7.1.4.5 Conclusion
This looks like a reasonably complex site, with relatively simple CSS that does most of the hard
work. By using CSS you’ve changed Plone’s look and feel without having to know a great deal
about Plone beyond the HTML. Also, by ensuring that the images are placed using CSS, you
maintained key accessibility functions.
Thank you to NASA and all the people involved from the Plone community for helping with
this site and case study. These include but are not limited to John Graham, Alma Ong, Joe
Geldart, Michael Zeltner, and Tom Croucher.
8 Chapter 8
8.1 Managing Workflow
One of Plone’s many strengths is the workflow component. Workflow fits into one of the core
themes of content management, which is the separation of logic, content, and presentation.
This chapter therefore covers Plone’s workflow in detail.
The chapter starts by covering some key definitions related to workflow, as well as the key tools
involved, so that you can begin to conceptualize workflows. Once these concepts are clear, I
then discuss how to add and edit your own workflows.
Throughout this chapter, I reference simple changes you can make to the workflow that comes
straight out of the box with Plone. I also provide a series of examples to help you perform tasks
such as creating notifications, moving content, and so on. Finally, I show some of the more
advanced features of workflow development and some of the useful tools that are available.
8.1.1 What Is Workflow?
Workflow is a chain of actions or events that occurs on something to achieve an objective.
Workflow often expresses business rules that may exist. Every business has different rules
and policies about tasks that must happen within that company. Examples of this include the
following:
• Before an employee’s time sheet is approved, it must be viewed and acknowl-
edged by a supervisor.
• In a widget factory, for each widget assembled, users must be notified of the
order and any change in the state of the widget as it passes through the factory.
• Before a Web page is published on a Web site, it must be approved by market-
ing, approved by the Webmaster, and translated by a linguist.
Workflow separates the logic of these business rules and standardizes the concept of thinking
about these changes. By having separate logic, it’s now easy for businesses to change the
application to fit their business and their business rules. Often applications try to enforce a
workflow on a business because the workflow is hard-coded into the application.
205
206
8.1.2 Understanding Workflow in Plone
Plone’s workflow tool provides certain features and limitations that are key to understanding
workflow in Plone. The workflow product used in Plone is DCWorkflow, which is an open-
source product released by Zope Corporation. Other workflow systems are available, and
some of them are being incorporated into Plone, such as OpenFlow (http://www.openflow.it).
However, for the moment, DCWorkflow is powerful and simple enough to provide all the
functionality most users will need.
DCWorkflow assumes there’s one object in the system that’s the target of the workflow-for
example, one piece of content or one widget. It further assumes that all objects of the same
type go through the same workflow. By repurposing content (see Chapter 11 for more on this),
you can have similar content use different workflows.
Since the DCWorkflow system is included in Plone, there’s nothing extra to install. It’s repre-
sented in the Zope Management Interface (ZMI) by the portal_workflow object.
8.1.2.1 Conceptualizing a Workflow
Before explaining a workflow, I’ll explain a few simple pieces of terminology: states and tran-
sitions.
A state is information about an item of content at a particular moment in time. Examples of
states are private, public, pending, and draft. All workflows have at least one starting state
in which all the content starts. The workflow will then move the content through a series of
states, either by user interaction or by some automation process. When the content reaches an
end state, it’ll remain in that state for a long time (usually forever). Content may reach one or
more different end states in the process of a workflow.
For that piece of content to move from one state to another, a transition is needed. A transition
connects a starting state and an ending state. A transition can have lots of different features
associated with it, as you’ll see later, but for the moment, you just need to know that a tran-
sition moves content between two states. Usually a transition is triggered by some external
force, such as a user clicking a button on a Web page or a script interacting with a page.
Visualizing a workflow, especially when talking about something as nebulous as content, can
be a little confusing. Thinking about an everyday occurrence will help. In this case, the
following example shows the workflow of my credit card bill, which I have the joy of getting
every month:
1. The credit card company prepares a bill and mails it to me.
2. I get the bill and put it on my desk. Sometimes the bill sits on my desk for
quite a while as I wait for the end of month. Occasionally I have to query
people about certain expenditures, such as “What were those clothes you
bought?”
3. Any serious queries or questions then go back to the credit card company,
perhaps causing a new bill to be created (although this happens quite
rarely).
207
4. Usually at the end of the month, when I do all the accounting, I then pay
the bill.
From this, then, you can come up with some states. Looking at the previous steps, you’ll
see you really have no need to create different states for receiving the bill, which includes
opening it and putting it on my desk. Similarly, you don’t need to bother with every review
that happens. Although these are all valid steps that take place, trying to make a workflow
for every state would be too cumbersome. Instead, you can summarize the workflow with the
following states:
• Draft: The credit card bill has been prepared and sent to me.
• Review: The credit card bill has been received and is on my desk, being re-
viewed.
• Paid: The credit card bill has been paid, put in my filing cabinet, and forgotten
about forever.
Now that you’ve come up with the states, you can think of the changes that need to occur. For
each of these states, you’ll have at least one transition that occurs to move the bill from one
state to another:
• Post: The bank sends the credit card bill.
• Pay: I pay the credit card bill.
• Reject: Something is wrong on the bill, and it isn’t approved.
Figure 8-1 shows this set of transitions and states. In the figure, boxes represent states, with
the state written in them. Arrows represent the transitions from one state to the next, with the
name of the transition in italics.
Figure 8-1. A simple state machine for paying credit card bills
You’ve now extracted this business process of paying a credit card bill into a workflow. The next
step is to think about roles and security for this credit card bill. This workflow now contains
the business logic for an application for processing credit cards.
8.1.2.2 Understanding Roles and Security in Workflow
In any complicated system, you’ll have users of all roles and groups. These roles give Plone a
large amount of flexibility with security, but they also can make it more complicated. Chapter
208
9 covers security, local roles, and groups, but this section covers some key points about how
these topics relate to workflow.
When a piece of content moves from one workflow state to another, the workflow process can
change the security settings on that content. The security settings determine what user can
perform what action on what piece of content. By manipulating the security settings through
workflow, you can cause the security to change on a piece of content through its life cycle.
Users from static systems or Zope often get confused because in Zope, all pieces of content
have the same security settings throughout their life cycle.
Returning to the credit card example, you can infer the security settings for the credit card bill.
One way to represent this is to produce a table that expands the security in general terms for
the transitions that can occur at each of the various states, as shown in Table 8-1.
Table 8-1. The Transitions and Entities That Can Make Them
State Me Bank
Draft Post
Review Pay Reject
Paid
At this stage in Table 8-1, you’ve seen the transitions and who can make them. You haven’t
thought about the access that each user has to perform an action on an object at each point.
For example, at which point can someone edit the bill, and when can it be viewed? These are
called actions in Plone terminology, as shown in Table 8-2. I hope that only I have access to my
own credit card statements! Likewise, at any stage, the bank is able to view the credit card bill
and answer queries on it.
Table 8-2. The Actions and Entities That Can Make Them
State Me Bank
Draft View, Edit
Review View View
Paid View View
Actually, as it turns out, I can’t edit my credit card bill; only the bank can. I can send back my
credit bill by rejecting it, but the bank is unlikely to want my edits. In this situation, assume
the bank is the owner of the credit card bill. This demonstrates a concept called ownership. I
may have several credit card bills from several banks, and in each case you can think of the
bank as the owner. Each bank owns its own credit card bills, but Bank A isn’t the owner of
Bank B’s bill. Table 8-3 combines the transitions and actions, changing the terms Me and Bank
to Payee and Owner, respectively.
Table 8-3. The Transitions and Actions Combined, plus the Roles of People
State Payee Owner
Draft Post, View, Edit
209
State Payee Owner
Review Pay, Reject, View View
Paid View View
Of course, this is a rather contrived example, but it illustrates how you can apply
workflow to basic states. More transitions can occur here-for instance, I’d be more
than happy for someone else to pay my credit card bill for me-but that’s so unlikely
that you shouldn’t add it to the workflow or security.
Before showing how to create and edit workflows, I’ll now show you the default workflows
that ship with Plone.
8.1.2.3 Introducing Plone Workflows
Plone ships with a set of default workflows for your Plone site. These workflows provide a
logical way of moving content through a Plone site. A standard Plone site ships with two
workflows: the default workflow and the folder workflow. The following sections present each
of these in turn.
8.1.2.3.1 Default Workflow
Chapter 3 covered the default workflow and the default settings when publishing content. I
discussed the security and settings for each state in the workflow. However, a picture is worth
a thousand words, so Figure 8-2 shows the workflow state.
210
Figure 8-2. The default workflow for content that comes with Plone
Figure 8-2 shows the main states and the transitions. This figure has a gray dotted line that
represents a sort of security divider. To the left of the line is where owners of the content
usually interact with the content. To the right of the line is where reviewers usually interact
with the content.
NOTE The owner of the content is the person who created the content originally.
An owner is one particular member of a Plone site. Although many members exist
in a Plone site, only one person can be the owner of a piece of content in a Plone
site. Because the owner role is calculated when an object is created, the owner role
is special.
Just like with the credit card example, an associated set of permissions exists for the default
workflow. Table 8-4 outlines all the permissions and the states.
Table 8-4. The Default Workflow Permissions
State Anonymous Authenticated Owner Manager Reviewer
Pending View View View Edit Edit
Private Edit Edit View
211
State Anonymous Authenticated Owner Manager Reviewer
Published View View View Edit View
Visible View View Edit Edit View
View refers to the following permissions: Access Contents Information and View
Edit refers to the following permission: Modify Portal Content
As you can see from Table 8-4, by default only when content is in the private state is it truly
hidden from everyone else. When content is in the published state, only the manager can edit
it. Later in the “Editing Permissions” section, I’ll show you how to change these permissions
easily through the Web.
8.1.2.3.2 Folder Workflow
I also discussed the folder workflow in Chapter 3, when I covered publishing content with you.
However, as I noted in that chapter, no pending state exists for folders. Instead, you have a
slightly simpler workflow, as shown in Figure 8-3.
Figure 8-3. The folder workflow for content that comes with Plone
212
8.1.2.3.3 Other Workflows
Numerous workflows are available to a Plone site, including private workflow, community
workflow, one-step publication workflow, and so on. ZopeZen comes with a workflow, and
PloneCollectorNG also comes with a workflow. DCWorkflow comes with four workflows.
Currently, two workflows come in the PloneWorkflows product in the collective project on
SourceForge (http://sf.net/projects/collective): the community workflow and one-step publica-
tion workflow. The community workflow is similar to the Plone workflow, with a few changes.
The one-step publication workflow has two states: private and published.
At the moment, you have no easy way to install and uninstall workflows, and you have no
real easy way to transition content between one state and another. For example, if you install
the one-step publication workflow into an existing state, you also need to fix the states for all
objects and move them into one of the new states. In this case, it’s probably simple-everything
in a published state should stay as it is, and everything else should move into the private state.
8.1.3 Adding and Editing Workflow
Now that I’ve discussed the default workflow, I come to the key point that’s probably most on
your mind: How can you change the defaults? Well, as with most of Plone, you can add, edit,
and delete all workflow through the ZMI. The tool that controls workflow is portal_workflow.
In the following sections, I cover how workflows are assigned and then go through all the
settings for a workflow in detail.
8.1.3.1 Setting Workflows to a Content Type
After clicking portal_workflow, you’ll see a list of workflow assignments. A feature of DCWork-
flow is that each content type has one and only one workflow assigned to it; Figure 8-4 shows
these assignments.
213
Figure 8-4. The list of workflow by type
On this page you’ll see a list of each content type and the workflow that has been applied
to it. If a workflow isn’t specified (in other words, the value is blank), then no workflow is
applied. As an example, the d