Templates
Templates are responsible for the output a user sees when requesting a page (while the PHP code is responsible for providing the data that will be shown).
Templates are text files with .tpl as the file extension.
WoltLab Suite Core compiles the template files once into a PHP file that is executed when a user requests the page.
In subsequent request, as the PHP file containing the compiled template already exists, compiling the template is not necessary anymore.
Template Types and Conventions
WoltLab Suite Core supports two types of templates:
frontend templates (or simply *templates*) and backend templates (*ACP templates*).
Each type of template is only available in its respective domain, thus frontend templates cannot be included or used in the ACP and vice versa.
For pages and forms, the name of the template matches the unqualified name of the PHP class except for the Page or Form suffix:
RegisterForm.class.php→register.tplUserPage.class.php→user.tpl
If you follow this convention, WoltLab Suite Core will automatically determine the template name so that you do not have to explicitly set it.
!!! info "For forms that handle creating and editing objects, in general, there are two form classes: FooAddForm and FooEditForm. WoltLab Suite Core, however, generally only uses one template fooAdd.tpl and the template variable $action to distinguish between creating a new object ($action = 'add') and editing an existing object ($action = 'edit') as the differences between templates for adding and editing an object are minimal."
Installing Templates
Templates and ACP templates are installed by two different package installation plugins:
the template PIP and the ACP template PIP.
More information about installing templates can be found on those pages.
Base Templates
Frontend
{include file='header'}
{* content *}
{include file='footer'}Backend
{include file='header' pageTitle='foo.bar.baz'}
<header class="contentHeader">
<div class="contentHeaderTitle">
<h1 class="contentTitle">Title</h1>
</div>
<nav class="contentHeaderNavigation">
<ul>
{* your default content header navigation buttons *}
{event name='contentHeaderNavigation'}
</ul>
</nav>
</header>
{* content *}
{include file='footer'}foo.bar.baz is the language item that contains the title of the page.
Common Template Komponenten
Forms
!!! info "For new forms, use the form builder API."
<form method="post" action="{link controller='FooBar'}{/link}">
<div class="section">
<dl{if $errorField == 'baz'} class="formError"{/if}>
<dt><label for="baz">{lang}foo.bar.baz{/lang}</label></dt>
<dd>
<input type="text" id="baz" name="baz" value="{$baz}" class="long" required autofocus>
{if $errorField == 'baz'}
<small class="innerError">
{if $errorType == 'empty'}
{lang}wcf.global.form.error.empty{/lang}
{else}
{lang}foo.bar.baz.error.{$errorType}{/lang}
{/if}
</small>
{/if}
</dd>
</dl>
<dl>
<dt><label for="bar">{lang}foo.bar.bar{/lang}</label></dt>
<dd>
<textarea name="bar" id="bar" cols="40" rows="10">{$bar}</textarea>
{if $errorField == 'bar'}
<small class="innerError">{lang}foo.bar.bar.error.{$errorType}{/lang}</small>
{/if}
</dd>
</dl>
{* other fields *}
{event name='dataFields'}
</div>
{* other sections *}
{event name='sections'}
<div class="formSubmit">
<input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
{csrfToken}
</div>
</form>Tab Menus
<div class="section tabMenuContainer">
<nav class="tabMenu">
<ul>
<li><a href="#tab1">Tab 1</a></li>
<li><a href="#tab2">Tab 2</a></li>
{event name='tabMenuTabs'}
</ul>
</nav>
<div id="tab1" class="tabMenuContent">
<div class="section">
{* contents of first tab *}
</div>
</div>
<div id="tab2" class="tabMenuContainer tabMenuContent">
<nav class="menu">
<ul>
<li><a href="#tab2A">Tab 2A</a></li>
<li><a href="#tab2B">Tab 2B</a></li>
{event name='tabMenuTab2Subtabs'}
</ul>
</nav>
<div id="tab2A" class="tabMenuContent">
<div class="section">
{* contents of first subtab for second tab *}
</div>
</div>
<div id="tab2B" class="tabMenuContent">
<div class="section">
{* contents of second subtab for second tab *}
</div>
</div>
{event name='tabMenuTab2Contents'}
</div>
{event name='tabMenuContents'}
</div>Template Scripting
Template Variables
Template variables can be assigned via WCF::getTPL()->assign('foo', 'bar') and accessed in templates via $foo:
{$foo}will result in the contents of$footo be passed toStringUtil::encodeHTML()before being printed.{#$foo}will result in the contents of$footo be passed toStringUtil::formatNumeric()before being printed.{unsafe:$foo}will result in the contents of$footo be printed directly.
Thus, this method is relevant when printing numbers and having them formatted correctly according the the user’s language.
This method should only be used if you want to output the content of the variable directly and unfiltered.
Never use this method for user-generated input that has not already been sanitized by other means.
Multiple template variables can be assigned by passing an array:
WCF::getTPL()->assign([
'foo' => 'bar',
'baz' => false
]);Modifiers
Variable modifiers are used to modify the output of variables within templates.
These modifiers allow you to perform various operations on the variables before displaying them.
The general syntax for applying a variable modifier is {$variable|modifier}.
Modifiers can be chained together to perform multiple operations on a variable. In such cases, the modifiers are applied from left to right. For example:
{$variable|modifier1|modifier2|modifier3}A modifier may accept additional parameters that affect its behavior. These parameters follow the modifier name and are separated by a :. For example:
{$variable|modifier:'param1':'param2'}An overview of all available modifiers is available on a dedicated page.
System Template Variable
$__wcfcontains theWCFobject (orWCFACPobject in the backend).
Comments
Comments are wrapped in {* and *} and can span multiple lines:
{* some
comment *}!!! info "The template compiler discards the comments, so that they not included in the compiled template."
Conditions
Conditions follow a similar syntax to PHP code:
{if $foo === 'bar'}
foo is bar
{elseif $foo === 'baz'}
foo is baz
{else}
foo is neither bar nor baz
{/if}The supported operators in conditions are ===, !==, ==, !=, <=, <, >=, >, ||, &&, !, and =.
More examples:
{if $bar|isset}…{/if}
{if $bar|count > 3 && $bar|count < 100}…{/if}Foreach Loops
Foreach loops allow to iterate over associative arrays or iterable objects:
<ul>
{foreach from=$array key=key item=value}
<li>{$key}: {$value}</li>
{/foreach}
</ul>While the from attribute containing the iterated structure and the item attribute containg the current value are mandatory, the key attribute is optional.
If the foreach loop has a name assigned to it via the name attribute, the $tpl template variable provides additional data about the loop:
<ul>
{foreach from=$array key=key item=value name=foo}
{if $tpl[foreach][foo][first]}
something special for the first iteration
{elseif $tpl[foreach][foo][last]}
something special for the last iteration
{/if}
<li>iteration {#$tpl[foreach][foo][iteration]+1} out of {#$tpl[foreach][foo][total]} {$key}: {$value}</li>
{/foreach}
</ul>In contrast to PHP’s foreach loop, templates also support foreachelse:
{foreach from=$array item=value}
…
{foreachelse}
there is nothing to iterate over
{/foreach}Sections
Section loops allow iterations without the use associative arrays or iterable objects:
{section name=month start=1 loop=12}
{$month}
{/section}Parameters
| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| name | string | Yes | n/a | The name of the section |
| loop | mixed | Yes | n/a | Value to determine the number of loop iterations |
| start | integer | No | 0 | The index position that the section will begin looping |
| step | integer | No | 1 | The step value that will be used to traverse the loop array |
| max | integer | No | n/a | The maximum number of times the section will loop |
| show | boolean | No | true | Determines whether or not to show the section |
{sectionelse}
If the section loop does not perform any iterations, {sectionelse} can be defined for an alternative output:
{section name=name start=1 loop=$loop}
…
{sectionelse}
there is nothing to iterate over
{/section}Including Other Templates
To include template named foo from the same domain (frontend/backend), you can use
{include file='foo'}If the template belongs to an application, you have to specify that application using the application attribute:
{include file='foo' application='app'}Additional template variables can be passed to the included template as additional attributes:
{include file='foo' application='app' var1='foo1' var2='foo2'}Template-Plugins
An overview of all available template plugins can be found here.
