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

VariableTypeDescription
optionsobjectMerged option values (instance overrides schema defaults)
clsobjectCSS class mapping from the active theme
(provider keys)array/objectData 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:

KeyTypical Value (Bootstrap theme)
cls.cls_widgetcard mb-4
cls.cls_widget_titlecard-header fw-bold
cls.cls_widget_bodycard-body
cls.cls_listlist-unstyled
cls.cls_list_itemmb-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 %}