XSL Coding

Documentation on writing XSL

February 22, 2016

Here is my running documentation for coding XSL. It’s geared towards OU Campus, but should be universally applicable.

Table of Contents

Foundation

XSL stands for eXtensible Stylesheet Language and is a subset of language sets

  • xslt (instruction elements)
  • XPath (syntax for navigating and manipulating)
  • XQuery (SQL like format - not really used in OU)

All tags in XML are yours and you need to define the tags. It is not a format in itself. HTML is a defined example based on XML.

Semantics

tag:

<a>

closing tag:

</a>

element:

<a>data</a>

tag with metadata:

<a i="metadata">data</a>

-for example, class or id name

element with no data:

<a></a>
<a/>
<a i="metadata"/>

Don’t put white space when declaring an attribute.

<!—- do -—> <a i="metadata">
<!—- don’t do -—> <a i ="metadata"/>

Naming

tags are case sensitive

  • Camel Case
  • snake_case
  • dash_case

Structure

Must begin with xml tag, no white space before hand.

<?xml?>

but include version number

<?xml version=“1.0”?>

but actually include encoding as well, see below.

Characters

Encoding Background

Apple created Unicode to include all known characters. But Unicode needs to be encoded to bytes. Use encoding utf-8 (most used)

<?xml version=“1.0” encoding=UTF-8”?>

###Encode Punctuation

In metadata,

must encode " with &quot;
' use &apos;
or can use ' if inside ", or vice-versa

In data, must encode

< with &lt;
> &gt;
& and &amp; (because of entity)

Other entity does not exist native/default. Can import others.

Comments

output comments with

<xsl:comment>this is a comment</xsl:comment>

regular comment will not output

In comments, don’t use – as text comment.

<!-- this is an illegal comment -- don't use a double dash inside a comment. -->

XSL is a transform language

  • XSL files take one piece and convert to another.
  • OUCampus: .pcf file is a normal xml file, plus a separate tag that points to where the transform goes
  • .pcf stands for publish control file

      <?pcf-stylesheet path="/xsl/numbers.xsl" extension="html"?>
    
  • pcf-stylesheet is a specific tag to OUCampus
  • Could add multiple processing instructions
  • Everything that precedes the first/primary element is called the prologue

Containment

Only 1 top level tag.

like <html>

Then nest additional elements inside the top element

Stylesheet Primary Element

In .xsl files, use stylesheet as the first/primary element (OUCampus)

<xsl:stylesheet></xsl:stylesheet><!-- or could use version 3.0 -->

Define version number

version="2.0"
<!-- or could use version 3.0 -->

“xsl:stylesheet” is an alias, but need to define the alias with namespace

xmlns:xsl=“http://www.w3.org/1999/XSL/Transform"
xmlns:xs=“http://www.w3.org/2001/XMLSchema" <!-- defines variable types --> 
exclude-result-prefixes=“xs” <!—don’t output xs -->

final element code:

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs">

Define Output

<xsl:output method="html" version="4.01" indent="yes" encoding="UTF-8"/>
<xsl:output doctype-public="-//W3C//DTD HTML 4.01//EN"/>
<xsl:output doctype-system="http://www.w3.org/TR/html4/strict.dtd"/>

remove doctype if html5

Processing vs Output

Begin element with xsl: for xsl tags and others will be default, which in our case will be assumed for output

<xsl:template match="/document">
	<html>
  		<head>
  			<title><xsl:value-of select="title"/></title>
			<!-- define variable with the path to the variable, including the element structure. document is the top level element, then title -->
		</head>
  		<body>
    			hello world
  		</body>
	</html>
</xsl:template>

Named Templates

Define a template with

<xsl:template name=“stuff”/>

Named templates is similar to functions in scripting - it doesn’t actually do anything until called/run. It is predefining some content for re-use.

Value-Of to Output Data

Define variable with the path to the variable, including the element structure.

XSL file:

<title><xsl:value-of select="/document/title"/></title>

or if document already defined above can skip the /document.

	<xsl:template match="/document">
		<title><xsl:value-of select="title"/></title>
  	</xsl:template>

or if title already defined above, can pair down to just a .

	<xsl:value-of select="."/>

PCF file:

<document>
  	<title>Example Document</title>
  	</document>

xsl:value-of strips tags and just includes data (so no HTML tags)

Metadata and @

Use slightly different syntax when pulling in metadata

PCF File:

<css type="text/css">
	span.red { color: red }
</css>

XSL file

  • use {} when inside metadata instead of just inside element
  • in value of, use @ in path for metadata

      <style type="{css/@type}">
          <!-- use @ in path for metadata -->
          <xsl:value-of select="css/@type"/>
          <xsl:value-of select="css"/>
      </style>
    
      <!-- or -->
    
      <xsl:apply-templates select="$superheroes/profile[@source='Marvel']">
    

And use @ on match template as well

<xsl:template match="@heading">

Predicates

Filter secondary information with [ ]

<xsl:apply-templates select="student[number cast as xs:integer ge 10 and number cast as xs:integer le 20]">
<!-- Just pull students with number between 10 and 20 -->

select="superheroes/profile[1]/element()"
<!-- shorthand for position = 1 -->

select="superheroes/profile[position()=1]/element()"
<!-- longhand for position = 1 -->

INJECT XPATH INTO HTML WITH {}

<span class="label label-{}">   <span class="label label-{if(ancestor::profile/@source = 'Marvel') then 'danger' else 'default'}">

Copy-of vs Apply-Templates vs Call-Template

copy-of may pull in namespace into output

apply-template will sift through details for additional parsing and matching templates - passive, but potential to be smarter

call-template for more procedural dropping in content

Import Element With or Without Tags

xsl:value-of strips tags and just includes data (so no HTML tags) select=”.” strips tags and just includes data

<xsl:value-of select="structure/." />

to extract data and tags use

<xsl:copy-of select="structure/node()"/>

Applying Templates (match)

  • Define how an element should be transformed
  • Can be used instead of for-each
  • Modular - use for when content is embedded in other content
  • Use for when content should be reused
  • Can still pass parameters
  • Can target specific elements within template, and sort
  • When target is embedded in other content
  • Priority
    • Import precedence
    • Highest priority based on priority attribute
    • Specific definition
      • Node() < specific element

Format

<xsl:template match="course"/>

Using Modes

  • Transforming source content into different output
  • Takes effect before priority rules

Example Rule:

<xsl:template match="course" mode="table"/>
	<tr>
		<td><xsl:value-of select="id" /></td>
		<td><xsl:value-of select="title" /></td>
	<tr>
</xsl:template>

Example Call:

<xsl:apply-templates select="/courses/course" mode="table"/>

Functions

xslt tag:

<xsl:function ></xsl:function>
  • Requires name attribute - unique identifier that utilized a namespace
    • Could have same name, but would need different paramaters
  • Parameters/arguments: cannot have default values
  • build output in the sequence construtor

Define function Example

<xsl:function name="ou:hello" as="xs:string">
	<xsl:param name="friend" as="xs:string" />
	<xsl:value-of select="concat('Hello ', $friend, '!')" />
</xsl:function>

Call a function

  • XPath
  • Pass all parameters in order
  • Correct data type

Call function example

	<h3>
  		<xsl:value-of select="ou:hello-friend('Robert')"/>
  		<!-- or -->
  		<xsl:value-of select="ou:hello-friend(powers/text())"/>
	</h3>

Function or Template

  • Function
    • Best for enhancing XPath functionality
    • Great for string/data
    • stricter requirements
  • Named Templates
    • structured xml
    • default values ok
    • don’t need to pass parameters

Passing Parameter

  1. Define template with parameter

     <xsl:template name="hero-graphic">
         <!-- define parameter -->
         <xsl:param name="image-url" />
         <!-- background image css in head -->
         <style>
             .background-image-container {
                 background-image: url(<xsl:value-of select="image-url"/>);
             }
         </style>
     </xsl:template>
    
  2. Call template and define parameter

     <xsl:call-template name="hero-graphic" >	
         <xsl:with-parameter name=“image-url" select="/document/img/@src" />
     </xsl:call-template>
    

Notes on passing parameters

  • Multiple parameters can be passed
  • Paramater names need to match (order is just for readability)
  • Variable types should match
  • Can be set with default value

Templating

Cascading Templates

When apply or call templates of same name, location precedence trumps. Import gets trumped by code in same document. Could use this feature to leverage for overrides. For example, build out default page and XSL on common.xsl. Then put template overrides on template-specific.xsl

Modular Templates

Break up processes into separate templates. And then apply by nesting.

Key Code

<xsl:apply-templates select="css"/>

xsl with content

<xsl:template match="/document">
  <html>
      <head>
          <title><xsl:value-of select="title"/></title>
      </head>
      <xsl:apply-templates select="css"/>
      <body>
          <span class="green">hello world</span>
      </body>
  </html>
</xsl:template>

<xsl:template match="css">
  <style type="{@type}">
      <xsl:value-of select="@type"/>
      <xsl:value-of select="."/>
  </style>
</xsl:template>

Break HTML pages into XSL templates

Template Types

Consider separating layouts by

  • Common HTML structure
  • Use caes
  • Functionality

Break Down Page Into

  • PCF properties (OU feature)
  • common.xsl
  • global include
  • template specific.xsl
  • global include
  • content regions

XSL or Include

  • XSL Template
    • Structure, non content
    • Output might change depending on folder or template - can write those conditions into XSL
    • Update requires site publish (OU feature)
  • Include
    • Content, not structure
    • Good for updating easily
    • Publish single file, entire site is automatically updated

XSL or Server Side Script

  • XSL
    • Content doesn’t need to change very often
    • Logic is based on location or section
    • Example: related content sidebar
  • Server Side Script
    • Content needs to be updated on page load
    • Source content located externally
    • Example: latest news from blog or social media

OU Specific Templating

Directory

Set Directory (folder variable) and then modify output

  • Prep Variable in common.xsl

      <xsl:param name="ou:theme-color" />
    
  • Add actual value in Directory (through interface)
    • variable name - theme color
    • value with actual value
  • Insert actual value
    • Test if string exists, then insert

      <xsl:if test="string-length($ou:theme-color) gt 0">
         	<xsl:value-of select="$ou:theme-color" />
        </xsl:if>
      

Edit Fields

In the .pcf

<ouc:div label="column2" group="Everyone" button-text="Column2">
	<ouc:editor csspath="/workshop-xsl-advanced/xtras/wysiwyg.css" cssmenu="/workshop-xsl-advanced/xtras/styles.txt" wysiwyg-class="maincontent"/>
	<p>column2</p>
</ouc:div>

label is the variable button-text appears on edit window after the editor comes the value

In the .xsl

<xsl:copy-of select="ouc:div[@label='column2']" />

Format Output

Date Type

Initial Format

<xsl:value-of select=“date"/>

Change Format

<xsl:value-of select="format-date(date, '[M01]/[D01]/[Y0001]')"/>

Add Attribute on Output

add/replace attribute

xsl:attribute

Even/Odd

Test if even

(position() - 1) mod 2 eq 0

position() - 1 shifts to 0 based number system mod 2 divides by 2 if remainder equals 0, then the number is even and the test is true.

Include within if statement

	<tr>
		<xsl:if test="(position() - 1) mod 2 eq 0">
			<xsl:attribute name="class">even</xsl:attribute>
		</xsl:if>
  	</tr>

Add class based on url depth

<li>
	<xsl:attribute name="class">depth-<xsl:value-of select="string-length(concat($link)) - string-length(translate(concat($link), '/', ''))"/></xsl:attribute>
	<xsl:value-of select="$title"/>
</li>
  • Determine depth by subtracting / from the URL
  • Add attribute to selector

Trim String

Just show 2 characters and beyond

<xsl:value-of select="substring($class, 2)"/>

Tokenize and Normalize

tokenize(normalize-spaces($text, ' '))
  • Tokenize breaks up strings into separate pieces
  • Normalize removes spaces

Add Space

If you need to print a space, &nbsp; will cause an error use. Try:

<xsl:text> </xsl:text>

Sort

Sort output by applying a sort command immediately following pulling the data.

<xsl:apply-templates select="student">
	<xsl:sort select="name" order="descending"/>
</xsl:apply-templates>

Conditionals

If

  • Simple check against condition
  • If true
    • Content enabled
    • No else statement

    <xsl:if test=“$x = 4”> </xsl:if>

Choose

  • 1 or 1+ when
  • First match condition
  • 0 or 1 otherwise (equivalent to else)

      <xsl:choose>
          <xsl:when test="">
          </xsl:when>
          <xsl:otherwise>
          </xsl:otherwise>
      </xsl:choose>
    

XPath Conditional

  • Used inside XPath expression - like an attribute
  • Need if(), then, else 0 could put else as empty
  • Can nest

      <xsl:value-of select="
          if ($temp le 0)
              then 'Solid'
          else 
              if ($temp lt 100)
                  then 'Liquid'
              else 'Gas'
      "/>
    

For-Each

  • Iterate for all matching in select
  • Cctive context is current iteration’s matched items
  • Can pass parameters
  • Output is concatenated - may need variabalize to adjust

      <xsl:for-each select="items/item"> </xsl:for-each>
    
  • Sort directly after for-each with

      <xsl:sort select=“[XPath]" order=“descending"/>
    

For-Each-Group

  • Output groups
  • Sort will sort groups
    • Could for-each items within to sort items
    • Or sort prior to for-each-group
  • Many grouping methods

      <xsl:for-each-group select="items/item" group-by="type"> </foreach>
    

Operators

  • not()
  • and, or
  • gt, ge, lt, le
  • ne for not equal
  • '' for empty

Examples

<xsl:when test="$days ge xs:dayTimeDuration('P90D') ">
<xsl:if test="$class ne ''">

String length

Check for length

<xsl:if test="string-length($ou:color-theme) gt 0">

Ends with

Check if string ends with string

<xsl:template match="a[ends-with(@href, '.pdf' ) or ends-with(@href, '.doc' ) or ends-with(@href, '.docx' )]">
	<!-- do something -->
</xsl:template>

First

Check if first of type

<!-- matches a tag with blockquote-last closing quote-->
<xsl:template match="blockquote/p[1]">
	<p class="{@class}"><span class="blockquote-start">&#8220;</span><xsl:apply-templates /></p>
</xsl:template>

Last

Check if last of type

<!-- matches a tag with blockquote and adds a start quote to first paragraph-->
<xsl:template match="blockquote/p[last()]">
	<p class="{@class}"><xsl:apply-templates /><span class="blockquote-mark">&#8221;</span></p>
</xsl:template>

Only

Check if no siblings

<!-- matches a tag with blockquote and adds a start quote to first paragraph-->
<xsl:template match="blockquote/p[not(preceding-sibling::p) and not(following-sibling::p)]">
	<p class="{@class}"><span class="blockquote-start">&#8220;</span><xsl:apply-templates /><span class="blockquote-mark">&#8221;</span></p>
</xsl:template>

Variables

Create variable

<xsl:variable name="days">
	value
</xsl:variable>

if global variable, put it at top. can’t be changed. if local variable, put inside tag. changes based on context.

Variable Types

XML is a strongly typed language, meaning that variables have specific types.

Define Variable Type with as

<xsl:variable name="days" as="xs:dayTimeDuration">

Test If Data is Within Data Type

<xsl:when test="date castable as xs:date">

Change Variable Type

Change variable type with select

<xsl:value-of select="xs:dayTimeDuration('P90D')"/>

Or Transform Data for Another Data Type

<xsl:value-of select="date cast as xs:date "/>

Calculate variable value

Global Variable

<xsl:variable name="today" select="current-date()"/>

Equation

	<xsl:variable name="days" as="xs:dayTimeDuration">
		<xsl:value-of select="$today - date"/>
  	</xsl:variable>

The variable days is today - date

Call variable with $

<xsl:value-of select="$today"/>

Loops

<xsl:for-each select="">
</xsl:for-each>
  • Use ancestor:: to back out of a directory. Similar ../ in PHP.
  • There are also rules for preceding, following, etc.

OU Campus 2016 Workshop Materials.