Apache's eXtended Server Side Includes
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).

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>
<h4 id="top"><!--#echo var="title" --></h4>
<p>A story of the French Revolution by Charles Dickens.</p>
<h4>Book the First--Recalled to Life</h4>
<!--#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" -->
<h4>I. The Period</h4>
<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" -->
<h4>II. The Mail</h4>
<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" -->
<h4>III. The Night Shadows</h4>
<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.
Prev [1] [2] [3]