Click here to Skip to main content
13,352,631 members (39,609 online)
Click here to Skip to main content
Add your own
alternative version

Stats

31.4K views
784 downloads
30 bookmarked
Posted 10 May 2015

ToDoList's StyleSheets: a tutorial

, 27 Dec 2015
Rate this:
Please Sign up or sign in to vote.
In this paper, I report what I have learned and all the problems I encountered while making my very first Stylesheet for ToDoList.

Introduction

I have just built a custom StyleSheet for TDL, my very first XSLT file, it is not an easy task when you have never touch to an XSLT file. It is not a surprise that StyleSheets delivered with TDL are not suited to beginners learning, but it was a surprise to me that all the needed information was spread across so many places.

So, I write this article to put together things that I have found useful in hope it will help you to put you on track to make your own StyleSheets for ToDoList.

Keep in mind that English is still a foreign language for me. Please, feel free to report mistakes.

Background

For beginners, basic knowledge of XML format is preferable, and knowledge of HTML will help too. For advanced StyleSheets, knowledge of XML, HTML and XPath is mandatory.

The XML file format

A quick introduction to XML file format. ToDoList's TaskLists and StyleSheets are XML files.

The basic structure of an XML file is an Element, an element is made of an opening tag and a closing tag.

<TODOLIST>
</TODOLIST>

The opening tag of an element can contain Attributes, a value is associated with each attibute, the value is always a string, even numeric values.

<TODOLIST PROJECTNAME="My TaskList" FILEFORMAT="10">
</TODOLIST>

And an element can contain some text or other elements nested between its two tags.

<TODOLIST PROJECTNAME="My TaskList" FILEFORMAT="10">
  <TASK TITLE="My first Task">
    <TASK TITTLE="My sub task">
      <COMMENTS>My comments</COMMENTS>
    </TASK>
  </TASK>
  <TASK TITLE="My second Task">
  </TASK>
</TODOLIST>

An XML file is made of a single main element containing everything nested inside. By itself, an XML file is organized as a tree, the single main element is the root of the tree.

In previous snipset:

  • TODOLIST is the root element. It contains other elements.
  • TASK is an element. It contains other elements.
  • COMMENTS is an element. It contains text.
  • PROJECTNAME is an attribute of TODOLIST
  • FILEFORMAT is an attribute of TODOLIST
  • TITLE is an attribute of TASK

A TaskList contains many more elements and attributes.

For more details on XML, XSLT, HTML or XPath, I recommend w3schools.com

First, get a TDL StyleSheet example

At first, I searched for an example StyleSheet and found that the article XSL Transform for ToDoList contains a StyleSheet for TDL aimed to be an example. My first experiments were rather frustrating until I understood that the example was buggy.

Here are the corrections for the StyleSheet.

Everywhere it is needed, replace

<xsl:apply-templates />

with

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

and, replace

... "@COMMENTS"

with

... "COMMENTS"

After correction, the example works far better. The corrected file is bpsToDoListStyler_Rev019.xsl.

Experimenting with the example StyleSheet

To play with the example, I recommend to start with Tree view and get all attributes. The output is formatted in HTML and can be used to Print, Preview or Transform the TaskList. A lot of options that are commented out , just uncomment them to see what append.

How ToDoList deals with StyleSheets

TDL can use StyleSheets for operations such as Print, Export or Transform. In every cases TDL generate a specific temporary TaskList that reflect actual settings, and the StyleSheet is applied on that temporary TaskList.

The specific TaskList is build using theses critera:

  • If in Tree view, the temporary list will be a tree, if in List view, the temporary list will be flat.
  • Actual sorting will be used.
  • The dialog box let you choose which tasks are included and which attributes.

Choose the result you want:

  • Print: the output will be printed on paper.
  • Preview: The output will be shown on screen the same as it would be printed.
  • Transform: The output will be saved on disk and shown on your internet navigator if Text or HTML formats are used. The difference with Print and Preview is that the HTML is not limited to printable features. The other usage of Transform is to generate other XML files.

The temporary TaskList file is stored in directory %temp%, the file name is TaskList name followed by .intermediate.txt. So, the temp file of MyTasks.tdl will be MyTasks.intermediate.txt in the %temp% directory. Open in a text editor to see the structure and which elements and attributes are in the file.

Pitfalls with TDL StyleSheets

Empty result

The problem:

It is all in the title. The result is empty.

The answer:

First possibility, there is a compilation error. You have to find what is the error and correct it. Unfortunately, TDL does not provide anything that can give even basic clue on what is going wrong. If you don't have any kind of debugger, the best is to start with a generic working example and make incremental changes with a test to validate each change.

Second possibility, there is no match or nothing to output. First make sure your StyleSheet always output something when the root match. The easiest way to do this is by ensuring that something will be put on the result what ever the content of the TaskList. By getting something as a result, you ensure that the StyleSheet compile OK. If you don't get what you should, something is certainly wrong in the logic of the StyleSheet.

Ignored XML document with an <xsl:template match="/"> template

The problem:

Obviously, the XML tree is not walked, and if

<xsl:template match="/">

is replaced with

<xsl:template match="/TODOLIST">

, everything works.

The answer:

The whole trick is that <xsl:template match="/"> match the XML document and the child element is TODOLIST when TASK is expected.

If you have a document matching template, you also need a root matching template, because the first have to call the second.

Accents in StyleSheets

The problem:

The file encoding can be a problem when you use accents, the problem is so huge that even if only in a comment, it is enough to fail to compile. You will need to apply the right encoding.

The answer:

To solve the problem, there is 2 places to Check:

  • Declare the encoding in the xml tag:
    <?xml version="1.0" encoding="utf-8" ?>
  • In your text editor, choose the char encoding that fit your needs: UTF-8, UTF-16, iso-8859-1 ...

UTF-8 is generally a good choice. In any case, make sure both match.

Doing "apply-templates" on elements that don't have "template"

The problem:

That was the error in the example for TDL. The generic

<xsl:apply-templates />

will match any element inside the current element. It is a problem when you encounter a COMMENT (or any element that is not a TASK). Since the COMMENT have no matching template, the default behavior is applied, the text in the COMMENT is simply dump as is to output.

This is the reason of the unformatted text that appear when you run the original buggy example as in file bpsToDoListStyler_Rev018.xsl.

The answer:

Use apply-templates only for nested TASKs.

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

Nota: The same problem also apply to TDL 6.9.6 and earlier StyleSheets: Project-Overview-HTML.xsl , SimpStyler0.2.xsl , TodoListStyler_Firefox.xsl and TodoListStyler_v1.5.xsl

An alternative is to declare a template that will match everything.

<xsl:template match="*">

Be careful of what you do, it is as powerful as dangerous if not mastered, it easily lead to unwanted results.

XML StyleSheets snipsets

XML files are organized like trees and XSLT are basically about walking the tree while applying the transformation, more sophisticated transformations can be done at cost of more complicated programming if input tree structure don't match the output tree structure.

Some of the examples are using html tags, look at next chapter for explanations about html tags in a StyleSheet.

XSLT Comments

Advice: Document your xslt StyleSheets with comments everywhere you do something not obvious.

<!-- My Comments anywhere I need -->

Beginners often think that documenting is a loss of time, it is wrong. You will see that it is not a loss of time when you will come back to your code 6 months or a year later because comments will help you to recall what the code was intended to do.

StyleSheet organization

xslt is based on a template matching language. It means that there is no explicit entry point and there is no explicit chaining of matching templates, it is data driven. If more than one template match the current data, it is not an error, the right template is simply chosen at runtime by following some rules.

StyleSheet launch

A StyleSheet always start by trying to execute a template that match the whole document.

<xsl:template match="/">

If it fails, the interpreter looks for a template that match the root of the XML file. Like

<xsl:template match="/TODOLIST">

Which is a template that match only a root element named TODOLIST. Or

<xsl:template match="TODOLIST">

Which is a template that match only an element named TODOLIST, not necessary a root.

Matching Templates

Note that matching templates are not linked anywhere in the XSLT code.

<xsl:apply-templates />

will decide dynamically which matching template to call and in which order. In order to keep some control over the StyleSheet execution, it is preferable to use the syntax

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

where you specify which element you want to match.

Hello World

The classical one. See file HelloWord.xsl for complete example.

<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text"/>
    
    <xsl:template match="/">
        <xsl:text>Hello Word</xsl:text>
    </xsl:template>
</xsl:stylesheet>

The <xsl:output method="text"/> tells that the output is raw text, no formatting, no nothing, simply text.

The <xsl:template match="/"> is matching the root element of any document.

The element <xsl:text> surround whatever text you want to output

Hello ToDoList

See file TDLSS 1.xsl for complete example.

<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html" indent="yes" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" />
    
    <xsl:template match="/TODOLIST">
        <xsl:element name="html">
            <xsl:element name="body">
                <xsl:text>Hello ToDoList</xsl:text>
            </xsl:element>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

The <xsl:output method="html" ... tells that the output is an html page, with all usual formatting, at cost of a little complication.
The <xsl:template match="/TODOLIST"> will match only a root element named TODOLIST, this is the name of the root element of a tasklist.

Reading attributes values

See file TDLSS 2.xsl for complete example.

<xsl:template match="TODOLIST">
    <xsl:element name="html">
        <xsl:element name="head">
            <xsl:element name="title">
                <xsl:value-of select="@PROJECTNAME" />
            </xsl:element>
        </xsl:element>
        <xsl:element name="body">
            <xsl:value-of select="@PROJECTNAME" />
            <xsl:element name="br" />
            <xsl:value-of select="@FILENAME" />
            <xsl:element name="br" />
        </xsl:element>
    </xsl:element>
</xsl:template>

The <xsl:value-of select="@PROJECTNAME" /> output the value of the PROJECTNAME attribute.

Walking the tree

See file TDLSS 3.xsl for complete example.

<xsl:template match="TODOLIST">
    <xsl:element name="html">
        <xsl:element name="head">
            <xsl:element name="title">
                <xsl:value-of select="@PROJECTNAME" />
            </xsl:element>
        </xsl:element>
        <xsl:element name="body">
            <xsl:value-of select="@PROJECTNAME" />
            <xsl:element name="br" />
            <xsl:value-of select="@FILENAME" />
            <xsl:element name="br" />
            <xsl:element name="br" />
            <xsl:apply-templates select="TASK" />
        </xsl:element>
    </xsl:element>
</xsl:template>

<xsl:template match="TASK">
    <xsl:value-of select="@TITLE" />
    <xsl:element name="br" />
</xsl:template>

The <xsl:apply-templates select="TASK" /> is looking for any element named TASK which is child of element TODOLIST and will apply the template <xsl:template match="TASK"> to each occurrence.

Walking the tree recursively

See file TDLSS 4.xsl for complete example.

<xsl:template match="TASK">
    <xsl:value-of select="@TITLE" />
    <xsl:element name="br" />
    <xsl:apply-templates select="TASK" />
</xsl:template>


The <xsl:apply-templates select="TASK" /> is what makes the tree walking recursive.

Reading text elements values

See file TDLSS 5.xsl for complete example.

<xsl:template match="TASK">
    <xsl:value-of select="@TITLE" />
    <xsl:element name="br" />
    <xsl:text>CATEGORY: </xsl:text>
    <xsl:value-of select="CATEGORY" />
    <xsl:element name="br" />
    <xsl:text>COMMENTS: </xsl:text>
    <xsl:value-of select="COMMENTS" />
    <xsl:element name="br" />
    <xsl:element name="br" />
    <xsl:apply-templates select="TASK" />
</xsl:template>

The <xsl:value-of select="COMMENTS" /> output the value of the COMMENTS text element.

Single condition: if

See file TDLSS 6.xsl for complete example.

<xsl:if test="COMMENTS">
    <xsl:text>COMMENTS: </xsl:text>
    <xsl:value-of select="COMMENTS" />
    <xsl:element name="br" />
</xsl:if>

The xsl:if  is a do something when test is true and do nothing when test is false.

Multiple condition: choose

See file TDLSS 7.xsl for complete example.

<xsl:choose>
    <xsl:when test="COMMENTS">
        <xsl:text>COMMENTS: </xsl:text>
        <xsl:value-of select="COMMENTS" />
        <xsl:element name="br" />
    </xsl:when>
    <xsl:otherwise>
        <xsl:text>No COMMENT</xsl:text>
    </xsl:otherwise>
</xsl:choose>

The xsl:choose is a multiple condition structure. The first when structure, which is true, is executed, if no test is true, the otherwise structure is executed.

Calling a template

See file TDLSS 8.xsl for complete example.

    <xsl:call-template name="fix-breaks">
        <xsl:with-param name="text">
            <xsl:value-of select="COMMENTS" />
        </xsl:with-param>
    </xsl:call-template>
    . . .
<xsl:template name="fix-breaks">
    <xsl:param name="text" />
    . . .
</xsl:template>

Named templates <xsl:template name="fix-breaks"> are nothing else that subprograms in other languages. They are called by <xsl:call-template name="fix-breaks">

Fixing breaks

See file TDLSS 8.xsl for complete example.

<xsl:template name="fix-breaks">
    <xsl:param name="text" />
    <xsl:choose>
        <xsl:when test="contains($text,'&#13;&#10;')">
            <xsl:value-of select="substring-before($text,'&#13;&#10;')" />
            <xsl:element name="br"/>
            <xsl:call-template name="fix-breaks">
                <xsl:with-param name="text">
                    <xsl:value-of select="substring-after($text,'&#13;&#10;')" />
                </xsl:with-param>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

Multi lines text need to be fixed for html output because CRLF is not understood and must be replaced with <br/>.

Getting Tasks path

See file TDLSS 9.xsl for complete example.

<xsl:template name="get_Task_Ancestors">
    <xsl:if test="count(ancestor::TASK)>0">
        <xsl:for-each select="(ancestor::TASK)">
            <xsl:value-of select="@TITLE"/>
            <xsl:text> - </xsl:text>
        </xsl:for-each>
    </xsl:if>
</xsl:template>

This template works only if tasks are nested (tree view). It list the TITLE of parents starting from the root.

Showing Tasks nesting

See file TDLSS10.xsl for complete example.

In html, lists are the natural way to show indenting by simply nesting lists.

<xsl:template match="/TODOLIST">
    <xsl:element name="html">
        <xsl:element name="head">
            <xsl:element name="title">
                <xsl:value-of select="@PROJECTNAME" />
            </xsl:element>
        </xsl:element>
        <xsl:element name="body">
            . . .
            <xsl:element name="ul">
                <xsl:apply-templates select="TASK" />
            </xsl:element>
        </xsl:element>
    </xsl:element>
</xsl:template>

<xsl:template match="TASK">
    <xsl:element name="li">
        <xsl:text>Title: </xsl:text>
        <xsl:value-of select="@TITLE" />
        <xsl:element name="br" />
    </xsl:element>
    <xsl:element name="ul">
        <xsl:apply-templates select="TASK" />
    </xsl:element>
</xsl:template>

Simply add <ul> and <li> at right place in the code walking the tree.

Using a XML document matching template

See files TDLSS11.xsl and TDLSS12.xsl for complete examples.

Usually, we start with the root matching template, like in example file TDLSS11.xsl.

<xsl:template match="/TODOLIST">
    <xsl:element name="html">
        <xsl:element name="head">
            <xsl:element name="title">
                <xsl:value-of select="@PROJECTNAME" />
            </xsl:element>
        </xsl:element>
        <xsl:element name="body">
            <xsl:text>Root matching template</xsl:text>
            <xsl:element name="br" />
            <xsl:element name="ul">
                <xsl:apply-templates select="TASK" />
            </xsl:element>
        </xsl:element>
    </xsl:element>
</xsl:template>

But sometimes, it is needed to start with a document matching template, like in example file TDLSS12.xsl which do mostly the same thing.

<xsl:template match="/">
    <xsl:element name="html">
        <xsl:element name="head">
            <xsl:element name="title">
                <xsl:value-of select="@PROJECTNAME" />
            </xsl:element>
        </xsl:element>
        <xsl:element name="body">
            <xsl:text>Document matching template</xsl:text>
            <xsl:element name="br" />
            <xsl:apply-templates />
        </xsl:element>
    </xsl:element>
</xsl:template>

<xsl:template match="/TODOLIST">
    <xsl:text>Root matching template</xsl:text>
    <xsl:element name="br" />
    <xsl:element name="ul">
        <xsl:apply-templates select="TASK" />
    </xsl:element>
</xsl:template>

You can see that the HTML document stuff is always in the first called template, and the document matching template call the root as a child element.

Including HTML tags in a StyleSheet

When you want to format the output, the best is to use html tags.

HTML root

The root structure of an html page is something like:

<html>
	<head>
	. . .
	</head>
	<body>
	. . .
	</body>
</html>

As it appear only once in the output, it must be in the template that match the root.

    <xsl:template match="/TODOLIST">
        <xsl:element name="html">
            <xsl:element name="head">
                . . .
            </xsl:element>
            <xsl:element name="body">
                . . .
		        <xsl:apply-templates select="TASK" />
                . . .
		    </xsl:element>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

See file TDLSS 3.xsl for complete example.

CRLF in HTML

CRLF is only used in html source code and don't do anything in the rendering of the page, the <br /> tag must be used instead.

<xsl:element name="br" />

and result in

<br />

List

An html list is a simple way to show tasks nesting by indenting the tasks in output. It is easy because the nesting of tasks match the nesting of lists.

An html list is made of 2 parts:

  • a List delimiter <ul>
  • a ListLine delimiter <li>

The List delimiter surround a list and appear once per list. Its natural place is in the template that treat a parent task.

<xsl:element name="ul">
    <xsl:apply-templates select="TASK" />
</xsl:element>

The ListLine delimiter appear once per list item. Its natural place is in the template that treat a child task.

<xsl:element name="li">
    <xsl:text>Title: </xsl:text>
    <xsl:value-of select="@TITLE" />
    <xsl:element name="br" />
</xsl:element>

Download

The zip contains the examples files:

  • File bpsToDoListStyler_Rev018.xsl is the example file from article XSL Transform for ToDoList
  • File bpsToDoListStyler_Rev019.xsl is the corrected file as described there after.
  • File HelloWord.xsl, example file, see snipset
  • File HelloTDL99.xsl, examples files, see snipset

Links

History

  • 2015/05/01 First draft.
  • 2015/05/19 Added some stuff, updated download and corrections.
  • 2015/06/01 Typos
  • 2015/06/15 Added more stuff and updated download

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

ppolymorphe
Database Developer
France France
I am a professional programmer.
Problem analyse is certainly what I am best at.
My main programming expertise is in the xBase languages (dBase, Clipper, FoxPro, Harbour, xHarbour), then VBA for Excel and advanced Excel WorkBooks.

I also have knowledge on C/C++, d language, HTML, SVG, XML, XSLT, Javascript, PHP, BASICs, Python, COBOL, Assembly.
My personal interests goes to algorithm optimization and puzzles.

You may also be interested in...

Comments and Discussions

 
QuestionCustom columns in Stylesheet Pin
Member 955678827-Aug-15 0:45
memberMember 955678827-Aug-15 0:45 
AnswerRe: Custom columns in Stylesheet Pin
ppolymorphe27-Aug-15 1:18
professionalppolymorphe27-Aug-15 1:18 
Questionadd "media-type" Pin
Pierre de la Verre28-May-15 13:16
memberPierre de la Verre28-May-15 13:16 
AnswerRe: add "media-type" Pin
ppolymorphe28-May-15 15:41
memberppolymorphe28-May-15 15:41 
GeneralRe: add "media-type" Pin
Pierre de la Verre29-May-15 23:24
memberPierre de la Verre29-May-15 23:24 
SuggestionTo Readers: Your feedback is appreciated Pin
ppolymorphe15-May-15 16:14
memberppolymorphe15-May-15 16:14 
GeneralRe: To Readers: Your feedback is appreciated Pin
EtherealMonkey19-May-15 16:10
memberEtherealMonkey19-May-15 16:10 
GeneralRe: To Readers: Your feedback is appreciated Pin
ppolymorphe19-May-15 17:47
memberppolymorphe19-May-15 17:47 
GeneralRe: To Readers: Your feedback is appreciated Pin
EtherealMonkey19-May-15 19:38
memberEtherealMonkey19-May-15 19:38 
QuestionMy ah ha moment Pin
zajchapp11-May-15 16:21
memberzajchapp11-May-15 16:21 
AnswerRe: My ah ha moment Pin
ppolymorphe11-May-15 16:42
memberppolymorphe11-May-15 16:42 
GeneralRe: My ah ha moment Pin
zajchapp11-May-15 20:40
memberzajchapp11-May-15 20:40 
AnswerRe: My ah ha moment Pin
ppolymorphe11-May-15 22:54
memberppolymorphe11-May-15 22:54 
GeneralRe: My ah ha moment Pin
zajchapp12-May-15 12:05
memberzajchapp12-May-15 12:05 
GeneralRe: My ah ha moment Pin
ppolymorphe12-May-15 12:21
memberppolymorphe12-May-15 12:21 
GeneralRe: My ah ha moment Pin
zajchapp12-May-15 15:22
memberzajchapp12-May-15 15:22 
QuestionI've added a link to the wiki Pin
.dan.g.11-May-15 15:45
professional.dan.g.11-May-15 15:45 
AnswerRe: I've added a link to the wiki Pin
ppolymorphe11-May-15 16:18
memberppolymorphe11-May-15 16:18 
AnswerRe: I've added a link to the wiki Pin
Pierre de la Verre22-May-15 7:59
memberPierre de la Verre22-May-15 7:59 
QuestionRe: I've added a link to the wiki Pin
ppolymorphe22-May-15 10:44
memberppolymorphe22-May-15 10:44 
AnswerRe: I've added a link to the wiki Pin
Pierre de la Verre22-May-15 21:45
memberPierre de la Verre22-May-15 21:45 
GeneralRe: I've added a link to the wiki Pin
.dan.g.22-May-15 16:31
professional.dan.g.22-May-15 16:31 
GeneralRe: I've added a link to the wiki Pin
Pierre de la Verre22-May-15 21:45
memberPierre de la Verre22-May-15 21:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.180111.1 | Last Updated 27 Dec 2015
Article Copyright 2015 by ppolymorphe
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid