From e1db1149d8e184011aa1a532da6b165bf3b263d9 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 30 May 2026 14:02:39 -0500 Subject: [PATCH] fix: protect all package extensions, keep update server enabled, clean up legacy mokowaasbrand - protectExtensions() now covers all MokoWaaS elements (package, system plugin, component, webservices, task, perfectpublisher) - enableUpdateServer() ensures #__update_sites stays enabled for MokoWaaS - cleanupLegacyExtensions() removes old mokowaasbrand entries from #__extensions and deletes plugins/system/mokowaasbrand/ from filesystem Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 2 + src/script.php | 149 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 148 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eafc6497..2591f74d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ - Content Sync: API endpoints `POST /?mokowaas=sync` (sender) and `POST /?mokowaas=sync-receive` (receiver) - Content Sync: REST endpoints `POST /api/v1/mokowaas/sync` and `POST /api/v1/mokowaas/sync-receive` - Content Sync: configurable sync targets with URL + API token in plugin settings +- Package installer: protect all MokoWaaS extensions (not just system plugin) and ensure update server stays enabled +- Package installer: clean up legacy `mokowaasbrand` extension entries and files on install/update ## [02.20.00] --- 2026-05-28 diff --git a/src/script.php b/src/script.php index 8bf5daaa..aab2566b 100644 --- a/src/script.php +++ b/src/script.php @@ -34,6 +34,9 @@ class Pkg_MokowaasInstallerScript */ public function postflight($type, $parent) { + // Remove legacy extensions from before the package rewrite + $this->cleanupLegacyExtensions(); + $this->enablePlugin('system', 'mokowaas'); $this->enablePlugin('webservices', 'mokowaas'); $this->enablePlugin('task', 'mokowaasdemo'); @@ -45,6 +48,101 @@ class Pkg_MokowaasInstallerScript $this->sendHeartbeat(); } + /** + * Remove legacy/stale extension entries and filesystem remnants. + * + * The old standalone plugin was named "mokowaasbrand" (plg_system_mokowaasbrand). + * After the rewrite into the pkg_mokowaas package, the old entries and files + * may linger — especially on sites restored from old backups. + * + * @return void + * + * @since 02.21.00 + */ + private function cleanupLegacyExtensions(): void + { + try + { + $db = Factory::getDbo(); + + // Legacy element names to remove from #__extensions + $legacy = [ + $db->quote('mokowaasbrand'), + $db->quote('plg_system_mokowaasbrand'), + ]; + + // Delete from #__extensions + $query = $db->getQuery(true) + ->delete($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' IN (' . implode(',', $legacy) . ')'); + $db->setQuery($query); + $affected = $db->execute(); + $count = $db->getAffectedRows(); + + // Remove legacy plugin files from the filesystem + $legacyDirs = [ + JPATH_PLUGINS . '/system/mokowaasbrand', + ]; + + foreach ($legacyDirs as $dir) + { + if (is_dir($dir)) + { + $this->rmdirRecursive($dir); + } + } + + if ($count > 0) + { + Factory::getApplication()->enqueueMessage( + sprintf('Removed %d legacy MokoWaaS extension(s).', $count), + 'message' + ); + + Log::add( + sprintf('Cleaned up %d legacy MokoWaaS extension entries', $count), + Log::INFO, + 'mokowaas' + ); + } + } + catch (\Throwable $e) + { + Log::add('Legacy cleanup error: ' . $e->getMessage(), Log::WARNING, 'jerror'); + } + } + + /** + * Recursively remove a directory. + * + * @param string $dir Directory path + * + * @return void + * + * @since 02.21.00 + */ + private function rmdirRecursive(string $dir): void + { + $items = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($items as $item) + { + if ($item->isDir()) + { + @rmdir($item->getPathname()); + } + else + { + @unlink($item->getPathname()); + } + } + + @rmdir($dir); + } + /** * Enable a plugin by group and element. * @@ -90,18 +188,63 @@ class Pkg_MokowaasInstallerScript try { $db = Factory::getDbo(); + + // All MokoWaaS elements: package, system plugin, component, + // webservices plugins, task plugin + $elements = [ + $db->quote('pkg_mokowaas'), + $db->quote('mokowaas'), + $db->quote('com_mokowaas'), + $db->quote('mokowaasdemo'), + $db->quote('perfectpublisher'), + ]; + $query = $db->getQuery(true) ->update($db->quoteName('#__extensions')) ->set($db->quoteName('protected') . ' = 1') ->set($db->quoteName('locked') . ' = 0') - ->where('(' . $db->quoteName('element') . ' = ' . $db->quote('mokowaas') - . ' OR ' . $db->quoteName('element') . ' = ' . $db->quote('pkg_mokowaas') . ')'); + ->where($db->quoteName('element') . ' IN (' . implode(',', $elements) . ')'); + $db->setQuery($query); + $db->execute(); + + // Ensure update server stays enabled + $this->enableUpdateServer(); + } + catch (\Throwable $e) + { + Log::add('Error protecting MokoWaaS extensions: ' . $e->getMessage(), Log::WARNING, 'jerror'); + } + } + + /** + * Ensure the MokoWaaS update server entry stays enabled. + * + * Joomla stores update server records in #__update_sites. If a tenant + * or automation disables it, the site stops receiving updates. This + * re-enables it on every install/update. + * + * @return void + * + * @since 02.21.00 + */ + private function enableUpdateServer(): void + { + try + { + $db = Factory::getDbo(); + + // Find update site by name or URL pattern + $query = $db->getQuery(true) + ->update($db->quoteName('#__update_sites')) + ->set($db->quoteName('enabled') . ' = 1') + ->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoWaaS%') + . ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoWaaS%') . ')'); $db->setQuery($query); $db->execute(); } catch (\Throwable $e) { - Log::add('Error protecting MokoWaaS extensions: ' . $e->getMessage(), Log::WARNING, 'jerror'); + Log::add('Error enabling update server: ' . $e->getMessage(), Log::WARNING, 'jerror'); } }