Widget Views
The widget template (views/widget.tpl) is rendered by the Pubvana template engine. It is a .tpl file using the custom tag/filter syntax — not PHP, not Twig, not Blade.
Available Variables
| Variable | Type | Description |
|---|---|---|
options | object | Merged option values (instance overrides schema defaults) |
cls | object | CSS class mapping from the active theme |
| (provider keys) | array/object | Data fetched by declared providers |
options is always available even if the widget has no admin.options defined — it will be an empty object.
Accessing Options
{{ options.title }}
{{ options.count }}
{% if options.show_date == "1" %}
{{ post.published_at | date "M d, Y" }}
{% endif %}
Checkbox values are stored as "1" or "0" (strings). Always compare with == "1".
The cls Object
The cls variable exposes the active theme's CSS class mapping. Always use cls keys for structural elements so your widget adapts to any theme automatically.
Standard cls keys relevant to widgets:
| Key | Typical Value (Bootstrap theme) |
|---|---|
cls.cls_widget | card mb-4 |
cls.cls_widget_title | card-header fw-bold |
cls.cls_widget_body | card-body |
cls.cls_list | list-unstyled |
cls.cls_list_item | mb-1 |
Use them:
<div class="{{ cls.cls_widget }}">
<div class="{{ cls.cls_widget_title }}">{{ options.title }}</div>
<div class="{{ cls.cls_widget_body }}">
<ul class="{{ cls.cls_list }}">
{% for post in posts %}
<li class="{{ cls.cls_list_item }}">
<a href="{! post_url post.slug !}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
Escaping
All {{ }} output is HTML-escaped by default. Use {! !} for raw unescaped output — only for trusted values like URLs generated by tag functions, or content that is already safe HTML.
{{ post.title }} {# escaped — safe for user content #}
{! render_content post !} {# raw — post body is already sanitised HTML #}
{! post_url post.slug !} {# raw — URL generated by trusted helper #}
Full Example — Recent Posts Template
{% if posts %}
<div class="{{ cls.cls_widget }}">
{% if options.title %}
<div class="{{ cls.cls_widget_title }}">{{ options.title }}</div>
{% endif %}
<div class="{{ cls.cls_widget_body }}">
<ul class="{{ cls.cls_list }}">
{% for post in posts %}
<li class="{{ cls.cls_list_item }}">
<a href="{! post_url post.slug !}">{{ post.title }}</a>
{% if options.show_date == "1" %}
<small class="text-muted d-block">{{ post.published_at | date "M d, Y" }}</small>
{% endif %}
{% if options.show_excerpt == "1" %}
<p class="small">{{ post.excerpt | excerpt 100 }}</p>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
Handling Empty Data
Wrap the entire widget output in a {% if provider_var %} guard. If a provider returns an empty result, the widget should render nothing rather than an empty container.
{% for post in posts %}
...
{% empty %}
{# renders when posts is empty — can show a fallback message #}
<p>{! lang "Widget.no_posts" !}</p>
{% endfor %}