feat: auto-discover installed Moko extensions in admin sidebar menu

Static MokoWaaS views listed first, then installed Moko components
auto-discovered from #__extensions. Supported: MokoJoomBackup,
MokoJoomCommunity, MokoJoomCalendar, MokoJoomGallery, MokoJoomCross,
Akeeba Backup, Akeeba Ticket System. New extensions appear automatically.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan Miller
2026-06-04 14:26:47 -05:00
parent 42b47be564
commit 1f89a323d5
+74 -19
View File
@@ -2,34 +2,89 @@
/**
* MokoWaaS Admin Sidebar Menu
*
* Uses native Joomla admin menu classes (MetisMenu) so it renders
* identically to Joomla's own sidebar menu items.
* Uses native Joomla admin menu classes (MetisMenu). Static MokoWaaS
* views are listed first, then installed Moko extensions are auto-discovered
* from #__extensions so they appear automatically when installed.
*/
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Factory;
use Joomla\CMS\Router\Route;
$items = [
['icon' => 'icon-cogs', 'title' => 'Dashboard', 'link' => 'index.php?option=com_mokowaas'],
['icon' => 'fa-solid fa-handshake-angle', 'title' => 'Helpdesk', 'link' => 'index.php?option=com_mokowaas&view=tickets'],
['icon' => 'icon-puzzle-piece', 'title' => 'Extensions', 'link' => 'index.php?option=com_mokowaas&view=extensions'],
['icon' => 'fa-solid fa-file-code', 'title' => '.htaccess Maker', 'link' => 'index.php?option=com_mokowaas&view=htaccess'],
['icon' => 'icon-lock', 'title' => 'Privacy Guard', 'link' => 'index.php?option=com_mokowaas&view=privacy'],
['icon' => 'icon-shield-alt', 'title' => 'WAF Log', 'link' => 'index.php?option=com_mokowaas&view=waflog'],
['icon' => 'icon-database', 'title' => 'Database Tools', 'link' => 'index.php?option=com_mokowaas&view=database'],
['icon' => 'icon-trash', 'title' => 'Cache Cleanup', 'link' => 'index.php?option=com_mokowaas&view=cleanup'],
['icon' => 'icon-power-off', 'title' => 'Feature Plugins', 'link' => 'index.php?option=com_plugins&filter[folder]=system&filter[search]=mokowaas'],
];
$app = \Joomla\CMS\Factory::getApplication();
$app = Factory::getApplication();
$currentOption = $app->getInput()->get('option', '');
$currentView = $app->getInput()->get('view', '');
// Determine if any child is active (auto-expand)
$anyActive = ($currentOption === 'com_mokowaas');
$parentClass = 'item parent item-level-1' . ($anyActive ? ' mm-active' : '');
// ── Static MokoWaaS views ────────────────────────────────────────────
$items = [
['icon' => 'icon-cogs', 'title' => 'Dashboard', 'link' => 'index.php?option=com_mokowaas'],
['icon' => 'fa-solid fa-handshake-angle', 'title' => 'Helpdesk', 'link' => 'index.php?option=com_mokowaas&view=tickets'],
['icon' => 'icon-puzzle-piece', 'title' => 'Extensions', 'link' => 'index.php?option=com_mokowaas&view=extensions'],
['icon' => 'fa-solid fa-file-code', 'title' => '.htaccess Maker', 'link' => 'index.php?option=com_mokowaas&view=htaccess'],
['icon' => 'icon-lock', 'title' => 'Privacy Guard', 'link' => 'index.php?option=com_mokowaas&view=privacy'],
['icon' => 'icon-shield-alt', 'title' => 'WAF Log', 'link' => 'index.php?option=com_mokowaas&view=waflog'],
['icon' => 'icon-database', 'title' => 'Database Tools', 'link' => 'index.php?option=com_mokowaas&view=database'],
['icon' => 'icon-trash', 'title' => 'Cache Cleanup', 'link' => 'index.php?option=com_mokowaas&view=cleanup'],
['icon' => 'icon-power-off', 'title' => 'Feature Plugins', 'link' => 'index.php?option=com_plugins&filter[folder]=system&filter[search]=mokowaas'],
];
// ── Auto-discover installed Moko extensions ──────────────────────────
// Map element → menu entry. Only shown when the extension is installed.
$mokoExtensions = [
'com_mokojoombackup' => ['icon' => 'fa-solid fa-box-archive', 'title' => 'MokoJoomBackup', 'link' => 'index.php?option=com_mokojoombackup'],
'com_mokojoomtos' => ['icon' => 'icon-file-contract', 'title' => 'MokoJoomTOS', 'link' => 'index.php?option=com_mokojoomtos'],
'com_comprofiler' => ['icon' => 'icon-users', 'title' => 'MokoJoomCommunity', 'link' => 'index.php?option=com_comprofiler'],
'com_dpcalendar' => ['icon' => 'icon-calendar', 'title' => 'MokoJoomCalendar', 'link' => 'index.php?option=com_dpcalendar'],
'com_joomgallery' => ['icon' => 'icon-images', 'title' => 'MokoJoomGallery', 'link' => 'index.php?option=com_joomgallery'],
'com_mokojoomcross' => ['icon' => 'fa-solid fa-arrow-right-arrow-left', 'title' => 'MokoJoomCross', 'link' => 'index.php?option=com_mokojoomcross'],
'com_ats' => ['icon' => 'fa-solid fa-headset', 'title' => 'Akeeba Ticket System','link' => 'index.php?option=com_ats'],
'com_akeebabackup' => ['icon' => 'fa-solid fa-hard-drive', 'title' => 'Akeeba Backup', 'link' => 'index.php?option=com_akeebabackup'],
];
try
{
$db = Factory::getContainer()->get(\Joomla\Database\DatabaseInterface::class);
$elements = array_keys($mokoExtensions);
$quoted = array_map([$db, 'quote'], $elements);
$db->setQuery(
$db->getQuery(true)
->select($db->quoteName('element'))
->from($db->quoteName('#__extensions'))
->where($db->quoteName('type') . ' = ' . $db->quote('component'))
->where($db->quoteName('element') . ' IN (' . implode(',', $quoted) . ')')
->where($db->quoteName('enabled') . ' = 1')
);
$installed = $db->loadColumn() ?: [];
foreach ($installed as $element)
{
if (isset($mokoExtensions[$element]))
{
$items[] = $mokoExtensions[$element];
}
}
}
catch (\Throwable $e)
{
// Silent — menu still works with static items only
}
// ── Determine active state ───────────────────────────────────────────
$anyActive = false;
foreach ($items as $item)
{
$parsed = [];
parse_str(parse_url($item['link'], PHP_URL_QUERY) ?? '', $parsed);
if (($parsed['option'] ?? '') === $currentOption)
{
$anyActive = true;
break;
}
}
$parentClass = 'item parent item-level-1' . ($anyActive ? ' mm-active' : '');
$collapseClass = 'collapse-level-1 mm-collapse' . ($anyActive ? ' mm-show' : '');
?>