The PHP framework behind SitioYa

Koshkil documentation

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.