Section 5.2.  Stylesheet first steps

Prev don't be afraid of buying books Next

5.2 Stylesheet: first steps

5.2.1 Setting up the environment

A typical transformation stylesheet is a much bigger piece of code than anything we've seen so far. You'll have to take care about a lot of things. In the rest of this chapter, we examine the principal parts of a stylesheet one by one, with the last section ( 5.7 ) presenting a complete, though bare-bones, stylesheet that can be used with our example XML sources from Chapter 3.

Preliminaries. Let's take a look at the stylesheet's opening lines (Example 5.2). An internal DTD subset is added in case we want to use mnemonic character references ( , page 75) for special characters . The namespace declarations in xsl:stylesheet cover XSLT 2.0 (the xsl prefix), our custom XSLT functions (the eg prefix), and a number of extensions written in Java for this stylesheet (we will talk about them later). Most of the setup work is done in our _lib.xsl shared library ( 5.1.1 ); here we just import it.

Output settings. There are three xsl:output declarations. The first one is unnamed; it sets the parameters of serializing the default output (i.e., the HTML pages we are creating). The second declaration will be used for plain text files we will be creating (e.g., the CSS style sheet), and the third one, for auxiliary XML documents (e.g., the SVG files for graphic generation, 5.5.2 ).

Example 5.2. Here our journey starts: The first lines of the main transformation stylesheet.
 <  ?xml version="1.0" encoding="iso-8859-1"?  > <  !DOCTYPE xsl:stylesheet [  <  !ENTITY copy "&#169;"  >   <  !ENTITY nbsp "&#160;"  >   <  !ENTITY mdash "&#8212;"  >  ]  > <xsl:stylesheet     xmlns:xsl=  ""  version=  "2.0"  xmlns:eg=  ""  xmlns:files=  "com.projectname.xslt.files"  xmlns:graph=  "com.projectname.xslt.graph"  xmlns:text=  "com.projectname.xslt.text"  extension-element-prefixes=  "eg files text graph"  > <xsl:import href=  "_lib.xsl"  /> <xsl:output method=  "html"  encoding=  "US-ASCII"  indent=  "no"  doctype-public=  "-//W3C//DTD HTML 4.0 Transitional//EN"  /> <xsl:output name=  "txt"  method=  "text"  encoding=  "iso-8859-1"  omit-xml-declaration=  "yes"  /> <xsl:output name=  "xml"  method=  "xml"  encoding=  "iso-8859-1"  indent=  "yes"  /> 

5.2.2 Page skeleton

Usually, the first template in a stylesheet is the one that matches the source's root element and builds the skeleton of the output HTML page. It is a typical pull-oriented trunk template and the main processing hub of the stylesheet. Example 5.3 gives an outline.

This template intersperses the basic elements of a web page ( head , body , and the top-level layout structures) with calls to templates that fill in the content. For those bits of content that are based on the XML source of the page, we launch applicable templates using xsl:apply-templates with appropriate select and sometimes mode attributes. For static content that does not depend on the XML source, we run callable templates using xsl:call-template instructions.

Example 5.3. Stylesheet's first template matches /page and builds the skeleton of the output HTML page.
 <xsl:template match=  "/page"  >   <xsl:message>  processing:  <xsl:value-of select=  "$current"  />   </xsl:message>   <!--  Credits are in XML comments,   so they can come before the root element:  -->   <xsl:call-template name=  "credits"  />   <html>     <head>       <xsl:apply-templates select=  "@keywords  @description"  mode=  "meta"  />       <xsl:call-template name=  "title"  />       <xsl:call-template name=  "css"  />       <xsl:call-template name=  "javascript"  />     </head>     <body bgcolor="#ffffff" background="  {$target-img}  bg.gif">       <!--  Main layout table starts here  -->       <table style="table-layout: fixed; width: 100%;"          cellpadding="0" cellspacing="0" border="0">         <xsl:choose>           <xsl:when test=  "$frontpage"  >             <!--  Static content (logo, etc.) specific to the front page  -->           </xsl:when>           <xsl:otherwise>             <!--  Static content specific to a subpage  -->           </xsl:otherwise>         </xsl:choose>         <!--  Layout code...  -->         <!--  Calling menu template:  -->         <xsl:call-template name=  "top-menu"  />         <!--  More layout code...  -->         <!--  Processing content blocks:  -->         <xsl:apply-templates select=  "block[not(@idref)]"  />         <!--  More layout code...  -->         <!--  Processing orthogonal blocks:  -->         <xsl:apply-templates select=  "block[@idref]"  />         <!--  Inserting page footer:  -->         <xsl:call-template name=  "page-footer"  />         <!--  Closing main layout table:  -->       </table>     </body>   </html>   <!--  Creating another version of the page:  -->   <xsl:result-document format=  "html"  href=  "{$out-path}{$lang}{$current}-print{$out-ext}"  >     <xsl:message>  creating printer-friendly page  </xsl:message>     <html>       <head>         <xsl:call-template name=  "title"  />         <xsl:call-template name=  "css-printable"  />       </head>       <body>         <!--  Inserting a simple page header:  -->         <xsl:call-template name=  "printable-head"  />         <!--  Processing content blocks:  -->         <xsl:apply-templates select=  "block[not(@idref)]"  />         <!--  Inserting page footer:  -->         <xsl:call-template name=  "page-footer"  />       </body>     </html>   </xsl:result-document> </xsl:template> 

Diagnostic output. Since this template will always run for any page documentand it will be executed only once per stylesheet runit is a good place to output any runtime xsl:messages to the terminal (see 4.5.3 for why you cannot put them into, e.g., global variable declarations). Alongside credits or copyright notices, you might want to display some important global variable values such as $current , $lang , or $env .

Page versions. This template is also where you can create versions of the page with the same or similar content but different formatting. For example, many sites provide a "printer-friendly" version of each pageno menu, no ads, no rigid table layout, just the body of content and a copyright notice. This is what the last part of Example 5.3 demonstrates : A second, much simpler HTML page skeleton wrapped into an xsl:result-document includes only direct (not orthogonal) blocks. Special templates are called to insert a printer-friendly CSS style sheet and a minimal header (containing, e.g., the site's logo and a link back to the full version of the page).

Other variants might include a paginated version of a document, where a single source XML document is transformed into a series of linked HTML pages. Under Cocoon (Chapter 7), it may be more convenient to remove the page version code into separate stylesheets run by different pipelines.

Layout tips. You can use any of the practical HTML methods of laying out a web page. Most commonly, it is a page-wide layout table , but you can also create a frameset or simply use CSS, perhaps with some floating or absolutely positioned blocks (the last option is the simplest to maintain and most standards-compliant).

Whatever your chosen layout method may be, make sure that each block's output fits nicely into the framework of the page layout. For example, if you have a frameset , make block s output properly nested frame s into separate documents.

With a table-based layout, avoid producing partial tables. It is a much more robust solution to let the main layout template produce a complete table with all tr s and td s in place. Subordinate templates will then spit out complete tables, too, to be embedded into the cells of the main layout table. An extra level of nested tables is an acceptable price for sane, readable templates and for avoiding the nightmare of debugging tables with fluctuating numbers of rows and columns .

5.2.3 Static templates

CSS. Let's see what a static (i.e., generating the same content every time) callable template might look like. Example 5.4 shows a simple CSS template to be called from the page skeleton template.

Example 5.4. The CSS template is static and callable.
 <xsl:template name=  "css"  >   <link rel="stylesheet" href="  {$target-path}  site.css"/>   <xsl:message>  creating:site.css  </xsl:message>   <xsl:result-document href=  "{$out-path}site.css"  format=  "txt"  >  body      {font-family:serif;background-color:#ffffff;}   h1        {font-family:sans-serif;font-size:120%;   font-weight:normal;}   a         {text-decoration:none;border-bottom:2px dotted  ;  color:#003399;}   .footer   {font-size:80%; margin-top:1em; margin-left:0.5em;}   .sideitem {text-align: middle; font-size:70%; padding-top:1em;   margin:0.5em; text-transform: uppercase;}  </xsl:result-document> </xsl:template> 

This template links up the external CSS style sheet (using HTML's link element) and immediately proceeds to creating this CSS file. This style sheet is shared by all pages of the site; if some pages need to override it, they can do so in their embedded style elements (although this is rarely needed).

Note that to access the CSS file in link , we prepend $target-path to its name. For creating the file, however, we use $out-path as the pathname prefix. See , page 189 for a description of these variables .

The benefits of automation. Why create CSS from XSLT and not just use a static CSS style sheet? Partly, this is the desire to have everything controlled from one place (the transformation stylesheet). There are other benefits as well: With complex CSS specifications, you may want to automate CSS generation using XSLT loops and master document variables for repeating or regularly changing values of colors, lengths, etc.

JavaScript. The JavaScript static template, if you need one, may be similar. Any JavaScript code used on more than one page can also be placed in an external script file to be imported with <script src="URI"> into those HTML pages that need it.

5.2.4 Miscellaneous pieces, from title to footer

Title. Our next template, title , is not staticwe need to write some XPath to build the value we want. This callable template composes the web page title from the common part stored in the master document (usually it is the company name) and the title of the current page document:

 <xsl:template name=  "title"  >   <title>     <xsl:value-of select=  "   $master//html-title/translation[@lang=$lang]/text()"  />     <xsl:text>  :  </xsl:text>     <xsl:value-of select=  "title/text()"  />   </title> </xsl:template> 

Any other information can be just as easily added to the title. For example, you could program the template to extract the appropriate translation from the label of that page's parent in the master document menu (see Example 3.2, page 143). Sometimes, the keywords of a page are also added to the title to boost the page's rank in web searches on these keywords.

Metadata. The templates for the page's keywords and description are applicable, not callable:

 <xsl:template match=  "page/@description"  mode=  "meta"  >   <meta name="description" content="  {.}  "/> </xsl:template> <xsl:template match=  "page/@keywords"  mode=  "meta"  >   <meta name="keywords" content="  {.}  "/> </xsl:template> 

All they do is convert the attributes of the page element into the corresponding HTML meta elements. The interesting point, however, is that you might want the same source data to trigger other processing as well. For example, you may want to insert the @description not only into the meta but also into the body of the page.

Such collisions are resolved by using template modes. XSLT allows you to create any number of templates with the same match but different values of the mode attribute. Then, you can call the necessary template with its mode supplied in the apply-templates instruction.

Bottom stuff. The last example in this section illustrates how applicable templates can be applied not only to the current source document but to any otherin this case, to the master document. Itself, the page-footer template is callable, but it takes whatever elements are inside the page-footer element in the master and pushes them into our stylesheet.

Then, an applicable template with an appropriate match (e.g., match="page-footer/*" ) grabs these pushed elements and processes them, unaware of where they come from. Below, such an applicable template selects the relevant translation and outputs a separator (a character surrounded by two no-break spaces) after every item except the last.

 <xsl:template name=  "page-footer"  >   <p class="bottom">     <xsl:apply-templates select=  "$master//page-footer/*"  />   </p> </xsl:template> <xsl:template match=  "page-footer/*"  >   <xsl:apply-templates select=  "translation[@lang=$lang]"  />   <xsl:if test=  "following-sibling::*"  >&nbsp;&nbsp;</xsl:if> </xsl:template> 

Alternatively, we could combine these two templates into one and use an xsl:for-each loop in the page-footer callable template to iterate over all page-footer children. But why fiddle with manual loops when we can get the same result from XSLT's built-in template-matching mechanism for free?


XSLT 2.0 Web Development
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2006
Pages: 90

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: