diff --git a/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini b/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini
index 14c2d8ed..824c6f0e 100644
--- a/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini
+++ b/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini
@@ -352,6 +352,10 @@ COM_MOKOJOOMBACKUP_RUNBACKUP_LEAVE_WARNING="A backup is in progress. Leaving now
COM_MOKOJOOMBACKUP_RUNBACKUP_AUTOCONTINUE="Automatically continue the update when the backup finishes"
COM_MOKOJOOMBACKUP_RUNBACKUP_DONT_CLOSE="Please do not close this window or switch to another window until the backup finishes."
COM_MOKOJOOMBACKUP_RUNBACKUP_UPDATE_RUNNING="Backup complete. The update is now running — please wait and do not close this window…"
+COM_MOKOJOOMBACKUP_RUNBACKUP_CANCEL="Cancel backup"
+COM_MOKOJOOMBACKUP_RUNBACKUP_CANCEL_CONFIRM1="Cancel the backup that is in progress?"
+COM_MOKOJOOMBACKUP_RUNBACKUP_CANCEL_CONFIRM2="Are you sure? The backup will be stopped and the update will NOT continue."
+COM_MOKOJOOMBACKUP_RUNBACKUP_CANCELLING="Cancelling…"
COM_MOKOJOOMBACKUP_RUNBACKUP_CONTINUE_UPDATE="Continue the update"
COM_MOKOJOOMBACKUP_CONFIG_CLEANUP="Cleanup Defaults"
diff --git a/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini b/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini
index 1985d78e..1f2d226d 100644
--- a/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini
+++ b/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini
@@ -74,6 +74,10 @@ COM_MOKOJOOMBACKUP_RUNBACKUP_LEAVE_WARNING="A backup is in progress. Leaving now
COM_MOKOJOOMBACKUP_RUNBACKUP_AUTOCONTINUE="Automatically continue the update when the backup finishes"
COM_MOKOJOOMBACKUP_RUNBACKUP_DONT_CLOSE="Please do not close this window or switch to another window until the backup finishes."
COM_MOKOJOOMBACKUP_RUNBACKUP_UPDATE_RUNNING="Backup complete. The update is now running — please wait and do not close this window…"
+COM_MOKOJOOMBACKUP_RUNBACKUP_CANCEL="Cancel backup"
+COM_MOKOJOOMBACKUP_RUNBACKUP_CANCEL_CONFIRM1="Cancel the backup that is in progress?"
+COM_MOKOJOOMBACKUP_RUNBACKUP_CANCEL_CONFIRM2="Are you sure? The backup will be stopped and the update will NOT continue."
+COM_MOKOJOOMBACKUP_RUNBACKUP_CANCELLING="Cancelling…"
COM_MOKOJOOMBACKUP_RUNBACKUP_CONTINUE_UPDATE="Continue the update"
COM_MOKOJOOMBACKUP_CONFIG_CLEANUP="Cleanup Defaults"
diff --git a/source/packages/com_mokosuitebackup/tmpl/runbackup/default.php b/source/packages/com_mokosuitebackup/tmpl/runbackup/default.php
index 9a447d5f..683f2e77 100644
--- a/source/packages/com_mokosuitebackup/tmpl/runbackup/default.php
+++ b/source/packages/com_mokosuitebackup/tmpl/runbackup/default.php
@@ -87,6 +87,9 @@ $config = [
'viewRecord' => Text::_('COM_MOKOJOOMBACKUP_RUNBACKUP_VIEW_RECORD'),
'leaveWarn' => Text::_('COM_MOKOJOOMBACKUP_RUNBACKUP_LEAVE_WARNING'),
'continueUpdate' => Text::_('COM_MOKOJOOMBACKUP_RUNBACKUP_CONTINUE_UPDATE'),
+ 'cancelConfirm1' => Text::_('COM_MOKOJOOMBACKUP_RUNBACKUP_CANCEL_CONFIRM1'),
+ 'cancelConfirm2' => Text::_('COM_MOKOJOOMBACKUP_RUNBACKUP_CANCEL_CONFIRM2'),
+ 'cancelling' => Text::_('COM_MOKOJOOMBACKUP_RUNBACKUP_CANCELLING'),
],
];
@@ -141,6 +144,13 @@ $this->getDocument()->addStyleSheet(Uri::root(true) . '/media/com_mokosuitebacku
+
+
+
+
+
@@ -157,6 +167,7 @@ $this->getDocument()->addStyleSheet(Uri::root(true) . '/media/com_mokosuitebacku
var CFG = ;
var L = CFG.labels || {};
var running = false;
+ var cancelled = false;
var lastRecordId = 0;
var el = {
@@ -172,9 +183,38 @@ $this->getDocument()->addStyleSheet(Uri::root(true) . '/media/com_mokosuitebacku
continue: document.getElementById('msb-rb-continue'),
dashboard: document.getElementById('msb-rb-dashboard'),
autocontinue: document.getElementById('msb-rb-autocontinue'),
- autocontinueWrap: document.getElementById('msb-rb-autocontinue-wrap')
+ autocontinueWrap: document.getElementById('msb-rb-autocontinue-wrap'),
+ cancel: document.getElementById('msb-rb-cancel'),
+ cancelWrap: document.getElementById('msb-rb-cancel-wrap')
};
+ function hideCancel() { if (el.cancelWrap) { el.cancelWrap.classList.add('d-none'); } }
+
+ /* Cancel the running backup — requires DOUBLE confirmation. Stops the step
+ loop, tells the server to cancel the record, and leaves to the dashboard
+ WITHOUT continuing any pending update. */
+ if (el.cancel) {
+ el.cancel.addEventListener('click', function () {
+ if (!running || cancelled) { return; }
+ if (!window.confirm(L.cancelConfirm1 || 'Cancel the backup that is in progress?')) { return; }
+ if (!window.confirm(L.cancelConfirm2 || 'Are you sure? The backup will be stopped and the update will NOT continue.')) { return; }
+
+ cancelled = true;
+ running = false; /* also disarms the leave-warning */
+ el.cancel.disabled = true;
+ if (el.warn) { el.warn.classList.add('d-none'); }
+ setStatus(L.cancelling || 'Cancelling…');
+
+ var go = function () { window.location.href = CFG.dashboardUrl; };
+ /* Best-effort server-side cancel of the record, then leave. */
+ if (lastRecordId > 0) {
+ post({ task: 'ajax.cancelBackup', id: lastRecordId }).then(go, go);
+ } else {
+ go();
+ }
+ });
+ }
+
function setBar(pct, striped) {
pct = Math.max(0, Math.min(100, parseInt(pct, 10) || 0));
el.bar.style.width = pct + '%';
@@ -211,6 +251,7 @@ $this->getDocument()->addStyleSheet(Uri::root(true) . '/media/com_mokosuitebacku
setBar(100, false);
el.title.textContent = L.complete || 'Backup complete';
if (el.warn) { el.warn.classList.add('d-none'); }
+ hideCancel();
/* Set the one-shot skip flag for THIS action so the following
server-side onExtensionBefore(Update|Uninstall) backup is skipped once
@@ -264,6 +305,7 @@ $this->getDocument()->addStyleSheet(Uri::root(true) . '/media/com_mokosuitebacku
if (el.warn) { el.warn.classList.add('d-none'); }
el.title.textContent = L.failed || 'Backup failed';
setStatus(message || 'Backup failed');
+ hideCancel();
showActions();
el.retry.textContent = L.retry || 'Retry';
@@ -285,7 +327,10 @@ $this->getDocument()->addStyleSheet(Uri::root(true) . '/media/com_mokosuitebacku
async function run() {
if (running) { return; }
running = true;
+ cancelled = false;
el.actions.classList.add('d-none');
+ if (el.cancelWrap) { el.cancelWrap.classList.remove('d-none'); }
+ if (el.cancel) { el.cancel.disabled = false; }
setBar(0, true);
setPhase('');
setStatus(L.starting || 'Starting backup…');
@@ -303,7 +348,7 @@ $this->getDocument()->addStyleSheet(Uri::root(true) . '/media/com_mokosuitebacku
if (init.record_id) { lastRecordId = init.record_id; }
var done = false;
- while (!done) {
+ while (!done && !cancelled) {
var step = await post({ task: 'ajax.step', session_id: init.session_id });
if (!step || step.error) {
@@ -317,9 +362,14 @@ $this->getDocument()->addStyleSheet(Uri::root(true) . '/media/com_mokosuitebacku
done = step.done || false;
}
+ /* User cancelled mid-flight — the click handler already stops the
+ loop and navigates away, so do not fall through to completion. */
+ if (cancelled) { return; }
+
running = false;
await onComplete();
} catch (err) {
+ if (cancelled) { return; }
onError(err && err.message ? err.message : String(err));
}
}