From e745735ccd19325f911f012b978f787a465809fd Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 13 Jun 2026 08:02:02 -0500 Subject: [PATCH] feat: auto-backup before extension update or uninstall Add two new options in component config (Pre-action Backups fieldset): - Backup Before Extension Update (default: No) - Backup Before Extension Uninstall (default: No) System plugin subscribes to onExtensionBeforeUpdate and onExtensionBeforeUninstall events. When enabled, runs a full backup using the default profile before any extension is updated or uninstalled. Throttled to once per 10 minutes via session flag to prevent duplicate backups during batch operations. --- .../packages/com_mokosuitebackup/config.xml | 25 +++++++ .../language/en-GB/com_mokosuitebackup.ini | 6 ++ .../language/en-US/com_mokosuitebackup.ini | 6 ++ .../src/Extension/MokoSuiteBackup.php | 68 ++++++++++++++++++- 4 files changed, 103 insertions(+), 2 deletions(-) diff --git a/source/packages/com_mokosuitebackup/config.xml b/source/packages/com_mokosuitebackup/config.xml index 7cee4ac..5b93f7f 100644 --- a/source/packages/com_mokosuitebackup/config.xml +++ b/source/packages/com_mokosuitebackup/config.xml @@ -71,6 +71,31 @@ /> +
+ + + + + + + + +
+
'onAfterInitialise', - 'onAfterRoute' => 'onAfterRoute', + 'onAfterInitialise' => 'onAfterInitialise', + 'onAfterRoute' => 'onAfterRoute', + 'onExtensionBeforeUpdate' => 'onExtensionBeforeUpdate', + 'onExtensionBeforeUninstall' => 'onExtensionBeforeUninstall', ]; } @@ -199,6 +201,68 @@ final class MokoSuiteBackup extends CMSPlugin implements SubscriberInterface } } + /** + * Run a backup before any extension is updated. + */ + public function onExtensionBeforeUpdate(Event $event): void + { + $this->runPreActionBackup('backup_before_update', 'Pre-update backup'); + } + + /** + * Run a backup before any extension is uninstalled. + */ + public function onExtensionBeforeUninstall(Event $event): void + { + $this->runPreActionBackup('backup_before_uninstall', 'Pre-uninstall backup'); + } + + /** + * Run a pre-action backup if the option is enabled and not already + * done in this session (throttled to once per 10 minutes to avoid + * duplicate backups during batch updates). + */ + private function runPreActionBackup(string $paramName, string $description): void + { + $params = ComponentHelper::getParams('com_mokosuitebackup'); + + if (!(int) $params->get($paramName, 0)) { + return; + } + + // Throttle: only run once per 10 minutes to prevent duplicate + // backups when multiple extensions are updated in a batch + $session = Factory::getSession(); + $sessionKey = 'mokosuitebackup.preaction_' . $paramName; + $lastRun = $session->get($sessionKey, 0); + + if (time() - $lastRun < 600) { + return; + } + + $session->set($sessionKey, time()); + + $profileId = (int) $params->get('default_profile', 1); + + try { + $engine = new BackupEngine(); + $result = $engine->run($profileId, $description, 'preaction'); + + if (!$result['success']) { + Factory::getApplication()->enqueueMessage( + 'MokoSuiteBackup: ' . $description . ' failed — ' . $result['message'], + 'warning' + ); + } + } catch (\Exception $e) { + error_log('MokoSuiteBackup: ' . $description . ' failed: ' . $e->getMessage()); + Factory::getApplication()->enqueueMessage( + 'MokoSuiteBackup: ' . $description . ' failed — ' . $e->getMessage(), + 'warning' + ); + } + } + /** * Send a JSON response and terminate — used by web cron handler. */