feat: auto-backup before extension update or uninstall
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 5s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 5s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Universal: Auto Version Bump / Version Bump (push) Successful in 9s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 6s
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
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled

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.
This commit is contained in:
Jonathan Miller
2026-06-13 08:02:02 -05:00
parent 330e7d96fe
commit e745735ccd
4 changed files with 103 additions and 2 deletions
@@ -71,6 +71,31 @@
/>
</fieldset>
<fieldset name="preaction" label="COM_MOKOJOOMBACKUP_CONFIG_PREACTION">
<field
name="backup_before_update"
type="radio"
label="COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UPDATE"
description="COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UPDATE_DESC"
default="0"
class="btn-group"
>
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field
name="backup_before_uninstall"
type="radio"
label="COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UNINSTALL"
description="COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UNINSTALL_DESC"
default="0"
class="btn-group"
>
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
</fieldset>
<fieldset name="cleanup" label="COM_MOKOJOOMBACKUP_CONFIG_CLEANUP">
<field
name="max_age_days"
@@ -235,6 +235,12 @@ COM_MOKOJOOMBACKUP_CONFIG_DEFAULT_PROFILE="Default Profile"
COM_MOKOJOOMBACKUP_CONFIG_DEFAULT_PROFILE_DESC="Default backup profile used by quick actions and CLI when no profile is specified."
COM_MOKOJOOMBACKUP_CONFIG_SHOW_UPDATE_NOTICE="Show Update Site Notice"
COM_MOKOJOOMBACKUP_CONFIG_SHOW_UPDATE_NOTICE_DESC="Display the update site configuration notice on the Backup Records view."
COM_MOKOJOOMBACKUP_CONFIG_PREACTION="Pre-action Backups"
COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UPDATE="Backup Before Extension Update"
COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UPDATE_DESC="Automatically run a full backup before any extension is updated. Uses the default profile. Throttled to once per 10 minutes to prevent duplicate backups during batch updates."
COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UNINSTALL="Backup Before Extension Uninstall"
COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UNINSTALL_DESC="Automatically run a full backup before any extension is uninstalled. Uses the default profile. Throttled to once per 10 minutes."
COM_MOKOJOOMBACKUP_CONFIG_CLEANUP="Cleanup Defaults"
COM_MOKOJOOMBACKUP_CONFIG_MAX_AGE="Max Backup Age (days)"
COM_MOKOJOOMBACKUP_CONFIG_MAX_AGE_DESC="Default maximum age for backup records. Used by the system plugin and CLI cleanup command."
@@ -45,6 +45,12 @@ COM_MOKOJOOMBACKUP_CONFIG_DEFAULT_PROFILE="Default Profile"
COM_MOKOJOOMBACKUP_CONFIG_DEFAULT_PROFILE_DESC="Default backup profile used by quick actions and CLI when no profile is specified."
COM_MOKOJOOMBACKUP_CONFIG_SHOW_UPDATE_NOTICE="Show Update Site Notice"
COM_MOKOJOOMBACKUP_CONFIG_SHOW_UPDATE_NOTICE_DESC="Display the update site configuration notice on the Backup Records view."
COM_MOKOJOOMBACKUP_CONFIG_PREACTION="Pre-action Backups"
COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UPDATE="Backup Before Extension Update"
COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UPDATE_DESC="Automatically run a full backup before any extension is updated. Uses the default profile. Throttled to once per 10 minutes to prevent duplicate backups during batch updates."
COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UNINSTALL="Backup Before Extension Uninstall"
COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UNINSTALL_DESC="Automatically run a full backup before any extension is uninstalled. Uses the default profile. Throttled to once per 10 minutes."
COM_MOKOJOOMBACKUP_CONFIG_CLEANUP="Cleanup Defaults"
COM_MOKOJOOMBACKUP_CONFIG_MAX_AGE="Max Backup Age (days)"
COM_MOKOJOOMBACKUP_CONFIG_MAX_AGE_DESC="Default maximum age for backup records. Used by the system plugin and CLI cleanup command."
@@ -26,8 +26,10 @@ final class MokoSuiteBackup extends CMSPlugin implements SubscriberInterface
public static function getSubscribedEvents(): array
{
return [
'onAfterInitialise' => '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.
*/