UML to Portal in a Few Easy Steps
This is a painless example that will help demonstrate just how fast we can get a new web application running in the EDRN Public Portal, or with any Plone instance.
Set It Up
Grab these packages and install on your desktop system:
- Plone 2.1.1 - see http://plone.org/products/plone/releases/2.1.1
- ArchGenXML 1.4.0-b2 - see http://sourceforge.net/project/showfiles.php?group_id=75272&package_id=103241
- ArgoUML 0.18.1 - see http://argouml.tigris.org/
Set up Plone as per instructions (Mac OS X users and Windows users have it especially easy). The home directory of your personal Plone site we'll call INSTANCE_HOME and you can identify it from the subdirectories that are in it: Extensions, Products, bin, etc, etc. The PORTAL_URL we'll call the URL of your Portal. Depending on your installer and your options, it could be http://localhost:8200 or http://localhost:8080/portal or some such.
Set up ArchGenXML by untarring it into a convenient directory. We'll call that ARCHGEN_HOME.
Set up ArgoUML by untarring it. We'll call that ARGOUML_HOME.
Create a Class
The role of a portal is to hold content. And content comes in various flavors, each with various schema, searchable fields, controlled vocabularies for fields, relationships, logic, and so on. By composition, aggregation, specializtion, and workflow you can define an entire web application through standard object-oriented modeling. But content comes first.
We'll create a simple content class as a demonstration. Fire up ArgoUML:
java -jar argouml.jar
(On some platforms, double-clicking argouml.jar will do the same thing.)
Click the button in the toolbar to create a new class. Then paint the class in the work area:

The red squiggle line is saying you should fill in a name for the class. So double-click on that area and type "Person". What we'll do is create a simple web application for capturing JPL employee details. Here's what we have now:

The little note icons are ArgoUML's way of providing hints about what you should do. Ignore them. What we need to do now is add tagged values to this class. Tagged values are simple keyword/value pairs that the code generator uses. Click the Tagged Values button near the bottom. It looks like this:

There are no tagged values to start, so start entering them. They're all just text. If you make a mistake you can delete a tagged value by selecting it and clicking trash can icon. We'll need five values, as shown:

Here's what these tags mean:
| Tag | Meaning |
|---|---|
| archetype_name | Label to use on the portal when people create these objects |
| allow_discussion | Can users add comments to these objects |
| content_icon | What icon to use for class and objects of it |
| label | Same as archetype_name; no idea why there's both |
| typeDescription | Full description shown when creating new objects |
(By the way, here's a person.gif icon,
, just save it locally for now.)
Now we'll add some attributes to this class. Double-click in the attributes section of the class (the middle section) and type
badgeNumber: int
Then press Return after typing that and you've got a new attribute (you can also create attributes by clicking
).
Click on the attribute in the class diagram to select it, then click Tagged Values. You'll notice that ArgoUML added two tagged values automatically:

Silly ArgoUML, we're not using Java! You can leave those attributes in there, because the code generator ArchGenXML will ignore them. Or delete them now; it's up to you.
Enter tagged values as shown:

Here's what these tags mean:
| Tag | Meaning |
|---|---|
| default | Default value if none is supplied |
| required | Is this field mandatory? |
| searchable | Index this field for quick searching? |
| widget:label | What text to show on the portal as a label for this field |
| widget:description | Help text |
Let's add one more field, emailAddr. Click the new attribute button or type it in the attribute area of the class:
emailAddr: String
And click Tagged Values again and set up these tags:

The new tag here is validators, and its value is a Python sequence of named validators to run on values for this attribute. In this case, we're naming a list of just one validator called isEmail which checks to make sure what looks like an email address is entered.
Now, how about a person's name? By default content comes with several built-in attributes:
- The ID, also known as the "short name," appears in web URLs to the content
- The Title; this is the official name that identifies the content.
- Dublin Core metadata; this is metadata based on the Dublin Core Metadata Initiative like Subject, Creator, Rights, etc. (except that Title is listed separately).
For a Person, we can use the Title as the name. But we don't want the word "Title" appearing in the user interface. Instead, we'd like "Full Name". To do that, we need to override the base schema that all content comes with a custom schema. The custom schema will be identical to the base schema, except that it changes the label from "Title" to "Full Name".
To change the schema for a class, click the class, click Tagged Values, and add a new tagged value. The tag is base_schema and the value based on the file "schema.txt". I link it rather than include it here because it's one huge long line!
That statement in schema.txt was taken from INSTANCE_HOME/Products/Archetypes/BaseObject.py line 93, and all we changed was the label from Title to Full Name. You can cut/paste that into ArgoUML. Finally, the tagged values should look something like this:

Generate the Product
We're now ready to generate a Product from our UML model. Save it, and then from the Tools menu choose "Export as XMI...". XMI means XML Metadata Interchange, and it's an interoperable XML vocabulary for exchanging metadata, like the UML model we just made. Save an XMI version of the model as "people.xmi".
From the model, we run a code generator called ArchGenXML. ArchGenXML takes an XMI model and generates Python classes and other items necessary to model the content type in a Plone web site. This is the huge time-saver we'll need to create the EDRN Knowledge Environment in the EDRN Public Portal.
ArchGenXML generates a Product. A Product is a directory that contains all the code necessary to provide some feature in Zope (which is the system on which Plone is built).
To generate our Product, run it as follows:
ARCHGEN_HOME/ArchGenXML.py people.xmi INSTANCE_HOME/Products/People
This will create (or update, if it already exists) the People directory in your Products directory.
You might get a few warnings from ArchGenXML. You can probably ignore them all. Warnings I usually see are:
WARNING Can't build i18n message catalog. Module 'i18ndude' not found.
WARNING Can't strip html from doc-strings. Module 'stripogram' not found.
One other thing you can do now is copy the person.gif icon to the INSTANCE_HOME/Products/People/skins/People directory.
Install the Product
If you're running Plone (Zope) in debug mode, it should already recognize a new product directory. Otherwise, stop and restart Plone (Zope). Then visit your Plone site, login, head over to your control panel (click "site setup" in the upper right corner), and click Add/Remove Products.
Check the box for "People" and click Install:

If it works, the product will move to the installed section. If not, you'll be able to click a link to view error messages. (If you run in debug mode, chances are Zope won't even start up if there's an error in the product.)
Once it's installed, you can try to create objects.
Creating a Person
With your People product installed, go to your folder (click "my folder" in the blue personal bar) and from the "add item" menu, choose "person", and you'll get:

And try it out! Leave out the name, or enter non-numbers for the badge number, and you'll be presented with friendly error messages. You can create all sorts of Person objects. And once you've created some, try doing a Live Search in the search box, and you'll see they show up instantly.
Pretty good for just painting a diagram and filling in a few fields? ^_^
Relationships Between Classes
OK, so modeling a single entity makes for a pretty simplistic web application, so let's get complicated. Return to ArgoUML and click the class button and paint a new class.
Call the new class Address and give the class the following tagged values:
| Tag | Value |
|---|---|
| archetype_name | Address |
| allow_discussion | 1 |
| content_icon | address.gif |
| label | Address |
| typeDescription | An address is a physical location or a postal mail destination. |
Here's the address.gif icon,
, which you should download to your INSTANCE_HOME/Products/People/skins/People directory.
Then add five fields to this class: line1, line2, city, state, and zip.
line1
line1 should be a String with the following tagged values:
| Tag | Value |
|---|---|
| required | 1 |
| searchable | 1 |
| widget:description | Line one of the street address usually contains the house number and street, or a PO box. |
| widget:label | Line 1 |
line2
line2 should also be a String; here are its tagged values:
| Tag | Value |
|---|---|
| required | 0 |
| searchable | 1 |
| widget:description | Line two may contain suite number, apartment number, etc. |
| widget:label | Line 2 |
While line1 is required, we've made line2 optional.
city
city should be a String with these tagged values:
| Tag | Value |
|---|---|
| required | 1 |
| searchable | 1 |
| widget:description | Name of the city, town, locale, principality, etc. |
| widget:label | City |
state
state will be a String but with a few new tagged values, so pay attention:
| Tag | Value |
|---|---|
| enforceVocabulary | 1 |
| required | 1 |
| searchable | 1 |
| multiValued | 0 |
| vocabulary | ['AL', 'AK', 'AS', 'AZ', 'AR', 'CA', 'CO', 'CT', ...] |
| widget | SelectionWidget |
| widget:description | Select the US state code. |
| widget:label | State |
Here, we add an attribute that can have only one of discrete values. The enforceVocabulary means that the application does not allow any non-listed values to be entered. If we set that to 0, then the user could both select existing states, or come up with brand new ones.
The multiValued set to 0 means that one and only one value may be selected. (If set it to 1, then the user could select multiple states.) Combined with required = 1, the user must select exactly one state.
The vocabulary is set to a Python list of strings. Each string is a state code. This makes it easy to enforce a specific vocabulary: what gets shown on screen is also what gets stored in the object. However, sometimes you want a different value stored in the object than you want shown on screen. For example, suppose you wanted the state field to hold the two-letter state code, but you wanted to show full state names on the screen. To do that, simply change the vocabulary from a Python list of strings to a DisplayList object:
DisplayList((('AL', 'Alabama'), ('AK', 'Alaska'),
('AS', 'American Samoa'), ('AZ', 'Arizona'),
('AR', 'Arkansas'), ('CA', 'California') ... ))
Finally, the widget tag tells the system to not use just a plain text field, but a widget that lets the user pick and choose values. It could be a drop-down menu or a set of radio buttons; the system will pick something appropriate.
zip
The zip field is an int field, but this time we include a special validator that checks for a zip code:
| Tag | Value |
|---|---|
| required | 1 |
| searchable | 1 |
| validators | ['isZipCode'] |
| widget:description | Enter the ZIP or ZIP+4 code but with no dashes |
| widget:label | ZIP Code |
| widget:size | 10 |
The isZipCode is a validator that looks for 5 or 9 digits. The other new tag here is widget:size which tells the system to make the text box for the ZIP code 10 characters wide.
Getting Complicated
OK, we've got a second class. But that's not fancy enough for me. ^_^ Let's do some specialization, composition, and referencing.
Specializing
Click the class tool and paint a new class under the Address class. Call it BusinessAddress. For tagged values, give it:
| Tag | Value |
|---|---|
| archetype_name | Business Address |
| allow_discussion | 1 |
| content_icon | business.gif |
| label | Business Address |
| typeDescription | A business address includes optional mail stop and suite number |
Here's an icon for it:
As usual, save a copy of it to your INSTANCE_HOME/Products/People/skins/People directory.
Now, give it one attribute called buildingNumber of type int with these tagged values:
| Tag | Value |
|---|---|
| required | 0 |
| searchable | 1 |
| widget:description | Locale-specific building number of a group of buildings. |
| widget:label | Building Number |
Now, click the New Generalization tool,
, and paint a generalization arrow from BusinessAddress to Address. So, a BusinessAddress is an Address but also includes an optional building number. If you exported the model and generated the code, you could create both regular Address objects and BusinessAddress objects. The form for the latter would have an additional field.
Composing
An object can contain other objects. In object-oriented modeling, there's two kinds of containment:
- Aggregation. Aggregation means that one object merely contains other objects, and the lifetimes of those other objects are not necessarily the same as the containing object. In UML you use an arrow with an open diamond base. In ArchGenXML, that generates a relationship such that one class can contain instances of other classes, but those other classes may also exist elsewhere in the object database.
- Composition. Composition means that one object is composed of other objects, and the composed objects never exist without their containing object. If the containing object disappears, so do all the contained objecs. In UML ou use an arrow with a solid diamond base. In ArchGenXML, that generates a relationship such that one class can contain instances of other classes, but those other classes may not exist anywhere else, only in the contained class.
Let's try aggregation. Grab the class tool and paint a new class called JPLBuildings. Tagged values for this are:
| Tag | Value |
|---|---|
| archetype_name | JPL Buildings |
| allow_discussion | 1 |
| label | JPL Buildings |
| typeDescription | A collection of business addresses that identify buildings at JPL |
Give this class no attributes. Then, click the unidirectional aggregation tool, and paint an arrow from JPL Buildings to Business Address. In the tool palette, that's the right column, middle row (open diamond on one end and an arrow on the other):

Then, select the arrow, and under the "Connections" list at the bottom, double-click the second "(anon AssociationEnd)". Then, over on the "Multiplicity" selector choose 0..*. This says that a JPLBuildings object can be composed of any number of BusinessAddress objects.
Referencing
Often, an object may need to reference another object, but not need to contain that other object. In this example, the relationship between Person and Address is just that: a person can possess multiple addresses for different purposes, and other people can possess some of those same addresses.
To do referencing, use association arrows. In the tool palette, it's the right column, top row. Select the tool, then paint an arrow from Person to Address. Then, select the arrow on the diagram and under "Connections", double-click the second "(anon AssociationEnd)".
Change the Name to "Home Address" and the Multiplicity to 0..*. (After all, a person could be homeless, or have both winter and summer homes.)
Using that same tool, again paint an arrow from Person to Address. And again, select he arrow on the diagram and double-click the second "(anon AssociationEnd)". Change the Name of this one to "Work Address" and the Multiplicity to 0..*. (After all, a person could be jobless, work from home, or have multiple jobs.)
Your final class diagram should look like this:

Putting it All Together
Again, save your model, and then export it as XMI again. Run ArchGenXML.py on the XMI as before. Then restart Plone, and reinstall the product, and head over to "My Folder."
(The Archetypes tool is supposed to automatically update objects whose schema have changed as a result of your twiddling with models. But if for some reason that doesn't happen, there's a migration tool in the archetype_tool in the Zope Management Interface.)
In your folder, you can create separate JPLBuildings objects to represent the main campus and Woodbury. You can create multiple BusinessAddress objects representing various buildings there. You can create other Address objects representing people's houses. And you can create Person objects and associate them with Address objects and BusnessAddress objects. And you can do a Live Search on the whole lot to quickly find what you need.
Objects you create this way go into the Zope Object Database, a fast, transaction database that obviates the need for old-fashioned SQL-based databases or other baroque paraphernalia. Full text catalogs, as you've seen, are automatically built, and other kinds of indexes are supported too, like dates and numeric ranges. Basically, this system rocks!
What Else?
- Get a copy of The Definitive Guide to Plone by Andy McKay. It's a hard book to read. ;_; McKay's writing is not that great, and he fails to motiviate many of the examples. But it is a very complete book! ^_^ Chapter 13 in particular will tell you all of the attribute types, widget types, and other juicy details.
- We covered adding attributes to classes, but what about methods? Yes, you can do that too! That's your homework assignment. ^_^
- What are all the tagged values you can use? See http://plone.org/documentation/tutorial/archgenxml-getting-started/tagged-value-overview
- In fact, you could read this for more details too: http://plone.org/documentation/tutorial/archgenxml-getting-started/

