Getting Started
A Pubvana widget requires exactly two files. No PHP. No composer package. No service registration. Create the files, run Discover, and the widget appears in the admin Widgets area.
Step 1 — Create the Directory
Widget directories live at widgets/{your_slug}/. The folder name becomes the internal identifier. Use snake_case.
widgets/
my_latest_quotes/
widget_info.json
views/
widget.tpl
Step 2 — Write widget_info.json
{
"name": "My Latest Quotes",
"description": "Displays recent quotes from the quotes table.",
"version": "1.0.0",
"min_pubvana_version": "2.2.3",
"author": "Your Name",
"admin": {
"options": {
"title": {"type": "text", "label": "Widget Title", "default": "Latest Quotes"},
"count": {"type": "number", "label": "Number of Quotes", "default": "3"}
}
},
"output": {
"template": "widget.tpl",
"providers": {
"quotes": {
"provider": "QuoteModel.getRecent",
"params": {"limit": "count"}
}
}
}
}
The slug field must match the directory name exactly. If they differ, discovery will fail silently.
Step 3 — Write views/widget.tpl
{% if quotes %}
<div class="{{ cls.cls_widget }}">
{% if options.title %}
<h3 class="{{ cls.cls_widget_title }}">{{ options.title }}</h3>
{% endif %}
<ul>
{% for quote in quotes %}
<li>{{ quote.body }} — <em>{{ quote.author }}</em></li>
{% endfor %}
</ul>
</div>
{% endif %}
Step 4 — Run Discover
In the admin panel: Appearance → Widgets → Discover Widgets. Pubvana scans all subdirectories of widgets/, parses widget_info.json files, and inserts new rows into the widgets table. Existing widget records are not overwritten.
Step 5 — Place the Widget
Go to Appearance → Widgets, find "My Latest Quotes" in the available list, and drag it into a widget area. Configure the options and save. The widget now renders on the front end wherever that widget area appears in the active theme.
Widget Without Data Providers
If your widget has no dynamic data (e.g. a static HTML block, a search form), omit the providers key entirely:
{
"name": "Search Box",
"admin": {
"options": {
"title": {"type": "text", "label": "Placeholder Text", "default": "Search..."}
}
},
"output": {
"template": "widget.tpl"
}
}
The template receives options but no provider-supplied variables.