This section describes some special effect recipes developed while working on the Olympics website.
The SportPage webapp directory contains some very skeletal example templates that demonstrate some of the features of the system.
Example 3. Sample Layout Template
At the very basic level, a website consists of a default.layout and default.page templates where the layout sets up the design of the browser window and includes the page at a suitable point:
<html> <head><title>Hello World</title></head> <body> #parse($page) </body> </html>
You could, of course, define all sorts of contant strings in the WEB-INF/sportpage.conf to set filenames, banner images or whatever you like to fill out that default layout. The only essential detail is the call to parse the $page.
Ok, that sets up the stunning graphic design of our site, now we need some content:
Example 4. Sample Page Template
The simplest page would be static, but to make it just a little interesting, we want to take the path information and use that to fetch another file to insert. The caveat here is that the URI is human-oriented; it has no extension, and there are several extensions listed in the defaults. The page template needs to handle the implied extension, and also must handle a missing page instance. To do this, SportPage includes a library macro method #insert( path ) which will test that the path exists and insert the error page if it does not:
## handle missing page inserts #macro (insert $path) #set ($p = false) #set ($p = $path.ext) #if ($p) #parse($p) #else #parse($missingpage) #end #end
This macro simplifies the page template to become:
<table> <tr><th>Hello Page</th></tr> <tr><td> #insert($path) </td></tr> </table>
In the macro, the PathBean .ext method will identify any file matching either the path as given, or some variation across all the template paths and extensions.
That's it, our first SportPage website!
The CBC 2002 Winter Olympics is arranged in a topic hierarchy as shown in Figure 1. In this site, the default page handler provides only a switch between index and document page templates. Exceptions to this are the /olympics/athletes area and the /news topic. Athlete URI paths actually specify an XML document (the athlete bio) and the template page is replaced (using a dot-variable) with a form page that fetches and reports the path as a JDOM object. The news path is used as the target of the headline links and uses the dot-variable to specifie a news item “fullstory” template where the specific story key is identified by the path; the URI /olympics/news/kelly001 pulls the story kelly001 from the Newsworld database.
Adding CBC Newsworld news items provides an example of implementing arbitrary java objects. In this situation, the Newsworld classes provide methods to access the key strings for news headline items.
Example 5. Adding Newsworld's Lineup
The following describes creating new story pages using the $path to get at the story key, and also formats main page headlines to make links relative to our own webapp /olympics/news.
The first necessary step is to add the Lineup object to the system through a dot-variable. Because we need to cache lineups on a per-category basis, we will want to define a category string (and possibly others within other topic contexts) and we need to create a wrapper class that will be able to store these cached items in the system $global properties; this is the function of the SportWire TemplateLineup class. The categories and this bean class are added to the sportpage.conf:
############################################## # Install the CBC presentation.Template lineups default.lineup.category=olympics hockey.lineup.category=olympichockey default.news.bean=ca.cbc.sportwire.servlet.data.TemplateLineup
We are free to make our own URIs because Brian provided an alternate getSlug() method that returns only the story key string slug:
insert the following into the home page template to render the headlines:
#foreach( $story in $news.lineup.getStories(1,3) ) <span class="HEADLINE">$story.headline</font> #if( $story.hasTopStoryImage() ) <img src="$story.topStoryImage" height="100" width="100" align="right"> #end <p class="LEADERS"> <span class="ABSTRACT">$story.abstract</span> <span class="ABSTRACTLINK"> <a href="/olympics/news/$story.slug"> <nobr>full story</nobr></a> </span> </p> <br clear="all"> #end <table class="HEADLINES" width="100%"> <tr> <td class="HEADLINES" colspan="2"> <span class="HEADLINEBANNER">H E A D L I N E S</span> </td> <tr> <td class="HEADLINES"> <ul> #foreach( $story in $news.lineup.getStories(4,10) ) <li><a href="/olympics/news/$story.slug"> $story.headline</a></li> #end </ul> </td> </tr> </table>
This much makes the lineup appear according to our own rules and are linked to our own full-story URI.
define the fullstory template in WEB-INF/sportpage.conf and add the StoryLoader object such that it's only available in the /news/ topic context:
news.page=/default/fullstory.vm news.db.bean=cbc.template.story.StoryLoader
The template fullstory.vm must fetch the Story object; this is done using StoryLoader and trimming the /news/ from the $path. Keep in mind this example alters the $path object, so you can't use it further down the page. $base should get set to news.
#set ($base=$path.remove(0)) ## trim path to /olympics/story99 #set ($slug = $path) #if ($slug == "") ## prevent accidental visits $res.sendRedirect("/olympics") #end #set ($story = $db.load($slug))
then follow with your own rendering of the $story object:
<span class="HEADER">$story.headline</span> <br> <span class="SUBHEADER">Last Updated: $story.lastUpdated</span> <br> <span class="SUBHEADER">$story.subheadline</span> <p>$story.dateline - $story.getParagraph(1)</p> ...
Incorporating the Newsworld weather forecast Java bean provides an example of translating JSP style to Sportpage. The original JSP code would read something like this:
<jsp:useBean id="weather" scope="page" class="cbc.beans.ShortWeatherBean"/> <jsp:setProperty name="weather" property="query" value="Toronto,ON"/> <%=weather.getWeather()%>
The SportPage equivalent would use the $javabeans utility to create the weather object, then use access methods to fetch the weather forecast:
#set ($forecast = $javabeans.getBean("cbc.beans.ShortWeatherBean")); $forecast.setQuery("Salt Lake,UT") ... <p>Weather for Salt Lake City: $forecast.weather</p>
Of course, if the bean doesn't cache it's results, it's going to run like arctic molasses.