diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 97cb4f09..41b45a86 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: MokoGitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.57.04 +# VERSION: 02.57.05 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 26def769..c95165fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased] ### Fixed +- Admin menu / duplicate-component fallout: (a) the orphaned `com_component-mokosuitebackup` is now removed in **preflight** too, so its "Backup" menu no longer collides on install ("The alias backup is already being used"); (b) `ensureSubmenuItems()` **recreates the top-level "Backup" menu** if it was deleted, instead of giving up — so Backup no longer disappears from the admin menu (and the cPanel module / MokoSuiteClient can find it again); (c) the component menu label now uses the short constant `COM_MOKOJOOMBACKUP_SHORT`. (#213 fallout) - Installer no longer aborts on MySQL 8 with "This command is not supported in the prepared statement protocol yet" (error 1295). The legacy remote-column purge migration (`02.56.01.sql`) used `PREPARE`/`EXECUTE`/`DEALLOCATE`, which Joomla's installer rejects; the drop now runs in the package installer script via an INFORMATION_SCHEMA-gated plain `ALTER` (portable across MariaDB and MySQL 8). (#213 update path) ## [02.57.00] --- 2026-07-05 diff --git a/SECURITY.md b/SECURITY.md index 8d010a9e..53020c69 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.57.04 +VERSION: 02.57.05 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index 6fb65dbe..3b32d925 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -17,7 +17,7 @@ display label there. --> MokoSuiteBackup - 02.57.04 + 02.57.05 2026-06-02 Moko Consulting hello@mokoconsulting.tech @@ -47,7 +47,7 @@ - Backup + COM_MOKOJOOMBACKUP_SHORT Module - MokoSuiteBackup - cPanel - 02.57.04 + 02.57.05 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index 7a6418bc..c2059c79 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.57.04 + 02.57.05 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 359bb07a..d2d09d64 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.57.04 + 02.57.05 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index bcacee04..2e1d2b3d 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.57.04 + 02.57.05 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index 4f17d8f3..9f1d3957 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.57.04 + 02.57.05 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index a8b7f693..00882897 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.57.04 + 02.57.05 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index f2c281ce..575fec88 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.57.04 + 02.57.05 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index e9863d3b..f2ca7f99 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.57.04 + 02.57.05 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index dfa5e0e0..19b7388d 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.57.04 + 02.57.05 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/script.php b/source/script.php index 4e209884..65ade9fe 100644 --- a/source/script.php +++ b/source/script.php @@ -77,6 +77,13 @@ class Pkg_MokoSuiteBackupInstallerScript $this->backupDownloadKey(); } + /* Remove any orphaned mis-registered component BEFORE the component install + recreates its admin menu. The orphan (element com_component-mokosuitebackup) + owns a "Backup" menu with alias "backup"; if it survives, the real + component's menu creation collides ("The alias backup is already being used") + and the whole install aborts. */ + $this->removeOrphanedComponent(); + return true; } @@ -622,23 +629,7 @@ class Pkg_MokoSuiteBackupInstallerScript 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) { - error_log('MokoSuiteBackup: ensureSubmenuItems() — parent menu item not found'); - return; - } - - /* Get the component extension_id */ + /* Component extension_id first — needed to (re)create the parent menu. */ $query = $db->getQuery(true) ->select($db->quoteName('extension_id')) ->from($db->quoteName('#__extensions')) @@ -653,6 +644,41 @@ class Pkg_MokoSuiteBackupInstallerScript return; } + /* Find the top-level "Backup" 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(); + + /* Self-heal: if the top-level menu was deleted (e.g. by a failed install + after the duplicate-component mess), recreate it — otherwise the whole + Backup menu stays gone and dependents (cPanel module, MokoSuiteClient) + can't locate the component. */ + if (!$parent) { + $parent = $this->createTopMenu($componentId); + + if (!$parent) { + error_log('MokoSuiteBackup: ensureSubmenuItems() — parent menu missing and could not be created'); + return; + } + } + + /* Keep the top-level menu label on the short constant and owned by the + real component. */ + $db->setQuery( + $db->getQuery(true) + ->update($db->quoteName('#__menu')) + ->set($db->quoteName('title') . ' = ' . $db->quote('COM_MOKOJOOMBACKUP_SHORT')) + ->set($db->quoteName('component_id') . ' = ' . (int) $componentId) + ->where($db->quoteName('id') . ' = ' . (int) $parent->id) + ); + $db->execute(); + foreach ($submenus as $submenu) { /* Check if this submenu item already exists */ $query = $db->getQuery(true) @@ -718,6 +744,59 @@ class Pkg_MokoSuiteBackupInstallerScript } } + /** + * Create the top-level "Backup" admin menu item for the component when it is + * missing (deleted by a failed install / the duplicate-component fallout). + * Label uses the short constant. Returns a lightweight {id, menutype} object, + * or null on failure. + * + * @param int $componentId Extension id of com_mokosuitebackup + * + * @return object|null + */ + private function createTopMenu(int $componentId): ?object + { + try { + $table = Factory::getApplication() + ->bootComponent('com_menus') + ->getMVCFactory() + ->createTable('Menu', 'Administrator'); + + $table->menutype = 'main'; + $table->title = 'COM_MOKOJOOMBACKUP_SHORT'; + $table->alias = 'backup'; + $table->link = 'index.php?option=com_mokosuitebackup'; + $table->type = 'component'; + $table->published = 1; + $table->parent_id = 1; + $table->level = 1; + $table->component_id = $componentId; + $table->client_id = 1; + $table->img = 'class:archive'; + $table->params = '{}'; + $table->language = '*'; + $table->access = 1; + + $table->setLocation(1, 'last-child'); + + if (!$table->check() || !$table->store()) { + error_log('MokoSuiteBackup: createTopMenu() failed: ' . $table->getError()); + + return null; + } + + $obj = new \stdClass(); + $obj->id = (int) $table->id; + $obj->menutype = 'main'; + + return $obj; + } catch (\Throwable $e) { + error_log('MokoSuiteBackup: createTopMenu() exception: ' . $e->getMessage()); + + return null; + } + } + private function fixPackageClientId(): void { try {