Docs Home Install Upgrade Troubleshoot Customize Admins Devs Content Query Templates Commands Skins


Overview

Query-graphic.png

ccHost 5.0 uses its own template engine to merge data queries with text layout. The graphic above shows a high level overview of how other components and data interact with the template engine.

As you can can see from the graphic, the template is a lower level component this is not called directly from URL invocation.

Instead the template engine is used in conjunction with the Query engine when displaying content stored in the database in ccHost (such as user upload meta-data or topics). The best way to understand the most common case usages of the template engine in this way is to study the query templates walk-through. For more advanced features make sure to see the Ultimate Template.

The template engine is also used when displaying pages authored outside the ccHost system as discussed in the Content section via the docs command.

Finally, the template engine is used by the ccHost Skins technology to build a consistent look and feel for pages throughout the site as well as display the results of the current command being processed.

While the template engine is not called directly, it is still important to understand it's functionality if you plan to do HTML or other rendering customization.

Variables

Every template is passed two global PHP variables:

$A - an array of all important variables
$T - a CCTemplate object (or derivation)

And don't forget to do regular variable dumps.

Types of Input

The template engine knows how to process these types of files:

  • PHP
  • HTML
  • TPL

What's a TPL File?

A TPL file is a PHP file with special macros in it. When the template engine sees a TPL extension, it will find all the macros and replace them with corresponding PHP.

For geeks: the template engine will run TPL files through a big preg_replace then treat the file as it would any PHP.

TPL Macros

The TPL macros were designed to reduce the typing for template writer. They are strictly meant as a convenience. If you are only hacking on one or very few templates then it might not even be worth your while to bother learning it. If, on the other hand, you plan to do extensive template editing (like creating your own skin) then it shouldn't be too hard to get used to using it.

The best way to learn to expand a bunch of TPL to PHP by using a utility that came with your installation bin/cc-host-parse-tpl.php

Variables Gotchas

In general, when the TPL search/replace parser see a value without quotes, it assume it is an index into the $A array. For example:

%call(macro-name)% expands to: <?= $T->Call($A['head-type']); ?>

If it sees single quotes it assumes it as a literal.

%call('template-file.ext')% expands to <?= $T->Call('template-file.ext'); ?>

In this documentation, most cases when you see var-name you can assume this type of either/or flexible look-up.

To signify a local variable (not an index into $A) use the pound sign (#):

 %call(#R)%  expands to: <?= $T->Call( $R ); ?>

Local variables can be initialized using the map and loop macros.

 %loop(records,R)%             // no '#' 
    <b>%(#R/upload_name)%</b>  // yes '#'
 %end_loop%

Array indexing is done using the forward slash ('/'). This code initializes a local variable 'R' with the contents of $A['records'][0]:

  %map( #R, records/0 )%  // yes '#'

Macro table

Macro Expands to
%(var-name)% <? echo $A['var-name']; ?>
%append(array_name,value)% <? $A['array_name'][] = 'value'; ?>
%call(var-name)% <?= $T->Call( var-name ); ?>
%chop(var-name,amount)% <?= cc_strchop(var-name,amount,true); ?>
%date('string-date', 'format')% <?= cc_datefmt('string-date','format'); ?>
%else% <? } else { ?>
%end_if% <? } ?>
%end_macro% <? } ?>"
%end_loop% <? } } ?>
%if('var-index')% <? if( !empty($A['var-index']) ) { ?>
%if_attr(var-index,value)% <?= empty($A['var-index']) ?  : "value=\"" . $A['var-index'] . '"'; ?>
%if_null(var-index)% <? if( empty($A['var-index']) ) { ?>
%if_empty(var-index)% (alias for %if_null%)
%if_not_null(var-index)% <? if( !empty($A['var-index']) ) { ?>
%if_not_empty(var-index)% (alias for %if_not_null%)
%if_class{var-name,classname)% <?= empty($var-name) ?  : "class=\"classname\""; ?>
%if_not_class{var-name,classname)% <?= empty($var-name) ?  : "class=\"classname\""; ?>
%if_first(#loop_var)% <? if( ($i_loop_var == 0) ) { ?>
%if_not_first(#loop_var)% <? if( !($i_loop_var == 0) ) { ?>
%if_last(#loop_var)% <? if( ($i_loop_var == $c_loop_var) ) { ?>
%if_not_last(#loop_var)% <? if( !($i_loop_var == $c_loop_var) ) { ?>
%inspect(var-name)% <? CCDebug::Enable(true); CCDebug::PrintVar(var-name); ?>
%loop( array, loop-var)%
 <? 
  if( !empty(array) ) 
  { $c_loop_var = count(array); 
    $i_loop_var = 0; 
    foreach( array as $k_loop_var => $loop_var) 
    { 
       $i_loop_var++; 
  ?>
%map('left-side','right-side')% <? 'left-side' = right-side; ?>

for example: %map(#R,records/0)% expands to <? $R = $A['records'][0]; ?>

%query(query-spec)% <?= cc_query_fmt('f=html&noexit=1&nomime=1&' . query_spec); ?>
%text(str)% <?= $T->String(str); ?>
%url(partial-url')% <?= $T->URL(partial-url); ?>

Searching for Files

The %url% template macro (expands to the PHP equavlent $T->URL()) will take a partial path and return a fully qualified URL that can be used in HREF, SRC and other HTML and CSS attributes that require web addresses.

Typical usage would be:

 <script src="%url('js/my_script.js')%"

The %call% (alias %call_macro%) template macro (expands to PHP equivalent $T->Call()) will take either:

  • a partial path to a template file (e.g. %call('body.tpl')% )
  • the name of macro appended to a partial path (e.g. %call('tabs.tpl/print_tabs')% )
  • a mapped template macro name which expands to either of the two above. (e.g. %call('prev_next_links')% which is mapped to 'util.php/prev_next_links')

In all cases the macro's file is parsed and any PHP code is executed.

The %import_skin% macro (in PHP expands to $T->ImportSkin()) works the same as %call% but sets up the current skin as having 'inherited' functionality from the imported template.


Resolving partial paths

The admin of a ccHost installation can determine the location(s) that the template engine will look for files using the 'Skins Path' field of the admin/paths configuration screens. However, the template engine is also very context sensitive and since calls to templates and macros can be nested in each other, it pays close attention to the stack of currently executing templates.

Before doing any searches, the template engine will create a list of directories to traverse based on contextual precedence. The partial path is appended to the end of each directory in to order to test the existence of the file being searched for.

Once the specific target file is located the searching stops. The engine keeps track of 'hits' and will only search the physical location the first time it looks for a file which can take a while. Subsequent searches for the same partial are very, very fast.

The following is the precedence used for resolving partial paths by the template engine:

1. Relative to the ccHost root installation directory

For example if you keep certain kinds of images in a directory directly accessible from your web root you could access them simply:

 <img src="%url(my_special_images/icons/pontiac.jpg)%"

2. Relative to the current template being parsed.

The directory of the template that is currently being is used to override all other path considerations. This way a stylesheet or script file the template is dependent on can not be overwritten somewhere else in the system.

For example, if a template is located at:

 mysite_files/skins/klondike/mytemplate.tpl

and a javascript script file located at:

 msite_file/skins/klondike/js/myscript.js

The following line in the template would create the proper URL to reach the javascript script when loaded into the browser:

 <script src="%url('js/myscript.js')%"></script>


3. Relative to the current skin's root

4. Relative to any imported skins

5. Relative to the paths found in the 'Skins Path' admin option

6. System default locations (ccskins/shared/pages, ccskins, ccskins/shared, ccskins/shared/formats)

Overriding system default files

As of 5.1 (currently in SVN) there is a new option in admin/paths called Prioritize Custom Skins which affects the precedence rules outlined above. If checked, it moves #5 to just below #1.

That means, with that option set, when a template in ccskins/shared refers to a javascript, image, style sheet or embedded template, the system will look at the admin 'Skin Path' option before looking anywhere else (except off the root of the installation).

Embedded Macros vs. Paths

A 'macro function' is function inside a PHP template with a specific naming pattern and signature:

 function _t_<file_name>_<macro_name>( &$T, &A )

In order to call this macro, the caller would specify:

 %call(<file_name>/<macro_name>)%

and the template parameter in a Query API URL would be:

 api/query?t=<file_name>/<macro_name>>

Using this calling convention turned out to create an unfortunate situation in which the template engine doesn't know if the forward slash is meant as a file system path separator or the indicator to look into a file for a macro function.

This can be a problem when callers are in the habit of leaving off the extension of the file name:

 api/query?t=formats/links_by

Does the above request mean a template called 'links_by' in the 'formats' directory? Or does it mean a macro function called 'links_by' in a template file called 'formats'?

The solution is not pretty.

In order to disambiguate the template engine requires that callers put a file extension on the last macro file in the path. So if you mean a file called 'links_by.tpl' you have to say so:

 api/query?t=formats/links_by.tpl
 

otherwise if you meant a macro called links_by in formats.tpl

 api/query?t=formats.php/links_by

(Sorry about that.)