Skip to main content

Dashboard Plugin Events

The J2Commerce admin dashboard exposes four plugin events that allow third-party plugins to inject content without modifying core files. All four events follow the same PSR-14 dispatcher pattern and must be implemented inside plugins in the j2commerce plugin group.

The events are dispatched by HtmlView in the Dashboard view using the J2CommerceHelper::plugin() singleton. Plugins subscribe via SubscriberInterface and write data back to the event object using $event->setArgument() — return values are silently discarded by the dispatcher.

Architecture​

Event System Classes​

ClassFilePurpose
PluginHelperadministrator/components/com_j2commerce/src/Helper/PluginHelper.phpSingleton that imports plugins and dispatches events
PluginEventadministrator/components/com_j2commerce/src/Event/PluginEvent.phpEvent object extending AbstractImmutableEvent

PluginHelper Methods​

MethodReturnPurpose
event(string $event, array $args, string $prefix)PluginEventDispatches event; plugins modify event arguments via setArgument(). Prefix defaults to 'onJ2Commerce'.
eventWithHtml(string $event, array $args, string $prefix)PluginEventSame as event(), then concatenates all string results into the html argument.
eventWithArray(string $event, array $args, string $prefix)arrayReturns the result array from the event.
eventWithAppData(string $event, array $args, string $prefix)arrayCollects structured app data for accordion UI rendering.

PluginEvent Mutable Arguments​

Only three argument keys may be modified by plugin handlers: result, forms, and html. Attempting to set any other key throws a BadMethodCallException.

MethodPurpose
setArgument(string $name, mixed $value)Set a mutable argument (result, forms, or html).
getArgument(string $name, mixed $default)Get an argument by name.
addResult(mixed $value)Append a value to the result array.
getEventResult()Return the full result argument.

Event 1: onJ2CommerceGetDashboardMessages​

Adds messages to the Swiper.js carousel rendered at the top of the dashboard.

Dispatched in HtmlView::display():

// File: administrator/components/com_j2commerce/src/View/Dashboard/HtmlView.php

$msgEvent = J2CommerceHelper::plugin()->event('GetDashboardMessages', ['context' => 'j2commerce_dashboard']);
$rawMessages = $msgEvent->getArgument('result', []);

Messages are validated for the presence of id and text, then sorted ascending by priority before being passed to the template.

Message Fields​

FieldTypeRequiredDescription
idstringYesUnique identifier used for dismiss tracking in browser storage.
textstringYesMessage content. HTML is allowed.
typestringNoBootstrap alert variant: danger, warning, info, success. Default: info.
iconstringNoFont Awesome class string (e.g., fa-solid fa-bell). Displayed left of text.
priorityintNoSort order. Lower numbers appear first. Default: 500.
dismissiblestringNonone — cannot dismiss; session — dismissed via sessionStorage; forever — dismissed via localStorage. Default: session.
linkstringNoURL for an action button displayed next to the text.
linkTextstringNoButton label text. Falls back to the Joomla core JOPTIONS string.

Dismiss Mechanics​

JavaScript stores dismissed message IDs in:

  • sessionStorage key j2commerce_dismissed_messages_session for dismissible: 'session'
  • localStorage key j2commerce_dismissed_messages_forever for dismissible: 'forever'

On page load, Swiper slides whose data-message-id appears in either store are hidden before the carousel initialises.

Swiper Configuration​

Auto-play every 10 seconds, fade transition effect, pauses on hover, loop disabled.

Rendering​

Each message is rendered via layouts/dashboard/message.php. The layout outputs a <div class="swiper-slide"> containing a Bootstrap alert with the icon, text, optional link button, and dismiss dropdown.

Handler Example​

// File: plugins/j2commerce/myplugin/src/Extension/MyPlugin.php

use Joomla\Event\Event;

public function onGetDashboardMessages(Event $event): void
{
$result = $event->getArgument('result', []);
$result[] = [
'id' => 'myplugin_update_notice',
'text' => Text::_('PLG_J2COMMERCE_MYPLUGIN_UPDATE_AVAILABLE'),
'type' => 'warning',
'icon' => 'fa-solid fa-triangle-exclamation',
'priority' => 200,
'dismissible' => 'session',
'link' => 'index.php?option=com_plugins&view=plugins&filter[folder]=j2commerce',
'linkText' => Text::_('PLG_J2COMMERCE_MYPLUGIN_UPDATE_NOW'),
];
$event->setArgument('result', $result);
}

Real-World Example​

From plugins/system/j2commerce/src/Extension/J2Commerce.php — adds a danger alert when no download ID is configured:

// File: plugins/system/j2commerce/src/Extension/J2Commerce.php

public function onGetDashboardMessages(Event $event): void
{
$params = ComponentHelper::getParams('com_j2commerce');

if (!empty($params->get('downloadid', ''))) {
return;
}

$result = $event->getArgument('result', []);
$result[] = [
'id' => 'j2commerce_download_id',
'text' => Text::_('COM_J2COMMERCE_DOWNLOAD_ID_DESC'),
'type' => 'danger',
'icon' => 'fa-solid fa-fingerprint',
'dismissible' => 'none',
'link' => Route::_('index.php?option=com_config&view=component&component=com_j2commerce'),
'linkText' => Text::_('COM_J2COMMERCE_DOWNLOAD_ID_BTN'),
'priority' => 100,
];
$event->setArgument('result', $result);
}

Event 2: onJ2CommerceGetQuickIcons​

Adds quick action icon tiles to the Plugin Icons card at the bottom of the dashboard.

Dispatched in HtmlView::display():

// File: administrator/components/com_j2commerce/src/View/Dashboard/HtmlView.php

$quickIconEvent = J2CommerceHelper::plugin()->event('GetQuickIcons', ['context' => 'j2commerce_dashboard']);
$rawIcons = $quickIconEvent->getArgument('result', []);

Icons are validated: entries missing id, link, or text are filtered out before rendering.

Icon Fields​

FieldTypeRequiredDescription
idstringYesUnique identifier for the icon tile element.
linkstringYesURL the icon links to.
textstringYesLabel displayed below the icon.
imagestringNoFont Awesome icon class (e.g., fa-solid fa-cube).
classstringNoBootstrap alert color: success, warning, info, danger.
badgestringNoBadge count displayed next to the label text.
ajaxUrlstringNoURL polled via AJAX to update badge count dynamically on page load.

AJAX Polling​

When ajaxUrl is set the dashboard JavaScript polls that endpoint immediately after page load. The endpoint must return JSON:

{
"amount": 5,
"class": "warning"
}

amount updates the badge text. class updates the alert color of the tile.

Rendering​

Each icon is rendered via layouts/dashboard/quickicon.php.

Handler Example​

// File: plugins/j2commerce/myplugin/src/Extension/MyPlugin.php

use J2Commerce\Component\J2commerce\Administrator\Event\PluginEvent;
use Joomla\CMS\Router\Route;

public function onGetQuickIcons(PluginEvent $event): void
{
$result = $event->getArgument('result', []);
$result[] = [
'id' => 'plg_j2commerce_myplugin_quickicon',
'link' => Route::_('index.php?option=com_j2commerce&view=orders'),
'text' => Text::_('PLG_J2COMMERCE_MYPLUGIN_TITLE'),
'image' => 'fa-solid fa-chart-pie',
'class' => 'info',
'badge' => '5',
'ajaxUrl' => 'index.php?option=com_j2commerce&task=myapp.getCount&format=json',
];
$event->setArgument('result', $result);
}

Real-World Example​

From plugins/j2commerce/app_bulkdiscount/src/Extension/AppBulkDiscount.php:

// File: plugins/j2commerce/app_bulkdiscount/src/Extension/AppBulkDiscount.php

use J2Commerce\Component\J2commerce\Administrator\Event\PluginEvent;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Registry\Registry;

public function onGetQuickIcons(PluginEvent $event): void
{
$plugin_data = PluginHelper::getPlugin('j2commerce', $this->_element);
$params = new Registry;
$params->loadString($plugin_data->params);

if ($params->get('enable_quickicon', 0) !== 1) {
return;
}

$result = $event->getArgument('result', []);
$result[] = [
'id' => 'plg_j2commerce_' . $this->_element . '_quickicon',
'link' => 'index.php?option=com_plugins&task=plugin.edit&extension_id=' . $plugin_data->id,
'image' => 'icon-cube',
'class' => 'info',
'text' => $params->get('quickicon_title', ''),
];
$event->setArgument('result', $result);
}

Event 3: onJ2CommerceDashboardMainTabContent​

Injects HTML tabs into the main chart area (left column, col-md-8).

Dispatched in HtmlView::display():

// File: administrator/components/com_j2commerce/src/View/Dashboard/HtmlView.php

$eventData = [$this->monthlySales, $this->yearlySales, $this->revenueByDay];
$this->dashboardMainTabHtml = J2CommerceHelper::plugin()
->eventWithHtml('DashboardMainTabContent', $eventData)
->getArgument('html', '');

Parameters Available to Handlers​

IndexVariableTypeDescription
0$monthlySalesarrayMonthly revenue, order count, and items sold — all-time aggregate.
1$yearlySalesarrayYearly revenue, order count, and items sold — all-time aggregate.
2$revenueByDayarrayDaily revenue for the currently selected date range.

Tab Set ID​

The tab set ID must be dashboardMainTabs. This matches the HTMLHelper::_('uitab.startTabSet') call in the dashboard template. Using any other ID will produce orphaned tab panels.

Rendering Order​

Plugin tabs appear after any module tabs rendered from the j2commerce-dashboard-main-tab module position.

Handler Example​

// File: plugins/j2commerce/myplugin/src/Extension/MyPlugin.php

use J2Commerce\Component\J2commerce\Administrator\Event\PluginEvent;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;

public function onDashboardMainTabContent(PluginEvent $event): void
{
$monthlySales = $event->getArgument(0, []);
$yearlySales = $event->getArgument(1, []);
$revenueByDay = $event->getArgument(2, []);

$html = HTMLHelper::_('uitab.addTab', 'dashboardMainTabs', 'myplugin-main-tab', Text::_('PLG_J2COMMERCE_MYPLUGIN_TAB_TITLE'));
$html .= '<div class="p-4" style="min-height:350px">';
$html .= '<canvas id="myplugin-main-chart"></canvas>';
$html .= '</div>';
$html .= HTMLHelper::_('uitab.endTab');

$existing = $event->getArgument('html', '');
$event->setArgument('html', $existing . $html);
}

Event 4: onJ2CommerceDashboardSideTabContent​

Injects HTML tabs into the side chart area (right column, col-md-4).

Dispatched in HtmlView::display():

// File: administrator/components/com_j2commerce/src/View/Dashboard/HtmlView.php

$this->dashboardSideTabHtml = J2CommerceHelper::plugin()
->eventWithHtml('DashboardSideTabContent', $eventData)
->getArgument('html', '');

The same $eventData array (monthly sales, yearly sales, revenue by day) is passed as for the main tab event.

Tab Set ID​

The tab set ID must be dashboardSideTabs. Plugin tabs appear after the built-in Monthly and Yearly tabs, and after any module tabs from j2commerce-dashboard-side-tab.

Handler Example​

// File: plugins/j2commerce/myplugin/src/Extension/MyPlugin.php

use J2Commerce\Component\J2commerce\Administrator\Event\PluginEvent;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;

public function onDashboardSideTabContent(PluginEvent $event): void
{
$html = HTMLHelper::_('uitab.addTab', 'dashboardSideTabs', 'myplugin-side-tab', Text::_('PLG_J2COMMERCE_MYPLUGIN_SIDE_TAB'));
$html .= '<div class="p-3" style="min-height:350px">';
$html .= '<ul class="list-group">';
// ... render content
$html .= '</ul>';
$html .= '</div>';
$html .= HTMLHelper::_('uitab.endTab');

$existing = $event->getArgument('html', '');
$event->setArgument('html', $existing . $html);
}

Plugin Registration​

All dashboard events are subscribed in a single getSubscribedEvents() declaration. The event names use the full onJ2Commerce prefix; the method names are local to your class.

<?php
// File: plugins/j2commerce/myplugin/src/Extension/MyPlugin.php

declare(strict_types=1);

namespace J2Commerce\Plugin\J2Commerce\MyPlugin\Extension;

defined('_JEXEC') or die;

use J2Commerce\Component\J2commerce\Administrator\Event\PluginEvent;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\Event;
use Joomla\Event\SubscriberInterface;

class MyPlugin extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'onJ2CommerceGetDashboardMessages' => 'onGetDashboardMessages',
'onJ2CommerceGetQuickIcons' => 'onGetQuickIcons',
'onJ2CommerceDashboardMainTabContent' => 'onDashboardMainTabContent',
'onJ2CommerceDashboardSideTabContent' => 'onDashboardSideTabContent',
];
}

public function onGetDashboardMessages(Event $event): void
{
// Use Joomla\Event\Event for this handler — PluginEvent is valid but not required
$result = $event->getArgument('result', []);
$result[] = [
'id' => 'myplugin_notice',
'text' => Text::_('PLG_J2COMMERCE_MYPLUGIN_NOTICE'),
'type' => 'info',
'icon' => 'fa-solid fa-puzzle-piece',
'priority' => 300,
'dismissible' => 'session',
];
$event->setArgument('result', $result);
}

// ... remaining handlers
}

Complete Working Example​

A fully implemented plugin demonstrating all four events:

<?php
// File: plugins/j2commerce/dashboard_example/src/Extension/DashboardExample.php

declare(strict_types=1);

namespace J2Commerce\Plugin\J2Commerce\DashboardExample\Extension;

defined('_JEXEC') or die;

use J2Commerce\Component\J2commerce\Administrator\Event\PluginEvent;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Router\Route;
use Joomla\Event\Event;
use Joomla\Event\SubscriberInterface;

class DashboardExample extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'onJ2CommerceGetDashboardMessages' => 'onGetDashboardMessages',
'onJ2CommerceGetQuickIcons' => 'onGetQuickIcons',
'onJ2CommerceDashboardMainTabContent' => 'onDashboardMainTab',
'onJ2CommerceDashboardSideTabContent' => 'onDashboardSideTab',
];
}

public function onGetDashboardMessages(Event $event): void
{
$result = $event->getArgument('result', []);
$result[] = [
'id' => 'dashboard_example_welcome',
'text' => 'Welcome to your custom dashboard extension!',
'type' => 'info',
'icon' => 'fa-solid fa-puzzle-piece',
'priority' => 300,
'dismissible' => 'forever',
];
$event->setArgument('result', $result);
}

public function onGetQuickIcons(PluginEvent $event): void
{
$result = $event->getArgument('result', []);
$result[] = [
'id' => 'dashboard_example_icon',
'link' => Route::_('index.php?option=com_j2commerce&view=orders'),
'text' => 'My Custom Tool',
'image' => 'fa-solid fa-screwdriver-wrench',
'class' => 'success',
];
$event->setArgument('result', $result);
}

public function onDashboardMainTab(PluginEvent $event): void
{
$revenueByDay = $event->getArgument(2, []);

$html = HTMLHelper::_('uitab.addTab', 'dashboardMainTabs', 'example-main-tab', 'My Report');
$html .= '<div class="p-4" style="min-height:350px">';
$html .= '<h3>Custom Report</h3>';
$html .= '<p>Revenue data points available: ' . count($revenueByDay) . '</p>';
$html .= '</div>';
$html .= HTMLHelper::_('uitab.endTab');

$existing = $event->getArgument('html', '');
$event->setArgument('html', $existing . $html);
}

public function onDashboardSideTab(PluginEvent $event): void
{
$html = HTMLHelper::_('uitab.addTab', 'dashboardSideTabs', 'example-side-tab', 'Quick Stats');
$html .= '<div class="p-3" style="min-height:350px">';
$html .= '<p>Custom sidebar content from DashboardExample plugin.</p>';
$html .= '</div>';
$html .= HTMLHelper::_('uitab.endTab');

$existing = $event->getArgument('html', '');
$event->setArgument('html', $existing . $html);
}
}

App Icon Image Resolution​

When providing quick icons, the image field accepts a Font Awesome class string directly. For file-based icon images, J2CommerceHelper::getAppImagePath() resolves the path using this fallback chain:

  1. media/plg_j2commerce_{element}/images/{element}.webp
  2. media/plg_j2commerce_{element}/images/{element}.png
  3. media/plg_j2commerce_{element}/images/{element}.jpg
  4. plugins/j2commerce/{element}/images/{element}.png
  5. media/com_j2commerce/images/{element}.png
  6. media/com_j2commerce/images/default-payment.png (final fallback)

Best Practices​

  • Check $event->getArgument('context') against 'j2commerce_dashboard' before adding content if your plugin also listens to other contexts.
  • Prefix id values with your plugin name to prevent collisions with core messages and other plugins.
  • Use priority values intentionally: 1–100 for critical system messages, 200–400 for important notices, 500+ for informational.
  • Set min-height: 350px on chart container elements inside tab content to prevent layout shift when the tab becomes active.
  • Tab IDs passed to uitab.addTab must be unique across all plugins — prefix with your plugin element name.
  • Never return a value from an event handler. The Joomla dispatcher discards return values. Always use $event->setArgument() or $event->addResult().
  • Load your plugin's language file explicitly if your handler calls Text::_() and $autoloadLanguage is false.
  • AJAX polling endpoints (ajaxUrl) must return JSON with amount (string or int) and class (Bootstrap color name) keys.