Jump To: Support > KB > Template
TemplateEngine
TemplateEngine is used as the core of a number of Precedence software products such as FileSurfer and NetManager Intranet. It is also used for many of our custom developments.
TemplateEngine allows the look and feel of dynamic webpages to be separated from the software that generates the pages. This means that the user interface can be significantly altered without any specific programming skills beyond the HTML and CSS necessary for the design work.
Introduction
The most common usage is for a template HTML file to be loaded by the software and then processed before sending the output to the web-browser. Most of the HTML/text contained will be sent through without being touched, however some characters have special meaning. The software itself creates what is known as a context. This contains the dynamic parts of the website. The context and template are combined to form the output, i.e. the template is static, but the context differs from page to page. More importantly, the information that the context contains will differ between different software. Therefore, this page will not document the details of the context, merely how it will be used. While TemplateEngine is usually used to generate HTML files for a web-browser, it can be used to generate any types of text file. For example, if your context contains data in an array, you could generate an HTML table, a CSV file, an XML file or JSON output just by switching to a different template without needing to alter the context.
Basic substitution
Text surrounded by [[ and ]] will be used to look up an item from the context.
Let us assume the template contains:<title>[[title]]</title>
...and that the context is specified as:
<title>My Web Page</title>
Multi-dimensional arrays can be accessed by separating the subscripts with commas, e.g. [[person,name]] will be replaced by context['person']['name'].
Literal blocks
You can surround blocks which should not be processed further with {{@ and @}}. This will avoid errors due to unintentional matches such as [[sdf*sdf]]
Conditional substitution
The character pairs {{ and }} are used to separate out blocks of text. You may use two question marks to define a conditions when this block will be displayed.
The simplest is whether a name is set in the context: {{?unread?You have unread emails}}
If unread is unset or set to zero (or false), then this will output nothing. However if unread has a value, the string You have unread emails will be output.
{{}} sections can span multiple lines and line breaks within these will be maintained in the output:
{{?option?<tr>
<td>line</td>
</tr>
}}
Remember you can also include normal substitutions (and even other nested conditional blocks):
{{?error?<h1>Error: [[error]]</h1>}}
You can use ! to reverse the logic:
{{?!unread?You have read all your email}}
If/Then/Else can be used by surrounding the Then and Else blocks with more {{ and }} characters. For example:
This option is currently {{?option?{{enabled}}{{disabled}}}}
Always remember to close off your [[ and {{ tags correctly.
Class functions
| function | returns |
|---|---|
| [static] singleton() | the (existing) class instance |
| [static] checkVersion($minimum) | is $minimum newer than the current class version |
| combine($template, $context, $emptytag = null) | the string $template, rendered with $context, use $emptytag for empty output |
| render($files, $context, $reload = false) | the string from file(s) $files, $rendered with $context, $reload disables file caching |
| reverse($html, $recurse = false, $tag = null) | return some context, from $html, $recurse through child elements, starting at $tag |
Conditional operators
| operator | returns |
|---|---|
| a==b or a:eq:b | a equals b |
| a!=b or a:ne:b | a is not equal to b |
| a<b or a:lt:b | a is less than b |
| a>b or a:gt:b | a is greater than b |
| a<=b or a:le:b | a is less than or equal to b |
| a>=b or a:ge:b | a is greater than or equal to b |
| a:=b | a contains substring b |
| a:|b | a contains one of | delimited substrings in b |
| a=|b | a equals one of | delimited strings in b |
| a:>b | uppercase a equals b |
| a:<b | lowercase a equals b |
| a%b | eval(a mod b) |
Strings should be quoted with single quotes (numeric values can be used as-is). For example, {{?food=='apple'?{{Apple}}{{Not apple}}}}
Context metadata
The context array supplied will have the following values added to it.
| key | value |
|---|---|
| _combines | the number of times a context was combined with a template |
| _emptytag | the value set to be used in place of empty output |
Calling PHP functions
PHP functions can be called by using the * character in your context lookups:
[[func*arg1]]
will be replaced by value of function func([[arg1]]).
Multiple arguments can be given separated by *, e.g.
[[func*arg1*arg2]]
will be replaced by func([[arg1]], [[arg2]])
If a function has no arguments, that's also fine:
[[func*]]
will be replaced by value of function func().
It is OK to call your own custom functions, but you will need to register them with the registerCallBack() method, i.e.
{
return str_repeat($str, $count);
}
$te = new TemplateEngine();
$te->registerCallBack($this, 'mycallback');
$template = "** [[mycallback*str*count]] **";
$context = array('str'=>'#', 'count'=>5);
echo $te->combine($template, $context);
#####
You can hardwire the value of an argument with single quotes. Numeric values are always hardwired, e.g.[[date*'j M Y g:i a'*1208419682]] will return date('j M Y g:i a', 1208419682) which is 17 Apr 2008 9:08 am
Looping over an array
Sometimes the context contains lists of data within a single name. These are looped over by using the | operator.
Arrays can be single-dimensional, so the following:
$template = '<ul>{{|fruits|{{<li>[[fruits]]</li>}}}}</ul>';
echo templateEngine::singleton()->combine($template, $context);
Will output:
- Apple
- Banana
- Fig
Associative arrays can also be used with the :key meta modifier, e.g.
'Apple' => 20,
'Banana' => 24,
'Fig' => 10
);
$template = 'I have [[fruits:count]] fruits for sale:<ul>{{|fruits|{{<li>[[fruits:key]] = [[fruits]]p</li>}}}}</ul>';
echo templateEngine::singleton()->combine($template, $context);
Will output:
I have 3 fruits for sale:
- Apple = 20p
- Banana = 24p
- Fig = 10p
Looping over nested/multi-dimension arrays
Example code of an array of arrays:
array('name'=>'Apple', 'type'=>'fruit'),
array('name'=>'Banana', 'type'=>'fruit'),
array('name'=>'Cheese', 'type'=>'dairy')
);
$template = '<html><body>I like to eat:
<ul>
{{|item|{{<li>Item [[item,name]] of type [[item,type]]</li>}}}}
</ul>
</body></html>';
echo templateEngine::singleton()->combine($template, $context);
Will output:
I like to eat:
- Item Apple of type fruit
- Item Banana of type fruit
- Item Cheese of type dairy
The above can be extended to deal with nested or multi-dimensional arrays. For example:
$context['how'][0]['item'] = array(
array('name' => 'Apple', 'type' => 'fruit'),
array('name' => 'Banana', 'type' => 'fruit'),
array('name' => 'Cheese', 'type' => 'dairy')
);
$context['how'][1]['method'] = 'drink';
$context['how'][1]['item'] = array(
array('name' => 'Apple juice', 'type' => 'fruit'),
array('name' => 'Beer', 'type' => 'alcohol'),
array('name' => 'Cherryade', 'type' => 'soda')
);
$template = '<ol>{{|how|{{<li>I like to [[how,method]]:</li>
<ul>
{{|how,item|{{<li>Item [[how,item,name]] of type [[how,item,type]]</li>}}}}
</ul>}}}}
</ol>';
echo templateEngine::singleton()->combine($template, $context);
Outputs:
- I like to eat:
- Item Apple of type fruit
- Item Banana of type fruit
- Item Cheese of type dairy
- I like to drink:
- Item Apple juice of type fruit
- Item Beer of type alcohol
- Item Cherryade of type soda
Looping over a subset of an array
The ^ operator allow you to use an array to select a subset of items from another array. So in the following example, the item array contains all possible items, but the filter array contains just the keys of the items we want to output (in the correct order). These are combined using item^filter.
$context['item']['apple'] = array('name'=>'Apple', 'type'=>'fruit');
$context['item']['banana'] = array('name'=>'Banana', 'type'=>'fruit');
$context['item']['cheese'] = array('name'=>'Cheese', 'type'=>'dairy');
$context['filter'] = array('cheese', 'apple');
$template = '<html><body>I like to eat:
<ul>
{{|item^filter|{{<li>Item [[item^filter,name]] of type [[item^filter,type]]</li>}}}}
</ul></body></html>';
echo templateEngine::singleton()->combine($template, $context);
Outputs:
I like to eat:
- Item Cheese of type dairy
- Item Apple of type fruit
Variable metadata and transformation functions
A context element can have transforms (such as upper-casing) or metadata lookups (such as string length) done on it.
To do this use the syntax context:function. Supported functions as listed below:
| function | returns |
|---|---|
| count / length | array or string length |
| html | string converted to HTML with htmlentities() - no quote conversion |
| htmlq | string converted to HTML with htmlentities() - Will convert both double and single quotes |
| htmldq | string converted to HTML with htmlentities() - Will convert double-quotes and leave single-quotes alone |
| url | string URL-encoded with rawurlencode() |
| lowercase | string converted to lower case |
| uppercase | string converted to upper case |
| int | number rounded-down to nearest integer |
| bool | is true or false? Recognises true/false, yes/no, 1/0 (boolean) |
| isarray | is value an array? (boolean) |
| isfirst | is value the first array element? (boolean) |
| islast | is value the last array element? (boolean) |
| loop / counter | element number of the local array |
| key | the key for this element of an array |
| level | string representation of context depth |
| levelid | unique ID of context depth |
| loopid | unique ID of context position |
For example:
- If
fruitis set toApple,fruit:uppercasewill beAPPLE islastandisfirstcould be used to insert <ul> and </ul> tags when generating a list from an array{{|fruit|{{?!fruit:isfirst{{<br/>}}}}[[fruit,name]]}}}}will do a line-break between each element of an array, but skipping one at the start


