ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


Apache's eXtended Server Side Includes

by Kostas Pentikousis
07/07/2005

Server-side includes allow web developers to reuse content across an entire site employing a simple but powerful structure. This tutorial illustrates how to use Apache eXtended Server Side Includes (XSSI) to create composite web sites and moderately complex, refined web applications without having to resort to more intricate frameworks. After introducing the basics, it shows several techniques that will give you an idea of what you can achieve with XSSI, but I will not address configuration issues. (For some pointers, see the section titled XSSI Resources.) XSSI is by no means a new web technology: server-side includes came about more than a decade ago. Nevertheless, XSSI is a workhorse and can prove indispensable while developing and maintaining dozen of sites.

Apache XSSI Advantages

Technically speaking, XSSI elements are instructions embedded into web documents. Traditionally this meant plain, standard (X)HTML pages; however, adding XSSI to any text-based document is straightforward. While Apache httpd serves static pages without any processing, the server must parse XSSI pages and then, in turn, execute the XSSI instructions and transmit the final result. Although this is similar to what other dynamic content-generation technologies use, many people consider XSSI a lackluster framework. Many web developers resort to more sophisticated technologies--dynamic content management systems such as PHP-Nuke on the one hand and web site development and delivery engines including Mason, Cold Fusion, and Lotus Notes/Domino on the other--and discount XSSI as "simplistic" and "limited." This is, to some extent, true. For example, XSSI does not allow you to connect to databases or perform mathematical operations. Nevertheless, this is by no means the whole story. XSSI has many features, including pattern matching. Many web sites work just fine without a database back end, especially if they use the database simply to store documents. In fact, you can even build a light content management system using XSSI.

XSSI has several advantages over other server-side technologies:

Easy to learn
A small learning curve makes XSSI accessible to people with limited programming experience. XSSI is programmer-efficient because it requires little if any debugging.
Mature
Well-tested code and a stable API make future surprises unlikely.
Lightweight
XSSI can deliver popular features such as printer-friendly versions, article partitioning, and hierarchical menus without client-side scripting or a database back end. Maintenance becomes easier--and cheaper.
Efficient
Contrary to early-day warnings that XSSI would bog down a web server, XSSI is probably the most resource-efficient of all dynamic technologies. For example, according to the Apache Hello World Benchmarks (see XSSI Resources), mod_include, the base module implementing XSSI in Apache, can serve more Hello World pages per second than other dynamic content-generation technologies, including PHP, Perl (ASP, Embperl, mod_perl, and CGI.pm), JSP, Mason, HTML::Template, and the Template Toolkit. The tests showed that there are only two ways you can deliver a Hello World page faster than with XSSI: transmitting a static page or using a custom-made, dedicated Hello World Apache module written in C.
Secure
In particular, if you do not include the output of arbitrary web server executable programs.
Ubiquitous
Present in virtually all Apache installations, because its implementation is a base module.
Complementary
Combined with web site templates, XSSI can decrease maintenance time and increase sitewide consistency. Furthermore, if necessary it is easy to integrate with client-side technologies, including cascaded style sheets, JavaScript, and other dynamic objects such as Flash and Java applets.
Priceless
As it's part of the Apache open source code, you can always enhance it if you need to.
Forward-looking
It can integrate with any text-based document, including XML and RSS.

XSSI Variables

XSSI has a small but powerful set of instructions that you can pick up in just a few hours. For example, to set the value of the variable who to the string World, use

<!--#set var="who" value="World" -->

After initializing who, you can recall its value anywhere in a page. For example, to display its value, use the echo instruction:

<!--#echo var="who"-->

XSSI variables are of a single type only: character strings. There are no integers, floats, and the like. Although you can assign a number to a variable, Apache XSSI does not handle it any differently than a letter-only string. Clearly, you cannot use XSSI to develop an online mortgage calculator. Listing 1 shows the Apache XSSI version of a Hello World page, which displays the value of who twice: once in the document <title> and later in the main body, <h1>.

Listing 1. Variable assignment and substitution

<html>
<head>
<!--#set var="who" value="World" -->
<title>Hello <!--#echo var="who" -->!</title>
</head>
<body>
<h1>Hello <!--#echo var="who" -->!</h1>
</body>
</html>

Besides programmer-defined variables, Apache makes more than 30 environment variables accessible through your code (see XSSI Resources) and gives you access to values passed as arguments to your XSSI code. Effectively, you can customize presentation and content based on a wide variety of parameters: browser make and version, user location or network domain, time of day, and user-specified information. You can always get a listing of the environment variables by embedding the printenv instruction in any (X)HTML document:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<head>
<title>The Apache Environment Variables</title>
</head>
<body>
<pre>
<!--#printenv -->
</pre>
</body>
</html>

Content Inclusion

The most common use of XSSI is file inclusion. Professionally designed web sites feature navigational aids, copyright notices, disclaimers, and other items that appear on every page. With XSSI you can store this content once and use it from any page on the site. This allows you to incorporate future updates and modifications virtually instantaneously. Listing 2 shows how to add a menu on the top of the Hello World page.

Listing 2. File inclusion

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<head>
<!--#set var="who" value="World" -->
<title>Hello <!--#echo var="who" -->!</title>
</head>
<body>
<!--#include file="top-menu.html"-->

<h1>Hello <!--#echo var="who" -->!</h1>

</body>
</html>

When a user requests the document shown in Listing 2, Apache will replace the file inclusion instruction (in bold) with the contents of the file top-menu.html (Listing 3), located in the same directory.

Listing 3. (X)HTML fragment used as an included menu

<p>Hello:  <a href="hello-world.shtml">World</a> - Africa -  
   Antarctica - America -Asia - Europe - Oceania</p>

Included files typically contain (X)HTML fragments only, not complete documents: reusable, common content, such as a navigational menu, makes a good (X)HTML fragment to store once and include throughout the site. When Apache receives a request for the document in Listing 2, it will parse Listing 2; process and execute the XSSI instructions (one variable assignment, two variable substitutions, and one file inclusion), effectively stitching together Listing 2 and Listing 3; and, finally, send the final output shown in Listing 4, which is a valid (X)HTML document, to the browser for display (Figure 1).

Listing 4. After Listing 2 and Listing 3 are processed, this is the final output sent to the requesting browser

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<head>
<title>Hello World!</title>
</head>
<body>

<p>Hello: <a href="hello-world.shtml">World</a> -Africa - 
   Antarctica - America - Asia - Europe - Oceania</p>

<h1>Hello World!</h1>
</body>
</html>

Hello World! in the
browser
Figure 1. "Hello World!" in the browser

Experienced web site authors may have noticed that XSSI syntax looks as if it is enclosed in (X)HTML comments. This is not an accident: XSSI elements are actually formatted just like SGML comments, which is a big advantage. First, if due to some misconfiguration the unparsed document is sent to the browser, it will not display the directives. Second, this format works well with both plain-text editors and WYSIWYG web authoring software. Finally, it means that documents written in any markup language stemming from SGML, including XML, can incorporate XSSI naturally.

Staying Up to Date

A standard element in most sites is the copyright notice, usually placed at the bottom of every page. Copyright notices comprise the copyright year and holder, and some statement regarding the retention or availability of the rights; for example, "All rights reserved." You can easily add such a "static" copyright notice in a fashion similar to that in Listing 2. However, web pages often evolve over time, with updates and modifications, and may include parts copyrighted in different years. Keeping track of which pages are copyrighted and when can be a maintenance nightmare. This is partly the reason that it is common to see copyrights that span from the first year the site went live to the current year, irrespective of when a single page's most recent update actually occurred. Listing 5 presents how XSSI can help you dynamically create such a copyright notice.

Listing 5. Sitewide copyright notice

<!--#config timefmt="%Y" -->
<p>©2003-<!--#echo var="DATE_LOCAL" --> 
   Kostas Pentikousis. All rights reserved.</p>

This approach has two steps. First, it must figure out what is the current year. The current date and time is always available through the environment variable DATE_LOCAL. However, the page should display only the current year, so it needs to select the appropriate time format. The instruction

<!--#config timefmt="%Y" -->

causes the display of only part of the date/time variables, such as DATE_LOCAL: the four-digit year (%Y). After this configuration, the next step is to display the current time using the environment variable DATE_LOCAL. If you decide that the copyright notice should not span to the current year but, instead, until the year in which the page had its last update, replace DATE_LOCAL with LAST_MODIFIED. Having already specified the time format, echo will display the current year only. This time format will be in effect for the remainder of the document, unless reconfigured. You can assign timefmt any string, which may include special time-conversion characters. Table 1 presents a partial listing of available conversion characters.

Table 1. Commonly used timefmt conversion characters

Conversion character Replaced by
%A or %a Locale's full or abbreviated weekday name
%e Day of the month: decimal number without a leading zero
%B or %b Locale's full or abbreviated month name
%Y Locale's 4-digit year

For example,

<!--#config timefmt="Today is %B %e, %Y." -->
<p><!--#echo var="DATE_LOCAL"--></p>

will produce

<p>Today is March 13, 2005</p>

Your options are limited only by the conversion characters and formats supported by the strftime() function available on your system. Finally, remember that there is more than one way to do it: Listing 6 presents a different implementation of the copyright notice, which produces the same output as that of Listing 5.

Listing 6. Sitewide copyright notice redux

<!--#config timefmt="2003-%Y" --> <p>©<!--#echo var="DATE_LOCAL" --> 
   Kostas Pentikousis. All rights reserved.</p>

Any file can include others, which in turn can contain more file inclusions and other dynamically created content. Listing 7 illustrates this by including the file in Listing 5 (in bold), exactly as shown earlier. This recursion is a very powerful XSSI feature not available in many competitive technologies. The rest of this article takes advantage of this feature to dynamically generate multiple versions of a document and create hierarchical menus with XSSI.

Listing 7. Inclusion of static (top-menu.html) and dynamic (copy.shtml) content

<html>
<head>
<!--#set var="who" value="World" -->
<title>Hello <!--#echo var="who" -->!</title>
</head>
<body>
<!--#include file="top-menu.html"-->

<h1>Hello <!--#echo var="who" -->!</h1>

<!--#include file="copy.shtml"-->
</body>
</html>

Printer-Friendly Versions Made Easy

Many sites offer each article in two different versions. One is heavy on graphics, pictures, navigational aids, possibly ads, and may partition each article to several parts or pages. The other version is simpler, often dubbed "printer friendly," and presents the same content in a single part but without most of the ads and navigational aids. Ideally you should be able to store each article in one document and allow for many different views or presentation formats. Note that you cannot really achieve this with, say, alternative style sheets. The alternative versions do include the same text, which could have been formatted using alternative style sheets, but they also include a variety of ancillary elements whose appearance style sheets alone cannot control. By making good use of cascaded style sheets and XSSI, it's possible to achieve this without the need for database lookups, content transformations, or storing the same content twice.

First, there must be a way to differentiate between the different versions a user may be requesting. For example, the user should be able to request the media-rich version with the URL

http://www.example.org/hello-world.shtml

and the printer-friendly version by asking for

http://www.example.org/hello-world.shtml?print

The trick is to scan the requested URL for the string print and set a variable to indicate which format the user has requested. The environment variable QUERY_STRING proves very useful in this case. Its value is the string past the ? up to the end of the URL, i.e. exactly where the code needs to focus its attention. In the first case, when the media-rich version is requested, QUERY_STRING is empty (""). In the second case, it contains the string print. Listing 8 uses an XSSI if else statement to check whether QUERY_STRING contains the string print.

Listing 8. Use of a conditional statement to determine whether the printer-friendly version is requested

<!--#if expr="$QUERY_STRING = /print/" -->
<!--#set var="view" value="print" -->
<!--#else -->
<!--#set var="view" value="fancy" -->
<!--#endif -->

Seasoned programmers may find the if else syntax slightly verbose. Remember, however, that XSSI directives should always be formatted as valid SGML comments. The condition fits in the expr argument inside double quotes ("").

So far, this has shown only how to display variables using the echo directive, which expects the variable name as its argument. On the other hand, the conditional statement (if) operates with the value of a variable. The code in Listing 8 accesses the value stored in QUERY_STRING by prepending the dollar sign ($) to the variable name (QUERY_STRING). If the pattern match succeeds, then view is set to print; otherwise it is set to fancy. Listing 9 includes the code shown in Listing 8 and uses the view variable to direct the browser to load the appropriate style sheet, named either print.css or fancy.css, before displaying the page. This highlights that XSSI instructions can appear anywhere in the content of a file. A few words of caution, though: make sure that you keep your single (') and double (") quotes, both of which (X)HTML and XSSI support, always balanced.

Listing 9. Use of a variable defined in another file

<html>
<head>
<!--#include file="meta.shtml" -->
<link rel="stylesheet" href="<!--#echo var='view'-->.css" type="text/css">

<!--#set var="who" value="World" -->
<title>Hello <!--#echo var="who" -->!</title>
</head>
<body>

<!--#include file="top-menu.shtml"-->

<h1>Hello <!--#echo var="who" -->!</h1>

<!--#include file="footer.shtml" -->

</body>
</html>

Although view is set in a different file (meta.shtml, from Listing 8), it's still usable in the current file (Listing 9). In programming language terminology, the scope of each XSSI variable is from the point of definition to the end of the document. Keep in mind that if a variable is undefined or the code never assigned it a value, echo will display (none).

Of course, you can use conditional statements not only with style sheets, but also with any kind of document content. For example, Listing 10 presents the new, XSSI-enabled navigation bar, which is not present in the printer-friendly version. In the same way, it's possible to replace a Flash ad with a simple image banner in the printer-friendly version. Listing 11 employs the same technique to include a link conditionally. If view is not equal to (!=) the string print, it adds a link to the printer-friendly version. Otherwise, it displays only the copyright notice.

Listing 10. This navigation bar does not appear in the printer-friendly version

<!--#if expr="$view != print" -->
<p>Hello: <a href="?World">World</a> - Africa - 
   Antarctica - America - Asia - Europe - Oceania</p>
<!--#endif -->

Listing 11. This content is common to all pages on the site and is placed in a single file (footer.shtml)

<!--#if expr="$view != print" -->
<p><a href="?print">Printer-friendly version</a></p>
<!--#endif -->
<!--#config timefmt="2003-%Y" -->
<p>©<!--#echo var="DATE_LOCAL" --> Kostas Pentikousis. All rights reserved.</p>

These examples use two different kinds of string matching. First, there's a pattern match in:

<!--#if expr="$QUERY_STRING = /print/" -->

This tries to match print anywhere in the contents of $QUERY_STRING. You can read this instruction as "If the expression $QUERY_STRING matches 'print', then." On the other hand,

<!--#if expr="$QUERY_STRING = print" -->

does an exact match on $QUERY_STRING. This directive essentially means "If the expression $QUERY_STRING is 'print', then." One implication of using pattern matching is that if the user requests any of the following:

http://www.example.org/hello-world.shtml?print-this
http://www.example.org/hello-world.shtml?printANDthis
http://www.example.org/hello-world.shtml?still_printer-friendly

Apache will serve the printer-friendly version. If the code uses exact matching instead, all three will result in serving the media-rich version instead.

Checking for inequality can prove quite useful as well. For example, to make sure that view does not contain the string print (pattern matching) as follows, write:

<!--#if expr="$view != /print/" -->

You may wonder why Listing 8 set a variable (view) instead of using if else statements and the same pattern matching on $QUERY_STRING several times. Pattern matching is computationally more expensive than exact matching, especially with long strings. QUERY_STRING can be long, so it is likely more efficient to do a single pattern match, set a variable, and then do multiple exact matches to determine which (X)HTML fragments to include. In general it is a good idea to avoid recurring pattern matches and, if possible, to replace (redundant) pattern matches with exact matches.

Conditional Redirects

Taking advantage of environment variables and conditional statements, you can use XSSI to redirect visitors to the most appropriate page based on the make and version of their browser, their IP address, fully qualified domain name, the time of day, and so on. Remember that you can always nest if elif else statements. Listing 12 illustrates a very simple case in which you want to redirect users from your local networks (*.example.org) to one page (http://www.example.org/local) and users from any other domain to another one (http://www.example.org/visitors). Note that this is a good solution only if you do not have administration access to Apache configuration files. Otherwise, you should use URL redirects instead.

Listing 12. Conditional redirect using XSSI

<!--#if expr="$REMOTE_HOST = /example.org/" -->
   <meta http-equiv="refresh" content="0;URL=/local">

<!--#elif expr="$REMOTE_HOST = '' " -->
   <meta http-equiv="refresh" content="0;URL=/not_fully_qualified_hostname">

<!--#else -->
   <meta http-equiv="refresh" content="0;URL=/visitors">
<!--#endif -->

Article Partitioning

Several online magazines partition their articles in an effort to increase ad revenue by creating more spots for ads. For example, splitting each article into two parts means that the site can double the number of banners, buttons, and "unobtrusive ads" it serves. There is also an old-style rule dictating that pages should be about one and a half "screens" long. In this fashion, the user does not have to scroll more than once or twice while browsing a page. For example, suppose that the article about World has two parts. The entire article content is stored once at

http://www.example.org/hello-world.shtml

This URL will also display the first part. The second part has a URL of

http://www.example.org/hello-world.shtml?p=2

In order to make the little application more robust, you have chosen to display the first part of the article unless the user explicitly requests the second one. That way if, for example, a user adds random characters after the ?, she will get a valid response and not an error message. In fact, the first part is the "proper" response, because more often than not articles progress serially and readers should read them in that order. Of course, you would also like to have a printer-friendly version that includes both parts available from:

http://www.example.org/hello-world.shtml?print

Listing 13 illustrates that four lines of XSSI code are enough to bisect the article. First, check that the user did not explicitly request the second page ($QUERY_STRING != 'p=2'). If he did, the first if else (in bold) will fail and the first part is skipped. Instead, the code displays the second part because the second condition ($QUERY_STRING = 'p=2') is met. If the user requests a printer-friendly version, both parts will display. This guarantees robustness.

Listing 13. Article partitioning with XSSI

<html>
<head>
<!--#include file="meta.shtml" -->
<link rel="stylesheet" href="<!--#echo var="view"-->.css" type="text/css">

<!--#set var="title" value="World Statistics" -->
<title><!--#echo var="title" --></title>
</head>
<body>

<h1><!--#echo var="title" --> </h1>

<!--#if expr="$QUERY_STRING != 'p=2' || $view = print" -->

Text for part 1 here...

<!--#set var="p" value="2" -->
<!--#endif -->>

<!--#if expr="$QUERY_STRING = 'p=2' || $view = print" -->
Text for part 2 here..
<!--#set var="p" value="1" -->
<!--#endif -->

<!--#include file="article-tools.shtml" -->

<!--#include file="copy.shtml" -->
</body>
</html>

An important component of article partitioning is the navigational aids that help the visitor move from one part to the next. First, the code needs to determine which part is currently on display. Because the article has only two parts, a single variable (p in Listing 13) will do: you simply need to set p to the value of the complementing part. Based on this value, the code in article-tools.shtml (Listing 14) generates either a navigation label of Next:

<p><a href="?p=2">Next</a></p>

or Previous:

<p><a href="?p=1">Previous</a></p>

Note that the first conditional statement in Listing 14 ensures that neither of the labels appear in the printer-friendly version. Listing 14 includes extraneous indentation in order to make the syntax easier to read. A real-world application would have put the entire snippet in three lines:

Listing 14. "Previous"/"Next" navigation label generation

<!--#if expr="$view != print" -->
<p>
 <a href="?p=<!--#echo var='p' -->">
   <!--#if expr="$p = 1" -->
       Previous
   <!--#else -->
       Next
   <!--#endif -->
 </a>
</p>
<!--#endif -->

An alternative version of article-tools.shtml (Listing 15) generates the "next" page number. With modification, this technique can also partition an article into more than two parts. However, XSSI lacks a repetitive or conditional loop instruction (such as for or while) and basic arithmetic functions. Thus, if you wish to divide an article into several pieces, adding the partitioning code by hand quickly becomes tedious.

Listing 15. Generation of numeric navigation labels

<!--#if expr="$view != print" -->
<p>
   Page
   <!--#if expr="$p = 1" -->
   <a href="?p=1" -->1</a> | 2
   <!--#else -->
   1 | <a href="?p=2" -->2</a>
   <!--#endif -->
</p>

<!--#endif -->

I find fragmenting a short article in more than two parts to be quite frustrating for the user. Nevertheless, doing so for a long report, user documentation, or even a book that has distinct parts or sections is a good idea in general. In fact, this technique is best to apply when the text has various independent sections that share a common introductory or concluding segment. In that case, partitioning also has the advantage of permitting the reader to bookmark each section individually, while at the same time providing many different search engine entry points. It is also a great feature if the site audience includes mobile users or visitors connecting through low-bandwidth connections. The following section explains how to take article partitioning with XSSI one step further.

Book Chapters à la Carte

Long texts such as books, standards documents, and user manuals are typically organized in chapters and sections. By exploiting this inherent structure, XSSI can provide an all-inclusive/printer-friendly version of the entire text; furnish each book chapter independently; and allow users to read any assortment of chapters à la carte. This portioning method is a form of basic content management, which allows for increased usability--making it possible to deliver long documents in palatable pieces while keeping the number of maintainable and updatable documents to a minimum.

To further illustrate the partitioning method using XSSI, consider a presentation of some text from the first book of Charles Dickens's A Tale of Two Cities, which is freely available from Project Gutenberg. Following the terminology used in the previous section, each book chapter becomes a part in the presentation. Chapter 1 is available through the URL

http://www.example.org/tale.shtml?p=1

Chapter 2 is available from

http://www.example.org/tale.shtml?p=2

and so on. Of course, a good application should also generate dynamic links between chapters (Previous/Next). A printer-friendly version of the entire book is available via

http://www.example.org/tale.shtml?print

while a printer-friendly version of the first chapter only is available at

http://www.example.org/tale.shtml?print&p=1

Finally, there's one more feature: if the user would like to read chapters 2 and 3 in one go, then she can do so by requesting:

http://www.example.org/tale.shtml?p=2&p=3

Figure 2 illustrates the opening page of the book, which contains the table of contents, an action button (titled "View selected"), and a link to a printer-friendly version of the entire book. The table of contents includes links to each chapter separately and check boxes that can be used to select a subset of chapters. Note that users can address and bookmark all these different views of the same document independently, yet the entire text is still in a single file (Listing 16).

the best and worst of
times
Figure 2. The best and worst of times

Listing 16. Book chapters à la carte using XSSI

<html>
<head>
<!--#include file="meta.shtml" -->
<link rel="stylesheet" href="<!--#echo var='view'-->.css" type="text/css">
<!--#set var="title" value="A Tale of Two Cities" -->
<title><!--#echo var="title" --></title>
</head>
<body>
<h1 id="top"><!--#echo var="title" --></h1>
<p>A story of the French Revolution by Charles Dickens.</p>
<h2>Book the First--Recalled to Life</h2>

<!--#if expr="$view != print" -->
<form method="GET">
<p><input type="submit" value="View selected"></p>

<p><input type="checkbox" name="p" value="1"> 
  <a href="?p=1">I. The Period</a><br />
   <input type="checkbox" name="p" value="2"> 
  <a href="?p=2">II. The Mail</a><br />
   <input type="checkbox" name="p" value="3"> 
  <a href="?p=3">III. The Night Shadows</a><br />
   ...

<p><input type="submit" value="View selected"></p>
</form>
<!--#endif -->

<!--#if expr="$QUERY_STRING = /p\=1/ || $QUERY_STRING= print" -->
   <h3>I. The Period</h3>
   <p>It was the best of times, it was the worst of times, ...</p>

   <!--#set var="next" value="p=2" -->
   <!--#include file="back2top.shtml" -->
<!--#endif -->

<!--#if expr="$QUERY_STRING = /p\=2/ || $QUERY_STRING = print" -->
   <h3>II. The Mail</h3>
   <p>It was the Dover road that lay, ...</p>

   <!--#set var="previous" value="p=1" -->
   <!--#set var="next" value="p=3" -->
   <!--#include file="back2top.shtml" -->
<!--#endif -->

<!--#if expr="$QUERY_STRING = /p\=3/ || $QUERY_STRING = print" -->
   <h3>III. The Night Shadows</h3>
   <p>A wonderful fact to reflect upon, ...</p>

   <!--#set var="previous" value="p=2" -->
   <!--#include file="back2top.shtml" -->
<!--#endif -->

<!--#if expr="$view != print && $QUERY_STRING != /&p\=/" -->
<p>
   <!--#if expr="$previous" -->
       <a href="?<!--#echo var="previous" -->">previous</a> |
   <!--#endif -->

   <!--#if expr="$next" -->
   | <a href="?<!--#echo var="next" -->">next</a>
   <!--#endif -->
</p>
<!--#endif -->

<!--#include file="book-tools.shtml" -->
</body>
</html>

The partitioning code is similar to that of the previous section. For example, the first part will display if the condition

<!--#if expr="$QUERY_STRING = /p\=1/ || $QUERY_STRING= print" -->

is true. Chapter 1 will display if p=1 is present anywhere in $QUERY_STRING; that is, when requested either as a single chapter with

http://www.example.org/tale.shtml?p=1

or as part of a collection of chapters with

http://www.example.org/tale.shtml?p=1&p=3

Note that code has to escape the equal sign in this conditional (\=) in order to differentiate the character equal sign in the string p=1 from the operator equal sign. Alternatively, the first chapter will display if the user requests the entire book in printer-friendly format. Listing 16 also includes code for generating the navigational labels (Previous/Next) and links that take the user back to the top of the page (and should not appear in the printer-friendly versions) as shown in Listing 17.

Listing 17. Back to the top

<!--#if expr="$view != print" -->
<p><a href="#top">Back to top ⇑</a></p>
<!--#endif -->

Finally, the code includes Listing 18, which generates links to the printer-friendly versions. The label "Print this" appears only when the user views a single chapter or a set of chapters, but not the opening screen (Figure 2).

Listing 18. Book-tools revealed

<!--#if expr="$view != print" -->
<p>
<!--#if expr="$QUERY_STRING = /p\=/" -->
   <a href="?print&<!--#echo var='QUERY_STRING' -->">Print this</a> -
<!--#endif -->

<a href="?print">Print entire book</a></p>

<!--#endif -->

The principle presented here applies to scores of different applications. For example, a bus operator web site can provide customized schedules using XSSI only. In this scenario, the schedule to and from a particular end point is stored in a single document with the timetables for weekdays and weekends forming the different parts of the text. In a fashion similar to the Dickens example, a passenger traveling out of town for the weekend may choose to get a printout of the Friday (weekday) timetable to her destination along with the Sunday (weekend) timetable from her destination. The site can go one step further and use time/date information to present today's schedule by default. Traveling directions can also be partitioned, for example, based on the different means of transportation. In this case, all parts may share some common text, such as the destination address and phone number, without the need to store this information and replicate it in several places.

As a last example, consider the web site of a registered charity that accepts donations via several different methods such as small cash, check, or credit card donations, automatic paycheck withholdings, and planned giving. The charity would like to offer online details about each method. Of course, this wide range of options comes with a diverse set of rules and regulations, which may force some designers to create different pages for each category--a decision that usually increases future maintenance time. Moreover, it is convenient to be able to pick and choose the method that is more interesting to a potential donor. Using the partitioning method above, the site can include details on all possible donation procedures in a single document while delivering customized "web brochures" to donors.

All XSSI solutions, including the partitioning method, can work with client-side scripting to enhance certain aspects of the application, if required. In addition, you can also generate the content itself offline using back-end solutions. For example, your content producers may edit the document using a wiki environment run on the company intranet server. At regular intervals, a back-end application may check for updates on the wiki and, if there are any, generate the XSSI-enriched document for publishing on the public company web site. That way, you can enforce document control via the wiki, which makes the public server content more secure, because a bare-bones Apache with XSSI enabled is less complex and in general more mature and robust than wiki environments. In addition, you can deliver the web site content with fewer hardware and software requirements, as there is no need for a document database. This is an efficient cost-cutting solution, because the web site can serve more users with the same configuration.

Hierarchical Menus with XSSI

Web sites usually have hierarchical menus for a couple of reasons. Easing site navigation and allowing the user to reach a large number of pages simultaneously is the most common one. The increased "coolness factor" is another; after all, if competitor sites have one, we should get it too! There are numerous JavaScript hierarchical menus implementations, many available for free, some featuring brilliant code. However, older browsers don't all support JavaScript well; it is plagued by inconsistencies on different platforms; and some security-conscious users may just disable it.

This section proposes an alternative implementation of hierarchical menus using XSSI. The main advantage with XSSI is that client make, capabilities, or configuration options do not affect the look and feel of the site at all. The menus will work just fine, because the final product is pure markup and the browser does not need to execute any client-side code. You will also get the coolness factor, at least to some extent. Moreover, the site will load significantly faster, because users will download only the menu items that they are interested in instead of downloading all menus and submenus at once. Last, the XSSI implementation presented below enforces structured thinking while laying down the site design, which leads to well-organized, functional web sites.

Assume that you are building a site that provides information on the planets of the solar system, the greater geographical areas on each planet (aka continents for Earth), and the political subdivisions in each area (countries). Clearly the content size in each category, and hence in each menu, is quite unbalanced. This, of course, is the case with most real-world web sites: some sections of the content are richer and more elaborate (Our Products or Our Services sections of corporate sites); others are bare-bones (the omnipresent Contact Us).

Table 2, Column a, shows a good candidate for this site's first-level, left-side vertical menu. Each planet is a link, an item in the menu, which when selected leads to the corresponding page. The heading Solar System is also a menu item. When the user selects Earth, the corresponding page loads and the relevant submenu opens up, displaying the second-level menu items (column b), the continents. Similarly, when the user selects Africa, the third-level menu (column c) opens up, displaying the list of African countries along with information on Africa.

Table 2. Hierarchical menus illustrated

(a) (b) (c)

Solar System
    Mercury
    Venus
    Earth
    Mars

Solar System
    Mercury
    Venus
    Earth
       Africa
       Antarctica
       America
       Asia
       Europe
       Oceania
    Mars
Solar System
    Mercury
    Venus
    Earth
        Africa
            Algeria
            Angola
            Benin
            ...
            Zimbabwe
        Antarctica
        America
        Asia
        Europe
        Oceania
    Mars

This menu structure is easy to translate into a directory structure, which I will exploit as follows. Each of the menu items maps to a directory in the implementation, and each directory has a single document titled index.shtml that includes the menu and the corresponding text. Listing 19 presents the outline of every such document.

Listing 19. Page template

<html>
<head><title>Solar System</title></head>
<body>
<!--#include virtual="/menu.shtml" -->

<p>Content particular to the Solar System...

</body></html>

Although this may seem a bit restrictive, it is not a prerequisite--it simply eases the presentation in this tutorial. The only XSSI directive is the familiar file inclusion. Notice that instead of including the file menu.shtml, the code includes its virtual location on the site. Where is all the magic, you ask? That happens in menu.shtml, presented in Listing 20. Each menu item is listed with the corresponding link, and each item that has subcategories conditionally includes the relevant submenu. This submenu displays only when the requested document is under the corresponding hierarchy.

Listing 20. The first-level hierarchical menu implementation includes the menu items ("Solar System") and, where needed, conditional inclusions of the relevant submenus

<p><a href="/">Solar System</a></p>
<ul>
<li><a href="/mercury">Mercury</a></li>
<li><a href="/venus">Venus</a></li>

<li><a href="/earth">Earth</a>
   <!--#if expr="$REQUEST_URI = /earth/" -->
       <!--#include virtual="/earth/menu.shtml" -->
   <!--#endif -->
</li>

<li><a href="/mars">Mars</a></li>
...
</ul>

For example, if the user requests the main page of the site:

http://www.example.org

Listing 20 will display the opening page with information on the solar system and the menu as shown in Table 2, Column a. The submenu for Earth will not display. In contrast, when the user clicks on the link for Earth, she effectively requests

http://www.example.org/earth

and the submenu shown in Listing 21 will be included and the full menu will display as in Table 2, Column b. Again, the menu.shtml found that the earth directory contains the links to the menu items (Africa, Antarctica) and a conditional submenu inclusion where appropriate. Finally, if the user clicks on Africa, she is effectively requesting

http://www.example.org/earth/africa

In that case, the complete submenu will be display as in Table 2, Column c.

Listing 21. Second-level submenu ("Earth")

<ul>
<li><a href="/earth/africa">Africa</a>
   <!--#if expr="$REQUEST_URI = /africa/" -->
       <!--#include virtual="/africa/menu.shtml" -->
   <!--#endif -->
</li>

<li><a href="/antarctica">Antarctica</a></li>

...
</ul>

Listing 22. Third-level submenu ("Africa")

<ul>
<li>Algeria</li>
<li>Angola</li>
<li>Benin</li>
...
<li>Zimbabwe</li>
</ul>

Effectively, this hierarchical menu's implementation takes advantage of the directory structure and conditional recursive file inclusions, delivering a simple yet powerful navigational aid. You can extend submenus and add new menu items on demand. Maintaining the menus is straightforward because the menu reflects the directory structure. In fact, you could even develop a back-end solution to create menus and the corresponding directory structure based on configuration files along the lines discussed in the previous section.

XSSI Resources

Although this tutorial presents several solutions based on XSSI, it does not cover configuration issues. If you are responsible for setting up XSSI on a server, your first reading should probably be the Apache Tutorial: Introduction to Server Side Includes. This excellent yet brief tutorial explains how to configure Apache to run XSSI and introduces several commands along with examples of typical usage. The Apache Module mod_include documentation is a great resource as well.

The NCSA HTTPd Tutorial: Server Side Includes introduces SSI as a technology that allows users "to provide simple information on the fly." The NCSA HTTPd is a web server that is now obsolete but is, after all, the granddaddy of Apache. This tutorial is from 1995 and is mostly of historical value today. Do pay attention to the warnings about security, but bypass the caveats about performance: every dynamic content technology places a burden on the server typically, as mentioned in the introduction, much heavier than XSSI.

The tutorial Using XSSI and ErrorDocument to configure customized international server error responses makes for very interesting reading and can give you many ideas for your applications. The XSSI Library by Ross Olson is full of recipes, tips, and code samples. For example, Olson categorized the 30-plus environment variables you can use in your XSSI code and lists them along with a short description and examples Finally, the Apache Hello World Benchmarks has more details than I mentioned in the beginning of the tutorial.

Kostas Pentikousis is currently an ERCIM Fellow at VTT, The Technical Research Center of Finland, and resides in Oulu, Finland.

Return to the Apache DevCenter.

Copyright © 2009 O'Reilly Media, Inc.