Totally Zenned
- Join Date:
- Jun 2007
- Location:
- Bronx, New York, United States
- Posts:
- 949
- Plugin Contributions:
- 3
Modern admin dashboard
Small question, I had a module that was being loaded in via notifiers... I don't see it up here. Where did it go?
Views: 9,707
Totally Zenned
Small question, I had a module that was being loaded in via notifiers... I don't see it up here. Where did it go?
Totally Zenned
Just a note: the admin/includes/functions/general.php undoes a change that moves that delete function to the orders class I submitted into 2.2.0
Totally Zenned
retched:
Small question, I had a module that was being loaded in via notifiers... I don't see it up here. Where did it go?
I'll assume you're talking about NOTIFY_ADMIN_DASHBOARD_WIDGETS, correct? Since I haven't seen ANY plugins ever that use that observer, I'm guessing it's something custom-built. You'll need to modify that observer because we're no longer adding to the $widgets[], but instead you need to define which zone it goes to.
Old code:
$zco_notifier->notify('NOTIFY_ADMIN_DASHBOARD_WIDGETS', null, $widgets);
New code:
$zco_notifier->notify('NOTIFY_ADMIN_DASHBOARD_ZONES', null, $zones);
You have the following zones: main, sidebar and bottom.
Also, I would suggest downloading the plugin from Github as it contains A LOT of improvements.
My site - Pro ZC Help
My portfolio | My plugins
Totally Zenned
retched:
Just a note: the admin/includes/functions/general.php undoes a change that moves that delete function to the orders class I submitted into 2.2.0
The plugin that's submitted here is for version 2.1.0, definitely NOT for 2.2.0.
There ARE some differences between the 2 version, and I was hoping for the changes to go into core, thus the 2.2.0 version is only as a PR on Github. The file you mentioned is modified for order status color coding, and if I'm not mistaken, it does contain your changes: https://github.com/zencart/zencart/pull/7461/changes#diff-3c56483a8106ec1a0ec4e2f2ac4286d357f044ef534e1bdf407f839cc96cf85e
The PR is currently on hold since Scott mentioned we'll discuss it later on. So, when I get some feedback and a go-ahead with changes expected, I'll update the PR to match the current state of the plugin for 2.1.0.
My site - Pro ZC Help
My portfolio | My plugins
Totally Zenned
balihr:
I'll assume you're talking about NOTIFY_ADMIN_DASHBOARD_WIDGETS, correct? Since I haven't seen ANY plugins ever that use that observer, I'm guessing it's something custom-built. You'll need to modify that observer because we're no longer adding to the $widgets[], but instead you need to define which zone it goes to.
Old code:
$zco_notifier->notify('NOTIFY_ADMIN_DASHBOARD_WIDGETS', null, $widgets);
> New code:
> ```
$zco_notifier->notify('NOTIFY_ADMIN_DASHBOARD_ZONES', null, $zones);
You have the following zones: main, sidebar and bottom.
Also, I would suggest downloading the plugin from Github as it contains A LOT of improvements.
There's one plugin that uses that notifier, the one I co-wrote (AbuseIPDB). It adds a widget to the dashboard for triggering boosted API limits for the AbuseIPDB API. But that can be changed (and modified) to handle both.
Totally Zenned
Ah, I had no idea, I don't use that plugin...
Since the dashboard now has a completely different structure, the old observer won't work, obviously.
I have to leave now, but I did download the plugin and I can see the admin observer and yes, there are changes needed there. Let me get back to you tomorrow with an updated observer.
My site - Pro ZC Help
My portfolio | My plugins
Administrator
balihr:
Ah, I had no idea, I don't use that plugin...
Since the dashboard now has a completely different structure, the old observer won't work, obviously.
I have to leave now, but I did download the plugin and I can see the admin observer and yes, there are changes needed there. Let me get back to you tomorrow with an updated observer.
I'll note that there's no harm, IMO, in leaving the original widget notification and adding the zone notification.
Totally Zenned
lat9:
I'll note that there's no harm, IMO, in leaving the original widget notification and adding the zone notification.
Oh no that was my general idea. To make two separate notifiers. One for the original layout and one for the new one.
Totally Zenned
Mind if I ask what's the point of leaving both notifiers? Either way, the observer needs to be modified to return now completely different code, so what's the gain in leaving the old notifier next to the new one in index_dashboard.php? I would understand leaving both in the observer files of a plugin so it can attach to either version of the dashboard, but leaving both notifiers could IMO just cause confusion and possibly issues down the road. Or am I missing something here because my coffee hasn't kicked in yet?
Here's an updated admin observer for AbuseIPDB plugin:
<?php
/**
* Module: AbuseIPDB
*
* @requires Zen Cart 2.1.0 or later, PHP 7.4+ (recommended: PHP 8.x)
* @author Marcopolo
* @copyright 2023-2025
* @license GNU General Public License (GPL) - https://www.gnu.org/licenses/gpl-3.0.html
* @version 4.0.5
* @updated 07-Feb-2026 (Modern Admin Dashboard)
* @github https://github.com/CcMarc/AbuseIPDB
*/
class zcObserverAbuseIPDBWidget extends base
{
function __construct()
{
$this->attach($this, array(
'NOTIFY_ADMIN_DASHBOARD_ZONES', // Modern Admin Dashboard
'NOTIFY_ADMIN_DASHBOARD_WIDGETS' // Legacy Dashboard
));
}
private function getPluginConfig()
{
if (!defined('ABUSEIPDB_WIDGET_ENABLED') || ABUSEIPDB_WIDGET_ENABLED !== 'true') {
return false;
}
global $db;
$version_sql = $db->execute("SELECT version FROM " . TABLE_PLUGIN_CONTROL . " WHERE unique_key = 'AbuseIPDB'");
if ($version_sql->EOF) return false;
$version = $version_sql->fields["version"];
$session_rate_limit_enabled = false;
$session_setting = $db->Execute("SELECT configuration_value FROM " . TABLE_CONFIGURATION . " WHERE configuration_key = 'ABUSEIPDB_SESSION_RATE_LIMIT_ENABLED'");
if (!$session_setting->EOF && $session_setting->fields['configuration_value'] === 'true') {
$session_rate_limit_enabled = true;
}
return [$version, $session_rate_limit_enabled];
}
/**
* handler for Modern Admin Dashboard
*/
public function updateNotifyAdminDashboardZones(&$class, $eventID, $empty, &$zones)
{
$config = $this->getPluginConfig();
if (!$config) return;
list($version, $session_rate_limit_enabled) = $config;
$widget_filename = 'AbuseIPDBDashboardWidget.php';
$widget_path = DIR_FS_CATALOG . "zc_plugins/AbuseIPDB/$version/admin/includes/modules/dashboard_widgets/$widget_filename";
$found = false;
// scan ALL zones. If widget found, update it to the full path
foreach ($zones as $zone_name => &$widgets_list) {
if (!is_array($widgets_list)) continue;
foreach ($widgets_list as $key => &$val) {
if (basename($val) === $widget_filename) {
$found = true;
// overwrite the simple filename with the full plugin path
$val = $widget_path;
}
}
}
// inject if NOT found anywhere
if (!$found) {
if ($session_rate_limit_enabled) {
// sidebar
if (!isset($zones['sidebar'])) $zones['sidebar'] = [];
array_unshift($zones['sidebar'], $widget_path);
} else {
// main
if (!isset($zones['main'])) $zones['main'] = [];
$zones['main'][] = $widget_path;
}
}
}
/**
* handler for Legacy Dashboard
*/
public function updateNotifyAdminDashboardWidgets(&$class, $eventID, $empty, &$widgets)
{
$config = $this->getPluginConfig();
if (!$config) return;
list($version, $session_rate_limit_enabled) = $config;
$widget_path = DIR_FS_CATALOG . "zc_plugins/AbuseIPDB/$version/admin/includes/modules/dashboard_widgets/AbuseIPDBDashboardWidget.php";
if ($session_rate_limit_enabled) {
// Place widget in column 3 (right), at the top
$filteredWidgets = array_filter($widgets, function($item) {
return $item['column'] === 3;
});
$sortValues = array_column($filteredWidgets, 'sort');
$minSort = empty($sortValues) ? 10 : min($sortValues) - 10;
$minSort = max(10, $minSort);
$widgets[] = [
'column' => 3,
'sort' => $minSort,
'visible' => true,
'path' => $widget_path
];
} else {
// Place widget in column 1 (left), at the bottom
$filteredWidgets = array_filter($widgets, function($item) {
return $item['column'] === 1;
});
$sortValues = array_column($filteredWidgets, 'sort');
$maxSort = empty($sortValues) ? 10 : max($sortValues) + 10;
$widgets[] = [
'column' => 1,
'sort' => $maxSort,
'visible' => true,
'path' => $widget_path
];
}
}
}
There's also a required change in index_dashboard.php (simply replace the current render_zone() function:
function render_zone($zone_name, $widgets_array)
{
global $db, $currencies, $show_status_pills, $target_status_ids, $sniffer, $zco_notifier, $messageStack; // Globals needed inside widgets
echo '<ul id="zone-' . $zone_name . '" class="sortable-list list-unstyled row" style="min-height: 200px; padding-bottom: 50px;">';
foreach ($widgets_array as $widget_file) {
$path = '';
// check if this is an Encapsulated Plugin (absolute path provided)
if (file_exists($widget_file)) {
$path = $widget_file;
}
// treat as Core Widget (relative filename provided)
else {
$path = DIR_WS_MODULES . 'dashboard_widgets/' . $widget_file;
}
if (file_exists($path)) {
$widget_name = basename($path);
$col_class = 'col-md-12';
if ($zone_name == 'bottom') {
if ($widget_name == 'TrafficDashboardWidget.php') {
$col_class = 'col-xs-12 col-md-6'; // traffic gets half width
} else {
$col_class = 'col-xs-12 col-md-3'; // others get quarter width
}
}
// data-markers for JS
$data_attr = 'data-id="' . $widget_name . '"';
$li_class = $col_class . ' widget-li';
// Traffic widget - prevent it moving to sidebar
if ($widget_name == 'TrafficDashboardWidget.php') {
$li_class .= ' locked-bottom';
}
echo '<li class="' . $li_class . '" ' . $data_attr . '>';
include $path;
echo '</li>';
}
}
echo '</ul>';
}
Please let me know how you feel about this before I update the plugin on github. THX
My site - Pro ZC Help
My portfolio | My plugins
Administrator
<?php /** * Module: AbuseIPDB * * @requires Zen Cart 2.1.0 or later, PHP 7.4+ (recommended: PHP 8.x) * @author Marcopolo * @copyright 2023-2025 * @license GNU General Public License (GPL) - https://www.gnu.org/licenses/gpl-3.0.html * @version 4.0.5 * @updated 07-Feb-2026 (Modern Admin Dashboard) * @github https://github.com/CcMarc/AbuseIPDB */ class zcObserverAbuseIPDBWidget extends base { function __construct() { $this->attach($this, array( 'NOTIFY_ADMIN_DASHBOARD_ZONES', // Modern Admin Dashboard 'NOTIFY_ADMIN_DASHBOARD_WIDGETS' // Legacy Dashboard )); } private function getPluginConfig() { if (!defined('ABUSEIPDB_WIDGET_ENABLED') || ABUSEIPDB_WIDGET_ENABLED !== 'true') { return false; } global $db; $version_sql = $db->execute("SELECT version FROM " . TABLE_PLUGIN_CONTROL . " WHERE unique_key = 'AbuseIPDB'"); if ($version_sql->EOF) return false; $version = $version_sql->fields["version"]; $session_rate_limit_enabled = false; $session_setting = $db->Execute("SELECT configuration_value FROM " . TABLE_CONFIGURATION . " WHERE configuration_key = 'ABUSEIPDB_SESSION_RATE_LIMIT_ENABLED'"); if (!$session_setting->EOF && $session_setting->fields['configuration_value'] === 'true') { $session_rate_limit_enabled = true; } return [$version, $session_rate_limit_enabled]; } /** * handler for Modern Admin Dashboard */ public function updateNotifyAdminDashboardZones(&$class, $eventID, $empty, &$zones) { $config = $this->getPluginConfig(); if (!$config) return; list($version, $session_rate_limit_enabled) = $config; $widget_filename = 'AbuseIPDBDashboardWidget.php'; $widget_path = DIR_FS_CATALOG . "zc_plugins/AbuseIPDB/$version/admin/includes/modules/dashboard_widgets/$widget_filename"; $found = false; // scan ALL zones. If widget found, update it to the full path foreach ($zones as $zone_name => &$widgets_list) { if (!is_array($widgets_list)) continue; foreach ($widgets_list as $key => &$val) { if (basename($val) === $widget_filename) { $found = true; // overwrite the simple filename with the full plugin path $val = $widget_path; } } } // inject if NOT found anywhere if (!$found) { if ($session_rate_limit_enabled) { // sidebar if (!isset($zones['sidebar'])) $zones['sidebar'] = []; array_unshift($zones['sidebar'], $widget_path); } else { // main if (!isset($zones['main'])) $zones['main'] = []; $zones['main'][] = $widget_path; } } } /** * handler for Legacy Dashboard */ public function updateNotifyAdminDashboardWidgets(&$class, $eventID, $empty, &$widgets) { $config = $this->getPluginConfig(); if (!$config) return; list($version, $session_rate_limit_enabled) = $config; $widget_path = DIR_FS_CATALOG . "zc_plugins/AbuseIPDB/$version/admin/includes/modules/dashboard_widgets/AbuseIPDBDashboardWidget.php"; if ($session_rate_limit_enabled) { // Place widget in column 3 (right), at the top $filteredWidgets = array_filter($widgets, function($item) { return $item['column'] === 3; }); $sortValues = array_column($filteredWidgets, 'sort'); $minSort = empty($sortValues) ? 10 : min($sortValues) - 10; $minSort = max(10, $minSort); $widgets[] = [ 'column' => 3, 'sort' => $minSort, 'visible' => true, 'path' => $widget_path ]; } else { // Place widget in column 1 (left), at the bottom $filteredWidgets = array_filter($widgets, function($item) { return $item['column'] === 1; }); $sortValues = array_column($filteredWidgets, 'sort'); $maxSort = empty($sortValues) ? 10 : max($sortValues) + 10; $widgets[] = [ 'column' => 1, 'sort' => $maxSort, 'visible' => true, 'path' => $widget_path ]; } } } ``` > > There's also a required change in index_dashboard.php (simply replace the current render_zone() function: > ``` function render_zone($zone_name, $widgets_array) { global $db, $currencies, $show_status_pills, $target_status_ids, $sniffer, $zco_notifier, $messageStack; // Globals needed inside widgets echo '<ul id="zone-' . $zone_name . '" class="sortable-list list-unstyled row" style="min-height: 200px; padding-bottom: 50px;">'; foreach ($widgets_array as $widget_file) { $path = ''; // check if this is an Encapsulated Plugin (absolute path provided) if (file_exists($widget_file)) { $path = $widget_file; } // treat as Core Widget (relative filename provided) else { $path = DIR_WS_MODULES . 'dashboard_widgets/' . $widget_file; } if (file_exists($path)) { $widget_name = basename($path); $col_class = 'col-md-12'; if ($zone_name == 'bottom') { if ($widget_name == 'TrafficDashboardWidget.php') { $col_class = 'col-xs-12 col-md-6'; // traffic gets half width } else { $col_class = 'col-xs-12 col-md-3'; // others get quarter width } } // data-markers for JS $data_attr = 'data-id="' . $widget_name . '"'; $li_class = $col_class . ' widget-li'; // Traffic widget - prevent it moving to sidebar if ($widget_name == 'TrafficDashboardWidget.php') { $li_class .= ' locked-bottom'; } echo '<li class="' . $li_class . '" ' . $data_attr . '>'; include $path; echo '</li>'; } } echo '</ul>'; } ``` > > Please let me know how you feel about this before I update the plugin on github. THX It's called backwards compatibility, so that plugins that use the "old" notifier don't need to change.balihr:
Mind if I ask what's the point of leaving both notifiers? Either way, the observer needs to be modified to return now completely different code, so what's the gain in leaving the old notifier next to the new one in index_dashboard.php? I would understand leaving both in the observer files of a plugin so it can attach to either version of the dashboard, but leaving both notifiers could IMO just cause confusion and possibly issues down the road. Or am I missing something here because my coffee hasn't kicked in yet?
Here's an updated admin observer for AbuseIPDB plugin:
Totally Zenned
lat9:
It's called backwards compatibility, so that plugins that use the "old" notifier don't need to change.
No no, it's called a brainf*rt. And I've been having a lot of those lately.
You really had me scratching my head here, but thanks for the push in the right direction! I really didn't think about the possibility of having a bridge to allow support for both...
So, if you could do me a favor and try this out. Revert the abuseipdb observer to the original, no changes to that file. And here's an updated version of index_dashboard.php: https://github.com/zenexpert/modern-admin-dashboard/blob/legacy-widgets/YOUR_ADMIN/index_dashboard.php
Thanks!
My site - Pro ZC Help
My portfolio | My plugins
Administrator
balihr:
No no, it's called a brainf*rt. And I've been having a lot of those lately.
You really had me scratching my head here, but thanks for the push in the right direction! I really didn't think about the possibility of having a bridge to allow support for both...
So, if you could do me a favor and try this out. Revert the abuseipdb observer to the original, no changes to that file. And here's an updated version of index_dashboard.php: https://github.com/zenexpert/modern-admin-dashboard/blob/legacy-widgets/YOUR_ADMIN/index_dashboard.php
Thanks!
I don't use the AbuseIPDB plugin; the interoperability issue was reported by @retched.
Totally Zenned
I discovered one item that is a no-go for now. If I am missing it, let me know, but there seems to be no notification of log files in this new admin.
I harp on customers maintaining a lookout for problems to get them resolved before they become major.
Having to go to Tools > Display Log Files means they are going to get missed.
Need a hand with Zen Cart? Hire the team at myZenCartZone.com for upgrades, repairs, and custom plugins.
Zen Cart Certified hosting at myZenCartHost.com
Zen Follower
Totally Zenned
dbltoe:
I discovered one item that is a no-go for now. If I am missing it, let me know, but there seems to be no notification of log files in this new admin.
I harp on customers maintaining a lookout for problems to get them resolved before they become major.
Having to go to Tools > Display Log Files means they are going to get missed.
I'm not sure what to tell you, but I do believe you're missing it. All $messagestack output is shown below the header navigation menu - it's the very few lines of admin/includes/header.php
I'm assuming you're using the whole module (dashboard + header + order status colors).
I've just tested with the Display Logs plugin installed, forced one error, the log file was created and after refreshing the admin home page, the notification was there, same as OldNGrey's screenshot (just the yellow one, of course).
My site - Pro ZC Help
My portfolio | My plugins
Totally Zenned
Loading on a new site, I got the following:
[23-Feb-2026 23:32:07 UTC] PHP Fatal error: Uncaught Error: Undefined constant "DATE_FORMAT_SHORT_NO_DAY" in /YOUR_ADMIN/stats_sales_report_graphs.php:260Stack trace:
#0 /YOUR_ADMIN/index.php(16): require()
#1 {main}
thrown in /YOUR_ADMIN/stats_sales_report_graphs.php on line 260
[23-Feb-2026 23:32:07 UTC] Request URI: /YOUR_ADMIN/index.php?cmd=stats_sales_report_graphs&report=4, IP address: 123.234.43.21
--> PHP Fatal error: Uncaught Error: Undefined constant "DATE_FORMAT_SHORT_NO_DAY" in /YOUR_ADMIN/stats_sales_report_graphs.php:260
Stack trace:
#0 /YOUR_ADMIN/index.php(16): require()
#1 {main}
thrown in /YOUR_ADMIN/stats_sales_report_graphs.php on line 260.
[23-Feb-2026 23:32:07 UTC] Request URI: /YOUR_ADMIN/index.php?cmd=stats_sales_report_graphs&report=4, IP address: 123.234.43.21
--> PHP Fatal error: Uncaught Error: Undefined constant "DATE_FORMAT_SHORT_NO_DAY" in /YOUR_ADMIN/stats_sales_report_graphs.php:260
Stack trace:
#0 /YOUR_ADMIN/index.php(16): require()
#1 {main}
thrown in /YOUR_ADMIN/stats_sales_report_graphs.php on line 260.
The define is missing from the file on GitHub but present in core files. I submitted a PR
'DATE_FORMAT' => 'm/d/Y', 'DATE_FORMAT_DATE_PICKER' => 'yy-mm-dd',
'DATE_FORMAT_LONG' => '%A %d %B, %Y',
'DATE_FORMAT_SHORT' => '%m/%d/%Y',
'DATE_FORMAT_SHORT_NO_DAY' => '%B %Y',
'DATE_FORMAT_SPIFFYCAL' => 'MM/dd/yyyy',
'DATE_TIME_FORMAT' => '%%DATE_FORMAT_SHORT%%' . ' %H:%M:%S',
'DEDUCTION_TYPE_DROPDOWN_0' => 'Deduct amount',
'DEDUCTION_TYPE_DROPDOWN_1' => 'Percent',
'DEDUCTION_TYPE_DROPDOWN_2' => 'New Price',
Need a hand with Zen Cart? Hire the team at myZenCartZone.com for upgrades, repairs, and custom plugins.
Zen Cart Certified hosting at myZenCartHost.com
zennedOut
dbltoe:
Loading on a new site, I got the following:
[23-Feb-2026 23:32:07 UTC] PHP Fatal error: Uncaught Error: Undefined constant "DATE_FORMAT_SHORT_NO_DAY" in /YOUR_ADMIN/stats_sales_report_graphs.php:260Stack trace:
#0 /YOUR_ADMIN/index.php(16): require()
#1 {main}
thrown in /YOUR_ADMIN/stats_sales_report_graphs.php on line 260
[23-Feb-2026 23:32:07 UTC] Request URI: /YOUR_ADMIN/index.php?cmd=stats_sales_report_graphs&report=4, IP address: 123.234.43.21
--> PHP Fatal error: Uncaught Error: Undefined constant "DATE_FORMAT_SHORT_NO_DAY" in /YOUR_ADMIN/stats_sales_report_graphs.php:260
Stack trace:
#0 /YOUR_ADMIN/index.php(16): require()
#1 {main}
thrown in /YOUR_ADMIN/stats_sales_report_graphs.php on line 260.
[23-Feb-2026 23:32:07 UTC] Request URI: /YOUR_ADMIN/index.php?cmd=stats_sales_report_graphs&report=4, IP address: 123.234.43.21
--> PHP Fatal error: Uncaught Error: Undefined constant "DATE_FORMAT_SHORT_NO_DAY" in /YOUR_ADMIN/stats_sales_report_graphs.php:260
Stack trace:
#0 /YOUR_ADMIN/index.php(16): require()
#1 {main}
thrown in /YOUR_ADMIN/stats_sales_report_graphs.php on line 260.
>
> The define is missing from the file on GitHub but present in core files. I submitted a PR
>
> ```
'DATE_FORMAT' => 'm/d/Y', 'DATE_FORMAT_DATE_PICKER' => 'yy-mm-dd',
'DATE_FORMAT_LONG' => '%A %d %B, %Y',
'DATE_FORMAT_SHORT' => '%m/%d/%Y',
'DATE_FORMAT_SHORT_NO_DAY' => '%B %Y',
'DATE_FORMAT_SPIFFYCAL' => 'MM/dd/yyyy',
'DATE_TIME_FORMAT' => '%%DATE_FORMAT_SHORT%%' . ' %H:%M:%S',
'DEDUCTION_TYPE_DROPDOWN_0' => 'Deduct amount',
'DEDUCTION_TYPE_DROPDOWN_1' => 'Percent',
'DEDUCTION_TYPE_DROPDOWN_2' => 'New Price',
couple of things:
best.
Totally Zenned
This error happens when you try to use this plugin on a Zen Cart 2.2.0 - the plugin was built for 2.1.0 and older and this constant was introduced in 2.2.0. Replacing the admin/includes/languages/lang.english.php in 2.2.0 with the one distributed with the plugin will cause this error on a 2.2.0 install, but that's not a bug in the plugin - it's user error for installing on a non-compatible version without merging files.
As mentioned on github, all development for this plugin for v2.2.0 has been put on hold as per Scott's request and me not wanting to flood the PR for 2.2.0 before it's discussed.
My site - Pro ZC Help
My portfolio | My plugins
Member
wish i had seen this earlier as it clearly doesnt work on 2/2/2 out fof the box on a new install, i will put things back for now hope this gets updated so its fully functional, but will look at merging in, but for now other things i need to concentrate on
Totally Zenned
I've just updated the plugin to support Zen Cart 2.2.x - code is available on Github (https://github.com/zenexpert/modern-admin-dashboard) and has been submitted to Plugins, pending approval.
My site - Pro ZC Help
My portfolio | My plugins
Tell staff why this post should be reviewed.