Templates

T

When creating a web application, it is sometimes useful to allow the basic page structure to be changed with a template, so that changes larger than those solely possible with stylesheets can be made without the need for a recompile.

For websites generated by a Kaya webapp, it is sometimes useful to be able to write large chunks of content without embedding that content inside the application, but retain the ability to call Kaya functions for mini-templates (for example, the generator for this site has functions for generating links to the API documentation).

The readFromTemplate function provides a basic method for constructing and interpreting these templates. This function is similar to readFromString with the major differences that TagSoup parsing mode is not supported (templates should be well-written) and custom tags can be specified to be replaced with the results of a Kaya function call.

Assigning function calls to custom tags

The mapping between custom tags and function calls is stored in a Dict with the key being the tag name, and the value being a function that takes a list of pairs of Strings, and returns an ElementTree. Combined with the normal whitelist and doctype parameters, this sets up the template parser.

Template tags can be given attributes, which will be passed in the list of pairs in ‘name, value’ order. The tag:

<MyTemplate attr1='foo' attr2='bar'>

will have attributes passed to the function associated with “MyTemplate” as

[("attr1","foo"),("attr2","bar")]

Constructing template functions

A template function must return a well-constructed block of HTML. The easiest way to do this is shown in this example:

public ElementTree myTemplate([(String,String)] attributes) {
    top = anonymousBlock; // makes a temporary holder for the template
    // create the template HTML block (in this case a paragraph, 
    // but it could be anything)
    template = addParagraph(top,"Test: ");
    // some code to fill in the paragraph goes here 
    return template; // returns the template 
    // (and discards the temporary holder)
}

A template function may call further instances of readFromTemplate. For example, there might be a high-level page template, with a content template function that read the content from a page-specific template file (with different template functions). The use of a separate template dictionary is recommended to avoid accidentally adding an infinite loop.

Writing templated documents

A templated document simply contains the appropriate HTML, with additional tags added for templating. These tags are always empty.

<MyTemplate> <!-- allowed -->
<MyTemplate /> <!-- allowed, but only recommended
                    if you're using some sort of XML-based
                    generator for the template itself -->
<MyTemplate attr1='val1' attr2="val2" attr3=val3> <!-- allowed,
                    (though you should always quote attributes! -->
<MyTemplate>Some content here</MyTemplate> <!-- allowed, but doesn't
                    mean what you expect -->
<MyTemplate content='Some content here'> <!-- the right way to do
                    the last one -->

Unless the WhiteList is Unchecked, the template containing text will be processed without Exceptions, as the </MyTemplate> will be ignored as a disallowed tag. The result will be that “Some content here” appears after the results of processing the template, rather than within it.

An example

This simple example allows links to Kaya API documentation to be easily added to a HTML document

ElementTree functionLink([(String,String)] attributes) {
    top = anonymousBlock;
    version = "latest";
    module = "";
    function = "";
    for attrib in attributes {
        if (attrib.fst == "module") {
             module = attrib.snd;
        } else if (attrib.fst == "function") {
             function = attrib.snd;
        } else if (attrib.fst == "version") {
             version = attrib.snd;
        }
    }
    url = "http://kayalang.org/library/"+version+"/"+module+"/"+function;
    link = appendInlineElement(top,Hyperlink(url),module+"::"+function);
    return link;
}

Void readTemplate(ElementTree body,String page) {
    templates = Dict::new();
    add(templates,"function",@functionLink);
    readFromTemplate(body,page,AllElements(Safe),HTML4Strict,templates);
}

This could then read the template file below.

<p>The <function module='Dict' function='new'> function
creates a new dictionary.</p>

Resilience and security

All template functions must be able to deal with any value in the attribute lists, including unexpected missing attributes. The function above could be improved in this way. Whether the missing attribute is replaced by a default or an Exception is thrown when parsing the template will depend on the circumstances.

If untrusted users are able to write template input (as web applications for collaborative sites might require) it is important that any template functions called in this way thoroughly check user input. For example, the function above would need to check that the module and function pointed to really existed (or at the very least, did not contain characters not allowed in Kaya module or function names).

Insecure templating may provide a way to bypass Kaya’s protection against cross-site scripting attacks (notice in the example above that the WhiteList prevents direct insertion of hyperlinks into the content, but the template function is not bound by this restriction. It may in many cases – especially since the template input must be of a better quality of HTML than many users will provide – be best to use another method for handling user input.

Recent Comments

No comments to show.

Pages