If you're a keen collector of evolt contributor bios, you'll notice that I'm

gently working on moving my own site

over to Zope. Now I'm a wee way through

this, and in the process, thinking about how I might make

managing the site easier with the

Content Management
capabilities Zope has to offer.

One of the obvious candidates is the news on the home page. The fact that

I've hardly touched it in months will give you the idea of how much of a

pain it is.

What that news update is is a single

static text file
which is included into the main page at run time. I

manage the include by telnetting (using SSH - I'm not totally

stupid) into the server and hand-editing using

vi.

This leaves much to be desired - not least that firewalls in various

locations I'd like to edit it from prevent me from doing it - and ensures

that it's generally too much hassle to actually do.

So with this Brave

New World
of Content Management, I thought I could improve it.

Is this a Blog I see before me?

Let's face it, my news system is basically a Blog,

as common as muck. So the problem is pretty well defined - everyone

knows what a Blog

should

look

like - a list of items

with links to permanent URLs.

The fun bit comes when you break that down into what a system should do,

and then how to code it for Zope. The first bit is what I'm used to doing,

the second is frustrating because Zope is not a well-documented system,

particularly with regard to code samples. When you hear that it has a

Cliff-like Learning Curve, you know what you're in for...

But as Zope is an Object Oriented database which thinks it's a file-system,

and has a bunch of simple (if badly documented)

APIs, it should be possible to work out and implement a simple set

of system requirements for a blog using the small amount of OOP theory I

know.

OOP Theory 101

(Apologies if this is either wrong or just plain over-simplified to the

point of error - it's a mental model which works for me at this level of

complexity)

In an OOP system, you have two kinds of things:

  1. Objects - things with properties which are exposed to

    other things.

  2. Methods - actions you can carry out on Objects without

    worrying about exactly how they work.

And in good programming practise, you keep the two nicely separated so you

can change the internals of each object and method without actually

affecting anything else.

So that sounded like my Blog could contain a number of News Item Objects

with properties I'd display and order using Methods.

This doesn't sound too hard, so let's think about the properties you might

need for an object which happens to be a Blog news item.

System Requirements

Note that this means the requirements the system you're working needs to fulfil, rather than the software you need to buy.

At its simplest, then, each news item is an Object containing content and

meta-content - information about the content. What we want to do is have

each news object as self-contained as possible so that we can build pages

and indices just by calling simple methods on that object - and that will be our

entire system.

So it makes sense for that meta-content to be as rich as is needed to

support all the requirements we might reasonably have.

To work out what meta-content we'll need, let's think about our

requirements for a moment. I'm going to use a simplified version of a

methodology called

Use

Cases
. Each Use Case is a description of what happens in a

situation when the system is used (not how it happens - that's a

lower level of design). You'll notice that requirements in the first Use

Case have implications for later ones.

Blog Use Cases

  1. User visits Home Page

    1. Home Page displays last five news items, ordered by date, most

      recent first.

    2. If there are no news items, the user is notified.

    3. Only news items which are past their launch date should be

      displayed.

    4. Only news items which have not expired should be displayed.

    5. Each item should have a link to a permanent URL to avoid

      Linkrot.

    6. Each item should display a headline, its launch date and its

      content.

    7. This short list of the most recent items should contain a link to a

      full list of news items.

    8. If the content has been updated since launch time, the user should

      see it flagged with the update date.

  2. User visits permanent URL

    1. The URL is displayed on its own page

    2. The news item is displayed in the site's standard style (layout,

      stylesheet etc).

    3. The page contains the item's headline, launch-date and body

      content.

    4. The item is only available between its launch and expiry dates

      inclusive (ie they are the first and last dates it is available). At

      other times, an error message is displayed.

  3. User visits full news listing page

    As per Home Page, but with all available news items listed (not just the

    most recent five).

News Object Properties

So, going through the Use Cases, here's what we need in terms of a news object's

properties:

Blog Methods

As we're in an OOP

environment, the other thing we need to do is specify what methods (aka

functions) we can perform on the news item objects.

Fortunately, looking at the Use Cases above, we've done that already - we

essentially need one method for each Use Case.

And it's even simpler than that, because Zope gives us one of them for free

- the Display Permanent URL Use Case is close enough to the

basic Zope View method as makes little difference. We'll need

to do a tiny bit of conditionalisation around launch and expiry

dates, but not enough to be worth writing a separate method specifically for

it.

System setup and coding

Right, now that's the hard work done, let's get our hands dirty. I've put

together target="_blank" title="Opens in a new window">a demo for you to have a

look at while you're doing yours.

Object setup

Create a folder below your site root called news_items. This is

where we'll store all our news objects.

Now create a basic DTML Document in that folder - this is the base object

type we're going to use as it lets us easily add all object properties we'll

need.

Ignore the Edit screen for a moment, and pop over to the

Properties tab. You're going to add three properties:

Property Type Format for values
launchDate date yyyy/mm/dd (in other words - the ISO standard for short dates)
expireDate date yyyy/mm/dd
newsContent text Text - this will be a <textarea> form field.

With your new object, fill in some dummy data - the content will need to be

normal HTML, but the rest is just a simple string for the title, or dates

formatted as above.

Now if you've browsed the

Zope Book you'll be

asking yourself Why keep the content in a property rather than in the

basic Edit slot?

The answer is that we're going to be producing a list of news objects,

including their main content. If we'd stuck it in the Edit

slot, we'd either get the standard page headers and footers (via

standard_html_header and standard_html_footer)

nested in our list, or we'd have problems with missing headers and footers

when we accessed the object's URL. There's another cunning thing we can do

if it's here, but I'll leave that surprise until later...

Method Setup

Enabling the View Permanent URL method

If you view the object now via its normal URL, you'll get a page without a

lot on it. That's fine, because the View method only uses the

basic Zope object properties like Title.

What we're going to do now is add to that basic method to drop in our

content. Here's the basic code for the View method, assuming

that your standard_html_header takes care of

<title> and sticking a sensible <h1>

title in:

<dtml-var standard_html_header>

<p><strong><dtml-var launchDate fmt="%d %B %Y"></strong></p>

<dtml-var newsContent>

<dtml-var standard_html_footer>

Easy, eh? The one slight complexity is the date formatting, which uses

these

codes
in a similar way to SSI date formatting.

Now view the object - you should see something closer to a correct rendition

of the news item in your site's style (you may need to add stylesheet

references to the above of course).

The other thing that the Visit Permanent URL Use Case called

for was a conditionalisation on launch and expiry dates. So we're going to

add that tiny bit of conditionalisation I mentioned:

<dtml-var standard_html_header>

<dtml-if "ZopeTime() - launchDate>= 0 and expireDate - ZopeTime()>= -1">

<p><strong><dtml-var launchDate fmt="%d %B %Y"></strong></p>

<dtml-var newsContent fmt=structured-text>

<dtml-else>

<h3>This news item is not available</h3>

<p>Please return to the <a href="./">full list of news stories</a>.</p>

</dtml-if>

<dtml-var standard_html_footer>

ZopeTime is just a variable containing the current date and

time. If you want to see what it contains, just try

<dtml-var ZopeTime> (all the custom date formats work

with this).

Adding the View Full News Listing Method

This method is more complex, and maybe we want to access it in more than one

place, so we're going to keep it separate from the page design and news

content (this is A Good Thing).

In your root folder, create a DTML Method called

news_full_list. We're going to build this up like the last

method, starting from the simple core, and adding conditionalisation as we

go.

What we need to do is iterate through the news_items folder

object and retrieve its subobjects - the news item objects which are all

DTML Documents. This is pretty simple stuff, it's the basic

dtml-in tag. So, with similar outputs to the View

method:

<dtml-in expr="news_items.objectValues(['DTML Document'])" reverse sort=launchDate>

<h2>

<dtml-var title> (<dtml-var launchDate fmt="%d %B %Y">)

</h2>

<dtml-var newsContent>

<small><a href="/news_items/<dtml-var id>">Permanent link</a></small></p>

</dtml-in>

Note the reverse and sort attributes in the

dtml-in statement. Again, your stylesheet and heading-level

mileage may vary.

This will give you a full list of all the news objects in the news_items

folder - if you view the method, you'll just get that output, with no HTML

round it. So we need to drop it into a page somehow.

Create a DTML document called news_html. By the miracle of

acquisition, this can be anywhere, but to make life easy, drop it in your

root folder.

All you need in this document is a title and the following DTML in the main

View method:

<dtml-var standard_html_header>

<dtml-var news_full_list>

<dtml-var standard_html_footer>

View that object, and you should get your one dummy object popping up. Add

some more and you'll have a whole list.

Right, back to the method, and fulfilling the date conditions.

Unsurprisingly, it's the same condition as in the View URL

method:

<dtml-in expr="news_items.objectValues(['DTML Document'])" reverse sort=launchDate>

<dtml-if "ZopeTime() - launchDate>= 0 and expireDate - ZopeTime()>= -1">

<h5>

<dtml-var title> (<dtml-var launchDate fmt="%d %B %Y">)

</h5>

<dtml-var newsContent>

<small><a href="/news_items/<dtml-var id>">Permanent link</a></small></p>

</dtml-if>

</dtml-in>

If you play about a bit with the launch and expiry properties for your

objects, you'll have them popping in and out like nobody's business.

Around

midnight
is a good time to do this so you can see the condition ticking

over.

The last bit of the puzzle is the automatic "Updated" note if an

item is updated past its launch date. My note is highlighted with a

superscript, styled to be white text on red (IMO, about one step down from

'blink'), but once more, yours may vary:

<dtml-in expr="news_items.objectValues(['DTML Document'])" reverse sort=launchDate>

<dtml-if "ZopeTime() - launchDate>= 0 and expireDate - ZopeTime()>= -1">

<h2>

<dtml-var title> (<dtml-var launchDate fmt="%d %B %Y">)

<dtml-if "bobobase_modification_time() - launchDate >= 1 and ZopeTime() - bobobase_modification_time()<=5">

<sup class="new">&nbsp;Updated&nbsp;<dtml-var bobobase_modification_time fmt="%d %B %Y">&nbsp;</sup>

</dtml-if>

</h2>

<dtml-var newsContent>

<small><a href="/news_items/<dtml-var id>">Permanent link</a></small></p>

</dtml-if>

</dtml-in>

You've probably guessed what bobobase_modification_time

contains.

We're nearly there - just one more method to go.

The Home Page Listing Method

This is very, very similar to the full listing method, except that we only

want the last four objects (by launch date). No problem - stick a

end=5 attribute in the dtml-in and we're

there.

But to give it the full instructions, add another DTML Method called

news, with the following code:

<dtml-in expr="news_items.objectValues(['DTML Document'])" reverse sort=launchDate end=5>

<dtml-if "ZopeTime() - launchDate>= 0 and expireDate - ZopeTime()>= -1">

<h5>

<dtml-var title> (<dtml-var launchDate fmt="%d %B %Y">)

<dtml-if "bobobase_modification_time() - launchDate >= 1 and ZopeTime() - bobobase_modification_time()<=5">

<sup class="new">&nbsp;Updated&nbsp;<dtml-var bobobase_modification_time fmt="%d %B %Y">&nbsp;</sup>

</dtml-if>

</h5>

<dtml-var newsContent>

<small><a href="/news_items/<dtml-var id>">Permanent link</a></small></p>

</dtml-if>

</dtml-in>

I've also changed the heading tag to fit better into my home page hierarchy

which has other content in it. Dropping it into your index_html

is a piece of cake:

<h4>In the News</h4>

<dtml-var news>

<p><a href="./news_html">All the news</a></p>

Final Finesse - Text Entry and Formatting

I mentioned one final piece of finesse which having the content in a defined

property allows us. That finesse is allowing formatted entry without knowing

one bit of HTML, using

Structured

Text

Structured Text is great. It lets a content editor do pretty much most of

the things most content editors want to do in the way of formatting, without

having to know HTML. It adds <p> tags for double lines,

<ul> and <li> tags with a simple

indented paragraph starting with an asterisk or an o - all

kinds of nice stuff. (If you want, you can intersperse HTML in there

too).

So for an easy to use Blog, it makes a lot of sense - you don't have to

think about code, just content (in fact, this article was written using

Structured Text, although I needed a couple of touchups for final

presentation). But how to get the content rendered into HTML in the pages

and full listings?

Well it turns out that if you're reading the content in from an object

property, it's really simple. All you have to do is add

fmt=structured-text to the dtml-var statment like

so:

<dtml-var newsContent fmt=structured-text>

So our full and final news View Full News Listing method looks

like this:

<dtml-in expr="news_items.objectValues(['DTML Document'])" reverse end=5 sort=launchDate>

<dtml-if "ZopeTime() - launchDate>= 0 and expireDate - ZopeTime()>= -1">

<h5>

<dtml-var title> (<dtml-var launchDate fmt="%d %B %Y">)

<dtml-if "bobobase_modification_time() - launchDate >= 1 and ZopeTime() - bobobase_modification_time()<=5">

<sup class="new">&nbsp;Updated&nbsp;<dtml-var bobobase_modification_time fmt="%d %B %Y">&nbsp;</sup>

</dtml-if>

</h5>

<dtml-var newsContent fmt=structured-text>

<small><a href="/news_items/<dtml-var id>">Permanent link</a></small></p>

</dtml-if>

</dtml-in>

and our final View Permanent URL method is this:

<dtml-var standard_html_header>

<dtml-if "ZopeTime() - launchDate>= 0 and expireDate - ZopeTime()>= -1">

<p><strong><dtml-var launchDate fmt="%d %B %Y"></strong></p>

<dtml-var newsContent fmt=structured-text>

<dtml-else>

<h3>This news item is not available</h3>

<p>Please return to the <a href="./">full list of news stories</a>.</p>

</dtml-if>

<dtml-var standard_html_footer>

Now you've got a working Blog system, the only thing left to do is to write

some original content. Ah, but that's when it get really hard...

 

More info

If your system is more complex than this, and you have lots of people working on different elements, you could do worse than looking to the Zope Fishbowl Process for some effective ways of working worth borrowing.