From d65d8faf65d7f4ea7b9b5f1f60cc771692c3c36e Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 27 Jun 2026 19:12:42 -0500 Subject: [PATCH] feat: single MokoSuite menu item with collapsible ecosystem children Consolidates all Moko components under one top-level "MokoSuite" sidebar entry. Each component with subviews is a nested collapsible. Also: Help link keeps target=_blank but hides external-link icon. Claude-Session: https://claude.ai/code/session_01Jo2JpjCwfHAh2HHRSjczKq --- .../mod_mokosuiteclient_menu/tmpl/default.php | 149 ++++++++---------- .../Extension/MokoSuiteClient.php | 9 ++ 2 files changed, 77 insertions(+), 81 deletions(-) diff --git a/source/packages/mod_mokosuiteclient_menu/tmpl/default.php b/source/packages/mod_mokosuiteclient_menu/tmpl/default.php index a55126cc..834987fb 100644 --- a/source/packages/mod_mokosuiteclient_menu/tmpl/default.php +++ b/source/packages/mod_mokosuiteclient_menu/tmpl/default.php @@ -2,9 +2,8 @@ /** * MokoSuiteClient Admin Sidebar Menu * - * Each installed Moko component gets its own top-level collapsible section. - * com_mokosuitehq is always pinned first. com_mokosuiteclient uses static views - * as children. All other components auto-discover their submenu items. + * Single "MokoSuite" top-level item with all Moko ecosystem components + * as collapsible children underneath. */ defined('_JEXEC') or die; @@ -122,7 +121,6 @@ try ); $menuItems = $db->loadObjectList() ?: []; - // Load language files for discovered components $lang = Factory::getLanguage(); $loadedLangs = []; foreach ($menuItems as $m) @@ -131,20 +129,16 @@ try { $lang->load($m->element . '.sys', JPATH_ADMINISTRATOR); $lang->load($m->element, JPATH_ADMINISTRATOR); - - // Also try component-local language path (Joomla 5/6 pattern) $compLangPath = JPATH_ADMINISTRATOR . '/components/' . $m->element; if (is_dir($compLangPath . '/language')) { $lang->load($m->element . '.sys', $compLangPath); $lang->load($m->element, $compLangPath); } - $loadedLangs[$m->element] = true; } } - // Group: level 1 = component parent, level 2 = children foreach ($menuItems as $m) { if ((int) $m->level === 1) @@ -178,10 +172,7 @@ try } } } -catch (\Throwable $e) -{ - // Silent — menu works without auto-discovered components -} +catch (\Throwable $e) {} // Override com_mokosuiteclient children with static views if (isset($mokoComponents['com_mokosuiteclient'])) @@ -191,7 +182,6 @@ if (isset($mokoComponents['com_mokosuiteclient'])) } else { - // com_mokosuiteclient not in admin menu — add it manually $mokoComponents['com_mokosuiteclient'] = [ 'id' => 0, 'title' => 'MokoSuite', @@ -209,9 +199,6 @@ $rest = []; foreach ($mokoComponents as $key => $comp) { - // Shorten display titles: - // MokoSuiteClient → MokoSuite, MokoSuiteHQ → MokoHQ - // Everything else: MokoSuiteBackup → Backup, MokoSuiteOpenGraph → OpenGraph if ($key === 'com_mokosuiteclient') { $comp['title'] = 'MokoSuite'; @@ -225,35 +212,29 @@ foreach ($mokoComponents as $key => $comp) $comp['title'] = preg_replace('/^MokoSuite\s*/i', '', $comp['title']); } - if ($key === 'com_mokosuitehq') - { - $hq = $comp; - } - elseif ($key === 'com_mokosuiteclient') - { - $client = $comp; - } - else - { - $rest[$key] = $comp; - } + if ($key === 'com_mokosuitehq') { $hq = $comp; } + elseif ($key === 'com_mokosuiteclient') { $client = $comp; } + else { $rest[$key] = $comp; } } usort($rest, fn($a, $b) => strcasecmp($a['title'], $b['title'])); $sorted = []; -if ($hq !== null) +if ($hq !== null) $sorted[] = $hq; +if ($client !== null) $sorted[] = $client; +foreach ($rest as $comp) $sorted[] = $comp; + +// Is ANY Moko component active? +$anyActive = false; +foreach ($sorted as $comp) { - $sorted[] = $hq; -} -if ($client !== null) -{ - $sorted[] = $client; -} -foreach ($rest as $comp) -{ - $sorted[] = $comp; + $p = []; + parse_str(parse_url($comp['link'], PHP_URL_QUERY) ?? '', $p); + if (($p['option'] ?? '') === $currentOption) { $anyActive = true; break; } } +if ($currentOption === 'com_plugins') $anyActive = true; + +$iconStyle = 'display:inline-block!important;width:1.25em;text-align:center;margin-inline-end:0.4em;'; ?>