feat: cpanel module slim bar with collapsible detail panel
Universal: Auto Version Bump / Version Bump (push) Successful in 11s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 17s

Replaces full-width card with a compact bar showing site name,
version, status badges, PIN, and IP. Click chevron to expand
the detail panel with environment, stats, disk, and plugin info.

Claude-Session: https://claude.ai/code/session_01Jo2JpjCwfHAh2HHRSjczKq
This commit is contained in:
2026-06-27 14:50:05 -05:00
parent 0288af9421
commit a8b9f7d165
@@ -1,9 +1,6 @@
<?php
/**
* @package MokoSuiteClient
* @subpackage mod_mokosuiteclient_cpanel
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
* MokoSuiteClient Cpanel — slim bar with dropdown detail panel
*/
defined('_JEXEC') or die;
@@ -13,35 +10,21 @@ use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Session\Session;
// Hidden when on MokoSuiteClient dashboard (redundant info)
if (!empty($hidden)) return;
$siteInfo = $siteInfo ?? (object) [];
$plugins = $plugins ?? [];
$healthOk = $healthOk ?? true;
$counts = $counts ?? (object) ['articles' => 0, 'users' => 0, 'extensions' => 0, 'updates' => 0];
$disk = $disk ?? (object) ['free_mb' => null, 'total_mb' => null];
$currentIp = $currentIp ?? '';
$collapsed = true;
$showHealth = $params->get('show_health', 1);
$showStats = $params->get('show_stats', 1);
$showDisk = $params->get('show_disk', 1);
$showIp = $params->get('show_ip', 1);
$showPlugins = $params->get('show_plugins', 1);
$showActions = $params->get('show_actions', 1);
$showVersions = $params->get('show_versions', 1);
$token = Session::getFormToken();
$token = Session::getFormToken();
$showPlugins = $params->get('show_plugins', 1);
$enabledCount = 0;
$totalCount = count($plugins);
foreach ($plugins as $p)
{
if ($p->enabled)
{
$enabledCount++;
}
foreach ($plugins as $p) {
if ($p->enabled) $enabledCount++;
}
$labels = [
@@ -52,41 +35,106 @@ $labels = [
'mokosuiteclient_offline' => 'Offline Bypass',
'mokosuiteclient_dbip' => 'GeoIP Lookup',
'mokosuiteclient_license' => 'License Manager',
'mokosuiteclient_backup' => 'Backup Bridge',
];
$diskPct = ($disk->total_mb && $disk->total_mb > 0)
? round((($disk->total_mb - ($disk->free_mb ?? 0)) / $disk->total_mb) * 100)
: null;
$diskColor = ($diskPct !== null && $diskPct > 90) ? 'bg-danger' : (($diskPct !== null && $diskPct > 75) ? 'bg-warning' : 'bg-success');
$diskPct = ($disk->total_mb && $disk->total_mb > 0)
? round((($disk->total_mb - ($disk->free_mb ?? 0)) / $disk->total_mb) * 100) : null;
$diskColor = ($diskPct !== null && $diskPct > 90) ? 'danger' : (($diskPct !== null && $diskPct > 75) ? 'warning' : 'success');
$canDashboard = Factory::getApplication()->getIdentity()->authorise('core.manage', 'com_mokosuiteclient');
$siteName = htmlspecialchars($siteInfo->sitename ?? '', ENT_QUOTES, 'UTF-8');
$mokoVer = htmlspecialchars($siteInfo->mokosuiteclient_version ?? '', ENT_QUOTES, 'UTF-8');
$joomlaVer = htmlspecialchars($siteInfo->joomla_version ?? '', ENT_QUOTES, 'UTF-8');
$phpVer = htmlspecialchars($siteInfo->php_version ?? '', ENT_QUOTES, 'UTF-8');
$dbType = htmlspecialchars($siteInfo->db_type ?? '', ENT_QUOTES, 'UTF-8');
$ipEscaped = htmlspecialchars($currentIp, ENT_QUOTES, 'UTF-8');
$statusDots = [];
if (!empty($siteInfo->debug)) $statusDots[] = '<span class="badge bg-warning text-dark" style="font-size:0.7rem">Debug</span>';
if (!empty($siteInfo->offline)) $statusDots[] = '<span class="badge bg-danger" style="font-size:0.7rem">Offline</span>';
if (($counts->updates ?? 0) > 0) $statusDots[] = '<span class="badge bg-info" style="font-size:0.7rem">' . (int)$counts->updates . ' updates</span>';
?>
<div class="mod-mokosuiteclient-cpanel card p-3 mb-4">
<div class="d-flex flex-wrap align-items-center gap-2" style="font-size:0.85rem;">
<?php $canDashboard = Factory::getApplication()->getIdentity()->authorise('core.manage', 'com_mokosuiteclient'); ?>
<div class="mod-mokosuiteclient-cpanel mb-2" style="font-size:0.82rem;">
<div class="d-flex align-items-center gap-2 px-3 py-1 rounded" style="background:linear-gradient(135deg,#f8f9fa 0%,#e9ecef 100%);border:1px solid #dee2e6;">
<?php if ($canDashboard): ?>
<a href="<?php echo Route::_('index.php?option=com_mokosuiteclient'); ?>" style="color:#1a2744;text-decoration:none;" title="MokoSuite Dashboard"><span class="icon-shield-alt" aria-hidden="true" style="font-size:1.1rem"></span></a>
<a href="<?php echo Route::_('index.php?option=com_mokosuiteclient'); ?>" style="color:#1a2744;text-decoration:none;" title="MokoSuite Dashboard">
<span class="icon-shield-alt" aria-hidden="true" style="font-size:1rem"></span>
</a>
<?php else: ?>
<span class="icon-shield-alt" aria-hidden="true" style="font-size:1.1rem;color:#1a2744"></span>
<span class="icon-shield-alt" aria-hidden="true" style="font-size:1rem;color:#1a2744"></span>
<?php endif; ?>
<span class="fw-bold"><?php echo htmlspecialchars($siteInfo->sitename ?? ''); ?></span>
<span class="badge bg-primary">MokoSuite <?php echo htmlspecialchars($siteInfo->mokosuiteclient_version ?? ''); ?></span>
<span class="fw-semibold"><?php echo $siteName; ?></span>
<span class="text-muted" style="font-size:0.75rem">v<?php echo $mokoVer; ?></span>
<?php echo implode(' ', $statusDots); ?>
<?php echo \Moko\Component\MokoSuiteClient\Administrator\Helper\SupportPinHelper::renderBadge(
['available' => !empty($supportPinAvailable), 'pin' => $supportPin ?? ''],
$token, 'cpanel'
); ?>
<span class="badge bg-secondary">Joomla <?php echo htmlspecialchars($siteInfo->joomla_version ?? ''); ?></span>
<span class="badge bg-secondary">PHP <?php echo htmlspecialchars($siteInfo->php_version ?? ''); ?></span>
<span class="badge bg-secondary"><?php echo htmlspecialchars($siteInfo->db_type ?? ''); ?></span>
<?php if (!empty($siteInfo->debug)): ?>
<span class="badge bg-warning text-dark">Debug ON</span>
<?php endif; ?>
<?php if (!empty($siteInfo->offline)): ?>
<span class="badge bg-danger">Offline</span>
<?php endif; ?>
<span class="ms-auto d-flex align-items-center gap-2">
<span class="icon-globe" aria-hidden="true"></span>
<code><?php echo htmlspecialchars($currentIp); ?></code>
<span class="text-muted" style="font-size:0.75rem">
<span class="icon-globe" aria-hidden="true"></span> <code style="font-size:0.75rem"><?php echo $ipEscaped; ?></code>
</span>
<button class="btn btn-sm btn-link text-muted p-0 ms-1" type="button" data-bs-toggle="collapse" data-bs-target="#mokosuite-cpanel-detail" aria-expanded="false" aria-controls="mokosuite-cpanel-detail" title="Show details" style="font-size:0.85rem;text-decoration:none;">
<span class="icon-chevron-down mokosuite-cpanel-chevron" aria-hidden="true" style="transition:transform 0.2s"></span>
</button>
</span>
</div>
<div class="collapse" id="mokosuite-cpanel-detail">
<div class="card card-body mt-1 p-3" style="font-size:0.82rem;border-color:#dee2e6;">
<div class="row g-3">
<div class="col-md-4">
<h6 class="text-muted mb-2" style="font-size:0.75rem;text-transform:uppercase;letter-spacing:0.05em">Environment</h6>
<div class="d-flex flex-wrap gap-1">
<span class="badge bg-primary">MokoSuite <?php echo $mokoVer; ?></span>
<span class="badge bg-secondary">Joomla <?php echo $joomlaVer; ?></span>
<span class="badge bg-secondary">PHP <?php echo $phpVer; ?></span>
<span class="badge bg-secondary"><?php echo $dbType; ?></span>
</div>
</div>
<div class="col-md-4">
<h6 class="text-muted mb-2" style="font-size:0.75rem;text-transform:uppercase;letter-spacing:0.05em">Stats</h6>
<div class="d-flex flex-wrap gap-2" style="font-size:0.8rem">
<span><span class="icon-file-alt" aria-hidden="true"></span> <?php echo (int)($counts->articles ?? 0); ?> articles</span>
<span><span class="icon-users" aria-hidden="true"></span> <?php echo (int)($counts->users ?? 0); ?> users</span>
<span><span class="icon-puzzle-piece" aria-hidden="true"></span> <?php echo (int)($counts->extensions ?? 0); ?> extensions</span>
</div>
<?php if ($diskPct !== null): ?>
<div class="mt-1">
<div class="progress" style="height:4px">
<div class="progress-bar bg-<?php echo $diskColor; ?>" style="width:<?php echo $diskPct; ?>%"></div>
</div>
<small class="text-muted">Disk <?php echo $diskPct; ?>% (<?php echo number_format(($disk->free_mb ?? 0) / 1024, 1); ?> GB free)</small>
</div>
<?php endif; ?>
</div>
<?php if ($showPlugins && $totalCount > 0): ?>
<div class="col-md-4">
<h6 class="text-muted mb-2" style="font-size:0.75rem;text-transform:uppercase;letter-spacing:0.05em">Plugins (<?php echo $enabledCount; ?>/<?php echo $totalCount; ?>)</h6>
<div class="d-flex flex-wrap gap-1">
<?php foreach ($plugins as $p):
$el = str_replace('plg_system_', '', $p->element);
$label = $labels[$el] ?? ucfirst(str_replace('mokosuiteclient_', '', $el));
$color = $p->enabled ? 'success' : 'secondary';
?>
<span class="badge bg-<?php echo $color; ?>" style="font-size:0.7rem"><?php echo htmlspecialchars($label); ?></span>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<style>
[aria-expanded="true"] .mokosuite-cpanel-chevron { transform: rotate(180deg); }
</style>
<?php echo \Moko\Component\MokoSuiteClient\Administrator\Helper\SupportPinHelper::renderScript(); ?>