Tải bản đầy đủ (.pdf) (89 trang)

Building Oracle XML Applications phần 9 potx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (608.16 KB, 89 trang )

<xsl:value-of select="oracleUrl:EncodeURLArgs($text)"/>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This example stylesheet selects a string value for a urlBase variable of:

and a string value for a urlArgs variable of:
company=at & t / mobilcom
which contains some spaces, an ampersand, and a slash. To test this example
transformation, you can use any XML file as the source and try the following:
• With James Clark's xt XSLT 1.0 processor:
$ xt anyfile.xml PortableURLEncoderTest.xsl
• With Michael Kay's Saxon XSLT 1.0 processor:
$ saxon anyfile.xml PortableURLEncoderTest.xsl
• With the Oracle XSLT 1.0 Processor:
$ oraxsl anyfile.xml PortableURLEncoderTest.xsl
All three processors produce the following identical output:

We can write a stylesheet that refers to extension functions in several different
namespaces (only one of which we're expecting to be usable by the current
processor at transformation time) because an XSLT 1.0-compliant processor
signals errors only if you actually try to use the function. By wrapping the
extension function in a named template that uses <xsl:choose> and
function-available( ) to selectively invoke the correct function, we can avoid
the situation of ever invoking a function that is not available.
16.3.4 Debugging Your XSLT Java Extension Functions
You will certainly need to debug your XSLT Java extension functions to make sure
they are working correctly. Using JDeveloper 3.1, you have two choices for how
to do this, both straightforward.
If you are using XSLT in combination with XSQL Pages, you can follow the steps


we outlined earlier for debugging XSQL page action handlers. If you set
breakpoints in the Java code for your own XSLT extension functions, the
debugger will stop at your breakpoint when your function is invoked by the XSLT
processor.
If you want to debug from the command line, you can use JDeveloper 3.1 remote
debugging support to debug the command-line Oracle XSLT processor. In order
to remotely debug the oraxsl command-line utility, do the following:
• For the simplest possible experience, make sure you run the oraxsl
command-line utility using the Java 1.2 VM supplied with JDeveloper 3.1.
This VM natively supports remote debugging on Windows NT, where you'll
be using JDeveloper 3.1. If you've installed JDeveloper 3.1 in the
C:\jdev31 directory, for example, the complete path to the Java VM is
C:\jdev31\java1.2\bin\java.exe.
• Run the oraxsl command-line utility using the remote debugging
argument to the Java VM. If you are trying to test the transformation of
source.xml by the style.xsl stylesheet that makes use of your extension
functions, the exact command will be:
java -XXdebug oracle.xml.parser.v2.oraxsl source.xml style.xsl
If you run this command with the JDeveloper 3.1 Java VM, you will
immediately see the following message on the console:
*** Port is 4000 ***
*** Waiting for debugger connection. ***
At this point, the oraxsl program is halted and will not proceed to execute
until you attach the JDeveloper 3.1 debugger to it.
• In your JDeveloper 3.1 project where the source code for your Java XSLT
extension functions resides, make sure that your project is set up for
remote debugging. To check this setting, select Project Project
Properties from the main menu, click the Run/Debug tab, and set the
Debug As pop-up list to "Remote Debugging".
• Set your desired breakpoints in your extension function source code.

• Click the Debug icon in the toolbar.
• When the Remote Debugging dialog appears, enter localhost for the
machine name and 4000 for the port, then click Attach.
When the debugger attaches to the oraxsl process, program execution begins,
and you'll hit your breakpoints as soon as the XSLT processor invokes your
extension functions.
Chapter 17. XSLT-Powered Portals and
Applications
In this last chapter, we'll work our way through all of the important capabilities of XSLT in the
context of building some interesting Oracle XML applications. By the end of this chapter, I predict
you will be an incurable XSLT fanatic as its amazing flexibility transforms you into a more
productive developer.
17.1 XSLT-Powered Web Store
In this section, we'll build a simple site for a web store that offers the basic functionality illustrated
in Figure 17.1
.
Figure 17.1. Page map of the Everything.com web site

Visitors to the site will be able to see featured items on the home page and can click the name of
one of our "shops" to see a home page specific to that shop. They can search the site for a product,
and whenever a product appears, they can click a link to see other products by the same
manufacturer.
17.1.1 Turning HTML Mockup into an XSLT stylesheet
All product information needs to be displayed consistently across the web store. Our web design
team has provided us with an HTML mockup, shown in Figure 17.2
, of how each product should
look on the site. The HTML file for the mockup is shown in Example 17.1
.
Figure 17.2. Mockup of product display for our web store


Example 17.1. Source for HTML Mockup of Product
Information

<html>
<body>
<center>
<table border="0">
<tr>
<td valign="top">
<b>Java in a Nutshell : A Desktop Quick Reference</b> by David Flanagan
<br>Other products by <a href="xxxx">O'Reilly</a>
<br>
<table border="0"><tr>
<td><img src="images/1565924878.gif"></td>
<td valign="middle">
<b>List Price:</b><strike>29.95</strike><br>
<b>Our Price: <font color="blue">20.97</font></b><br>
You Save: <b>8.98</b>
</td>
</tr></table>
</td>
</tr>
</table>
</center>
</body>
</html>
We'll use Dave Raggett's Tidy tool (described in Chapter 6
) to convert the web designer's HTML
source code into an indented, well-formed XML file that will evolve into an XSLT stylesheet that
produces the same look and feel. Run Tidy as follows:

tidy -asxml -indent ProductMockup.html > Product.xsl
The tidy command produces a well-formed XML document in the Product.xsl file, shown in
Example 17.2
, which we can use as a starting point for the XSLT transformation.
Example 17.2. Tidied HTML Mockup in XML
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"
<html xmlns="
<head>
<meta name="generator" content="HTML Tidy, see www.w3.org" />
<title>
</title>
</head>
<body>
<center>
<table border="0">
<tr>
<td valign="top">
<b>Java in a Nutshell : A Desktop Quick Reference</b>
by David Flanagan<br />
Other products by <a href="xxxx">O'Reilly</a><br />

<table border="0">
<tr>
<td>
<img src="images/1565924878.gif" />
</td>
<td valign="middle">
<b>List Price:</b><strike>29.95</strike><br />

<b>Our Price: <font color="blue">
20.97</font></b><br />
You Save: <b>8.98</b>
</td>
</tr>
</table>
</td>
</tr >
</table>
</center>
</body>
</html>
We can easily turn this tidied-up HTML into an XSLT stylesheet that uses a single root template.
We just need to wrap the entire content of the document by:
<xsl:stylesheet version="1.0" xmlns:xsl="
<! For best results with HTML, best not to introduce any extra whitespace >
<xsl:output method="html" indent="no"/>
<xsl:template match="/">
<! Tidied-up HTML document goes here >
</xsl:template>
</xsl:stylesheet>
We don't need the <!DOCTYPE> and/or <META> for our purposes, so we just delete it. At this point
we have a valid, functional XSLT stylesheet that will output the static HTML mockup. In order to
turn the static template into a page that dynamically formats production information from our
database, we need to perform two basic steps:
• Build an XSQL page to assemble the dynamic XML data page with all necessary production
information
• Replace the static sample information in the XSLT stylesheet with XSLT actions to plug our
dynamic XML into the template HTML format.
First things first; we'll start by building the XSQL page.

17.1.2 Building an XSQL Page to Assemble Data
Figure 17.3 illustrates our database schema related to products on the web store.
Figure 17.3. Database tables involved in the web store

We create the following Store.xsql page to query the product information from the item, author,
maker, and shop tables, using an outer join for the optional author information. Note that we've
planned ahead and already associated the Product.xsl stylesheet we're building to format
products for the Web.
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="Product.xsl"?>
<xsql:query max-rows="1" sid="1" connection="xmlbook"
xmlns:xsql="urn:oracle-xsql">
SELECT description,
author_name,
maker_name,
maker.id AS maker_id,
shop_name,
list_price,
price,
list_price - price AS yousave,
sku
FROM item, author, maker, shop
WHERE item.author_id = author.id (+)
AND item.maker_id = maker.id
AND item.shop_id = shop.id
AND shop.id = {@sid}
</xsql:query>
Since we'll just be testing the page initially, we add the following two additional attributes on the
<xsql:query> action element:
max-rows="1"

Limits the product rows retrieved to a single row
sid="1"
Produces a default value for the "shop id" parameter sid in case none is specified in the
request
Requesting the Shop.xsql page through our browser, or using the xsql command-line utility, we
see that this produces the following raw XML datagram:
<?xml version = '1.0'?>
<ROWSET>
<ROW num="1">
<DESCRIPTION>Manifest Destiny</DESCRIPTION>
<AUTHOR_NAME>Daisy Dines</AUTHOR_NAME>
<MAKER_NAME>Fly Rite</MAKER_NAME>
<MAKER_ID>3</MAKER_ID>
<SHOP_NAME>Outdoors</SHOP_NAME>
<LIST_PRICE>13.95</LIST_PRICE>
<PRICE>11.16</PRICE>
<YOUSAVE>2.79</YOUSAVE>
<SKU>0140060898</SKU>
</ROW>
</ROWSET>
With the basic data in place, we turn our attention to evolving the tidied-up HTML mockup, which
is now an XSLT stylesheet, into a dynamic data display.
17.1.3 Plugging Dynamic Data into the Stylesheet
We want to design the stylesheet to handle any number of rows of products that might be
produced by the query over the product information. For each <ROW> element in the <ROWSET>, we
want to create an HTML table row (<tr>) containing the product formatted in the standard way.
So we introduce an <xsl:for-each> element to wrap the repeating HTML table row, with a
select="ROWSET/ROW" pattern to select all rows in the dynamic XSQL data page. The contents of
the <xsl:for-each> element will be instantiated in the resulting page for each ROWSET/ROW node
selected.

Inside the <xsl:for-each> loop, the selected <ROW> element is the current node, so any XPath
expressions needed to refer to data in the row can use relative patterns. In particular, we do the
following, using relative XPath expressions:
1. Replace the static product description:
2. <b>Java in a Nutshell : A Desktop Quick Reference</b>
by David Flanagan
with the dynamic expression:
<b><xsl:value-of select="DESCRIPTION"/></b>
by <xsl:value-of select="AUTHOR_NAME"/>
3. Replace the static text and hyperlink href value in:
Other products by <a href="xxxx">O'Reilly</a>
with:
Other products by <a href="Maker.xsql?id={MAKER_ID}">
<xsl:value-of select="MAKER_NAME"/>
</a>
using an attribute value template {MAKER_ID} to substitute the current row's MAKER_ID
into the href attribute value. This will be the id parameter passed to the Maker.xsql page
we'll build later showing all products for a given maker ID.
4. Replace the static src attribute's URL:
<img src="images/1565924878.gif" />
with:
<img src="images/{SKU}.gif" />
5. Replace the static price information:
6. <b>List Price:</b><strike>29.95</strike><br />
7. <b>Our Price: <font color="blue">20.97</font></b><br />
You Save: <b>8.98</b>
with:
<b>List Price:</b><strike>
<xsl:value-of select="LIST_PRICE"/>
</strike><br/>

<b>Our Price: <font color="blue">
<xsl:value-of select="PRICE"/>
</font></b><br />
You Save: <b><xsl:value-of select="YOUSAVE"/></b>
The resulting XSLT stylesheet is shown in Example 17.3
.
Example 17.3. Stylesheet to Format Web Store Product
Information

<! Product.xsl: Format Web Store product information >
<xsl:stylesheet version="1.0" xmlns:xsl="
<! For best results with HTML, best not to introduce extra whitespace >
<xsl:output method="html" indent="no"/>
<xsl:template match="/">
<html>
<body>
<center>
<table border="0">
<xsl:for-each select="ROWSET/ROW">
<tr>
<td valign="top">
<b><xsl:value-of select="DESCRIPTION"/></b>
by <xsl:value-of select="AUTHOR_NAME"/><br/>
Other products by <a href="Maker.xsql?id={MAKER_ID}">
<xsl:value-of select="MAKER_NAME"/>
</a><br/>
<table border="0">
<tr>
<td>
<img src="images/{SKU}.gif" />

</td>
<td valign="middle">
<b>List Price:</b><strike>
<xsl:value-of select="LIST_PRICE"/>
</strike><br/>
<b>Our Price: <font color="blue">
<xsl:value-of select="PRICE"/>
</font></b><br />
You Save: <b><xsl:value-of select="YOUSAVE"/></b><br/>
</td>
</tr>
</table>
</td>
</tr>
</xsl:for-each>
</table>
</center>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Requesting the Store.xsql page in our browser with the URL:
http://server/Store.xsql
uses the default store ID of 1, retrieves the first product row, and transforms it for display,
producing the output shown in Figure 17.4
.
Figure 17.4. HTML output of product from the Outdoors shop

This looks just like what the web designers wanted.
17.1.4 Formatting Numbers and Conditional Formatting

If we request the Store.xsql page in our browser, passing a value of 4 for the sid parameter to
test out an example product in our Toys shop:
http://server/Store.xsql?sid=4
we get the result shown in Figure 17.5
.
Figure 17.5. Same stylesheet formatting product from the
Toys shop


We immediately notice a few problems:
• The word "by" appears all alone after the product description because toys don't have
authors recorded in our database.
• The "List Price" and "You Save" sections come up blank since we apparently don't record
a list_price for toys.
• The $6.00 price for the Cymbal Clapping Monkey formats as 6 instead of 6.00
Let's temporarily turn off the stylesheet processing by adding an xml-stylesheet=none to the
end of the URL:
http://server/Store.xsql?sid=4&xml-stylesheet=none
This allows us to see the underlying raw dynamic data page that the XSLT stylesheet is seeing
during the transformation:
<?xml version = '1.0'?>
<ROWSET>
<ROW num="1">
<DESCRIPTION>Cymbal Clapping Monkey</DESCRIPTION>
<MAKER_NAME>Circus Fun</MAKER_NAME>
<MAKER_ID>1</MAKER_ID>
<SHOP_NAME>Toys</SHOP_NAME>
<PRICE>6</PRICE>
<SKU>B00000IWIT</SKU>
</ROW>

</ROWSET>
We see that the value 6 is being selected from the database as is, so we'll change our query to
format the numbers the way we want them. This will fix one problem:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="Product.xsl"?>
<xsql:query max-rows="1" sid="1" connection="xmlbook"
xmlns:xsql="urn:oracle-xsql">
SELECT description,
author_name,
maker_name,
maker.id as maker_id,
shop_name,
TO_CHAR(list_price,'999999.00') AS list_price,
TO_CHAR(price,'999999.00') AS price,
TO_CHAR(list_price - price,'999999.00') AS yousave,
sku
FROM item, author, maker, shop
WHERE item.author_id = author.id (+)
AND item.maker_id = maker.id
AND item.shop_id = shop.id
AND shop.id = {@sid}
</xsql:query>
The other two problems indicate a need to conditionally format information. We want to display
the author name if there is one and handle the "List Price" and the corresponding "You Save"
sections similarly. We can use the <xsl:if> action to wrap portions of the stylesheet template
that need to be conditionally included. The <xsl:if> element has a required test="expression"
attribute that indicates an XPath expression to be evaluated. If the expression tests true, then
the content of the <xsl:if> is instantiated in the result tree. Otherwise, it is left out.
While any XPath expression is valid to include in an <xsl:if> element's test attribute, one of the
most common expressions used is a pattern to match an element or attribute. If the pattern

matches at least one node, the test evaluates to true. If no matches are selected by the pattern,
the test evaluates to false. For example, to test whether an <AUTHOR_NAME> child element exists
for the current node, you can use:
<xsl:if test="AUTHOR_NAME">
This returns true if an <AUTHOR_NAME> child element exists and false otherwise. If you need to
check that an element exists and that it has a non-empty value, you can use a test like:
<xsl:if test="AUTHOR_NAME != ''">
We wrap the display of <AUTHOR_NAME>, <LIST_PRICE>, and <YOUSAVE> elements with
appropriate <xsl:if> tests to produce the Product.xsl stylesheet in Example 17.4
.
Example 17.4. Stylesheet to Correctly Format All Web Store
Products

<! Product.xsl: Format Web Store product information >
<xsl:stylesheet version="1.0" xmlns:xsl="
<! For best results with HTML, best not to introduce extra whitespace >
<xsl:output method="html" indent="no"/>
<xsl:template match="/">
<html>
<body>
<center>
<table border="0">
<xsl:for-each select="ROWSET/ROW">
<tr>
<td valign="top">
<b><xsl:value-of select="DESCRIPTION"/></b>
<xsl:if test="AUTHOR_NAME">
by <xsl:value-of select="AUTHOR_NAME"/>
</xsl:if><br/>
Other products by <a href="Maker.xsql?id={MAKER_ID}">

<xsl:value-of select="MAKER_NAME"/>
</a><br/>
<table border="0">
<tr>
<td>
<img src="images/{SKU}.gif" />
</td>
<td valign="middle">
<xsl:if test="LIST_PRICE">
<b>List Price:</b><strike>
<xsl:value-of select="LIST_PRICE"/>
</strike><br/>
</xsl:if>
<b>Our Price: <font color="blue">
<xsl:value-of select="PRICE"/>
</font></b><br />
<xsl:if test="LIST_PRICE">
You Save: <b><xsl:value-of select="YOUSAVE"/></b>
</xsl:if>
</td>
</tr>
</table>
</td>
</tr>
</xsl:for-each>
</table>
</center>
</body>
</html>
</xsl:template>

</xsl:stylesheet>
Refreshing the browser page, we see in Figure 17.6
that this now properly handles the missing
author and list price.
Figure 17.6. Formatting price and handling missing author
and list price


Going back to our numerical formatting, when using XSLT to format database query results, there
are two strategies for formatting numbers:
• Use the database TO_CHAR( ) function in your SELECT statement to format the number
using SQL format masks.
• Use the XSLT format-number( ) function.
The first strategy is easier if you're already familiar with the SQL TO_CHAR( ) function and its
format mask language. The latter is better if you need to work with the numerical value of an
element within the stylesheet for calculations. For example, if your query includes
TO_CHAR(SAL,'$9,999.00') AS SALARY in its SELECT list, the corresponding
<SALARY>$2,450.00</SALARY> will work fine if you are simply including its value verbatim into
the transformed result. However, if your stylesheet needs to test if the expression SALARY > 4500
is true, or needs to use the value of <SALARY> in a calculated expression, having a <SALARY>
value from the database that includes a dollar sign and commas will cause problems.
The XSLT format-number( ) function takes arguments for the number and the format mask, but
it uses the format masks supported by the java.text.DecimalFormat class in the JDK. While
other pattern characters are supported, the four key pattern characters of immediate interest
are:
0
A digit
#
A digit, zero shows as absent
.

Placeholder for a decimal separator
,
Placeholder for a group separator
Any character, such as a dollar sign, can appear at the start or the end of a format mask. To
format our <LIST_PRICE> element using format-number( ) we would use the syntax:
<xsl:value-of select="format-number(LIST_PRICE,'0.00')"/>
17.1.5 Handling Raw HTML/XML from Database Columns
For some products in the Electronics shop, vendors supply us with a short product blurb in HTML
that highlights the key features of the product. An example blurb looks like this:
<ul>
<li>AM/FM and shortwave world-time travel clock radio</li>
<li>11-band travel radio (AM, FM, SW 1-9)</li>
<li>LED tuning indicator</li>
<li>Built-in alarm with snooze button</li>
<li>Compact design and protective lid</li>
</ul>
We can easily add the BLURB column to the SELECT list in our Store.xsql page, and add another
conditional section in our stylesheet:
<xsl:if test="BLURB">
<br/>
<b><u>Features</u></b><br/>
<xsl:value-of select="BLURB"/>
</xsl:if>
However, if we attempt to request Store.xsql on an item in the Electronics shop with a URL like
this:
http://server/Store.xsql?sid=3
we'll see a "Features" section in the browser like that shown in Figure 17.7
.
Figure 17.7. Display of HTML markup escaped by XSLT
processor



Yikes! This occurs because an XSLT processor is required to escape all illegal characters like angle
brackets and ampersands when they occur in text values, so the browser displays the literal angle
bracket characters and ampersands as text. We want these literal characters to appear verbatim
and unescaped in the output of the transform so the browser will interpret them as HTML tags and
not display them as literal angle brackets. For these cases, XSLT supplies the optional
disable-output-escaping="yes" attribute on <xsl:value-of> or <xsl:text>. This instructs
the processor to disregard the normal escaping mechanism and allows the literal HTML or XML
markup to pass through verbatim to the result document. So if we rewrite our BLURB section of
the stylesheet to look like this:
<xsl:if test="BLURB">
<br/>
<b><u>Features</u></b><br/>
<xsl:value-of disable-output-escaping="yes" select="BLURB"/>
</xsl:if>
then the products in our Electronics shop will now display correctly, as shown in Figure 17.8
.
Figure 17.8. Correctly displaying HTML-based product blurb


Serving raw HTML fragments out of database columns directly
into web pages can be very useful when you control and trust
the content. However, if you are serving information from the
database that end users might have entered, there are
potential security risks in serving this unverified HTML markup
back into the browser. The risks a developer must be aware of
are documented at
Although
such occurrences are pretty rare, forewarned is forearmed.


Now that we've established the basic product formatting, we need to build these four web site
pages:
• Featured Items page
• Search Results page
• Items by Maker page
• Shop Home page
We'll build the four XSQL pages shown in Figure 17.9
, leaving StoreTop.xsql for later.
Figure 17.9. Map of XSQL pages to implement the web store

Basically, these pages differ only in the WHERE clauses of their queries. The query in the
FeaturedItems.xsql page needs to look like this:
SELECT description,
author_name,
maker_name,
maker.id AS maker_id,
shop_name,
blurb,
TO_CHAR(list_price,'999999.00') AS list_price,
TO_CHAR(price,'999999.00') AS price,
TO_CHAR(list_price - price,'999999.00') AS yousave,
sku
FROM item, author, maker, shop
WHERE item.author_id = author.id (+)
AND item.maker_id = maker.id
AND item.shop_id = shop.id
AND item.id IN ( SELECT itemid
FROM featured_items
WHERE sysdate BETWEEN start_date AND end_date )

ORDER BY description
This queries products that appear in our featured_items table where the current date falls
between the start_date and the end_date of the featured period for the item. The query in the
SearchResults.xsql page looks about the same, but has the following WHERE clause:
WHERE item.author_id = author.id (+)
AND item.maker_id = maker.id
AND item.shop_id = shop.id
AND UPPER(item.description) LIKE UPPER('%{@find}%')
The same goes for the Maker.xsql page, which shows additional products from the same maker as
another product. Its WHERE clause looks like this:
WHERE item.author_id = author.id (+)
AND item.maker_id = maker.id
AND item.shop_id = shop.id
AND maker.id = {@id}
For the ShopHomePage.xsql, we'll use a CURSOR expression to retrieve in a single query the
name of the shop, given the shop ID that will be passed in as a parameter as well as the nested
set of available products:
SELECT shop_name,
CURSOR( SELECT description,
author_name,
maker_name,
maker.id AS maker_id,
shop_name,
blurb,
TO_CHAR(list_price,'999999.00') AS list_price,
TO_CHAR(price,'999999.00') AS price,
TO_CHAR(list_price - price,'999999.00') AS yousave,
sku
FROM item, author, maker
WHERE item.author_id = author.id (+)

AND item.maker_id = maker.id
AND item.shop_id = shop.id
ORDER BY shop_name, maker_name
) AS items
FROM shop
WHERE id = {@id}
We could have just selected the shop name and product information in the same flat query, but
then we would have ended up with the shop name in every row. XSLT 1.0 does not provide
facilities for grouping information containing repeating values to factor out unique groups, so we
leverage the database to handle it.
17.1.6 Factoring Reusable Transformation Routines
Finally, we attack the XSLT stylesheets for the four key product pages. All of these pages will be
using different queries to display products. We can take advantage of XSLT named templates to
factor the product display part of the template we created earlier into a subroutine to be
leveraged by multiple stylesheets. After adding another conditional section to avoid displaying
"Other products by " on a page that does not include a MAKER_ID, we end up with the named
displayProduct template in Example 17.5
.
Example 17.5. Named Template to Display Web Store
Products

<xsl:stylesheet version="1.0" xmlns:xsl="
<xsl:template name="displayProduct">
<b><xsl:value-of select="DESCRIPTION"/></b>
<xsl:if test="AUTHOR_NAME">
by <xsl:value-of select="AUTHOR_NAME"/>
</xsl:if>
<xsl:if test="MAKER_ID">
<br/>
<xsl:text>Other products by </xsl:text>

<a href="Maker.xsql?id={MAKER_ID}">
<xsl:value-of select="MAKER_NAME"/>
</a>
</xsl:if>
<br/>
<table border="0">
<tr>
<td><img src="images/{SKU}.gif"/></td>
<td valign="middle">
<xsl:if test="LIST_PRICE">
<b><xsl:text>List Price:</xsl:text></b>
<strike>
<xsl:value-of select="LIST_PRICE"/>
</strike>
<br/>
</xsl:if>
< b>
<xsl:text>Our Price: </xsl:text>
<font color="blue">
<xsl:value-of select="PRICE"/>
</font>
</b>
<br/>
<xsl:if test="LIST_PRICE">
<xsl:text>You Save: </xsl:text>
<b>
<xsl:value-of select="YOUSAVE"/>
</b>
</xsl:if>
</td >

</tr>
</table>
<xsl:if test="BLURB">
<br/>
<b><u><xsl:text>Features</xsl:text></u></b><br/>
<xsl:value-of disable-output-escaping="yes" select="BLURB"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Now the stylesheets for our XSQL pages can invoke displayProduct as a named template to
make sure they are all formatting products in a uniform way. Example 17.6
shows the source for
the FeaturedItems.xsl stylesheet, which includes DisplayProduct.xsl, then uses
<xsl:call-template> to invoke the displayProduct template inside its <xsl:for-each> loop
to format the current <ROW> of product information.
Example 17.6. Calling Reusable Formatting with Named
Templates

<xsl:stylesheet version="1.0" xmlns:xsl="
<! Include the stylesheet containing the "displayProducts" template >
<xsl:include href="DisplayProduct.xsl"/>
<! Set the output to not indent so no extra whitespace is introduced >
<xsl:output method="html" indent="no"/>
<xsl:template match="/">
<html>
<body>
<center>
<h2>Welcome! These Items are featured today </h2>
<table border="0">
<xsl:for-each select="ROWSET/ROW">

<tr>
<td valign="top">
<xsl:call-template name="displayProduct"/>
</td>
</tr>
</xsl:for-each>
</table>
</center>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
The stylesheets for the other three pages look nearly identical, so these are left as an exercise for
the reader.
To build the title frame for the site, we can encapsulate the structure of the store in an XSQL page
containing just static XML elements, as shown in StoreTop.xsql. Each store and the shops it
contains can be represented by <store> and <shop> elements:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="StoreTop.xsl"?>
<store>
<shop name="Outdoors" id="1"/>
<shop name="Electronics" id="3"/>
<shop name="Books" id="2"/>
<shop name="Toys" id="4"/>
</store>
The simple-form XSLT stylesheet in Example 17.7
transforms the store structure into a
nice-looking banner with a search box and hyperlinks to each particular shop in our store.
Example 17.7. Simple-Form Stylesheet to Handle Web Store
Home Page


<! StoreTop.xsl: format the main homepage of the Web Store >
<xsl:stylesheet version="1.0" xmlns:xsl="
<! For best results with HTML, best not to introduce extra whitespace >
<xsl:output method="html" indent="no"/>
<xsl:template match="/">
<html>
<head><link rel="stylesheet" type="text/css" href="Store.css" /></head>
<body>
<form target="main" action="SearchResults.xsql" method="post">
<center>
<table border="0">
<tr>
<td>
<img src="images/Store.gif"/>
</td>
<td>
<table>
<tr>
<td>
<xsl:text>Search the store:</xsl:text>
<input type="text" name="find"/>
</td>
</tr>
<tr>
<td>
<xsl:for-each select="store/shop">
<a target="main" href="ShopHomePage.xsql?id={@id}">
<xsl:value-of select="@name"/>
</a>

&#160;&#160;
</xsl:for-each>
</td>
</tr>
</table>
</td>
</tr>
</table>
</center>
</form>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
By tying the StoreTop.xsql and the FeaturedItems.xsql pages into an HTML frameset with a little
Store.html document like this:
<html>
<head>
<title>XML Book XSQL Sample Site</title>
</head>
<frameset border="no" rows="90,*">
<frame frameborder=no noresize name="top" src="StoreTop.xsql">
<frame frameborder=no name="main" src="FeaturedItems.xsql">
</frameset>
</html>
we end up with the clean, simple site in Figure 17.10
that shows products in a uniform way across
the whole site.
Figure 17.10. Everything.com home page


17.2 Building a Personalized News Portal
In this section, we'll build our second application—a dynamic, database-driven news portal that
derives its content from user preference information.
17.2.1 News Category Paging Example
Web-based applications frequently present information to the user in pages. Just consider some
sites you might be familiar with. If you perform a search at Amazon.com, WilliamsSonoma.com,
or Google.com, you will see your search results a page at a time with a little paging widget that
shows you where you are. As Figure 17.11
illustrates, each site has a slightly different look to its
paging widget, but each one offers the same functionality.

×