From 0abbfc709bdf6366a2d05ee9cc6167f000881c38 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 11 Jun 2026 21:53:26 -0500 Subject: [PATCH] fix: create admin submenu items on update via Joomla MenuTable API The block in the manifest was empty, so no submenu items were ever created. Additionally, Joomla skips submenu creation on updates, so ensureSubmenuItems() now programmatically creates missing entries using the MenuTable API with proper nested set positioning. --- .../com_mokosuitebackup/mokosuitebackup.xml | 9 ++ source/script.php | 106 ++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index 76d0579..4efcd62 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -39,6 +39,15 @@ COM_MOKOJOOMBACKUP + COM_MOKOJOOMBACKUP_SUBMENU_DASHBOARD + COM_MOKOJOOMBACKUP_SUBMENU_BACKUPS + COM_MOKOJOOMBACKUP_SUBMENU_PROFILES cli diff --git a/source/script.php b/source/script.php index e5a044c..3c79849 100644 --- a/source/script.php +++ b/source/script.php @@ -206,6 +206,9 @@ class Pkg_MokoSuiteBackupInstallerScript return; } + // Ensure submenu items exist (Joomla only creates them on fresh install) + $this->ensureSubmenuItems(); + // Sync submenu icons in #__menu (Joomla doesn't update icons on upgrades) $this->syncMenuIcons(); @@ -327,6 +330,109 @@ class Pkg_MokoSuiteBackupInstallerScript } } + /** + * Ensure admin submenu items exist in #__menu. + * + * On updates Joomla does not re-create submenu entries from the manifest, + * so we use the Installer's own _buildAdminMenus pathway via the + * component's MenuTable API to create any missing items. + */ + private function ensureSubmenuItems(): void + { + $submenus = [ + [ + 'link' => 'index.php?option=com_mokosuitebackup&view=dashboard', + 'title' => 'COM_MOKOJOOMBACKUP_SUBMENU_DASHBOARD', + 'img' => 'class:home', + ], + [ + 'link' => 'index.php?option=com_mokosuitebackup&view=backups', + 'title' => 'COM_MOKOJOOMBACKUP_SUBMENU_BACKUPS', + 'img' => 'class:database', + ], + [ + 'link' => 'index.php?option=com_mokosuitebackup&view=profiles', + 'title' => 'COM_MOKOJOOMBACKUP_SUBMENU_PROFILES', + 'img' => 'class:cog', + ], + ]; + + try { + $db = Factory::getDbo(); + + // Find the parent menu item for our component + $query = $db->getQuery(true) + ->select([$db->quoteName('id'), $db->quoteName('menutype')]) + ->from($db->quoteName('#__menu')) + ->where($db->quoteName('client_id') . ' = 1') + ->where($db->quoteName('level') . ' = 1') + ->where($db->quoteName('link') . ' LIKE ' . $db->quote('index.php?option=com_mokosuitebackup%')) + ->setLimit(1); + $db->setQuery($query); + $parent = $db->loadObject(); + + if (!$parent) { + return; + } + + // Get the component extension_id + $query = $db->getQuery(true) + ->select($db->quoteName('extension_id')) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote('com_mokosuitebackup')) + ->where($db->quoteName('type') . ' = ' . $db->quote('component')) + ->setLimit(1); + $db->setQuery($query); + $componentId = (int) $db->loadResult(); + + if (!$componentId) { + return; + } + + foreach ($submenus as $submenu) { + // Check if this submenu item already exists + $query = $db->getQuery(true) + ->select('COUNT(*)') + ->from($db->quoteName('#__menu')) + ->where($db->quoteName('client_id') . ' = 1') + ->where($db->quoteName('link') . ' = ' . $db->quote($submenu['link'])); + $db->setQuery($query); + + if ((int) $db->loadResult() > 0) { + continue; + } + + // Use Joomla's MenuTable to create the item properly + $table = Factory::getApplication() + ->bootComponent('com_menus') + ->getMVCFactory() + ->createTable('Menu', 'Administrator'); + + $table->menutype = $parent->menutype; + $table->title = $submenu['title']; + $table->alias = strtolower(str_replace(' ', '-', $submenu['title'])); + $table->link = $submenu['link']; + $table->type = 'component'; + $table->published = 1; + $table->parent_id = $parent->id; + $table->level = 2; + $table->component_id = $componentId; + $table->client_id = 1; + $table->img = $submenu['img']; + $table->language = '*'; + $table->access = 1; + + $table->setLocation($parent->id, 'last-child'); + + if (!$table->check() || !$table->store()) { + error_log('MokoSuiteBackup: Failed to create submenu "' . $submenu['title'] . '": ' . $table->getError()); + } + } + } catch (\Throwable $e) { + error_log('MokoSuiteBackup: ensureSubmenuItems() failed: ' . $e->getMessage()); + } + } + private function syncMenuIcons(): void { $iconMap = [