From e9bcee71be1cc9c170cffad601ab85a94b4aa964 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sun, 21 Jun 2026 08:45:15 -0500 Subject: [PATCH] fix(install): move empty-element cleanup to postflight, drop ALTER The ALTER TABLE NOT NULL breaks Joomla's package installer on MySQL strict mode. Instead, clean up empty-element rows and stale files in postflight AFTER Joomla finishes installing sub-extensions. --- source/script.php | 87 ++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/source/script.php b/source/script.php index 7e46ce24..956920bf 100644 --- a/source/script.php +++ b/source/script.php @@ -46,45 +46,6 @@ class Pkg_MokosuiteclientInstallerScript { $this->saveDownloadKey(); - // Remove DEFAULT '' from element column — a previous version set this - // which caused Joomla to install plugin files to the group root - try - { - $db = Factory::getDbo(); - $db->setQuery("ALTER TABLE " . $db->quoteName('#__extensions') - . " MODIFY " . $db->quoteName('element') . " VARCHAR(100) NOT NULL"); - $db->execute(); - - // Delete orphaned rows with empty element (created by the old DEFAULT '') - $db->setQuery("DELETE FROM " . $db->quoteName('#__extensions') - . " WHERE " . $db->quoteName('element') . " = ''"); - $db->execute(); - $deleted = $db->getAffectedRows(); - - if ($deleted > 0) - { - Log::add("Deleted {$deleted} orphaned extension row(s) with empty element", Log::INFO, 'mokosuiteclient'); - } - - // Remove stale plugin files that leaked to group root - $staleFiles = [ - JPATH_PLUGINS . '/system/services', - JPATH_PLUGINS . '/system/src', - JPATH_PLUGINS . '/system/language', - ]; - - foreach ($staleFiles as $stale) - { - if (is_dir($stale)) - { - $this->rmdirRecursive($stale); - } - } - } - catch (\Throwable $e) - { - // Non-fatal - } } public function postflight($type, $parent) @@ -136,6 +97,9 @@ class Pkg_MokosuiteclientInstallerScript // Set up MokoSuiteClient guided tours and unpublish Joomla defaults $this->setupGuidedTours(); + // Clean up orphaned empty-element rows and stale files from old DEFAULT '' bug + $this->cleanupEmptyElements(); + // Mark MokoSuiteClient extensions as protected (prevents disable/uninstall at framework level) $this->protectExtensions(); @@ -543,6 +507,51 @@ class Pkg_MokosuiteclientInstallerScript * * @since 02.03.10 */ + private function cleanupEmptyElements(): void + { + try + { + $db = Factory::getDbo(); + + // Delete orphaned extension rows with empty element + $db->setQuery("DELETE FROM " . $db->quoteName('#__extensions') + . " WHERE " . $db->quoteName('element') . " = ''"); + $db->execute(); + $deleted = $db->getAffectedRows(); + + if ($deleted > 0) + { + Log::add("Deleted {$deleted} orphaned extension row(s) with empty element", Log::INFO, 'mokosuiteclient'); + Factory::getApplication()->enqueueMessage( + sprintf('Cleaned up %d orphaned extension record(s).', $deleted), + 'message' + ); + } + + // Remove stale plugin files that leaked to group root + foreach (['services', 'src', 'language'] as $dir) + { + $path = JPATH_PLUGINS . '/system/' . $dir; + + if (is_dir($path)) + { + $this->rmdirRecursive($path); + Log::add("Removed stale directory: plugins/system/{$dir}", Log::INFO, 'mokosuiteclient'); + } + } + + // Remove stale XML manifests at group root + foreach (glob(JPATH_PLUGINS . '/system/mokosuiteclient_*.xml') ?: [] as $staleXml) + { + @unlink($staleXml); + } + } + catch (\Throwable $e) + { + Log::add('Empty element cleanup error: ' . $e->getMessage(), Log::WARNING, 'mokosuiteclient'); + } + } + private function protectExtensions(): void { try