Apache DevCenter
oreilly.comSafari Books Online.Conferences.

advertisement


Apache's eXtended Server Side Includes
Pages: 1, 2, 3

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.

Pages: 1, 2, 3

Next Pagearrow





Sponsored by: