Skip to page content or skip to Accesskey List.
Search evolt.org
evolt.org login: or register

Work

Main Page Content

A Quick and Dirty Blog using Zope

Rated 4.13 (Ratings: 6) (Add your rating)

Log in to add a comment
(12 comments so far)

Want more?

 
Picture of MartinB

Martin Burns

Member info | Full bio

User since: April 26, 1999

Last login: March 30, 2010

Articles written: 128

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:

  • Headline The standard title Zope property can do this nicely
  • Content The main content of the news item
  • URL The uniqueness can be derived from the standard Zope object id property - they need to be unique anyway. The content manager will have to manually choose these, but with a sensible naming schema (eg yyyy_mm_dd, this shouldn't be a problem.
  • Launch Date - the first date the item is available. To give the content manager flexibility, this should be manually specified, and will have to be a date-format property.
  • Expiry Date See Launch Date
  • Updated Date This needn't be manually edited, and is probably better not anyway so that the system can automatically pick up re-edited items. This might be a problem if you want to sneakily re-edit something embarrassing, and another issue could be that changes in properties (eg launch date) are classed as an edit by Zope.

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 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 &lt;dtml-var ZopeTime&gt; (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 &lt;p&gt; tags for double lines, &lt;ul&gt; and &lt;li&gt; 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.

Martin Burns has been doing this stuff since Netscape 1.0 days. Starting with the communication ends that online media support, he moved back through design, HTML and server-side code. Then he got into running the whole show. These days he's working for these people as a Project Manager, and still thinks (nearly 6 years on) it's a hell of a lot better than working for a dot-com. In his Copious Free Time™, he helps out running a Cloth Nappies online store.

Amongst his favourite things is ZopeDrupal, which he uses to run his personal site. He's starting to (re)gain a sneaking regard for ECMAscript since the arrival of unobtrusive scripting.

He's been a member of evolt.org since the very early days, a board member, a president, a writer and even contributed a modest amount of template code for the current site. Above all, he likes evolt.org to do things because it knowingly chooses to do so, rather than randomly stumbling into them. He's also one of the boys and girls who beervolts in the UK, although the arrival of small children in his life have knocked the frequency for 6.

Most likely to ask: Why would a client pay you to do that?

Least likely to ask: Why isn't that navigation frame in Flash?

Zope QA

Submitted by Junglee on May 9, 2002 - 07:26.

I just downloaded zope after reading this article & i read your bio which talks about your personal website.

When I actually saw your website you say there that you generate the website on your comp at home and then ftp all the files , since you are not running Zope on the hosting server . OK finally (!) here comes my question -- does Zope come with kind of inbuilt functionality which will periodically generate changed content as static html files (or) do you use some other method ?

login or register to post comments

Zope content promotion

Submitted by MartinB on May 9, 2002 - 07:39.

Hi Junglee - no, Zope doesn't come with any kind of replication (hmmm nice idea). I pull the rendered pages from the Zope box with wget. (Actually, I pull them onto my home box as static pages and then have ftp them up as my host doesn't have wget, but same idea).

You can ftp into your Zope server (or use WebDav), but then you get the source code pages, not the rendered HTML

login or register to post comments

a comparison

Submitted by Junglee on May 10, 2002 - 04:08.

Martin :

just some observations after installing zope it seems to be quite similar in some ways to another product i 've seen : lotus domino .

domino architecturaly seems to be similar at 1st glance in the following ways :
  • the object database in domino has a similar hierarchial files system kind of structure - and even the way to access the objects within a database is similar http://database/folder/document ...
  • there is something very similar to the structured text bit you mentioned - though domino has an rtf -> html translator
  • A document centric approach , and some built in functional workflow

zope definitely seems to be standards oriented (domino is still quite a distance from that). - though the replication you mentioned in your mentioned is a major selling point in domino

Thanks for the article - it was interesting enough to get me to download zope.

login or register to post comments

Replication between Zopes

Submitted by MartinB on October 11, 2002 - 01:27.

I've just remembered about ZSyncer - a product which allows replication between Zope instances.

You'll probably also want this mod which provides the security missing from ZSyncer.

It doesn't replicate to static HTML, though, which is what I really need.

login or register to post comments

RE:Replication between...

Submitted by Junglee on October 11, 2002 - 01:59.

I did some R&D when I read this article the 1st time. I found a couple of links that were pretty decent :
Zope for managing nearly static sites
Using RSYNC to synchronize websites

OK, the guy doesnt *really* replicate, like synchronizing deletion of content from one end to the other; he does provide some useful scripts that can modified to do some kind of replication.

login or register to post comments

RSync between Zopes

Submitted by MartinB on October 11, 2002 - 02:14.

If you can automate copying your master ZODB file over from master to slave, then you've essentially achieved syncing the content and users with all additions, deletions and modifications intact. What that won't do is synchronise any products you've installed, but that's not a common thing once you're up and running.

login or register to post comments

Zope can cope.

Submitted by adaw on December 20, 2002 - 08:17.

Great article! We are using Zope to build globalhand.org It's powerful, opensource, hard to beat. If you're looking for news management which is slightly more comprehensive and robust, Zope has a number of CMF products that will work out of the box, for example:

http://cmf.zope.org/
(and some nice skins):
http://plone.org/

http://www.squishdot.com (modelled on slashdot) uses dtml methods for it's news management.

I would love to find a similar news management system which uses page templates instead of dtml. Are there any Zope users out there who know of such?

login or register to post comments

News Management system with Zope Page Templates

Submitted by MartinB on January 27, 2003 - 06:59.

Adaw

Plone is now ZPT based. This has the advantage of making it very easy to develop different-looking sites from the same system.

login or register to post comments

Does anyone 'get' Zope?

Submitted by SolKarma on July 17, 2003 - 01:44.

Everything I read about Zope leads me to believe that many people don't understand Zope. This includes much of the content available at Zope.org.

The salient features of Zope are the Zope Object Database (ZODB), the ZCatalog and delegation-based inheritance. Essentially, the ZODB combines the network data model and the hierarchical data model to describe data, data relationships and data contraints.

The network data model uses records, i.e., structures that contain fields of simple data types associated with a name, and sets, i.e., a named relationship between an owner record and one or more of its contained records. The hierarchical data model stores records within a general tree structure starting from a root record from which additional records ascend (note: The author uses 'ascend' over the wrongly used 'descend'. Trees ascend from roots, not descend from them, i.e., are not inverted).

The ZCatalog provides an index of records, in much the same way as a RDMS does. Delegation-based inheritance enables a given record to share values for fields that exist within precedent records.

Despite the misguided push to develop web applications by creating Zope Products, the best way to develop web applications with Zope is to do the following:

1. Define the data structure of your records 2. Use external methods to provide fuctions to manage values within fields of named records, i.e., perform calculations 3. Use either ZPT or DTML to provide a 'face' to display values within a browser, i.e., for display only, not calculations 4. Manage records using the ZCatalog

Why should one develop in this manner? It's much simplier than trying to determine what 'behavior' your Zope 'objects' need., Moreover, this way enables the programmer to use the full power of Python.

The ZCatalog tracks changes to 'objects', i.e., records it 'knows' about. In the Zope world, A blog is nothing more than a 'face' to created/edited records based upon their creation/modification dates (or other user-defined criteria). One can define any kind of 'face' to records.

The best (and probably only) Zope Project one needs to create ANY applicatoin is Kube. Combine Kube with user-defined external methods and you can unlock the 'Zen of Zope'.

login or register to post comments

Re: Does anyone get Zope?

Submitted by MartinB on July 17, 2003 - 06:26.

SolKarma wrote:

Despite the misguided push to develop web applications by creating Zope Products,

Misguided? For whom? For clients who want packaged applications that perform 90% of their needs at a lower cost? For developers who can offer much more advanced functionality to a wider range of clients? To end users who gain a more robust experience through standard (rather than bespoke) solutions to standard problems? Does the world really need another BB system coded from scratch?

Nice bit of FUD, anyway, combined with your punt for Kube.

the best way to develop web applications with Zope is to do the following:

1. Define the data structure of your records 2. Use external methods to provide fuctions to manage values within fields of named records, i.e., perform calculations 3. Use either ZPT or DTML to provide a 'face' to display values within a browser, i.e., for display only, not calculations 4. Manage records using the ZCatalog

Which (other than the ZCatalog part, and using ZODB (rather than external) methods) is largely what I've done. You'll note that this is a quick and dirty blog, designed and documented for Zope newbies to use, not a perfect system.

login or register to post comments

People 'get' Zope

Submitted by Seb on July 18, 2003 - 03:31.

I've been developing with Zope for quite a while now, and have implemented a number of commerical and personal projects using it, from small sites through to enterprise-level multi-tiered websites/extranets/intranets for government organisations.

The assertion that developing in Zope should be focussed around use of the Kube object with a few external methods is short-sighted simplification, and displays a complete lack of understanding of the application framework that Zope provides.

Note that I say "application framework", not CMS or anything specific. One of Zope's fundamental strengths is its extensibility through development of python "products", which are powerful components that can make full use of both the Zope architecture (and therefore any object or method available within it, even content) as well as the full range of any other python library available.

For example, for one project, I wrote a python wrapper to a commercial C library for converting Word documents to DocBook XML. Combined with the Docutils reStructuredText and docbook writer, this allowed me to extend Zope to the point that to publish a folder of word documents (including full metadata), all I had to do was create a new product to hold the data (easy, using Plone and Archetypes, two very powerful extensions to Zope), add a few utility methods to the product, and have the product inherit from the converter utility. Using the FTP upload methods that Zope provides, this allows FTP-ing a directory full of Word docs into Zope, all of which are automatically converted to XML, and can be edited in either XHTML or reStructuredText through the web interface. (Total development effort on this was less than 5 days.)

Of course, this then ties in with ZCatalog, which when using the full range of plug-in indexers, allows full-text searching of all these word documents, offering possibilites of dynamically re-organizing an entire intranet's worth of information on-the-fly, so that content is presented by keyword, date, author, by ranked relevance to any other document, or even totally randomly.

The assertion that the only requirements for using Zope are a Kube object, the ZCatalog and some External Methods, is fundamentally flawed, because no consideration is given to storage and performance restraints within a system larger than a few thousand objects. Display of objects in aggregate form (such as a list of content) can either be done through the container's interface (list all objects in a folder) or through the ZCatalog, but it's important to understand that for hierarchical navigation, performance of the container is going to be faster when correctly using the caching mechanisms provided. Furthermore, this displays an ignorance of additional functional requirements such as state- or process-based workflow management.

Avoiding the use of ZCatalog to retrieve content allows full manipulation of the objects retrieved through any of the methods available to the user. This is important, because Zope provides a full security framework when accessing objects, and avoiding this by manipulating content directly through external methods shows a complete lack of consideration for the security of a system. It's rather like knocking a hole in the wall of a house because you don't understand how to use the door.

For those interested in the full potential of Zope, and how to build robust network applications from the framework that it provides, I would suggest joining the Zope Users Mailing List on the Zope.org website, and investigating the content management system provided by Plone.

login or register to post comments

Zope blog products

Submitted by iber on July 16, 2004 - 05:24.

Very nice article! Greate list of the ready for use Zope blog products can be found on content management software info

login or register to post comments

The access keys for this page are: ALT (Control on a Mac) plus:

evolt.orgEvolt.org is an all-volunteer resource for web developers made up of a discussion list, a browser archive, and member-submitted articles. This article is the property of its author, please do not redistribute or use elsewhere without checking with the author.