Customizing AT Templates

« Return to page index

Plone Theme Reference

1. Introduction

Goals, Pre-Requisites, and Tools

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

Goals: What will I learn?

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

Pre-Requisites: What do I need to know?

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

Tools: What do I need to have installed?

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

2. What Makes It Tick?

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

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

  • All the pages look the same.

However, it also has the following drawback:

  • All the pages look the same.

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

The base_view Template

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

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

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

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

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

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

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

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

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

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

The base Template

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

  • header
  • body
  • folderlisting
  • footer

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

Widgets

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

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

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

[1] Unless you're wiggy.

3. Customizing Widgets

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

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

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

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

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

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

Customizing Widget Templates

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

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

myfield: some value

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

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

  • view
  • edit
  • search

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

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

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

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

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

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

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

Custom Widget

Yes, folks, it's just that easy.

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

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

 

4. Total Control: The View Template

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

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

Archetypes and Type-Specific View Templates

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

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

Type Name

View Template Name

My Type

my_type_view

SomeTypeV2

sometypev2_view

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

  • header
  • body
  • folderlisting
  • footer

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

          Foo

          Foo

          Foo

          Foo

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

The Infamous "Foo" View

But Wait! Where Are All My Fields?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Custom Body Macro

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

Customizing Labels

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

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

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

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

Customized Label

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

5. Conclusion

Some final notes about customizing Archetypes view templates

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

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

Some Final Notes

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

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

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

6. Reference

A reference for customizing Archetypes view templates

View Templates

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

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

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

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

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

Widget Templates

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

  • view
  • edit
  • search

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

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