Is this a Blog I see before me?
title
Zope property can do this nicely 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. 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.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. |
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...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).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"> Updated <dtml-var bobobase_modification_time fmt="%d %B %Y"> </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.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"> Updated <dtml-var bobobase_modification_time fmt="%d %B %Y"> </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>
<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"> Updated <dtml-var bobobase_modification_time fmt="%d %B %Y"> </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...
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.