From 3742477aef746dff1de2be3548087504a9acf2b9 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 25 Jun 2026 08:35:40 -0500 Subject: [PATCH] fix: convert inline modals to Bootstrap 5, fix language keys, ntfy default, and MokoRestore error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Convert 10 inline CSS modals to Bootstrap 5 (backups: 7, snapshots: 3) - Replace style.display show/hide with Bootstrap Modal API - Fix JFIELD_ORDERING_LABEL_ASC → JFIELD_ORDERING_ASC in profile filter - Add COM_MOKOJOOMBACKUP_CONFIGURATION key for Options page title - Change ntfy default server to ntfy.mokoconsulting.tech - Add profile ID to dropdown labels across backups, dashboard, cpanel module - Add error handling to MokoRestore post() and runPreflight() to prevent UI stalling - Remove outdated SSH auth pattern references from field descriptions --- .../packages/com_mokosuitebackup/config.xml | 2 +- .../forms/filter_profiles.xml | 2 +- .../language/en-GB/com_mokosuitebackup.ini | 5 +- .../src/Engine/MokoRestore.php | 90 ++-- .../tmpl/backups/default.php | 390 +++++++++--------- .../tmpl/dashboard/default.php | 1 + .../tmpl/snapshots/default.php | 229 +++++----- .../tmpl/default.php | 2 +- 8 files changed, 374 insertions(+), 347 deletions(-) diff --git a/source/packages/com_mokosuitebackup/config.xml b/source/packages/com_mokosuitebackup/config.xml index ff63899..923006a 100644 --- a/source/packages/com_mokosuitebackup/config.xml +++ b/source/packages/com_mokosuitebackup/config.xml @@ -245,7 +245,7 @@ type="text" label="COM_MOKOJOOMBACKUP_CONFIG_NTFY_SERVER" description="COM_MOKOJOOMBACKUP_CONFIG_NTFY_SERVER_DESC" - default="https://ntfy.sh" + default="https://ntfy.mokoconsulting.tech" filter="url" /> - + 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 8942f04..7d96705 100644 --- a/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini +++ b/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini @@ -5,6 +5,7 @@ ; @license GPL-3.0-or-later COM_MOKOJOOMBACKUP="MokoSuiteBackup" +COM_MOKOJOOMBACKUP_CONFIGURATION="MokoSuiteBackup Options" COM_MOKOJOOMBACKUP_DESCRIPTION="Full-site backup and restore for Joomla" ; Submenu @@ -275,9 +276,9 @@ COM_MOKOJOOMBACKUP_FIELD_SFTP_PORT_DESC="SSH port (default: 22)" COM_MOKOJOOMBACKUP_FIELD_SFTP_USERNAME="SSH Username" COM_MOKOJOOMBACKUP_FIELD_SFTP_USERNAME_DESC="Username for SSH authentication" COM_MOKOJOOMBACKUP_FIELD_SFTP_PASSWORD="SSH Password" -COM_MOKOJOOMBACKUP_FIELD_SFTP_PASSWORD_DESC="Password for SSH authentication. Leave blank if using a key file." +COM_MOKOJOOMBACKUP_FIELD_SFTP_PASSWORD_DESC="Password for SSH authentication." COM_MOKOJOOMBACKUP_FIELD_SFTP_KEY="SSH Private Key" -COM_MOKOJOOMBACKUP_FIELD_SFTP_KEY_DESC="Upload your SSH private key (id_rsa, id_ed25519). Stored base64-encoded in DB, written to temp file during upload only. Leave blank for password auth." +COM_MOKOJOOMBACKUP_FIELD_SFTP_KEY_DESC="Upload your SSH private key (id_rsa, id_ed25519). Stored base64-encoded in DB, written to temp file during upload only." COM_MOKOJOOMBACKUP_FIELD_SFTP_KEY_UPLOAD="Upload Key File" COM_MOKOJOOMBACKUP_FIELD_SFTP_KEY_REPLACE="Replace Key" COM_MOKOJOOMBACKUP_FIELD_SFTP_KEY_LOADED="Key loaded" diff --git a/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php b/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php index f72f513..dfb6ffc 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php +++ b/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php @@ -1769,8 +1769,23 @@ async function post(action, extra) { form.append(k, v); } } - const res = await fetch('restore.php', { method: 'POST', body: form }); - return res.json(); + var res; + try { + res = await fetch('restore.php', { method: 'POST', body: form }); + } catch (e) { + log('Network error: ' + e.message); + return { success: false, message: 'Network error: ' + e.message, checks: [] }; + } + if (!res.ok) { + log('Server error: HTTP ' + res.status); + return { success: false, message: 'Server error (HTTP ' + res.status + ')', checks: [] }; + } + try { + return await res.json(); + } catch (e) { + log('Invalid response from server (not JSON)'); + return { success: false, message: 'Invalid server response — check PHP error log', checks: [] }; + } } function goStep(n) { @@ -1845,42 +1860,57 @@ async function runPreflight() { setBtnLoading(btn, true); log('Running pre-flight checks...'); - const r = await post('preflight'); - const list = document.getElementById('checkList'); - while (list.firstChild) list.removeChild(list.firstChild); + try { + const r = await post('preflight'); - r.checks.forEach(function(c) { - const li = document.createElement('li'); - const icon = document.createElement('span'); - icon.className = 'mr-check-icon ' + (c.ok ? 'mr-check-ok' : 'mr-check-fail'); - icon.textContent = c.ok ? '\u2713' : '\u2717'; + if (!r.success && !r.checks.length) { + log('Pre-flight error: ' + (r.message || 'Unknown error')); + setBtnLoading(btn, false); + btn.textContent = 'Re-check'; + setStatus('checkList', r.message || 'Pre-flight check failed', 'error'); + return; + } - const label = document.createElement('span'); - label.className = 'mr-check-label'; - label.textContent = c.label; + const list = document.getElementById('checkList'); + while (list.firstChild) list.removeChild(list.firstChild); - const val = document.createElement('span'); - val.className = 'mr-check-value'; - val.textContent = c.value; + r.checks.forEach(function(c) { + const li = document.createElement('li'); + const icon = document.createElement('span'); + icon.className = 'mr-check-icon ' + (c.ok ? 'mr-check-ok' : 'mr-check-fail'); + icon.textContent = c.ok ? '\u2713' : '\u2717'; - li.appendChild(icon); - li.appendChild(label); - li.appendChild(val); - list.appendChild(li); + const label = document.createElement('span'); + label.className = 'mr-check-label'; + label.textContent = c.label; - log(' ' + (c.ok ? 'OK' : 'FAIL') + ': ' + c.label + ' = ' + c.value); - }); + const val = document.createElement('span'); + val.className = 'mr-check-value'; + val.textContent = c.value; - setBtnLoading(btn, false); + li.appendChild(icon); + li.appendChild(label); + li.appendChild(val); + list.appendChild(li); - if (r.success) { - btn.textContent = 'Next \u2192'; - btn.onclick = function() { goStep(2); }; - btn.className = 'mr-btn mr-btn-success'; - log('All checks passed'); - } else { + log(' ' + (c.ok ? 'OK' : 'FAIL') + ': ' + c.label + ' = ' + c.value); + }); + + setBtnLoading(btn, false); + + if (r.success) { + btn.textContent = 'Next \u2192'; + btn.onclick = function() { goStep(2); }; + btn.className = 'mr-btn mr-btn-success'; + log('All checks passed'); + } else { + btn.textContent = 'Re-check'; + log('Some checks failed'); + } + } catch (e) { + log('Pre-flight error: ' + e.message); + setBtnLoading(btn, false); btn.textContent = 'Re-check'; - log('Some checks failed'); } } diff --git a/source/packages/com_mokosuitebackup/tmpl/backups/default.php b/source/packages/com_mokosuitebackup/tmpl/backups/default.php index 91e8ebb..29c1e26 100644 --- a/source/packages/com_mokosuitebackup/tmpl/backups/default.php +++ b/source/packages/com_mokosuitebackup/tmpl/backups/default.php @@ -42,6 +42,7 @@ $listDirn = $this->escape($this->state->get('list.direction')); + + + + -
- -
-
- - -
- -
-
- - -
-
- - -
-
- - -
-
- -
- - -
-
-
- - -
- -
- -