Core
Views, themes and widgets
Themes
A theme is a folder in webroot/themes/ holding the Smarty templates and assets of one look. The public site uses the one in Templates.Theme; the admin panel uses inspinia (set by AdminSuperController).
webroot/themes/<theme>/
├── css/ js/ img/ assets (Koshkil::getThemePath('css/theme.css'))
└── templates/
├── front/ site templates ($templateFolder = "front")
│ ├── main.tpl layout
│ ├── index/index.tpl IndexController::index()
│ └── cuentas/…
├── admin/ admin templates
├── widgets/
│ └── front/header.tpl THeader widget of the "front" group
└── error/ error.tpl, error404.tpl, error4xx.tpl, error500.tpl
To create a new theme, copy an existing one (for example brillare) and change Templates.Theme in the domain configuration.
Where templates are looked up
The view looks first in templates/<templateFolder>/ and then in templates/. So index/index.tpl resolves to templates/front/index/index.tpl, and error/error404.tpl to templates/error/. Inside a plugin, the templates in plugins/<plugin>/webroot/templates/ are used.
Compiled templates are stored in tmp/cache/view/<theme>/.
The layout
Controller::$templateFile (usually main.tpl) is the full page. It receives the action's template in $template and includes it:
{* templates/front/main.tpl *}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{if $webpageTitle}{$webpageTitle} · {/if}{Configure::read('Web.SiteName')}</title>
<link rel="stylesheet" href="{Koshkil::getThemePath('css/theme.css')}">
{foreach Configure::read('styles') as $style}<link rel="stylesheet" href="{$style}">{/foreach}
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script>var MAIN_URL='{Koshkil::GetLink('/')}';</script>
</head>
<body>
{html_widget type="front.header"}
<main{if $data_controller} data-controller="{$data_controller}"{/if}>
{html_include file="{$template}"}
</main>
{html_widget type="front.footer"}
{html_widget type="front.scripts"}
</body>
</html>
Templates have access to every variable set with $this->set(), plus $usuario (when logged in), $Response and $_SERVER. Static classes can be called directly: {Koshkil::getLink('/')}, {Configure::read('Web.SiteName')}.
Per-page scripts and styles
The controller adds files that the layout and the scripts widget print:
WebElements::addScript(Koshkil::getThemePath('js/productos.js'));
WebElements::addScript('https://www.google.com/recaptcha/api.js');
WebElements::addScript('/js/first.js', true); // at the top of the list
WebElements::addStyle('/css/plugins/datatables.css');
They are collected in Configure::read('scripts') and Configure::read('styles') without duplicates. With Debug.Javascript set to false, file.min.js is used when it exists.
Widgets
A widget is an interface component with its own logic and template: header, footer, menu, search box…
{html_widget type="front.header"}
{html_widget type="admin.uploads" name="galeria"}
{html_widget type="plugins.noticias.front.noticias"} {* a plugin's widget *}
type |
Class | File | Template |
|---|---|---|---|
front.header |
THeader |
src/Widgets/front/THeader.php |
templates/widgets/front/header.tpl |
admin.uploads |
TUploads |
src/Widgets/admin/TUploads.php |
templates/widgets/admin/uploads.tpl |
plugins.noticias.front.noticias |
TNoticias |
plugins/noticias/src/Widgets/Front/TNoticias.php |
plugins/noticias/webroot/templates/widgets/front/noticias.tpl |
<?php
// src/Widgets/front/TDestacados.php
Koshkil::Uses('sys.web.TWidget');
Koshkil::UsesModel('productos');
class TDestacados extends TWidget {
public function run() {
$count = intval($this->parameters['cantidad'] ?? 4);
$this->set([
'destacados' => TMProductos::where('prd_destacado', '1')->take($count)->get(),
]);
}
}
{html_widget type="front.destacados" cantidad=6}
A widget runs create(), init() and run(); init() sets up the template folders, so if you override it call parent::init(). The tag's parameters arrive in $this->parameters, and the template also receives all of the controller's variables.
Bundled Smarty plugins
They live in src/Smarty/plugins/ and are registered automatically.
| Tag | Type | Purpose |
|---|---|---|
{html_include file="…"} |
function | Includes a template relative to the theme folder; if it doesn't exist, shows error/error404.tpl. Accepts plugin="name". |
{html_widget type="…"} |
function | Runs a widget. |
{t key="…" domain="…"} |
function | Translation (see Internationalization). |
{trans}key{/trans} |
block | Block translation. |
{rule_or_role roles="…" rules="…"}…{/rule_or_role} |
block | Shows the content only if the user has the role or rule. |
{field_maxlength model="…" field="…"} |
function | Prints maxlength="N" from the column length. |
{call_method name="…"} |
function | Calls a public method of the controller. |
{include_plugins folder="…"} |
function | Includes that folder's templates from every active plugin. |
{include_plugins_scripts file="…"} |
function | Prints that file's <script> tag for every active plugin. |
|t |
modifier | Translation: {'save'|t} (common domain). |
|formatdate:"DD/MM/YYYY" |
modifier | Date format with Spanish names (DDDD, MMMM, HH, mm…). |
|elapsed |
modifier | Time elapsed since a date. |
|excerpt:120 |
modifier | Text excerpt. |
|slug |
modifier | URL-friendly text. |
|decamelize, |php_strtolower, |php_ucwords, |replace_str |
modifiers | Text helpers. |
|php_json_encode, |php_base64_encode |
modifiers | Encoding. |
|gravatar |
modifier | Gravatar URL for an email. |
To add your own, create src/Smarty/plugins/function.myfunction.php (or modifier., block.) with the function smarty_function_myfunction($params, $template).
Escape user input
Smarty does not escape by default. Use {$variable|escape} for any text entered by users.