Antville Project

Extensible Stories in Antville

Yesterday I checked in a patch to the xml_content branch of the CVS that changes how Antville stores Stories and Comments in the relational DB. Instead of storing titles and bodies of stories in a separate column each, everything is encoded into an XML document using Helma's native XML extension and stored into a generic "content" column.

This great advantage to this is that it is now possible to add custom properties to stories (and, if desired, comments) with almost no effort. The only thing one has to do is to add macros for the custom parts both in the story (or comment) editor skin and in the story (or comment) skins where that part is to be displayed. Antville still requires a text property to be there, and text and title are assumed by some internal parts like the recently modified list. Other than that, there is no limit on the number and names of custom story properties.

The macro used to edit or display story properties is called content and it is very similar to the old title and text macros (which are still supported for backward compatibility), except that it requires an attribute called part describing the name of the content part to be edited or displayed. For example, editing a story title with the content macro looks like this:

<% this.content part="title" as="editor" width="24" style="formTitle" %>

while displaying a custom property called "teaser" might look like this:

<% this.content part="teaser" %>

The content macro takes one extra attribute called fallback, which describes a content part which is to be used in case that the one specified by the part attribute is not set. Thus, the following code

<% this.content part="teaser" fallback="text" %>

would display the content part called "teaser" if it exists and the content part called "text" if not. If a part is not defined, nothing is displayed.

I must say that I'm myself astounded by the flexibilty, simplicity and usefulness of this concept. A lot of things suddenly become very easy. Most of these are beyond what we will likely ever want to do with Antville, but it's still worth to pick out a few: stories in multiple languages, partitioning a story into multiple parts or over multiple pages, versioning ... the list goes on.

Here's a quick look into how this is implemented. All in all, it was surprisingly simple because of Stefan's XML Extension. I'm using the native dumping/reading of Hopobjects, so all I have to do is create a transient Hopobject and add the content parts to it as properties. The Hopobject can be transformed into an XML string in one call of XML.writeToString(hopobject), and converted back from XML to Hopobject calling XML.readFromString(xmlstring). Basically, story content is managed via these three simple functions in story/objectFunctions.js:

/**
 *  Get a content part by name.
 */
function getContentPart (name) {
   var cnt = this.getContent();
   return cnt[name];
}

/**
 *  Return the content parsed into a HopObject.
 */
function getContent () {
  if (!this.content)
     return new HopObject ();
  return Xml.readFromString (this.content);
}

/**
 *  Set the content of this story object.
 */
function setContent (cnt) {
    this.content = Xml.writeToString (cnt);
    var raw = "";
    for (var i in cnt)
       raw += cnt[i]+" ";
    this.rawcontent = raw;
}

(The last section in setContent() is used for writing the raw content without XML markup into a separate column for searching. I'm still thinking about other ways to search over story content. Just as a note, it would technically be possible to search for content in specific content parts using something like "%<partname>%searchterm%</partname>%".)

The XML that's written to the DB looks something like this:

<?xml version="1.0" encoding="UTF-8"?>
<!-- printed by helma object publisher     -->
<!-- created Wed Jun 12 18:52:09 CEST 2002 -->
<xmlroot xmlns:hop="http://www.helma.org/docs/gui
de/features/database"> <hopobject id="t15" name="" prototype="hopobject" created="1023900729721" lastModified="1023900729722"> <text>some text</text> <title>hello world!</title> </hopobject> </xmlroot>

I hope this gave you a good impression of what this feature is about and how it works. I'm curious to hear what you think.

comment    

 
kris, June 13, 2002 at 4:53:50 PM CEST

genius!

link  


... comment
 
chris, June 13, 2002 at 5:09:01 PM CEST

simple

elegant and très très useful. that's how it is supposed to be ;-)

link  


... comment
 
earl, June 13, 2002 at 7:07:38 PM CEST

gefaellt!

link  

 
earl, June 13, 2002 at 7:15:50 PM CEST

xml frage

[the following assumes that the xml sample matches the xml in production use]

the namespace declaration (xmlns:hop='...') only associates the 'hop' prefix with the given namespace.

as a result, none of the xml elements used in the document are bound to the namespace. if that behaves as intented -> skip this comment ;)

if you originally intended a default namespace bind of xmlroot and all subelements, just junk the prefix and write xmlns='...' instead. as a result xmlroot and all subelements are associated with the provided namespace.

as a semtical equivalent you could prefix all xml elements (tags and attributes) with 'hop:' - but the overall look would be kinda messed up ;)

link  

 
hns, June 13, 2002 at 7:30:21 PM CEST

yes, that's intentional

the hop namespace is used to distinguish special elements like child object collections from ordinary HopObject properties, which are out in the open, i.e. don't have a namespace. It's right that the namespace is never used in this application, since all we ever have here is a single object with string properties. Stefan Pollach can tell us more about this (e.g. where exactly the namespace is used), if he happens to come by here.

link  

 
stefanp, June 13, 2002 at 8:16:31 PM CEST

yes it is intentional. the namespace-prefix is only used for children and parents to avoid clashing children/parents with properties (as hannes wrote). children/parents are written as elements like <hop:child id=...> or <hop:parent id=...>. the default namespace-binding would only be useful if the above xml-code ever appeared in another context - is that needed?

very cool thing, hannes, btw!

link  

 
robert, June 14, 2002 at 11:59:50 AM CEST

just thinking loud

what about storing the xml-representation of the whole text-object in the database (not just the content as it's now)? of course this would mean bigger records in the database, but i think this would (for example) also ease the export of a site's contents.

and yes: this is really a milestone!

link  

 
hns, June 14, 2002 at 1:35:36 PM CEST

not sure what you mean

by "whole text object". Everything including dates, topic, author, etc? I don't see any good reason to do that. You'd have to parse the XML to make sense of the data (and find relations to other tables), and translation from Hopobjects to XML and back are very fast (I think 1000 or 10000 XML-> Hopobject parsings took something like 2 seconds on my PC).

link  

 
robert, June 15, 2002 at 1:30:03 PM CEST

yes, i meant the complete hopobject including dates, topic ... but forget it, really doesn't help much, and it doesnt justify the db-overhead.

link  


... comment
 
tobi, June 15, 2002 at 2:33:59 PM CEST

fallback?

i don't like the "fallback" parameter in the macro. how about using "default" instead?

link  

 
hns, June 15, 2002 at 10:11:54 PM CEST

Hi Tobi, glad to see you're back. The reason why I used "fallback" over "default" is twofold. One, there's a feature request to make "default" an predefined Helma macro parameter, which makes it look unwise to use it as an application level parameter with a different meaning. Two, in the semantic circus of my mind, default would fit where a premeditated value is passed to the macro in case it can't produce a value itself, but what we do here is pass an alternative recipe to come up with a value. To put it more simply, "default" denotes something that would be used ordinarily, when in fact in this case it would not ordinarily be used, but only in the exceptional case where the primary content part is not defined. To me, "fallback" seemed to convey this the best.

link  

 
tobi, July 10, 2002 at 11:51:25 AM CEST

because i am just starting to explore this kind of extending stories in antville, i came across this naming convention. thank you for the explanation but i don't see the distinction.

i still think that "fallback" is a technical term and actually you described it as i would describe "default", too.

the feature request for a "default" parameter imho exactly describes the same circumstance as the "fallback" parameter at least from a non-technical point of view – and that's what macros were created for, weren't they?

link  


... comment


The Antville Server Fund has been a great success. Thanks to everybody who contributed!
online for 8334 Days
last updated: 1/4/11, 10:22 AM
status
Youre not logged in ... Login
menu
April 2024
SunMonTueWedThuFriSat
123456
78910111213
14151617181920
21222324252627
282930
July
recent
zfuture's house here is zfuture's
house
by zfuture (7/31/03, 2:59 AM)
i understand your concerns however,
i hardly can think of a solution. certainly, if the...
by tobi (7/29/03, 9:47 AM)
Found several more similar sites
listed This is getting to be quite a concern to...
by cobalt123 (7/27/03, 7:56 PM)
Second Post Alert on Referrer
bug livecatz I put this into "help" and now here:...
by cobalt123 (7/26/03, 7:14 PM)
well it's not easy to
find from here, anyway. think we should include a link,...
by tobi (7/24/03, 11:25 AM)
So finally I found
the helma Bugzilla - stupid me.
by mdornseif (7/24/03, 10:28 AM)
clock not that it's particularly
earthshattering but the antclock is running slow by about 15...
by kohlehydrat (7/23/03, 8:25 PM)
but blogosphere.us isn't can't really
be rated as spam can it?
by kohlehydrat (7/23/03, 8:08 PM)
More referrer spam www.webfrost.com
by Irene (7/23/03, 7:55 PM)
How to log skin names
I accessed to console?? Hi, I would like to know...
by winson (7/23/03, 4:12 PM)

Click here to get an XML version of this weblog.

Made with Antville
powered by
Helma Object Publisher