fix: create admin submenu items on update via Joomla MenuTable API
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 3s
Universal: Auto Version Bump / Version Bump (push) Successful in 8s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 9s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled

The <submenu> 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.
This commit is contained in:
Jonathan Miller
2026-06-11 21:53:26 -05:00
parent b7dad35671
commit 0abbfc709b
2 changed files with 115 additions and 0 deletions
+106
View File
@@ -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 = [