From afffef78bda7770626d7413eea9b89c431ac9c37 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 19:12:12 +0000 Subject: [PATCH 01/70] chore: sync issue-branch.yml from Template-Generic [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 719d327..11958bd 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.44.01 +# VERSION: 01.00.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" From 57c9ea600b2ec01a0f208658439d1baa47fc1759 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 19:25:18 +0000 Subject: [PATCH 02/70] ci: add submodules: recursive to checkout (fixes MokoSuiteClient packaging) --- .mokogitea/workflows/auto-release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 5865324..4489ae0 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -7,7 +7,7 @@ # INGROUP: mokocli.Release # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli # PATH: /templates/workflows/universal/auto-release.yml.template -# VERSION: 05.00.00 +# VERSION: 05.01.00 # BRIEF: Universal build & release � detects platform from manifest.xml # # +=======================================================================+ @@ -75,6 +75,7 @@ jobs: with: token: ${{ secrets.MOKOGITEA_TOKEN }} fetch-depth: 1 + submodules: recursive - name: Setup mokocli tools env: @@ -173,6 +174,7 @@ jobs: with: token: ${{ secrets.MOKOGITEA_TOKEN }} fetch-depth: 0 + submodules: recursive - name: Configure git for bot pushes run: | From babdb9e390aca878abe2bb9bf84fee90cbad4889 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 19:30:15 +0000 Subject: [PATCH 03/70] ci: add submodules: recursive to checkout (fixes MokoSuiteClient packaging) From 13a526d6bec24710f25976cd9f025bfba8d9d91f Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sun, 28 Jun 2026 19:31:21 +0000 Subject: [PATCH 04/70] chore(release): build 01.45.00 [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 127 +----------------- SECURITY.md | 2 +- .../com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../sql/updates/mysql/01.45.00.sql | 1 + .../mod_mokosuitebackup_cpanel.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 14 files changed, 16 insertions(+), 136 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/01.45.00.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 11958bd..cf5cb9a 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.00.00 +# VERSION: 01.45.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index f6e78b0..864eb00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ # Changelog - ## [Unreleased] + +## [01.45.00] --- 2026-06-28 + ## [01.43.35] --- 2026-06-28 ### Added @@ -33,126 +35,3 @@ ## [01.42.00] --- 2026-06-23 - -## [01.41.00] — 2026-06-23 - -### Added — Multi-Remote Storage -- New `#__mokosuitebackup_remotes` table for multiple destinations per profile -- Remote destinations UI: AJAX-driven add/edit/delete/toggle modal on profile edit -- Engine uploads to ALL enabled destinations (BackupEngine + SteppedBackupEngine) -- Migration auto-converts existing SFTP/S3/GDrive/FTP profile columns to new table -- Backward compatibility: falls back to legacy single-remote columns if table empty -- Secrets masked in API responses, merged from DB on save - -### Added — Content Snapshots -- Lightweight JSON snapshots of articles, categories, and modules -- Includes tags, custom fields, workflow associations, field values -- Restore modes: Replace (clean slate), Merge (upsert), Selective (per-article) -- Snapshot retention: max count + max age with automatic cleanup -- Scheduled snapshot task via com_scheduler -- CLI: `mokosuitebackup:snapshot create|restore|list|delete` -- REST API: create, list, restore, delete, download snapshots -- Tabbed browse modal: Articles / Categories / Modules with item counts - -### Added — SFTP Remote Storage -- SFTP support with SSH key file authentication (key stored base64 in database) -- Auth type dropdown: Password / Key File / Key File + Passphrase -- SshKeyField: file upload via FileReader, key never exposed in HTML -- SFTP remote directory browser for path selection -- `__KEEP_EXISTING__` sentinel preserves key on profile re-save - -### Added — MokoRestore Wizard (9 steps) -- Per-table conflict resolution: Replace / Skip / Merge / Data Only -- Preset buttons: "All Replace", "All Skip", "Everything except users" -- Post-restore actions: reset passwords, hits, versions, sessions, cache -- Auto-detect sanitized passwords and prompt for reset (random temp password) -- Standalone mode: restore.php scans directory for ZIP files -- Wrapped mode: restore.php bundled inside backup ZIP -- Security gate with filesystem verification + path traversal protection - -### Added — Data Sanitization -- Sanitize user passwords: replace hashes with invalid sentinel -- Sanitize user emails: replace with dummy values -- Clear session data: exclude `#__session` table -- Preserve super admin credentials (optional) -- GDPR-friendly backup sharing for demos and staging sites - -### Added — Backup Engine -- Pre-flight validation: directory, disk space, extensions, credentials, running backups -- Auto-verify archive integrity after creation (ZIP, tar.gz, 7z) -- 7z archive format via system 7za/7z CLI binary with native encryption -- Streaming database dump to temp file (prevents OOM on large sites) -- S3 streaming upload via CURLOPT_PUT (prevents OOM) -- Graceful remote degradation: local backup preserved if upload fails -- DatabaseDumper::dumpToFile() for memory-efficient operation - -### Added — Admin UI -- Dashboard: snapshot widget, 30-day backup trend chart, per-profile storage breakdown -- CPanel admin dashboard module (mod_mokosuitebackup_cpanel) with quick actions -- Backup type filter dropdown in backups list -- Backup comparison: select two backups for side-by-side diff -- Archive browser: view files inside backup without extracting -- Manual purge: delete backups older than a date with count preview -- Backup count badges on profile list -- "Do not navigate away" warning in backup/restore progress modals -- Clickable placeholder pills for backup directory and archive name fields -- Comprehensive help modal with absolute/relative/placeholder path documentation -- Placeholder resolution display with EXAMPLE prefix -- All placeholders UPPERCASE: [HOST], [SITE_NAME], [DATE], [DATETIME], etc. - -### Added — CLI & API -- `mokosuitebackup:restore` with --files-only, --db-only, --password options -- `mokosuitebackup:snapshot` with create, restore, list, delete actions -- REST API for snapshots: create, list, restore, delete, download -- Profile credentials masked in API responses - -### Added — Notifications & Logging -- Email/ntfy notifications for site restore, snapshot create/restore -- Joomla Action Logs for restore, snapshot, and snapshot restore events -- Global ntfy server/topic/token settings (fallback for profiles) - -### Added — Security & Configuration -- Webcron secret field with CSPRNG generator + strength meter -- IP whitelist field with current IP detection + one-click "Add my IP" -- 10 ACL permissions with full enforcement audit across all controllers -- Config defaults: archive format, MokoRestore mode, sanitization settings -- Path traversal protection on all archive extraction (ZIP, tar.gz, JPA) - -### Fixed -- CLI RestoreCommand passed wrong arguments (filepath instead of record ID) -- JPA path traversal: reject `../` in archive entry paths -- S3Uploader OOM: streaming upload instead of file_get_contents -- DatabaseDumper OOM: streaming to file instead of in-memory string -- AkeebaImporter: removed unserialize() (PHP object injection risk) -- BackupTable: delete DB row before file (prevents data loss) -- RestoreEngine: staging path sanitized with preg_replace -- API profiles: sensitive fields masked with `***` -- Webcron: missing return after sendJsonResponse on auth failure -- loadFormData(): cast array to object (PHP 8.x TypeError fix) -- MokoRestore data-only mode: uses REPLACE INTO for existing rows -- Plaintext archive deleted on encryption failure -- TarGzArchiver: intermediate .tar cleaned in finally block -- Install script: single-line comments converted to block comments -- Orphaned root-level webservices plugin files removed -- include_mokorestore column: TINYINT changed to VARCHAR(20) -- Snapshot fields_values: scoped dump and restore to com_content.article (previously destroyed values for contacts, users, etc.) -- Run Backup button: accept CSRF token from GET (fixes "token did not match" on profile edit) -- SFTP fields: moved into remote fieldset for showon visibility; removed required attr that blocked non-SFTP saves -- Script.php merge conflict markers resolved - -## [01.24.00] — 2026-06-02 - -### Added -- Initial release: full-site backup and restore for Joomla 6 -- Database, files, and configuration backup -- ZIP and tar.gz archive formats with AES-256 encryption -- Differential backups based on file manifests -- FTP/FTPS, S3, Google Drive remote storage -- MokoRestore standalone restore wizard -- CLI backup and restore commands -- REST API for remote management -- Scheduled tasks via com_scheduler -- Email and ntfy push notifications -- Per-profile retention, exclusions, and notifications -- Akeeba Backup migration tool -- Admin dashboard with system health checks diff --git a/SECURITY.md b/SECURITY.md index d6dc672..2286acd 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.44.01 +VERSION: 01.45.00 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index d426355..1bba702 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 01.44.01 + 01.45.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/01.45.00.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/01.45.00.sql new file mode 100644 index 0000000..2865b45 --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/01.45.00.sql @@ -0,0 +1 @@ +/* 01.45.00 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index 98faafe..e6eb337 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 01.44.01 + 01.45.00 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index b6153a0..22bb14b 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 01.44.01 + 01.45.00 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 8406be3..5b1fe35 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 01.44.01 + 01.45.00 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 11b81ef..44f4a47 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 01.44.01 + 01.45.00 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index 660403e..345b8bd 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 01.44.01 + 01.45.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 024e237..9d0f954 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 01.44.01 + 01.45.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index 3b86900..d9a104f 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 01.44.01 + 01.45.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index eab68d5..cb64b0d 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 01.44.01 + 01.45.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index ac0f65d..a88829b 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 01.44.01 + 01.45.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech From 1a4bb32c6c610c536e6116f87ed2ba4d320da2e3 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sun, 28 Jun 2026 19:31:25 +0000 Subject: [PATCH 05/70] =?UTF-8?q?chore:=20promote=20changelog=20[Unrelease?= =?UTF-8?q?d]=20=E2=86=92=20[01.45.00]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 864eb00..00c43ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +## [01.45.00] --- 2026-06-28 + ## [01.45.00] --- 2026-06-28 From 4d5711c304f674683b4e826bdfb1176dfcf96808 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 19:39:22 +0000 Subject: [PATCH 06/70] chore: sync auto-release.yml from Template-Generic [skip ci] --- .mokogitea/workflows/auto-release.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 4489ae0..5865324 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -7,7 +7,7 @@ # INGROUP: mokocli.Release # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli # PATH: /templates/workflows/universal/auto-release.yml.template -# VERSION: 05.01.00 +# VERSION: 05.00.00 # BRIEF: Universal build & release � detects platform from manifest.xml # # +=======================================================================+ @@ -75,7 +75,6 @@ jobs: with: token: ${{ secrets.MOKOGITEA_TOKEN }} fetch-depth: 1 - submodules: recursive - name: Setup mokocli tools env: @@ -174,7 +173,6 @@ jobs: with: token: ${{ secrets.MOKOGITEA_TOKEN }} fetch-depth: 0 - submodules: recursive - name: Configure git for bot pushes run: | From 71da4af64b07d11b8c83bfbe27ebf36cb5565299 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 19:39:25 +0000 Subject: [PATCH 07/70] chore: sync issue-branch.yml from Template-Generic [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index cf5cb9a..11958bd 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.45.00 +# VERSION: 01.00.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" From 785ffd85a3f300b24a775488ed15a3511691424d Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 20:10:19 +0000 Subject: [PATCH 08/70] chore: sync auto-release.yml from Template-Generic [skip ci] --- .mokogitea/workflows/auto-release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 5865324..4489ae0 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -7,7 +7,7 @@ # INGROUP: mokocli.Release # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli # PATH: /templates/workflows/universal/auto-release.yml.template -# VERSION: 05.00.00 +# VERSION: 05.01.00 # BRIEF: Universal build & release � detects platform from manifest.xml # # +=======================================================================+ @@ -75,6 +75,7 @@ jobs: with: token: ${{ secrets.MOKOGITEA_TOKEN }} fetch-depth: 1 + submodules: recursive - name: Setup mokocli tools env: @@ -173,6 +174,7 @@ jobs: with: token: ${{ secrets.MOKOGITEA_TOKEN }} fetch-depth: 0 + submodules: recursive - name: Configure git for bot pushes run: | From 3a6bb1c7838d4d4d5f886d920b47980cc0eb2166 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 30 Jun 2026 12:50:02 -0500 Subject: [PATCH 09/70] fix: remote upload prefix mismatch and restore security file visibility Remote uploaders (SFTP, FTP, S3, Google Drive) expect type-prefixed property names (sftp_host, ftp_port, etc.) but createUploaderFromParams passes unprefixed keys from the remotes table params JSON. Add prefix mapping in createUploaderFromParams to bridge the naming gap. Rename .mokorestore-security.php to mokorestore-security.php (no leading dot) so the file is visible in file managers and not blocked by web server dotfile rules. Also clean it up in actionCleanup. Closes #13 Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG --- .../com_mokosuitebackup/src/Engine/BackupEngine.php | 11 ++++++++++- .../com_mokosuitebackup/src/Engine/MokoRestore.php | 12 ++++++------ .../src/Engine/SteppedBackupEngine.php | 11 ++++++++++- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php b/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php index 5eb994c..aaf8a56 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php +++ b/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php @@ -547,7 +547,16 @@ class BackupEngine */ private function createUploaderFromParams(string $type, array $params): RemoteUploaderInterface { - $fake = (object) $params; + $prefixMap = ['ftp' => 'ftp_', 'sftp' => 'sftp_', 's3' => 's3_', 'google_drive' => 'gdrive_']; + $prefix = $prefixMap[$type] ?? ''; + + $prefixed = []; + + foreach ($params as $key => $value) { + $prefixed[$prefix . $key] = $value; + } + + $fake = (object) $prefixed; return match ($type) { 'ftp' => new FtpUploader($fake), diff --git a/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php b/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php index 78c4b2a..a66dd33 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php +++ b/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php @@ -358,7 +358,7 @@ $token = $_SESSION['restore_token']; // Write a security file to the web root with a random code. // The user must read the code from the file and enter it in the browser // to prove they have filesystem access before any restore actions are allowed. -$securityFile = RESTORE_DIR . '/.mokorestore-security.php'; +$securityFile = RESTORE_DIR . '/mokorestore-security.php'; $securityCode = $_SESSION['security_code'] ?? ''; if (empty($securityCode)) { @@ -395,7 +395,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' echo json_encode(['success' => true, 'message' => 'Security verified']); } else { - echo json_encode(['success' => false, 'message' => 'Incorrect security code. Check the file: .mokorestore-security.php']); + echo json_encode(['success' => false, 'message' => 'Incorrect security code. Check the file: mokorestore-security.php']); } exit; @@ -414,7 +414,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { } if (!$securityVerified) { - echo json_encode(['success' => false, 'message' => 'Security verification required. Enter the code from .mokorestore-security.php']); + echo json_encode(['success' => false, 'message' => 'Security verification required. Enter the code from mokorestore-security.php']); exit; } @@ -1397,7 +1397,7 @@ function actionCleanup(): array { $removed = []; - foreach (['database.sql', 'site-backup.zip'] as $file) { + foreach (['database.sql', 'site-backup.zip', 'mokorestore-security.php'] as $file) { $path = RESTORE_DIR . '/' . $file; if (is_file($path) && @unlink($path)) { @@ -1570,14 +1570,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica N

Security Verification

-

To prevent unauthorized access, enter the security code from the file .mokorestore-security.php in your site root.

+

To prevent unauthorized access, enter the security code from the file mokorestore-security.php in your site root.

🔒 How to find the code
  1. Connect to your server via FTP, SSH, or file manager
  2. -
  3. Open .mokorestore-security.php in the site root directory
  4. +
  5. Open mokorestore-security.php in the site root directory
  6. Copy the 8-character code and enter it below
diff --git a/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php b/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php index 9e18d04..a9dbb08 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php +++ b/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php @@ -889,7 +889,16 @@ class SteppedBackupEngine */ private function createUploaderFromParams(string $type, array $params): RemoteUploaderInterface { - $fake = (object) $params; + $prefixMap = ['ftp' => 'ftp_', 'sftp' => 'sftp_', 's3' => 's3_', 'google_drive' => 'gdrive_']; + $prefix = $prefixMap[$type] ?? ''; + + $prefixed = []; + + foreach ($params as $key => $value) { + $prefixed[$prefix . $key] = $value; + } + + $fake = (object) $prefixed; return match ($type) { 'ftp' => new FtpUploader($fake), From f2f424a5653abb4345ba86fedfd3ce43503651d1 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 17:50:30 +0000 Subject: [PATCH 10/70] chore(version): pre-release bump to 02.52.16-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- SECURITY.md | 2 +- source/packages/MokoSuiteClient | 2 +- source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/com_mokosuitebackup/sql/updates/mysql/02.52.16.sql | 1 + .../mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml | 2 +- .../packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_webservices_mokosuitebackup/mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 14 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.16.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 11958bd..0567b3c 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.00.00 +# VERSION: 02.52.16 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/SECURITY.md b/SECURITY.md index 2286acd..c488295 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.45.00 +VERSION: 02.52.16 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/MokoSuiteClient b/source/packages/MokoSuiteClient index 9df6bea..0a9125e 160000 --- a/source/packages/MokoSuiteClient +++ b/source/packages/MokoSuiteClient @@ -1 +1 @@ -Subproject commit 9df6bea4b7480b2e443898ad84a279070ba4a7f6 +Subproject commit 0a9125e51956a084941abccdf2de8ddd064777e8 diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index 1bba702..f481e8d 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 01.45.00 + 02.52.16 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.16.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.16.sql new file mode 100644 index 0000000..cd9e5d7 --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.16.sql @@ -0,0 +1 @@ +/* 02.52.16 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index e6eb337..1f4465b 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 01.45.00 + 02.52.16 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index 22bb14b..b7b8892 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 01.45.00 + 02.52.16 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 5b1fe35..449d6cb 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 01.45.00 + 02.52.16 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 44f4a47..5dc9251 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 01.45.00 + 02.52.16 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index 345b8bd..eae02d4 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 01.45.00 + 02.52.16 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 9d0f954..41c491d 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 01.45.00 + 02.52.16 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index d9a104f..065010a 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 01.45.00 + 02.52.16 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index cb64b0d..850b06f 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 01.45.00 + 02.52.16 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index a88829b..510a1d2 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 01.45.00 + 02.52.16 2026-06-02 Moko Consulting hello@mokoconsulting.tech From 20ce945e73de66559975bab8f876d1ead1a6a4b2 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 30 Jun 2026 12:53:03 -0500 Subject: [PATCH 11/70] fix: recreate security file if missing while verification is pending The security file was only written inside the code-generation block (first page load). If the file was deleted or failed to write, it was never recreated because the session already held the code. Now file writing is a separate check that runs whenever verification is pending and the file is missing. Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG --- .../packages/com_mokosuitebackup/src/Engine/MokoRestore.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php b/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php index a66dd33..0079a1f 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php +++ b/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php @@ -365,8 +365,10 @@ if (empty($securityCode)) { $securityCode = strtoupper(substr(bin2hex(random_bytes(4)), 0, 8)); $_SESSION['security_code'] = $securityCode; $_SESSION['security_verified'] = false; +} - // Write security file with the code +// Write (or recreate) the security file whenever verification is still pending +if (empty($_SESSION['security_verified']) && !is_file($securityFile)) { $securityContent = "\n" . "MokoRestore Security Verification\n" . "==================================\n" @@ -374,7 +376,6 @@ if (empty($securityCode)) { . "Enter this code in the MokoRestore browser interface to proceed.\n" . "This file will be deleted automatically after verification.\n"; if (file_put_contents($securityFile, $securityContent) === false) { - // Cannot write security file — skip verification to avoid locking user out $_SESSION['security_verified'] = true; error_log('MokoRestore: Cannot write security file — verification skipped (check directory permissions)'); } From 236609341f4b83b3795986ab6ca83e1156ea22df Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 17:53:59 +0000 Subject: [PATCH 12/70] chore(version): pre-release bump to 02.52.17-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- SECURITY.md | 2 +- source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/com_mokosuitebackup/sql/updates/mysql/02.52.17.sql | 1 + .../mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml | 2 +- .../packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_webservices_mokosuitebackup/mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 13 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.17.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 0567b3c..6245055 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.52.16 +# VERSION: 02.52.17 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/SECURITY.md b/SECURITY.md index c488295..7a7da29 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.52.16 +VERSION: 02.52.17 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index f481e8d..b96f870 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 02.52.16 + 02.52.17 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.17.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.17.sql new file mode 100644 index 0000000..bd657ea --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.17.sql @@ -0,0 +1 @@ +/* 02.52.17 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index 1f4465b..624746b 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 02.52.16 + 02.52.17 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index b7b8892..bb6b2e4 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.52.16 + 02.52.17 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 449d6cb..a77f40c 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.52.16 + 02.52.17 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 5dc9251..21e7829 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.52.16 + 02.52.17 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index eae02d4..11e4151 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.52.16 + 02.52.17 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 41c491d..d386a41 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.52.16 + 02.52.17 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index 065010a..c97e6c6 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.52.16 + 02.52.17 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index 850b06f..d3add2e 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.52.16 + 02.52.17 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index 510a1d2..8848c3b 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.52.16 + 02.52.17 2026-06-02 Moko Consulting hello@mokoconsulting.tech From 4093267984f1067a5e8e205c9297894c07a111c9 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 30 Jun 2026 12:58:53 -0500 Subject: [PATCH 13/70] fix: add verbose error_log throughout restore script Every action handler now logs entry, key parameters, outcomes, and failures to PHP error_log. Security file creation logs directory permissions, PHP user, and the specific error when file_put_contents fails. Database import logs SQL file size, statement counts, and individual errors. Cleanup logs each file removal success/failure. Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG --- .../src/Engine/MokoRestore.php | 66 +++++++++++++++++-- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php b/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php index 0079a1f..eb6ea0a 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php +++ b/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php @@ -346,6 +346,9 @@ define('MOKOJOOMBACKUP_RESTORE', 1); define('RESTORE_DIR', __DIR__); define('BACKUP_FILE', RESTORE_DIR . '/site-backup.zip'); +error_log('MokoRestore: Script loaded — RESTORE_DIR=' . RESTORE_DIR); +error_log('MokoRestore: PHP ' . PHP_VERSION . ', SAPI=' . php_sapi_name() . ', memory_limit=' . ini_get('memory_limit')); + session_start(); if (empty($_SESSION['restore_token'])) { @@ -369,15 +372,26 @@ if (empty($securityCode)) { // Write (or recreate) the security file whenever verification is still pending if (empty($_SESSION['security_verified']) && !is_file($securityFile)) { + error_log('MokoRestore: Writing security file: ' . $securityFile); + error_log('MokoRestore: Target directory: ' . RESTORE_DIR . ' (writable: ' . (is_writable(RESTORE_DIR) ? 'yes' : 'NO') . ')'); + $securityContent = "\n" . "MokoRestore Security Verification\n" . "==================================\n" . "Code: " . $securityCode . "\n" . "Enter this code in the MokoRestore browser interface to proceed.\n" . "This file will be deleted automatically after verification.\n"; - if (file_put_contents($securityFile, $securityContent) === false) { + + $written = @file_put_contents($securityFile, $securityContent); + + if ($written === false) { + $err = error_get_last(); + error_log('MokoRestore: FAILED to write security file — ' . ($err['message'] ?? 'unknown error')); + error_log('MokoRestore: Directory permissions: ' . decoct(@fileperms(RESTORE_DIR) & 0777) . ', owner: ' . @fileowner(RESTORE_DIR) . ', PHP user: ' . (function_exists('posix_getuid') ? posix_getuid() : 'n/a')); + error_log('MokoRestore: Security verification SKIPPED — user will not be challenged'); $_SESSION['security_verified'] = true; - error_log('MokoRestore: Cannot write security file — verification skipped (check directory permissions)'); + } else { + error_log('MokoRestore: Security file created (' . $written . ' bytes)'); } } @@ -388,14 +402,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' if ($inputCode === $securityCode) { $_SESSION['security_verified'] = true; + error_log('MokoRestore: Security code VERIFIED'); - // Delete the security file if (is_file($securityFile)) { @unlink($securityFile); + error_log('MokoRestore: Security file deleted'); } echo json_encode(['success' => true, 'message' => 'Security verified']); } else { + error_log('MokoRestore: Security code REJECTED (input=' . $inputCode . ')'); echo json_encode(['success' => false, 'message' => 'Incorrect security code. Check the file: mokorestore-security.php']); } @@ -425,9 +441,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { @ignore_user_abort(true); try { + error_log('MokoRestore: Action dispatched — ' . $_POST['action']); $result = handleAction($_POST['action'], $_POST); + error_log('MokoRestore: Action ' . $_POST['action'] . ' completed — ' . ($result['success'] ? 'OK' : 'FAIL: ' . ($result['message'] ?? ''))); echo json_encode($result); } catch (Throwable $e) { + error_log('MokoRestore: Action ' . $_POST['action'] . ' EXCEPTION — ' . $e->getMessage()); echo json_encode(['success' => false, 'message' => $e->getMessage()]); } @@ -552,10 +571,14 @@ function actionPreflight(): array function actionExtract(array $data): array { + error_log('MokoRestore: Extract — target=' . BACKUP_FILE . ', exists=' . (file_exists(BACKUP_FILE) ? 'yes' : 'no')); + if (!file_exists(BACKUP_FILE)) { throw new RuntimeException('Backup file not found: site-backup.zip'); } + error_log('MokoRestore: Extract — archive size=' . number_format(filesize(BACKUP_FILE) / 1048576, 2) . ' MB'); + $zip = new ZipArchive(); if ($zip->open(BACKUP_FILE) !== true) { @@ -592,6 +615,8 @@ function actionExtract(array $data): array $count = $zip->numFiles; $zip->close(); + error_log('MokoRestore: Extract — ' . $count . ' files extracted to ' . RESTORE_DIR); + // Pre-fill from configuration.php.bak (sanitized backup) or // configuration.php (legacy/unsanitized backup). Skip [SANITIZED:] values. $existingConfig = []; @@ -720,6 +745,8 @@ function actionDatabase(array $data): array $user = $data['db_user'] ?? ''; $pass = $data['db_pass'] ?? ''; + error_log('MokoRestore: Database import — host=' . $host . ', db=' . $name . ', user=' . $user); + if (empty($name) || empty($user)) { throw new RuntimeException('Database name and user are required'); } @@ -727,9 +754,12 @@ function actionDatabase(array $data): array $sqlFile = RESTORE_DIR . '/database.sql'; if (!is_file($sqlFile)) { + error_log('MokoRestore: Database import — no database.sql found, skipping'); return ['success' => true, 'message' => 'No database.sql found — skipped', 'statements' => 0, 'errors' => 0]; } + error_log('MokoRestore: Database import — SQL file size=' . number_format(filesize($sqlFile) / 1048576, 2) . ' MB'); + $pdo = new PDO( "mysql:host={$host};dbname={$name};charset=utf8mb4", $user, @@ -836,6 +866,14 @@ function actionDatabase(array $data): array $msg .= " ({$errors} warnings)"; } + error_log('MokoRestore: Database import — ' . $msg); + + if (!empty($errorList)) { + foreach ($errorList as $i => $err) { + error_log('MokoRestore: DB error ' . ($i + 1) . ': ' . $err); + } + } + return [ 'success' => ($statements > 0 || $errors === 0), 'message' => $msg, @@ -848,6 +886,7 @@ function actionDatabase(array $data): array function actionConfig(array $data): array { + error_log('MokoRestore: Config rebuild started'); $host = $data['db_host'] ?? 'localhost'; $dbName = $data['db_name'] ?? ''; $dbUser = $data['db_user'] ?? ''; @@ -868,6 +907,7 @@ function actionConfig(array $data): array // debug, cache, SEF, editor, etc.). Fall back to existing config // for legacy/unsanitized backups, or build from scratch if neither exists. $basePath = is_file($bakPath) ? $bakPath : (is_file($configPath) ? $configPath : null); + error_log('MokoRestore: Config — base template: ' . ($basePath ?? 'none (building from scratch)')); if ($basePath !== null) { $config = file_get_contents($basePath); @@ -920,9 +960,12 @@ function actionConfig(array $data): array } if (file_put_contents($configPath, $config) === false) { + error_log('MokoRestore: Config — FAILED to write ' . $configPath); return ['success' => false, 'message' => 'Failed to write Joomla config file — check directory permissions']; } + error_log('MokoRestore: Config — written to ' . $configPath . ' (' . filesize($configPath) . ' bytes)'); + // Remove .bak after successful rebuild if (is_file($bakPath)) { @unlink($bakPath); @@ -1176,6 +1219,8 @@ function actionResetAdmin(array $data): array $userId = (int) ($data['admin_id'] ?? 0); $password = $data['new_password'] ?? ''; + error_log('MokoRestore: Admin password reset — user_id=' . $userId); + if ($userId < 1 || strlen($password) < 8) { throw new RuntimeException('Select an admin and enter a password (8+ characters)'); } @@ -1189,6 +1234,7 @@ function actionResetAdmin(array $data): array throw new RuntimeException('User not found or password unchanged'); } + error_log('MokoRestore: Admin password reset — success'); return ['success' => true, 'message' => 'Admin password updated successfully']; } @@ -1198,6 +1244,7 @@ function actionPostRestore(array $data): array $prefix = getValidatedPrefix($data); $tasks = json_decode($data['tasks'] ?? '[]', true) ?: []; $results = []; + error_log('MokoRestore: Post-restore — ' . count($tasks) . ' task(s): ' . implode(', ', $tasks)); foreach ($tasks as $task) { try { @@ -1320,6 +1367,7 @@ function actionProvision(array $data): array $prefix = getValidatedPrefix($data); $tasks = json_decode($data['tasks'] ?? '[]', true) ?: []; $results = []; + error_log('MokoRestore: Provisioning — ' . count($tasks) . ' task(s): ' . implode(', ', $tasks)); foreach ($tasks as $task) { try { @@ -1396,16 +1444,24 @@ function actionProvision(array $data): array function actionCleanup(): array { + error_log('MokoRestore: Cleanup started'); $removed = []; foreach (['database.sql', 'site-backup.zip', 'mokorestore-security.php'] as $file) { $path = RESTORE_DIR . '/' . $file; - if (is_file($path) && @unlink($path)) { - $removed[] = $file; + if (is_file($path)) { + if (@unlink($path)) { + $removed[] = $file; + error_log('MokoRestore: Cleanup — removed ' . $file); + } else { + error_log('MokoRestore: Cleanup — FAILED to remove ' . $file); + } } } + error_log('MokoRestore: Cleanup complete — removed ' . count($removed) . ' file(s)'); + return [ 'success' => true, 'message' => 'Removed: ' . (empty($removed) ? '(none)' : implode(', ', $removed)) From 3d9c48f40fd99f7282a5221691428f933e818428 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 18:00:27 +0000 Subject: [PATCH 14/70] chore(version): pre-release bump to 02.52.18-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- SECURITY.md | 2 +- source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/com_mokosuitebackup/sql/updates/mysql/02.52.18.sql | 1 + .../mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml | 2 +- .../packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_webservices_mokosuitebackup/mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 13 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.18.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 6245055..136d61e 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.52.17 +# VERSION: 02.52.18 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/SECURITY.md b/SECURITY.md index 7a7da29..5dfd6f1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.52.17 +VERSION: 02.52.18 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index b96f870..916c79c 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 02.52.17 + 02.52.18 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.18.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.18.sql new file mode 100644 index 0000000..27c00ce --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.18.sql @@ -0,0 +1 @@ +/* 02.52.18 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index 624746b..8c6c5ba 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 02.52.17 + 02.52.18 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index bb6b2e4..14db151 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.52.17 + 02.52.18 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index a77f40c..1fdeb74 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.52.17 + 02.52.18 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 21e7829..0c325b4 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.52.17 + 02.52.18 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index 11e4151..d00530b 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.52.17 + 02.52.18 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index d386a41..7ad71bf 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.52.17 + 02.52.18 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index c97e6c6..0a4e874 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.52.17 + 02.52.18 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index d3add2e..acd32a2 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.52.17 + 02.52.18 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index 8848c3b..aa7a8ae 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.52.17 + 02.52.18 2026-06-02 Moko Consulting hello@mokoconsulting.tech From a4df3a651db6ebcf7a84479bb8063b94b62ddc79 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 30 Jun 2026 18:07:18 +0000 Subject: [PATCH 15/70] fix: remove orphaned deploy-manual workflow [skip ci] --- .mokogitea/workflows/deploy-manual.yml | 126 ------------------------- 1 file changed, 126 deletions(-) delete mode 100644 .mokogitea/workflows/deploy-manual.yml diff --git a/.mokogitea/workflows/deploy-manual.yml b/.mokogitea/workflows/deploy-manual.yml deleted file mode 100644 index 1af323c..0000000 --- a/.mokogitea/workflows/deploy-manual.yml +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Deploy -# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API -# PATH: /templates/workflows/joomla/deploy-manual.yml.template -# VERSION: 04.07.00 -# BRIEF: Manual SFTP deploy to dev server for Joomla repos - -name: "Universal: Deploy to Dev (Manual)" - -on: - workflow_dispatch: - inputs: - clear_remote: - description: 'Delete all remote files before uploading' - required: false - default: 'false' - type: boolean - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -permissions: - contents: read - -jobs: - deploy: - name: SFTP Deploy to Dev - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Setup PHP - run: | - php -v && composer --version - - - name: Setup MokoStandards tools - env: - MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN || github.token }} - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN || github.token }} - MOKO_CLONE_HOST: ${{ secrets.MOKOGITEA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }} - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.MOKOGITEA_TOKEN || github.token }}"}}' - run: | - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \ - /tmp/mokostandards-api 2>/dev/null || true - if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then - cd /tmp/mokostandards-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true - fi - - - name: Check FTP configuration - id: check - env: - HOST: ${{ vars.DEV_FTP_HOST }} - PATH_VAR: ${{ vars.DEV_FTP_PATH }} - PORT: ${{ vars.DEV_FTP_PORT }} - run: | - if [ -z "$HOST" ] || [ -z "$PATH_VAR" ]; then - echo "DEV_FTP_HOST or DEV_FTP_PATH not configured -- cannot deploy" - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - echo "skip=false" >> "$GITHUB_OUTPUT" - echo "host=$HOST" >> "$GITHUB_OUTPUT" - - REMOTE="${PATH_VAR%/}" - echo "remote=$REMOTE" >> "$GITHUB_OUTPUT" - - [ -z "$PORT" ] && PORT="22" - echo "port=$PORT" >> "$GITHUB_OUTPUT" - - - name: Deploy via SFTP - if: steps.check.outputs.skip != 'true' - env: - SFTP_KEY: ${{ secrets.DEV_FTP_KEY }} - SFTP_PASS: ${{ secrets.DEV_FTP_PASSWORD }} - SFTP_USER: ${{ vars.DEV_FTP_USERNAME }} - run: | - SOURCE_DIR="src" - [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" - [ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ -- nothing to deploy"; exit 0; } - - printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \ - "${{ steps.check.outputs.host }}" "${{ steps.check.outputs.port }}" "$SFTP_USER" "${{ steps.check.outputs.remote }}" \ - > /tmp/sftp-config.json - - if [ -n "$SFTP_KEY" ]; then - echo "$SFTP_KEY" > /tmp/deploy_key - chmod 600 /tmp/deploy_key - printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json - else - printf ',"password":"%s"}' "$SFTP_PASS" >> /tmp/sftp-config.json - fi - - DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json) - [ "${{ inputs.clear_remote }}" = "true" ] && DEPLOY_ARGS+=(--clear-remote) - - PLATFORM=$(php /tmp/mokostandards-api/cli/platform_detect.php --path . 2>/dev/null || true) - if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards-api/deploy/deploy-joomla.php" ]; then - php /tmp/mokostandards-api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}" - else - php /tmp/mokostandards-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}" - fi - - rm -f /tmp/deploy_key /tmp/sftp-config.json - - - name: Summary - if: always() - run: | - if [ "${{ steps.check.outputs.skip }}" = "true" ]; then - echo "### Deploy Skipped -- FTP not configured" >> $GITHUB_STEP_SUMMARY - else - echo "### Manual Dev Deploy Complete" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| Host | \`${{ steps.check.outputs.host }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Remote | \`${{ steps.check.outputs.remote }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Clear | ${{ inputs.clear_remote }} |" >> $GITHUB_STEP_SUMMARY - fi From 25cf65f4cfc11b6cf65d2a31181b85aa28d80f1a Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 18:11:56 +0000 Subject: [PATCH 16/70] chore(release): build 02.52.18 [skip ci] --- CHANGELOG.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00c43ad..15d941d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +## [02.52.18] --- 2026-06-30 + ## [01.45.00] --- 2026-06-28 @@ -32,8 +34,3 @@ - MokoRestore stalling: unhandled promise rejections from network errors or non-JSON responses left UI in loading state ## [01.43.00] --- 2026-06-24 - -## [01.42.00] --- 2026-06-23 - - -## [01.42.00] --- 2026-06-23 From 983ec77dbda724509b3c92a38caa8dfa7a1b77da Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 18:12:06 +0000 Subject: [PATCH 17/70] =?UTF-8?q?chore:=20promote=20changelog=20[Unrelease?= =?UTF-8?q?d]=20=E2=86=92=20[02.52.18]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15d941d..42cc677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## [02.52.18] --- 2026-06-30 +## [02.52.18] --- 2026-06-30 + ## [01.45.00] --- 2026-06-28 From ebc692789cec3a91d8aec307a4733740b66fa180 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 30 Jun 2026 18:12:34 +0000 Subject: [PATCH 18/70] chore: sync issue-branch.yml from Template-Generic [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 136d61e..11958bd 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.52.18 +# VERSION: 01.00.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" From 1eb1c18bdfbd0ad581231d70059e3910f78f1658 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 30 Jun 2026 13:48:51 -0500 Subject: [PATCH 19/70] fix: add cancel stalled backup action with ACL permission Backups stuck in "running" status block all future backups for the same profile via the preflight check. Previously the only fix was a manual DB update. Adds a toolbar button and AJAX endpoint to cancel stalled backups: - New ACL permission: mokosuitebackup.backup.cancel - BackupsController::cancelStalled() for toolbar (multi-select) - AjaxController::cancelBackup() for AJAX/API use - Sets status to "fail", cleans up partial archive files - Updated preflight error message to mention the cancel action - Language keys for en-GB and en-US Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG --- .../packages/com_mokosuitebackup/access.xml | 1 + .../language/en-GB/com_mokosuitebackup.ini | 8 +++ .../language/en-US/com_mokosuitebackup.ini | 10 +++ .../src/Controller/AjaxController.php | 61 ++++++++++++++++ .../src/Controller/BackupsController.php | 70 +++++++++++++++++++ .../src/Engine/PreflightCheck.php | 2 +- .../src/View/Backups/HtmlView.php | 4 ++ 7 files changed, 155 insertions(+), 1 deletion(-) diff --git a/source/packages/com_mokosuitebackup/access.xml b/source/packages/com_mokosuitebackup/access.xml index 37c2f9d..6f3220e 100644 --- a/source/packages/com_mokosuitebackup/access.xml +++ b/source/packages/com_mokosuitebackup/access.xml @@ -15,5 +15,6 @@ + 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 8547103..738fb5a 100644 --- a/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini +++ b/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini @@ -450,6 +450,8 @@ COM_MOKOSUITEBACKUP_ACTION_BACKUP_COMPARE="Compare Backups" COM_MOKOSUITEBACKUP_ACTION_BACKUP_COMPARE_DESC="Allows users to compare two backup records side-by-side." COM_MOKOSUITEBACKUP_ACTION_BACKUP_BROWSE="Browse Archives" COM_MOKOSUITEBACKUP_ACTION_BACKUP_BROWSE_DESC="Allows users to view file listings inside backup archives without extracting." +COM_MOKOSUITEBACKUP_ACTION_BACKUP_CANCEL="Cancel Stalled Backup" +COM_MOKOSUITEBACKUP_ACTION_BACKUP_CANCEL_DESC="Allows users to cancel backup records stuck in running status and clean up partial archive files." ; Snapshot ACL COM_MOKOSUITEBACKUP_ACTION_SNAPSHOT_MANAGE="Manage Snapshots" @@ -500,6 +502,12 @@ COM_MOKOJOOMBACKUP_PURGE_INVALID_DATE="Invalid date. Please select a valid date. COM_MOKOJOOMBACKUP_PURGE_SUCCESS="%d backup(s) purged successfully." COM_MOKOJOOMBACKUP_PURGE_PARTIAL="%d backup(s) purged, but %d could not be deleted." +; Cancel Stalled Backup +COM_MOKOJOOMBACKUP_TOOLBAR_CANCEL_STALLED="Cancel Stalled" +COM_MOKOJOOMBACKUP_CANCEL_NONE_SELECTED="No backup records selected." +COM_MOKOJOOMBACKUP_CANCEL_NONE_RUNNING="None of the selected backups are in running status." +COM_MOKOJOOMBACKUP_CANCEL_SUCCESS="%d stalled backup(s) cancelled." + ; Remote Destinations (multi-remote) COM_MOKOJOOMBACKUP_REMOTE_DESTINATIONS="Remote Destinations" COM_MOKOJOOMBACKUP_REMOTE_ADD="Add Destination" 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 ef2e72b..1d7328d 100644 --- a/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini +++ b/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini @@ -116,3 +116,13 @@ COM_MOKOJOOMBACKUP_PURGE_NONE_FOUND="No completed backups found before the selec COM_MOKOJOOMBACKUP_PURGE_INVALID_DATE="Invalid date. Please select a valid date." COM_MOKOJOOMBACKUP_PURGE_SUCCESS="%d backup(s) purged successfully." COM_MOKOJOOMBACKUP_PURGE_PARTIAL="%d backup(s) purged, but %d could not be deleted." + +; Cancel Stalled Backup +COM_MOKOJOOMBACKUP_TOOLBAR_CANCEL_STALLED="Cancel Stalled" +COM_MOKOJOOMBACKUP_CANCEL_NONE_SELECTED="No backup records selected." +COM_MOKOJOOMBACKUP_CANCEL_NONE_RUNNING="None of the selected backups are in running status." +COM_MOKOJOOMBACKUP_CANCEL_SUCCESS="%d stalled backup(s) cancelled." + +; ACL - Cancel +COM_MOKOSUITEBACKUP_ACTION_BACKUP_CANCEL="Cancel Stalled Backup" +COM_MOKOSUITEBACKUP_ACTION_BACKUP_CANCEL_DESC="Allows users to cancel backup records stuck in running status and clean up partial archive files." diff --git a/source/packages/com_mokosuitebackup/src/Controller/AjaxController.php b/source/packages/com_mokosuitebackup/src/Controller/AjaxController.php index 159af3b..f064e0b 100644 --- a/source/packages/com_mokosuitebackup/src/Controller/AjaxController.php +++ b/source/packages/com_mokosuitebackup/src/Controller/AjaxController.php @@ -84,6 +84,67 @@ class AjaxController extends BaseController $this->sendJson($result); } + /** + * Cancel a backup record stuck in "running" status. + * POST: task=ajax.cancelBackup&id=123 + */ + public function cancelBackup(): void + { + if (!Session::checkToken('get') && !Session::checkToken('post')) { + $this->sendJson(['error' => true, 'message' => 'Invalid token'], 403); + + return; + } + + if (!$this->app->getIdentity()->authorise('mokosuitebackup.backup.cancel', 'com_mokosuitebackup')) { + $this->sendJson(['error' => true, 'message' => 'Access denied'], 403); + + return; + } + + $id = $this->input->getInt('id', 0); + + if (!$id) { + $this->sendJson(['error' => true, 'message' => 'Missing record ID']); + + return; + } + + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName(['id', 'status', 'absolute_path'])) + ->from($db->quoteName('#__mokosuitebackup_records')) + ->where($db->quoteName('id') . ' = ' . $id); + $db->setQuery($query); + $record = $db->loadObject(); + + if (!$record) { + $this->sendJson(['error' => true, 'message' => 'Record not found'], 404); + + return; + } + + if ($record->status !== 'running') { + $this->sendJson(['error' => true, 'message' => 'Backup is not in running status']); + + return; + } + + $update = $db->getQuery(true) + ->update($db->quoteName('#__mokosuitebackup_records')) + ->set($db->quoteName('status') . ' = ' . $db->quote('fail')) + ->set($db->quoteName('backupend') . ' = ' . $db->quote(date('Y-m-d H:i:s'))) + ->where($db->quoteName('id') . ' = ' . $id); + $db->setQuery($update); + $db->execute(); + + if (!empty($record->absolute_path) && is_file($record->absolute_path)) { + @unlink($record->absolute_path); + } + + $this->sendJson(['error' => false, 'message' => 'Backup cancelled']); + } + /** * Browse server directories for the folder picker field. * POST: task=ajax.browseDir&path=/some/path diff --git a/source/packages/com_mokosuitebackup/src/Controller/BackupsController.php b/source/packages/com_mokosuitebackup/src/Controller/BackupsController.php index 1f09d49..b2bad20 100644 --- a/source/packages/com_mokosuitebackup/src/Controller/BackupsController.php +++ b/source/packages/com_mokosuitebackup/src/Controller/BackupsController.php @@ -235,6 +235,76 @@ class BackupsController extends AdminController $this->setRedirect(Route::_('index.php?option=com_mokosuitebackup&view=backups', false)); } + /** + * Cancel selected backup records that are stuck in "running" status. + * + * Sets their status to "fail", cleans up partial archive files, + * and destroys any associated stepped session. + */ + public function cancelStalled(): void + { + $this->checkToken(); + + if (!$this->app->getIdentity()->authorise('mokosuitebackup.backup.cancel', 'com_mokosuitebackup')) { + $this->setMessage(Text::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'), 'error'); + $this->setRedirect(Route::_('index.php?option=com_mokosuitebackup&view=backups', false)); + + return; + } + + $cid = $this->input->get('cid', [], 'array'); + + if (empty($cid)) { + $this->setMessage(Text::_('COM_MOKOJOOMBACKUP_CANCEL_NONE_SELECTED'), 'warning'); + $this->setRedirect(Route::_('index.php?option=com_mokosuitebackup&view=backups', false)); + + return; + } + + $db = $this->app->getContainer()->get('DatabaseDriver'); + $cancelled = 0; + $skipped = 0; + + foreach ($cid as $id) { + $id = (int) $id; + + $query = $db->getQuery(true) + ->select($db->quoteName(['id', 'status', 'absolute_path'])) + ->from($db->quoteName('#__mokosuitebackup_records')) + ->where($db->quoteName('id') . ' = ' . $id); + $db->setQuery($query); + $record = $db->loadObject(); + + if (!$record || $record->status !== 'running') { + $skipped++; + + continue; + } + + $update = $db->getQuery(true) + ->update($db->quoteName('#__mokosuitebackup_records')) + ->set($db->quoteName('status') . ' = ' . $db->quote('fail')) + ->set($db->quoteName('backupend') . ' = ' . $db->quote(date('Y-m-d H:i:s'))) + ->where($db->quoteName('id') . ' = ' . $id); + $db->setQuery($update); + $db->execute(); + + if (!empty($record->absolute_path) && is_file($record->absolute_path)) { + @unlink($record->absolute_path); + } + + $cancelled++; + } + + if ($cancelled > 0) { + $this->setMessage(Text::sprintf('COM_MOKOJOOMBACKUP_CANCEL_SUCCESS', $cancelled)); + } elseif ($skipped > 0) { + $this->setMessage(Text::_('COM_MOKOJOOMBACKUP_CANCEL_NONE_RUNNING'), 'warning'); + } + + $this->setRedirect(Route::_('index.php?option=com_mokosuitebackup&view=backups', false)); + } + /** * No-op target for the purge toolbar button. * diff --git a/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php b/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php index ac62cd3..d85e821 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php +++ b/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php @@ -209,7 +209,7 @@ class PreflightCheck if ($running > 0) { $this->errors[] = 'Another backup is already running for profile: ' . $profile->title - . ' — wait for it to finish or delete the stale record'; + . ' — wait for it to finish or use Cancel Stalled from the Backup Records toolbar'; } } diff --git a/source/packages/com_mokosuitebackup/src/View/Backups/HtmlView.php b/source/packages/com_mokosuitebackup/src/View/Backups/HtmlView.php index 4816383..3b45d0f 100644 --- a/source/packages/com_mokosuitebackup/src/View/Backups/HtmlView.php +++ b/source/packages/com_mokosuitebackup/src/View/Backups/HtmlView.php @@ -113,6 +113,10 @@ class HtmlView extends BaseHtmlView ToolbarHelper::custom('backups.compare', 'copy', '', 'COM_MOKOJOOMBACKUP_TOOLBAR_COMPARE', true); } + if ($user->authorise('mokosuitebackup.backup.cancel', 'com_mokosuitebackup')) { + ToolbarHelper::custom('backups.cancelStalled', 'stop-circle', '', 'COM_MOKOJOOMBACKUP_TOOLBAR_CANCEL_STALLED', true); + } + if ($user->authorise('core.delete', 'com_mokosuitebackup')) { ToolbarHelper::deleteList('JGLOBAL_CONFIRM_DELETE', 'backups.delete'); } From a56f72b186226ef1e30234924c05d4b1c1a4164f Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 18:49:16 +0000 Subject: [PATCH 20/70] chore(version): pre-release bump to 02.52.20-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- SECURITY.md | 2 +- source/packages/MokoSuiteClient | 2 +- source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/com_mokosuitebackup/sql/updates/mysql/02.52.20.sql | 1 + .../mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml | 2 +- .../packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_webservices_mokosuitebackup/mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 14 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.20.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 11958bd..5fcb249 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.00.00 +# VERSION: 02.52.20 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/SECURITY.md b/SECURITY.md index 5dfd6f1..df2069c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.52.18 +VERSION: 02.52.20 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/MokoSuiteClient b/source/packages/MokoSuiteClient index 0a9125e..c7e6670 160000 --- a/source/packages/MokoSuiteClient +++ b/source/packages/MokoSuiteClient @@ -1 +1 @@ -Subproject commit 0a9125e51956a084941abccdf2de8ddd064777e8 +Subproject commit c7e66705443f74e3ee2ffdfecc08224cc40240aa diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index 916c79c..3c01478 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 02.52.18 + 02.52.20 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.20.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.20.sql new file mode 100644 index 0000000..4812b35 --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.20.sql @@ -0,0 +1 @@ +/* 02.52.20 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index 8c6c5ba..88c0bdf 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 02.52.18 + 02.52.20 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index 14db151..1aac1ef 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.52.18 + 02.52.20 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 1fdeb74..765d8ec 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.52.18 + 02.52.20 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 0c325b4..17b2685 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.52.18 + 02.52.20 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index d00530b..b736034 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.52.18 + 02.52.20 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 7ad71bf..5e11793 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.52.18 + 02.52.20 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index 0a4e874..d2a84bf 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.52.18 + 02.52.20 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index acd32a2..ad258c0 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.52.18 + 02.52.20 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index aa7a8ae..2c90373 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.52.18 + 02.52.20 2026-06-02 Moko Consulting hello@mokoconsulting.tech From b26a21820ba47a10934ee78e7ad58c7116e01a00 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 30 Jun 2026 13:49:56 -0500 Subject: [PATCH 21/70] fix: auto-cancel stalled backups after 30 min timeout PreflightCheck now auto-cancels "running" backup records that have exceeded 30 minutes, treating them as stalled. Partial archive files are cleaned up. The auto-cancelled records are surfaced as warnings so the user knows what happened. Records younger than 30 minutes are assumed to be legitimately running and still block new backups for the same profile. Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG --- .../src/Engine/PreflightCheck.php | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php b/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php index d85e821..64b42b5 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php +++ b/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php @@ -194,20 +194,56 @@ class PreflightCheck } } + private const STALE_TIMEOUT_MINUTES = 30; + /** * Check if another backup is already running for this profile. + * + * Backups running longer than STALE_TIMEOUT_MINUTES are automatically + * marked as failed so they don't permanently block future runs. */ private function checkRunningBackup(object $profile, object $db): void { $query = $db->getQuery(true) - ->select('COUNT(*)') + ->select($db->quoteName(['id', 'backupstart', 'absolute_path'])) ->from($db->quoteName('#__mokosuitebackup_records')) ->where($db->quoteName('profile_id') . ' = ' . (int) $profile->id) ->where($db->quoteName('status') . ' = ' . $db->quote('running')); $db->setQuery($query); - $running = (int) $db->loadResult(); + $rows = $db->loadObjectList(); - if ($running > 0) { + if (empty($rows)) { + return; + } + + $cutoff = time() - (self::STALE_TIMEOUT_MINUTES * 60); + $stillAlive = 0; + + foreach ($rows as $row) { + $started = strtotime($row->backupstart); + + if ($started !== false && $started < $cutoff) { + $update = $db->getQuery(true) + ->update($db->quoteName('#__mokosuitebackup_records')) + ->set($db->quoteName('status') . ' = ' . $db->quote('fail')) + ->set($db->quoteName('backupend') . ' = ' . $db->quote(date('Y-m-d H:i:s'))) + ->where($db->quoteName('id') . ' = ' . (int) $row->id); + $db->setQuery($update); + $db->execute(); + + if (!empty($row->absolute_path) && is_file($row->absolute_path)) { + @unlink($row->absolute_path); + } + + $this->warnings[] = 'Auto-cancelled stalled backup #' . $row->id + . ' (started ' . $row->backupstart . ', exceeded ' + . self::STALE_TIMEOUT_MINUTES . ' min timeout)'; + } else { + $stillAlive++; + } + } + + if ($stillAlive > 0) { $this->errors[] = 'Another backup is already running for profile: ' . $profile->title . ' — wait for it to finish or use Cancel Stalled from the Backup Records toolbar'; } From 5be922613fc887345b9f432874d6dd39476cd16f Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 18:50:40 +0000 Subject: [PATCH 22/70] chore(version): pre-release bump to 02.52.21-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- SECURITY.md | 2 +- source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/com_mokosuitebackup/sql/updates/mysql/02.52.21.sql | 1 + .../mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml | 2 +- .../packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_webservices_mokosuitebackup/mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 13 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.21.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 5fcb249..4f2a190 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.52.20 +# VERSION: 02.52.21 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/SECURITY.md b/SECURITY.md index df2069c..297840f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.52.20 +VERSION: 02.52.21 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index 3c01478..55474f9 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 02.52.20 + 02.52.21 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.21.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.21.sql new file mode 100644 index 0000000..5727cbf --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.21.sql @@ -0,0 +1 @@ +/* 02.52.21 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index 88c0bdf..77bb70c 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 02.52.20 + 02.52.21 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index 1aac1ef..c783628 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.52.20 + 02.52.21 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 765d8ec..7b110a8 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.52.20 + 02.52.21 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 17b2685..5356723 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.52.20 + 02.52.21 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index b736034..421af36 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.52.20 + 02.52.21 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 5e11793..71bc16e 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.52.20 + 02.52.21 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index d2a84bf..cd5ff7b 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.52.20 + 02.52.21 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index ad258c0..2dbdd87 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.52.20 + 02.52.21 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index 2c90373..34ccd67 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.52.20 + 02.52.21 2026-06-02 Moko Consulting hello@mokoconsulting.tech From 4560ffb84b9da81b574ec3a8d02b9fe1aca3993f Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 30 Jun 2026 13:52:19 -0500 Subject: [PATCH 23/70] docs: update changelog with cancel stalled and pre-update modal Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG --- CHANGELOG.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42cc677..d8f3a35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,20 @@ # Changelog ## [Unreleased] -## [02.52.18] --- 2026-06-30 +### Added +- Cancel Stalled toolbar button on Backup Records view to cancel backups stuck in "running" status +- New ACL permission `mokosuitebackup.backup.cancel` for cancel stalled action +- AJAX endpoint `ajax.cancelBackup` for programmatic/API cancel +- Auto-timeout failsafe: preflight auto-cancels "running" backups older than 30 minutes +- Pre-extension-update backup progress modal (Bootstrap 5 modal with stepped AJAX progress bar) + +### Fixed +- Pre-update backup ran synchronously with no browser feedback — page hung until complete +- Stalled backups permanently blocked future backups for the same profile +- Preflight error message now directs users to Cancel Stalled action ## [02.52.18] --- 2026-06-30 -## [01.45.00] --- 2026-06-28 - - ## [01.45.00] --- 2026-06-28 ## [01.43.35] --- 2026-06-28 From 8dac5a7448d2594888395c7de8050c702a83f94d Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 18:53:06 +0000 Subject: [PATCH 24/70] chore(version): pre-release bump to 02.52.22-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- SECURITY.md | 2 +- source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/com_mokosuitebackup/sql/updates/mysql/02.52.22.sql | 1 + .../mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml | 2 +- .../packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_webservices_mokosuitebackup/mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 13 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.22.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 4f2a190..9a5ae00 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.52.21 +# VERSION: 02.52.22 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/SECURITY.md b/SECURITY.md index 297840f..46c440f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.52.21 +VERSION: 02.52.22 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index 55474f9..6aeb44e 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 02.52.21 + 02.52.22 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.22.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.22.sql new file mode 100644 index 0000000..dd661bd --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.22.sql @@ -0,0 +1 @@ +/* 02.52.22 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index 77bb70c..f9cd809 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 02.52.21 + 02.52.22 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index c783628..e7759ab 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.52.21 + 02.52.22 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 7b110a8..94003d1 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.52.21 + 02.52.22 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 5356723..38e1384 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.52.21 + 02.52.22 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index 421af36..a757f43 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.52.21 + 02.52.22 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 71bc16e..0f6282c 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.52.21 + 02.52.22 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index cd5ff7b..4643360 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.52.21 + 02.52.22 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index 2dbdd87..868f770 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.52.21 + 02.52.22 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index 34ccd67..fa57eb2 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.52.21 + 02.52.22 2026-06-02 Moko Consulting hello@mokoconsulting.tech From 007036301d71b8edd410ec748334716ab6277de0 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 18:53:45 +0000 Subject: [PATCH 25/70] chore(release): build 02.52.22 [skip ci] --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8f3a35..fc9dd10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog + ## [Unreleased] +## [02.52.22] --- 2026-06-30 + ### Added - Cancel Stalled toolbar button on Backup Records view to cancel backups stuck in "running" status - New ACL permission `mokosuitebackup.backup.cancel` for cancel stalled action From 765e6feea6ee49f54788372822d5289287f14771 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 18:53:53 +0000 Subject: [PATCH 26/70] =?UTF-8?q?chore:=20promote=20changelog=20[Unrelease?= =?UTF-8?q?d]=20=E2=86=92=20[02.52.22]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc9dd10..1c3df82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ## [02.52.22] --- 2026-06-30 +## [02.52.22] --- 2026-06-30 + ### Added - Cancel Stalled toolbar button on Backup Records view to cancel backups stuck in "running" status - New ACL permission `mokosuitebackup.backup.cancel` for cancel stalled action From ed55ab068b9cfda2406ca3b7d71cdd0ad58f0a5a Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 30 Jun 2026 18:56:24 +0000 Subject: [PATCH 27/70] chore: sync issue-branch.yml from Template-Generic [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 9a5ae00..11958bd 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.52.22 +# VERSION: 01.00.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" From aefa46e0c40ec17e3b82ad398214ca896e284d05 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 30 Jun 2026 14:14:06 -0500 Subject: [PATCH 28/70] fix: use warning status when backup succeeds but remote upload fails Previously a successful backup with a failed remote upload was marked as "complete", hiding the upload failure. Now these records get a "warning" status with a yellow badge so operators can see at a glance which backups didn't reach their remote destination. Warning-status records are treated as usable backups throughout: - Downloadable, browsable, and restorable (the archive is intact) - Counted in dashboard stats, storage totals, and success streaks - Included in purge operations and differential base lookups - Shown with yellow "warning" badge in list, detail, and cpanel module - Filterable via the status dropdown on Backup Records Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG --- .../forms/filter_backups.xml | 1 + .../language/en-GB/com_mokosuitebackup.ini | 1 + .../language/en-US/com_mokosuitebackup.ini | 3 +++ .../com_mokosuitebackup/sql/install.mysql.sql | 2 +- .../src/Controller/AjaxController.php | 4 ++-- .../src/Controller/BackupsController.php | 2 +- .../src/Engine/BackupEngine.php | 4 ++-- .../src/Engine/PreflightCheck.php | 2 +- .../src/Engine/RestoreEngine.php | 2 +- .../src/Engine/SteppedBackupEngine.php | 2 +- .../src/Engine/SteppedRestoreEngine.php | 2 +- .../src/Helper/BackupStatusHelper.php | 6 +++--- .../src/Model/DashboardModel.php | 6 +++--- .../src/View/Backup/HtmlView.php | 2 +- .../tmpl/backup/default.php | 1 + .../tmpl/backups/default.php | 1 + .../en-GB/mod_mokosuitebackup_cpanel.ini | 1 + .../tmpl/default.php | 18 ++++++++++++++---- 18 files changed, 39 insertions(+), 21 deletions(-) diff --git a/source/packages/com_mokosuitebackup/forms/filter_backups.xml b/source/packages/com_mokosuitebackup/forms/filter_backups.xml index a44abd5..fa78e99 100644 --- a/source/packages/com_mokosuitebackup/forms/filter_backups.xml +++ b/source/packages/com_mokosuitebackup/forms/filter_backups.xml @@ -15,6 +15,7 @@ > + 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 738fb5a..c21b6d1 100644 --- a/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini +++ b/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini @@ -207,6 +207,7 @@ COM_MOKOJOOMBACKUP_TYPE_DIFFERENTIAL="Differential (changed files + full DB)" ; Status labels COM_MOKOJOOMBACKUP_STATUS_COMPLETE="Complete" +COM_MOKOJOOMBACKUP_STATUS_WARNING="Warning" COM_MOKOJOOMBACKUP_STATUS_RUNNING="Running" COM_MOKOJOOMBACKUP_STATUS_FAIL="Failed" COM_MOKOJOOMBACKUP_STATUS_PENDING="Pending" 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 1d7328d..db5e85b 100644 --- a/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini +++ b/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini @@ -123,6 +123,9 @@ COM_MOKOJOOMBACKUP_CANCEL_NONE_SELECTED="No backup records selected." COM_MOKOJOOMBACKUP_CANCEL_NONE_RUNNING="None of the selected backups are in running status." COM_MOKOJOOMBACKUP_CANCEL_SUCCESS="%d stalled backup(s) cancelled." +; Backup status +COM_MOKOJOOMBACKUP_STATUS_WARNING="Warning" + ; ACL - Cancel COM_MOKOSUITEBACKUP_ACTION_BACKUP_CANCEL="Cancel Stalled Backup" COM_MOKOSUITEBACKUP_ACTION_BACKUP_CANCEL_DESC="Allows users to cancel backup records stuck in running status and clean up partial archive files." diff --git a/source/packages/com_mokosuitebackup/sql/install.mysql.sql b/source/packages/com_mokosuitebackup/sql/install.mysql.sql index 984ff45..705ebfd 100644 --- a/source/packages/com_mokosuitebackup/sql/install.mysql.sql +++ b/source/packages/com_mokosuitebackup/sql/install.mysql.sql @@ -65,7 +65,7 @@ CREATE TABLE IF NOT EXISTS `#__mokosuitebackup_records` ( `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `profile_id` INT(11) UNSIGNED NOT NULL DEFAULT 1, `description` VARCHAR(255) NOT NULL DEFAULT '', - `status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending, running, complete, fail', + `status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending, running, complete, warning, fail', `origin` VARCHAR(20) NOT NULL DEFAULT 'backend' COMMENT 'backend, cli, api, scheduled', `backup_type` VARCHAR(20) NOT NULL DEFAULT 'full' COMMENT 'full, database, files', `archivename` VARCHAR(512) NOT NULL DEFAULT '', diff --git a/source/packages/com_mokosuitebackup/src/Controller/AjaxController.php b/source/packages/com_mokosuitebackup/src/Controller/AjaxController.php index f064e0b..264464f 100644 --- a/source/packages/com_mokosuitebackup/src/Controller/AjaxController.php +++ b/source/packages/com_mokosuitebackup/src/Controller/AjaxController.php @@ -512,7 +512,7 @@ class AjaxController extends BaseController return; } - if ($record->status !== 'complete' || !$record->filesexist) { + if (!\in_array($record->status, ['complete', 'warning'], true) || !$record->filesexist) { $this->sendJson(['error' => true, 'message' => 'Archive not available']); return; @@ -808,7 +808,7 @@ class AjaxController extends BaseController ->select('COUNT(*)') ->from($db->quoteName('#__mokosuitebackup_records')) ->where($db->quoteName('backupstart') . ' < ' . $db->quote($cutoff)) - ->where($db->quoteName('status') . ' = ' . $db->quote('complete')); + ->where($db->quoteName('status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'warning'])) . ')'); $db->setQuery($query); $count = (int) $db->loadResult(); } catch (\Exception $e) { diff --git a/source/packages/com_mokosuitebackup/src/Controller/BackupsController.php b/source/packages/com_mokosuitebackup/src/Controller/BackupsController.php index b2bad20..59a725b 100644 --- a/source/packages/com_mokosuitebackup/src/Controller/BackupsController.php +++ b/source/packages/com_mokosuitebackup/src/Controller/BackupsController.php @@ -199,7 +199,7 @@ class BackupsController extends AdminController ->select($db->quoteName('id')) ->from($db->quoteName('#__mokosuitebackup_records')) ->where($db->quoteName('backupstart') . ' < ' . $db->quote($cutoff)) - ->where($db->quoteName('status') . ' = ' . $db->quote('complete')); + ->where($db->quoteName('status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'warning'])) . ')'); $db->setQuery($query); $ids = $db->loadColumn(); diff --git a/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php b/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php index aaf8a56..04b8f33 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php +++ b/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php @@ -375,7 +375,7 @@ class BackupEngine // Final record update (includes fields needed by NotificationSender) $update = (object) [ 'id' => $recordId, - 'status' => 'complete', + 'status' => $uploadFailed ? 'warning' : 'complete', 'description' => $description, 'backup_type' => $profile->backup_type, 'archivename' => $archiveName, @@ -606,7 +606,7 @@ class BackupEngine ->select($db->quoteName('manifest')) ->from($db->quoteName('#__mokosuitebackup_records')) ->where($db->quoteName('profile_id') . ' = ' . $profileId) - ->where($db->quoteName('status') . ' = ' . $db->quote('complete')) + ->where($db->quoteName('status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'warning'])) . ')') ->where($db->quoteName('manifest') . ' != ' . $db->quote('')) ->where($db->quoteName('backup_type') . ' = ' . $db->quote('full')) ->order($db->quoteName('backupstart') . ' DESC'); diff --git a/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php b/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php index 64b42b5..664a3e8 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php +++ b/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php @@ -165,7 +165,7 @@ class PreflightCheck ->select($db->quoteName('total_size')) ->from($db->quoteName('#__mokosuitebackup_records')) ->where($db->quoteName('profile_id') . ' = ' . (int) $profile->id) - ->where($db->quoteName('status') . ' = ' . $db->quote('complete')) + ->where($db->quoteName('status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'warning'])) . ')') ->where($db->quoteName('total_size') . ' > 0') ->order($db->quoteName('backupstart') . ' DESC'); $db->setQuery($query, 0, 1); diff --git a/source/packages/com_mokosuitebackup/src/Engine/RestoreEngine.php b/source/packages/com_mokosuitebackup/src/Engine/RestoreEngine.php index 55202d0..88ad222 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/RestoreEngine.php +++ b/source/packages/com_mokosuitebackup/src/Engine/RestoreEngine.php @@ -67,7 +67,7 @@ class RestoreEngine return ['success' => false, 'message' => 'Backup record not found: ' . $recordId]; } - if ($record->status !== 'complete') { + if ($record->status !== 'complete' && $record->status !== 'warning') { return ['success' => false, 'message' => 'Cannot restore from incomplete backup (status: ' . $record->status . ')']; } diff --git a/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php b/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php index a9dbb08..ee640e7 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php +++ b/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php @@ -647,7 +647,7 @@ class SteppedBackupEngine $update = (object) [ 'id' => $session->recordId, - 'status' => 'complete', + 'status' => $uploadFailed ? 'warning' : 'complete', 'backupend' => date('Y-m-d H:i:s'), 'total_size' => $totalSize, 'checksum' => $checksum, diff --git a/source/packages/com_mokosuitebackup/src/Engine/SteppedRestoreEngine.php b/source/packages/com_mokosuitebackup/src/Engine/SteppedRestoreEngine.php index cf1b9ef..25668b1 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/SteppedRestoreEngine.php +++ b/source/packages/com_mokosuitebackup/src/Engine/SteppedRestoreEngine.php @@ -64,7 +64,7 @@ class SteppedRestoreEngine return ['error' => true, 'message' => 'Backup record not found: ' . $recordId]; } - if ($record->status !== 'complete') { + if ($record->status !== 'complete' && $record->status !== 'warning') { return ['error' => true, 'message' => 'Cannot restore from incomplete backup (status: ' . $record->status . ')']; } diff --git a/source/packages/com_mokosuitebackup/src/Helper/BackupStatusHelper.php b/source/packages/com_mokosuitebackup/src/Helper/BackupStatusHelper.php index 7de26bc..a711f97 100644 --- a/source/packages/com_mokosuitebackup/src/Helper/BackupStatusHelper.php +++ b/source/packages/com_mokosuitebackup/src/Helper/BackupStatusHelper.php @@ -70,7 +70,7 @@ class BackupStatusHelper ]) ->from($db->quoteName('#__mokosuitebackup_records', 'r')) ->join('LEFT', $db->quoteName('#__mokosuitebackup_profiles', 'p') . ' ON p.id = r.profile_id') - ->where($db->quoteName('r.status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'fail'])) . ')') + ->where($db->quoteName('r.status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'warning', 'fail'])) . ')') ->order($db->quoteName('r.backupstart') . ' DESC'); if ($profileId !== null) { @@ -148,7 +148,7 @@ class BackupStatusHelper $query = $db->getQuery(true) ->select($db->quoteName('status')) ->from($db->quoteName('#__mokosuitebackup_records')) - ->where($db->quoteName('status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'fail'])) . ')') + ->where($db->quoteName('status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'warning', 'fail'])) . ')') ->order($db->quoteName('backupstart') . ' DESC') ->setLimit(50); @@ -156,7 +156,7 @@ class BackupStatusHelper $streak = 0; foreach ($statuses as $s) { - if ($s === 'complete') { + if ($s === 'complete' || $s === 'warning') { $streak++; } else { break; diff --git a/source/packages/com_mokosuitebackup/src/Model/DashboardModel.php b/source/packages/com_mokosuitebackup/src/Model/DashboardModel.php index ae85d6e..8ad63df 100644 --- a/source/packages/com_mokosuitebackup/src/Model/DashboardModel.php +++ b/source/packages/com_mokosuitebackup/src/Model/DashboardModel.php @@ -30,7 +30,7 @@ class DashboardModel extends BaseDatabaseModel ->select('r.*, p.title AS profile_title') ->from($db->quoteName('#__mokosuitebackup_records', 'r')) ->join('LEFT', $db->quoteName('#__mokosuitebackup_profiles', 'p') . ' ON p.id = r.profile_id') - ->where($db->quoteName('r.status') . ' = ' . $db->quote('complete')) + ->where($db->quoteName('r.status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'warning'])) . ')') ->order($db->quoteName('r.backupend') . ' DESC'); $db->setQuery($query, 0, 1); @@ -75,7 +75,7 @@ class DashboardModel extends BaseDatabaseModel ->select('COUNT(*) AS total_count') ->select('COALESCE(SUM(' . $db->quoteName('total_size') . '), 0) AS total_size') ->from($db->quoteName('#__mokosuitebackup_records')) - ->where($db->quoteName('status') . ' = ' . $db->quote('complete')); + ->where($db->quoteName('status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'warning'])) . ')'); $db->setQuery($query); $stats = $db->loadObject(); @@ -274,7 +274,7 @@ class DashboardModel extends BaseDatabaseModel ->select('COALESCE(SUM(r.total_size), 0) AS total_size') ->from($db->quoteName('#__mokosuitebackup_records', 'r')) ->join('LEFT', $db->quoteName('#__mokosuitebackup_profiles', 'p') . ' ON p.id = r.profile_id') - ->where($db->quoteName('r.status') . ' = ' . $db->quote('complete')) + ->where($db->quoteName('r.status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'warning'])) . ')') ->group($db->quoteName('r.profile_id')) ->order('total_size DESC'); $db->setQuery($query); diff --git a/source/packages/com_mokosuitebackup/src/View/Backup/HtmlView.php b/source/packages/com_mokosuitebackup/src/View/Backup/HtmlView.php index cdc2c7f..d1fe0c9 100644 --- a/source/packages/com_mokosuitebackup/src/View/Backup/HtmlView.php +++ b/source/packages/com_mokosuitebackup/src/View/Backup/HtmlView.php @@ -41,7 +41,7 @@ class HtmlView extends BaseHtmlView $user = Factory::getApplication()->getIdentity(); - if ($this->item->status === 'complete' + if (\in_array($this->item->status, ['complete', 'warning'], true) && !empty($this->item->filesexist) && $user->authorise('mokosuitebackup.backup.download', 'com_mokosuitebackup') ) { diff --git a/source/packages/com_mokosuitebackup/tmpl/backup/default.php b/source/packages/com_mokosuitebackup/tmpl/backup/default.php index bcaf51f..49dfd58 100644 --- a/source/packages/com_mokosuitebackup/tmpl/backup/default.php +++ b/source/packages/com_mokosuitebackup/tmpl/backup/default.php @@ -30,6 +30,7 @@ $ajaxUrl = Route::_('index.php?option=com_mokosuitebackup&format=json', false) item->status) { 'complete' => 'badge bg-success', + 'warning' => 'badge bg-warning text-dark', 'running' => 'badge bg-info', 'fail' => 'badge bg-danger', default => 'badge bg-secondary', diff --git a/source/packages/com_mokosuitebackup/tmpl/backups/default.php b/source/packages/com_mokosuitebackup/tmpl/backups/default.php index f5e664a..5417461 100644 --- a/source/packages/com_mokosuitebackup/tmpl/backups/default.php +++ b/source/packages/com_mokosuitebackup/tmpl/backups/default.php @@ -92,6 +92,7 @@ $listDirn = $this->escape($this->state->get('list.direction')); status) { 'complete' => 'badge bg-success', + 'warning' => 'badge bg-warning text-dark', 'running' => 'badge bg-info', 'fail' => 'badge bg-danger', default => 'badge bg-secondary', diff --git a/source/packages/mod_mokosuitebackup_cpanel/language/en-GB/mod_mokosuitebackup_cpanel.ini b/source/packages/mod_mokosuitebackup_cpanel/language/en-GB/mod_mokosuitebackup_cpanel.ini index 50cb35a..6ca28a1 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/language/en-GB/mod_mokosuitebackup_cpanel.ini +++ b/source/packages/mod_mokosuitebackup_cpanel/language/en-GB/mod_mokosuitebackup_cpanel.ini @@ -11,6 +11,7 @@ MOD_MOKOSUITEBACKUP_CPANEL_NOT_INSTALLED="MokoSuiteBackup is not installed or is MOD_MOKOSUITEBACKUP_CPANEL_LAST_BACKUP="Last Backup" MOD_MOKOSUITEBACKUP_CPANEL_STATUS_OK="Success" +MOD_MOKOSUITEBACKUP_CPANEL_STATUS_WARNING="Warning" MOD_MOKOSUITEBACKUP_CPANEL_STATUS_FAIL="Failed" MOD_MOKOSUITEBACKUP_CPANEL_NO_BACKUPS="No backups yet." MOD_MOKOSUITEBACKUP_CPANEL_FILES_TABLES="%d files, %d tables" diff --git a/source/packages/mod_mokosuitebackup_cpanel/tmpl/default.php b/source/packages/mod_mokosuitebackup_cpanel/tmpl/default.php index bad6adf..456d8fa 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/tmpl/default.php +++ b/source/packages/mod_mokosuitebackup_cpanel/tmpl/default.php @@ -51,10 +51,20 @@ $moduleId = 'mod-msb-cpanel-' . $displayData['module']->id;
- - + 'bg-success', + 'warning' => 'bg-warning text-dark', + default => 'bg-danger', + }; + $cpanelLabel = match ($latest['status']) { + 'complete' => Text::_('MOD_MOKOSUITEBACKUP_CPANEL_STATUS_OK'), + 'warning' => Text::_('MOD_MOKOSUITEBACKUP_CPANEL_STATUS_WARNING'), + default => Text::_('MOD_MOKOSUITEBACKUP_CPANEL_STATUS_FAIL'), + }; + ?> + + From 1daa6869ccd46f81197a6e0db111d843dd108a9f Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 19:14:33 +0000 Subject: [PATCH 29/70] chore(version): pre-release bump to 02.52.23-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- SECURITY.md | 2 +- source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/com_mokosuitebackup/sql/updates/mysql/02.52.23.sql | 1 + .../mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml | 2 +- .../packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_webservices_mokosuitebackup/mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 13 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.23.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 11958bd..bb4266e 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.00.00 +# VERSION: 02.52.23 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/SECURITY.md b/SECURITY.md index 46c440f..48acb91 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.52.22 +VERSION: 02.52.23 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index 6aeb44e..efe870a 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 02.52.22 + 02.52.23 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.23.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.23.sql new file mode 100644 index 0000000..0af7fa5 --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.23.sql @@ -0,0 +1 @@ +/* 02.52.23 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index f9cd809..bc246b5 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 02.52.22 + 02.52.23 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index e7759ab..3071612 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.52.22 + 02.52.23 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 94003d1..d425609 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.52.22 + 02.52.23 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 38e1384..cb70875 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.52.22 + 02.52.23 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index a757f43..49a07f1 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.52.22 + 02.52.23 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 0f6282c..ad1885f 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.52.22 + 02.52.23 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index 4643360..6caf75d 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.52.22 + 02.52.23 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index 868f770..b67f89e 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.52.22 + 02.52.23 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index fa57eb2..f63fa37 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.52.22 + 02.52.23 2026-06-02 Moko Consulting hello@mokoconsulting.tech From 9c0c6eae15cd34e67b3927e54334b51aa90a59eb Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 30 Jun 2026 14:17:13 -0500 Subject: [PATCH 30/70] docs: add warning status changes to changelog Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c3df82..308c250 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,19 +4,22 @@ ## [02.52.22] --- 2026-06-30 -## [02.52.22] --- 2026-06-30 - ### Added - Cancel Stalled toolbar button on Backup Records view to cancel backups stuck in "running" status - New ACL permission `mokosuitebackup.backup.cancel` for cancel stalled action - AJAX endpoint `ajax.cancelBackup` for programmatic/API cancel - Auto-timeout failsafe: preflight auto-cancels "running" backups older than 30 minutes - Pre-extension-update backup progress modal (Bootstrap 5 modal with stepped AJAX progress bar) +- New `warning` backup status for records where archive succeeded but remote upload failed +- Warning-status records are downloadable, browsable, restorable, and purgeable +- Warning status filter option in Backup Records dropdown +- Yellow "Warning" badge in backup list, detail view, and cpanel module ### Fixed - Pre-update backup ran synchronously with no browser feedback — page hung until complete - Stalled backups permanently blocked future backups for the same profile - Preflight error message now directs users to Cancel Stalled action +- Backups with failed remote uploads were marked as "complete", hiding the upload failure ## [02.52.18] --- 2026-06-30 From 9820d7521290e7c49707ab06a08ffdae33fdeaf0 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 19:17:57 +0000 Subject: [PATCH 31/70] chore(version): pre-release bump to 02.52.24-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- SECURITY.md | 2 +- source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/com_mokosuitebackup/sql/updates/mysql/02.52.24.sql | 1 + .../mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml | 2 +- .../packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_webservices_mokosuitebackup/mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 13 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.24.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index bb4266e..78cb625 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.52.23 +# VERSION: 02.52.24 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/SECURITY.md b/SECURITY.md index 48acb91..c72f18e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.52.23 +VERSION: 02.52.24 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index efe870a..f099e1a 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 02.52.23 + 02.52.24 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.24.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.24.sql new file mode 100644 index 0000000..5fbc0be --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.24.sql @@ -0,0 +1 @@ +/* 02.52.24 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index bc246b5..099fe46 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 02.52.23 + 02.52.24 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index 3071612..ced288a 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.52.23 + 02.52.24 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index d425609..1e8f1a8 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.52.23 + 02.52.24 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index cb70875..7d17290 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.52.23 + 02.52.24 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index 49a07f1..e9fa579 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.52.23 + 02.52.24 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index ad1885f..4cefd2c 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.52.23 + 02.52.24 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index 6caf75d..4381063 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.52.23 + 02.52.24 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index b67f89e..bc268fc 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.52.23 + 02.52.24 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index f63fa37..15b18fd 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.52.23 + 02.52.24 2026-06-02 Moko Consulting hello@mokoconsulting.tech From 202a26847bf5380bc3c6e312d7940dfd72bb22be Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 19:18:21 +0000 Subject: [PATCH 32/70] chore(release): build 02.52.24 [skip ci] --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 308c250..d9df437 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ # Changelog - ## [Unreleased] + +## [02.52.24] --- 2026-06-30 + ## [02.52.22] --- 2026-06-30 ### Added @@ -49,5 +51,3 @@ - Options page title now shows "MokoSuiteBackup Options" instead of raw language key - Profile dropdown IDs in backup records and dashboard show "#ID — Title (type)" format - MokoRestore stalling: unhandled promise rejections from network errors or non-JSON responses left UI in loading state - -## [01.43.00] --- 2026-06-24 From 481e39562470e41b5a19dbd3e25f5c89d09e0b20 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 30 Jun 2026 19:18:28 +0000 Subject: [PATCH 33/70] =?UTF-8?q?chore:=20promote=20changelog=20[Unrelease?= =?UTF-8?q?d]=20=E2=86=92=20[02.52.24]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9df437..a2ef16d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +## [02.52.24] --- 2026-06-30 + ## [02.52.24] --- 2026-06-30 From 13af787c13cb6f30faef44e386122079bd918957 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 30 Jun 2026 19:19:09 +0000 Subject: [PATCH 34/70] chore: sync issue-branch.yml from Template-Generic [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 78cb625..11958bd 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.52.24 +# VERSION: 01.00.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" From 68605ffc0559ef9a7965aedb00b07ce57a6f724e Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 4 Jul 2026 13:22:44 -0500 Subject: [PATCH 35/70] refactor(remote): remove legacy single-remote storage in favor of remotes table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drops the per-profile remote_storage column and all legacy FTP/SFTP/S3/ Google Drive credential columns. Remote destinations are now sourced exclusively from #__mokosuitebackup_remotes (multi-remote), which is created at install time — so the backward-compat fallback branches in BackupEngine, SteppedBackupEngine and loadRemoteDestinations are removed. - sql: drop 26 legacy columns (install.mysql.sql + 02.52.25.sql migration) - forms/profile.xml: remove legacy remote fields and ftp/gdrive/s3 fieldsets - tmpl/profile/edit.php: drop legacy UI, add save-first prompt, use getOrCreateInstance for the modal, read item.params (was item.config) - PreflightCheck: validate credentials from the remotes table; curl warning now applies to ntfy only - SteppedSession: drop remoteStorage property - language: add backup-record delete-count strings - script.php: simplify postflight license-key prompt --- .../com_mokosuitebackup/forms/profile.xml | 247 ------------------ .../language/en-US/com_mokosuitebackup.ini | 4 + .../com_mokosuitebackup/sql/install.mysql.sql | 26 -- .../sql/updates/mysql/02.52.25.sql | 27 ++ .../src/Engine/BackupEngine.php | 89 +------ .../src/Engine/PreflightCheck.php | 101 +++---- .../src/Engine/SteppedBackupEngine.php | 190 ++++---------- .../src/Engine/SteppedSession.php | 1 - .../com_mokosuitebackup/tmpl/profile/edit.php | 31 +-- source/script.php | 46 +--- 10 files changed, 167 insertions(+), 595 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.25.sql diff --git a/source/packages/com_mokosuitebackup/forms/profile.xml b/source/packages/com_mokosuitebackup/forms/profile.xml index 81a46de..d7fa692 100644 --- a/source/packages/com_mokosuitebackup/forms/profile.xml +++ b/source/packages/com_mokosuitebackup/forms/profile.xml @@ -206,25 +206,6 @@
- - - - - - - JYES - - - - - - - - - - - - - -
@@ -408,157 +314,4 @@ />
-
- - - - - - - - - - - - - -
- -
- - - - -
- -
- - - - - - -
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 db5e85b..fc50e39 100644 --- a/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini +++ b/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini @@ -126,6 +126,10 @@ COM_MOKOJOOMBACKUP_CANCEL_SUCCESS="%d stalled backup(s) cancelled." ; Backup status COM_MOKOJOOMBACKUP_STATUS_WARNING="Warning" +; Delete feedback +COM_MOKOJOOMBACKUP_BACKUPS_N_ITEMS_DELETED="%d backup records deleted." +COM_MOKOJOOMBACKUP_BACKUPS_N_ITEMS_DELETED_1="%d backup record deleted." + ; ACL - Cancel COM_MOKOSUITEBACKUP_ACTION_BACKUP_CANCEL="Cancel Stalled Backup" COM_MOKOSUITEBACKUP_ACTION_BACKUP_CANCEL_DESC="Allows users to cancel backup records stuck in running status and clean up partial archive files." diff --git a/source/packages/com_mokosuitebackup/sql/install.mysql.sql b/source/packages/com_mokosuitebackup/sql/install.mysql.sql index 705ebfd..8b16d99 100644 --- a/source/packages/com_mokosuitebackup/sql/install.mysql.sql +++ b/source/packages/com_mokosuitebackup/sql/install.mysql.sql @@ -11,32 +11,6 @@ CREATE TABLE IF NOT EXISTS `#__mokosuitebackup_profiles` ( `exclude_dirs` TEXT NOT NULL COMMENT 'Newline-separated directory paths to exclude', `exclude_files` TEXT NOT NULL COMMENT 'Newline-separated filename patterns to exclude', `exclude_tables` TEXT NOT NULL COMMENT 'Newline-separated table names to exclude', - `remote_storage` VARCHAR(20) NOT NULL DEFAULT 'none' COMMENT 'none, ftp, google_drive, s3', - `ftp_host` VARCHAR(255) NOT NULL DEFAULT '', - `ftp_port` INT(5) UNSIGNED NOT NULL DEFAULT 21, - `ftp_username` VARCHAR(255) NOT NULL DEFAULT '', - `ftp_password` VARCHAR(255) NOT NULL DEFAULT '', - `ftp_path` VARCHAR(512) NOT NULL DEFAULT '/backups', - `ftp_passive` TINYINT(1) NOT NULL DEFAULT 1, - `ftp_ssl` TINYINT(1) NOT NULL DEFAULT 0, - `sftp_host` VARCHAR(255) NOT NULL DEFAULT '', - `sftp_port` INT(5) UNSIGNED NOT NULL DEFAULT 22, - `sftp_username` VARCHAR(255) NOT NULL DEFAULT '', - `sftp_auth_type` VARCHAR(20) NOT NULL DEFAULT 'key', - `sftp_password` VARCHAR(255) NOT NULL DEFAULT '', - `sftp_key_data` MEDIUMTEXT, - `sftp_passphrase` VARCHAR(255) NOT NULL DEFAULT '', - `sftp_path` VARCHAR(512) NOT NULL DEFAULT '/backups', - `gdrive_client_id` VARCHAR(255) NOT NULL DEFAULT '', - `gdrive_client_secret` VARCHAR(255) NOT NULL DEFAULT '', - `gdrive_refresh_token` VARCHAR(512) NOT NULL DEFAULT '', - `gdrive_folder_id` VARCHAR(255) NOT NULL DEFAULT '', - `s3_endpoint` VARCHAR(512) NOT NULL DEFAULT '' COMMENT 'S3 endpoint URL (blank = AWS default)', - `s3_region` VARCHAR(50) NOT NULL DEFAULT 'us-east-1', - `s3_access_key` VARCHAR(255) NOT NULL DEFAULT '', - `s3_secret_key` VARCHAR(255) NOT NULL DEFAULT '', - `s3_bucket` VARCHAR(255) NOT NULL DEFAULT '', - `s3_path` VARCHAR(512) NOT NULL DEFAULT '/backups', `remote_keep_local` TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'Keep local copy after upload', `encryption_password` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'AES-256 archive encryption password (blank = no encryption)', `include_mokorestore` VARCHAR(20) NOT NULL DEFAULT '0' COMMENT 'MokoRestore mode: 0=none, 1=wrapped, standalone', diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.25.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.25.sql new file mode 100644 index 0000000..0551d9d --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.25.sql @@ -0,0 +1,27 @@ +ALTER TABLE `#__mokosuitebackup_profiles` + DROP COLUMN IF EXISTS `remote_storage`, + DROP COLUMN IF EXISTS `ftp_host`, + DROP COLUMN IF EXISTS `ftp_port`, + DROP COLUMN IF EXISTS `ftp_username`, + DROP COLUMN IF EXISTS `ftp_password`, + DROP COLUMN IF EXISTS `ftp_path`, + DROP COLUMN IF EXISTS `ftp_passive`, + DROP COLUMN IF EXISTS `ftp_ssl`, + DROP COLUMN IF EXISTS `sftp_host`, + DROP COLUMN IF EXISTS `sftp_port`, + DROP COLUMN IF EXISTS `sftp_username`, + DROP COLUMN IF EXISTS `sftp_auth_type`, + DROP COLUMN IF EXISTS `sftp_password`, + DROP COLUMN IF EXISTS `sftp_key_data`, + DROP COLUMN IF EXISTS `sftp_passphrase`, + DROP COLUMN IF EXISTS `sftp_path`, + DROP COLUMN IF EXISTS `gdrive_client_id`, + DROP COLUMN IF EXISTS `gdrive_client_secret`, + DROP COLUMN IF EXISTS `gdrive_refresh_token`, + DROP COLUMN IF EXISTS `gdrive_folder_id`, + DROP COLUMN IF EXISTS `s3_endpoint`, + DROP COLUMN IF EXISTS `s3_region`, + DROP COLUMN IF EXISTS `s3_access_key`, + DROP COLUMN IF EXISTS `s3_secret_key`, + DROP COLUMN IF EXISTS `s3_bucket`, + DROP COLUMN IF EXISTS `s3_path`; diff --git a/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php b/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php index 04b8f33..b0a8c92 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php +++ b/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php @@ -321,48 +321,6 @@ class BackupEngine @unlink($archivePath); $this->log('Local copy removed (remote_keep_local = off)'); } - } else { - /* Backward-compat: fall back to legacy single-remote column */ - $remoteStorage = $profile->remote_storage ?? 'none'; - - if ($remoteStorage !== 'none') { - try { - $this->log('Starting remote upload (' . $remoteStorage . ')...'); - $uploader = $this->createUploader($remoteStorage, $profile); - $uploadResult = $uploader->upload($archivePath, $archiveName); - - if ($uploadResult['success']) { - $remoteFilename = $uploadResult['remote_path'] ?? $archiveName; - $this->log('Remote upload complete: ' . $uploadResult['message']); - - if (!empty($restoreScriptPath) && is_file($restoreScriptPath)) { - $restoreBasename = basename($restoreScriptPath); - $this->log('Uploading standalone ' . $restoreBasename . '...'); - $restoreUpload = $uploader->upload($restoreScriptPath, $restoreBasename); - - if ($restoreUpload['success']) { - $this->log('Standalone ' . $restoreBasename . ' uploaded'); - } else { - $this->log('WARNING: ' . $restoreBasename . ' upload failed: ' . $restoreUpload['message']); - } - } - - // Delete local copy if configured - if (empty($profile->remote_keep_local) && is_file($archivePath)) { - @unlink($archivePath); - $this->log('Local copy removed (remote_keep_local = off)'); - } - } else { - $uploadFailed = true; - $this->log('WARNING: Remote upload failed: ' . $uploadResult['message']); - $this->log('Local backup is preserved.'); - } - } catch (\Throwable $e) { - $uploadFailed = true; - $this->log('WARNING: Remote upload threw an exception: ' . $e->getMessage()); - $this->log('Local backup is preserved.'); - } - } } // Write log file alongside the archive @@ -519,23 +477,7 @@ class BackupEngine } /** - * Create the appropriate remote uploader based on the storage type. - * Legacy method — used by backward-compat fallback when remotes table - * does not exist. - */ - private function createUploader(string $type, object $profile): RemoteUploaderInterface - { - return match ($type) { - 'ftp' => new FtpUploader($profile), - 'sftp' => new SftpUploader($profile), - 'google_drive' => new GoogleDriveUploader($profile), - 's3' => new S3Uploader($profile), - default => throw new \InvalidArgumentException('Unknown remote storage type: ' . $type), - }; - } - - /** - * Create a remote uploader from JSON params (multi-remote destinations). + * Create a remote uploader from JSON params. * * Builds a fake profile-like object from the params array so the existing * uploader constructors work without modification. @@ -569,31 +511,18 @@ class BackupEngine /** * Load enabled remote destinations for a profile from the remotes table. - * - * Returns an empty array when the table does not exist (pre-migration) - * so the caller can fall back to the legacy single-remote column. - * - * @param object $db Database driver - * @param int $profileId Profile ID - * - * @return object[] Array of remote destination rows */ private function loadRemoteDestinations(object $db, int $profileId): array { - try { - $query = $db->getQuery(true) - ->select('*') - ->from($db->quoteName('#__mokosuitebackup_remotes')) - ->where($db->quoteName('profile_id') . ' = ' . (int) $profileId) - ->where($db->quoteName('enabled') . ' = 1') - ->order($db->quoteName('ordering') . ' ASC'); - $db->setQuery($query); + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__mokosuitebackup_remotes')) + ->where($db->quoteName('profile_id') . ' = ' . (int) $profileId) + ->where($db->quoteName('enabled') . ' = 1') + ->order($db->quoteName('ordering') . ' ASC'); + $db->setQuery($query); - return $db->loadObjectList() ?: []; - } catch (\Throwable $e) { - // Table does not exist yet (pre-migration) — fall back to legacy - return []; - } + return $db->loadObjectList() ?: []; } /** diff --git a/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php b/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php index 664a3e8..6ccbd90 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php +++ b/source/packages/com_mokosuitebackup/src/Engine/PreflightCheck.php @@ -77,7 +77,7 @@ class PreflightCheck $this->checkDiskSpace($profile, $db); $this->checkRunningBackup($profile, $db); $this->checkExcludedTables($profile, $db); - $this->checkRemoteCredentials($profile); + $this->checkRemoteCredentials($profile, $db); return $this->result(); } @@ -102,12 +102,8 @@ class PreflightCheck } } - // curl is only needed for remote upload and ntfy notifications - $needsCurl = ($profile->remote_storage ?? 'none') !== 'none' - || !empty($profile->ntfy_topic); - - if ($needsCurl && !extension_loaded('curl')) { - $this->warnings[] = 'ext-curl is not loaded — remote upload and ntfy notifications will not work'; + if (!empty($profile->ntfy_topic) && !extension_loaded('curl')) { + $this->warnings[] = 'ext-curl is not loaded — ntfy notifications will not work'; } } @@ -280,65 +276,76 @@ class PreflightCheck } /** - * Check that remote storage credentials are minimally configured. + * Check that remote destination credentials are minimally configured. * Does not test the actual connection (too slow for preflight). */ - private function checkRemoteCredentials(object $profile): void + private function checkRemoteCredentials(object $profile, object $db): void { - $remote = $profile->remote_storage ?? 'none'; + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__mokosuitebackup_remotes')) + ->where($db->quoteName('profile_id') . ' = ' . (int) $profile->id) + ->where($db->quoteName('enabled') . ' = 1'); + $db->setQuery($query); + $remotes = $db->loadObjectList(); - if ($remote === 'none') { + if (empty($remotes)) { return; } - switch ($remote) { - case 'ftp': - if (empty($profile->ftp_host)) { - $this->warnings[] = 'FTP host is not configured — remote upload will fail'; - } + foreach ($remotes as $remote) { + $params = json_decode($remote->params, true) ?: []; + $label = $remote->title ?: ('Remote #' . $remote->id); - if (empty($profile->ftp_username)) { - $this->warnings[] = 'FTP username is not configured — remote upload will fail'; - } + switch ($remote->type) { + case 'ftp': + if (empty($params['host'])) { + $this->warnings[] = $label . ': FTP host is not configured — upload will fail'; + } - break; + if (empty($params['username'])) { + $this->warnings[] = $label . ': FTP username is not configured — upload will fail'; + } - case 's3': - if (empty($profile->s3_bucket)) { - $this->warnings[] = 'S3 bucket is not configured — remote upload will fail'; - } + break; - if (empty($profile->s3_access_key) || empty($profile->s3_secret_key)) { - $this->warnings[] = 'S3 credentials are not configured — remote upload will fail'; - } + case 's3': + if (empty($params['bucket'])) { + $this->warnings[] = $label . ': S3 bucket is not configured — upload will fail'; + } - break; + if (empty($params['access_key']) || empty($params['secret_key'])) { + $this->warnings[] = $label . ': S3 credentials are not configured — upload will fail'; + } - case 'sftp': - if (empty($profile->sftp_host)) { - $this->warnings[] = 'SFTP host is not configured — remote upload will fail'; - } + break; - if (empty($profile->sftp_username)) { - $this->warnings[] = 'SFTP username is not configured — remote upload will fail'; - } + case 'sftp': + if (empty($params['host'])) { + $this->warnings[] = $label . ': SFTP host is not configured — upload will fail'; + } - if (empty($profile->sftp_key_data) && empty($profile->sftp_password)) { - $this->warnings[] = 'SFTP requires either a private key or password — remote upload will fail'; - } + if (empty($params['username'])) { + $this->warnings[] = $label . ': SFTP username is not configured — upload will fail'; + } - break; + if (empty($params['key_data']) && empty($params['password'])) { + $this->warnings[] = $label . ': SFTP requires either a private key or password — upload will fail'; + } - case 'google_drive': - if (empty($profile->gdrive_client_id) || empty($profile->gdrive_client_secret)) { - $this->warnings[] = 'Google Drive OAuth credentials are not configured — remote upload will fail'; - } + break; - if (empty($profile->gdrive_refresh_token)) { - $this->warnings[] = 'Google Drive refresh token is missing — remote upload will fail'; - } + case 'google_drive': + if (empty($params['client_id']) || empty($params['client_secret'])) { + $this->warnings[] = $label . ': Google Drive OAuth credentials are not configured — upload will fail'; + } - break; + if (empty($params['refresh_token'])) { + $this->warnings[] = $label . ': Google Drive refresh token is missing — upload will fail'; + } + + break; + } } } diff --git a/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php b/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php index ee640e7..467b616 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php +++ b/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php @@ -69,7 +69,6 @@ class SteppedBackupEngine $session->excludeFiles = BackupDirectory::parseNewlineList($profile->exclude_files ?? ''); $session->excludeTables = BackupDirectory::parseNewlineList($profile->exclude_tables ?? ''); $session->backupDir = $profile->backup_dir ?: BackupDirectory::PLACEHOLDER; - $session->remoteStorage = $profile->remote_storage ?? 'none'; $session->includeMokoRestore = $profile->include_mokorestore ?? '0'; $session->restoreScriptName = $profile->restore_script_name ?? 'restore.php'; $session->remoteKeepLocal = (bool) ($profile->remote_keep_local ?? true); @@ -153,15 +152,8 @@ class SteppedBackupEngine $totalSteps += 1; // finalize step - // Determine upload step count: one step per remote destination, - // or one step for legacy single-remote, or zero if no remotes. $remoteCount = count($session->remoteDestinations); - - if ($remoteCount > 0) { - $totalSteps += $remoteCount; - } elseif ($session->remoteStorage !== 'none') { - $totalSteps += 1; - } + $totalSteps += $remoteCount; $session->totalSteps = $totalSteps; $session->currentStep = 1; @@ -421,11 +413,7 @@ class SteppedBackupEngine $session->currentStep++; - // Determine next phase: multi-remote, legacy single-remote, or complete - $hasMultiRemote = !empty($session->remoteDestinations); - $hasLegacyRemote = $session->remoteStorage !== 'none'; - - if ($hasMultiRemote || $hasLegacyRemote) { + if (!empty($session->remoteDestinations)) { $session->phase = 'upload'; } else { $session->phase = 'complete'; @@ -440,11 +428,7 @@ class SteppedBackupEngine } /** - * Upload phase: send archive to remote storage. - * - * When multi-remote destinations are configured, each call uploads to - * one destination (one step per remote). When only the legacy - * single-remote column is set, uploads in a single step. + * Upload phase: send archive to one remote destination per call. */ private function stepUpload(SteppedSession $session): void { @@ -452,133 +436,65 @@ class SteppedBackupEngine $remoteFilename = ''; $uploadFailed = false; - if (!empty($session->remoteDestinations)) { - // ── Multi-remote path ────────────────────────────────── - $index = $session->remoteIndex; + $index = $session->remoteIndex; - if ($index >= count($session->remoteDestinations)) { - // All remotes processed — move to complete - $session->phase = 'complete'; - $session->statusMessage = 'All remote uploads finished'; - $this->completeRecord($session); + if ($index >= count($session->remoteDestinations)) { + $session->phase = 'complete'; + $session->statusMessage = 'All remote uploads finished'; + $this->completeRecord($session); - return; - } + return; + } - $remote = (object) $session->remoteDestinations[$index]; + $remote = (object) $session->remoteDestinations[$index]; - try { - $title = $remote->title ?? ('Remote #' . ($index + 1)); - $type = $remote->type ?? 'unknown'; - $params = json_decode($remote->params ?? '{}', true) ?: []; + try { + $title = $remote->title ?? ('Remote #' . ($index + 1)); + $type = $remote->type ?? 'unknown'; + $params = json_decode($remote->params ?? '{}', true) ?: []; - $session->log('Uploading to: ' . $title . ' (' . $type . ')...'); - $uploader = $this->createUploaderFromParams($type, $params); - $result = $uploader->upload($session->archivePath, $session->archiveName); + $session->log('Uploading to: ' . $title . ' (' . $type . ')...'); + $uploader = $this->createUploaderFromParams($type, $params); + $result = $uploader->upload($session->archivePath, $session->archiveName); - if ($result['success']) { - $remoteFilename = $result['remote_path'] ?? $session->archiveName; - $session->log(' Upload complete: ' . $result['message']); + if ($result['success']) { + $remoteFilename = $result['remote_path'] ?? $session->archiveName; + $session->log(' Upload complete: ' . $result['message']); - if (!empty($session->restoreScriptPath) && is_file($session->restoreScriptPath)) { - $uploader->upload($session->restoreScriptPath, basename($session->restoreScriptPath)); - } - } else { - $uploadFailed = true; - $session->log(' WARNING: Upload failed: ' . $result['message']); + if (!empty($session->restoreScriptPath) && is_file($session->restoreScriptPath)) { + $uploader->upload($session->restoreScriptPath, basename($session->restoreScriptPath)); } - } catch (\Throwable $e) { + } else { $uploadFailed = true; - $session->log(' WARNING: Upload exception: ' . $e->getMessage()); + $session->log(' WARNING: Upload failed: ' . $result['message']); + } + } catch (\Throwable $e) { + $uploadFailed = true; + $session->log(' WARNING: Upload exception: ' . $e->getMessage()); + } + + $session->remoteIndex++; + $session->currentStep++; + + $remaining = count($session->remoteDestinations) - $session->remoteIndex; + $session->statusMessage = 'Uploaded to ' . ($remote->title ?? 'remote') . ($remaining > 0 ? ' (' . $remaining . ' remaining)' : ''); + + if ($session->remoteIndex >= count($session->remoteDestinations)) { + if (!$uploadFailed && !$session->remoteKeepLocal && is_file($session->archivePath)) { + @unlink($session->archivePath); + $session->log('Local copy removed (remote_keep_local = off)'); } - $session->remoteIndex++; - $session->currentStep++; - - $remaining = count($session->remoteDestinations) - $session->remoteIndex; - $session->statusMessage = 'Uploaded to ' . ($remote->title ?? 'remote') . ($remaining > 0 ? ' (' . $remaining . ' remaining)' : ''); - - if ($session->remoteIndex >= count($session->remoteDestinations)) { - // All remotes done — delete local if configured and no failures - if (!$uploadFailed && !$session->remoteKeepLocal && is_file($session->archivePath)) { - @unlink($session->archivePath); - $session->log('Local copy removed (remote_keep_local = off)'); - } - - // Update record with remote filename - $update = (object) [ - 'id' => $session->recordId, - 'remote_filename' => $remoteFilename, - 'filesexist' => is_file($session->archivePath) ? 1 : 0, - ]; - $db->updateObject('#__mokosuitebackup_records', $update, 'id'); - - $session->phase = 'complete'; - $session->statusMessage = $uploadFailed - ? 'Backup complete (some remote uploads failed — local archive preserved)' - : 'Backup complete'; - $this->completeRecord($session, $uploadFailed); - } - } else { - // ── Legacy single-remote fallback ────────────────────── - try { - // Reload profile for remote settings - $query = $db->getQuery(true) - ->select('*') - ->from($db->quoteName('#__mokosuitebackup_profiles')) - ->where($db->quoteName('id') . ' = ' . $session->profileId); - $db->setQuery($query); - $profile = $db->loadObject(); - - $uploader = match ($session->remoteStorage) { - 'ftp' => new FtpUploader($profile), - 'sftp' => new SftpUploader($profile), - 'google_drive' => new GoogleDriveUploader($profile), - 's3' => new S3Uploader($profile), - default => throw new \InvalidArgumentException('Unknown storage: ' . $session->remoteStorage), - }; - - $session->log('Starting remote upload (' . $session->remoteStorage . ')...'); - $result = $uploader->upload($session->archivePath, $session->archiveName); - - if ($result['success']) { - $remoteFilename = $result['remote_path'] ?? $session->archiveName; - $session->log('Remote upload complete: ' . $result['message']); - - if (!empty($session->restoreScriptPath) && is_file($session->restoreScriptPath)) { - $restoreBasename = basename($session->restoreScriptPath); - $session->log('Uploading standalone ' . $restoreBasename . '...'); - $uploader->upload($session->restoreScriptPath, $restoreBasename); - } - - if (!$session->remoteKeepLocal && is_file($session->archivePath)) { - @unlink($session->archivePath); - $session->log('Local copy removed'); - } - } else { - $uploadFailed = true; - $session->log('WARNING: Remote upload failed: ' . $result['message']); - $session->log('Local backup is preserved.'); - } - } catch (\Throwable $e) { - $uploadFailed = true; - $session->log('WARNING: Remote upload threw an exception: ' . $e->getMessage()); - $session->log('Local backup is preserved.'); - } - - // Update record with remote filename $update = (object) [ 'id' => $session->recordId, 'remote_filename' => $remoteFilename, 'filesexist' => is_file($session->archivePath) ? 1 : 0, ]; - $db->updateObject('#__mokosuitebackup_records', $update, 'id'); - $session->currentStep++; $session->phase = 'complete'; $session->statusMessage = $uploadFailed - ? 'Backup complete (remote upload failed — local archive preserved)' + ? 'Backup complete (some remote uploads failed — local archive preserved)' : 'Backup complete'; $this->completeRecord($session, $uploadFailed); } @@ -859,21 +775,15 @@ class SteppedBackupEngine */ private function loadRemoteDestinations(object $db, int $profileId): array { - try { - $query = $db->getQuery(true) - ->select('*') - ->from($db->quoteName('#__mokosuitebackup_remotes')) - ->where($db->quoteName('profile_id') . ' = ' . (int) $profileId) - ->where($db->quoteName('enabled') . ' = 1') - ->order($db->quoteName('ordering') . ' ASC'); - $db->setQuery($query); + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__mokosuitebackup_remotes')) + ->where($db->quoteName('profile_id') . ' = ' . (int) $profileId) + ->where($db->quoteName('enabled') . ' = 1') + ->order($db->quoteName('ordering') . ' ASC'); + $db->setQuery($query); - // Use loadAssocList so the data survives JSON serialization in SteppedSession - return $db->loadAssocList() ?: []; - } catch (\Throwable $e) { - // Table does not exist yet (pre-migration) — fall back to legacy - return []; - } + return $db->loadAssocList() ?: []; } /** diff --git a/source/packages/com_mokosuitebackup/src/Engine/SteppedSession.php b/source/packages/com_mokosuitebackup/src/Engine/SteppedSession.php index c153c52..3a103b3 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/SteppedSession.php +++ b/source/packages/com_mokosuitebackup/src/Engine/SteppedSession.php @@ -50,7 +50,6 @@ class SteppedSession public array $excludeDirs = []; public array $excludeFiles = []; public array $excludeTables = []; - public string $remoteStorage = 'none'; public string $includeMokoRestore = '0'; public string $restoreScriptName = 'restore.php'; public string $restoreScriptPath = ''; diff --git a/source/packages/com_mokosuitebackup/tmpl/profile/edit.php b/source/packages/com_mokosuitebackup/tmpl/profile/edit.php index 1f6e636..779a212 100644 --- a/source/packages/com_mokosuitebackup/tmpl/profile/edit.php +++ b/source/packages/com_mokosuitebackup/tmpl/profile/edit.php @@ -65,7 +65,6 @@ $token = Session::getFormToken();
-
@@ -97,20 +96,13 @@ $token = Session::getFormToken();

-
+ +
+ +
- -
- - form->renderFieldset('remote'); ?> - form->renderFieldset('ftp'); ?> - form->renderFieldset('google_drive'); ?> - form->renderFieldset('s3'); ?> -
+ form->renderFieldset('remote'); ?>
@@ -280,9 +272,8 @@ document.addEventListener('DOMContentLoaded', function() { const tbody = document.getElementById('remoteDestBody'); const emptyMsg = document.getElementById('remoteDestEmpty'); const loadingTr = document.getElementById('remoteDestLoading'); - const legacy = document.getElementById('legacyRemoteFields'); - const legacyNote = document.getElementById('legacyRemoteNote'); - const modal = new bootstrap.Modal(document.getElementById('remoteModal')); + const modalEl = document.getElementById('remoteModal'); + const modal = bootstrap.Modal.getOrCreateInstance(modalEl); // Type badge colours const typeBadge = {sftp: 'bg-primary', s3: 'bg-warning text-dark', google_drive: 'bg-success'}; @@ -336,14 +327,10 @@ document.addEventListener('DOMContentLoaded', function() { if (!remotesData.length) { emptyMsg.style.display = ''; - legacy.style.display = ''; - legacyNote.style.display = 'none'; return; } emptyMsg.style.display = 'none'; - legacy.style.display = 'none'; - legacyNote.style.display = 'block'; remotesData.forEach(function(item) { const tr = document.createElement('tr'); @@ -506,8 +493,8 @@ document.addEventListener('DOMContentLoaded', function() { const fields = configFields[item.type] || []; fields.forEach(function(f) { const el = document.getElementById('remoteCfg_' + prefix + f); - if (el && item.config && item.config[f] !== undefined) { - el.value = item.config[f]; + if (el && item.params && item.params[f] !== undefined) { + el.value = item.params[f]; } }); } diff --git a/source/script.php b/source/script.php index 67668df..b3d47fc 100644 --- a/source/script.php +++ b/source/script.php @@ -665,41 +665,23 @@ class Pkg_MokoSuiteBackupInstallerScript { try { - $db = Factory::getDbo(); - $db->setQuery( - $db->getQuery(true) - ->select([$db->quoteName('update_site_id'), $db->quoteName('extra_query')]) - ->from($db->quoteName('#__update_sites')) - ->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoSuiteBackup%') . ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoSuiteBackup%') . ')') - ->setLimit(1) - ); - $site = $db->loadObject(); - - if ($site) - { - $eq = (string) ($site->extra_query ?? ''); - if (!empty($eq) && strpos($eq, 'dlid=') !== false) { parse_str($eq, $p); if (!empty($p['dlid'])) { return; } } - $editUrl = 'index.php?option=com_installer&task=updatesite.edit&update_site_id=' . (int) $site->update_site_id; - } - else - { - $editUrl = 'index.php?option=com_installer&view=updatesites'; - } - Factory::getApplication()->enqueueMessage( - 'Moko Consulting License Key Required — ' - . 'No download key is configured. Updates will not be available until a valid license key is entered. ' - . 'Enter License Key', - 'warning' + '

MokoSuiteBackup installed successfully!

', + 'info' + ); + + // Show post-install license key prompt + Factory::getApplication()->enqueueMessage( + 'Moko Consulting License Key Required
' + . 'A download key (DLID) is required to receive updates. ' + . 'Enter your key in the Update Sites manager ' + . 'or contact Moko Consulting Support to obtain one.', + 'warning' ); } - catch (\Exception $e) { - error_log('MokoSuiteBackup: License key check failed: ' . $e->getMessage()); - Factory::getApplication()->enqueueMessage( - 'MokoSuiteBackup could not verify your license key status. ' - . 'Please check System → Update Sites to ensure a valid license key is configured.', - 'warning' - ); + catch (\Exception $e) + { + error_log('MokoSuiteBackup: License key prompt failed: ' . $e->getMessage()); } } } From 5d4662342fb2ac9bcc39ac4330d74c4eb949b5e2 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 4 Jul 2026 18:23:15 +0000 Subject: [PATCH 36/70] chore(version): pre-release bump to 02.52.25-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- SECURITY.md | 2 +- source/packages/MokoSuiteClient | 2 +- source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml | 2 +- .../packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_webservices_mokosuitebackup/mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 11958bd..2b91385 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.00.00 +# VERSION: 02.52.25 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/SECURITY.md b/SECURITY.md index c72f18e..73cecfe 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.52.24 +VERSION: 02.52.25 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/MokoSuiteClient b/source/packages/MokoSuiteClient index c7e6670..bb0b6ec 160000 --- a/source/packages/MokoSuiteClient +++ b/source/packages/MokoSuiteClient @@ -1 +1 @@ -Subproject commit c7e66705443f74e3ee2ffdfecc08224cc40240aa +Subproject commit bb0b6ecac46a5c0a14606e16411121a9d158695b diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index f099e1a..61696e8 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 02.52.24 + 02.52.25 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index 099fe46..09c6624 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 02.52.24 + 02.52.25 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index ced288a..1c25140 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.52.24 + 02.52.25 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 1e8f1a8..1612b4a 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.52.24 + 02.52.25 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 7d17290..967a767 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.52.24 + 02.52.25 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index e9fa579..af8383d 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.52.24 + 02.52.25 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 4cefd2c..23218c4 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.52.24 + 02.52.25 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index 4381063..452d63d 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.52.24 + 02.52.25 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index bc268fc..c854bc0 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.52.24 + 02.52.25 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index 15b18fd..e89b07e 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.52.24 + 02.52.25 2026-06-02 Moko Consulting hello@mokoconsulting.tech From b6b101be3a0472b3dbd6bb2a148f2037c12ba822 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 19:06:36 +0000 Subject: [PATCH 37/70] chore: sync auto-release.yml from Template-Generic [skip ci] --- .mokogitea/workflows/auto-release.yml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 4489ae0..8337c71 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -64,10 +64,14 @@ jobs: promote-rc: name: Promote to RC runs-on: release + # Skip on template repos (Template-*) — they scaffold other repos and do not release. if: >- - (github.event.action == 'opened' && github.event.pull_request.merged != true) || - (github.event.action == 'synchronize' && github.event.pull_request.merged != true) || - (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') + !startsWith(github.event.repository.name, 'Template-') && + ( + (github.event.action == 'opened' && github.event.pull_request.merged != true) || + (github.event.action == 'synchronize' && github.event.pull_request.merged != true) || + (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') + ) steps: - name: Checkout repository @@ -164,9 +168,13 @@ jobs: release: name: Build & Release Pipeline runs-on: release + # Skip on template repos (Template-*) — they scaffold other repos and do not release. if: >- - github.event.pull_request.merged == true || - (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') + !startsWith(github.event.repository.name, 'Template-') && + ( + github.event.pull_request.merged == true || + (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') + ) steps: - name: Checkout repository From 318b1469e33dcb1a129f1509056dab523b7a55a6 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 19:06:37 +0000 Subject: [PATCH 38/70] chore: sync ci-generic.yml from Template-Generic [skip ci] --- .mokogitea/workflows/ci-generic.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/ci-generic.yml b/.mokogitea/workflows/ci-generic.yml index 92d2685..3a942ad 100644 --- a/.mokogitea/workflows/ci-generic.yml +++ b/.mokogitea/workflows/ci-generic.yml @@ -6,7 +6,7 @@ # DEFGROUP: Gitea.Workflow # INGROUP: MokoStandards.CI # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic -# PATH: /.gitea/workflows/ci-generic.yml +# PATH: /.mokogitea/workflows/ci-generic.yml # VERSION: 01.00.00 # BRIEF: CI pipeline — lint, validate, and test for generic projects (PHP + Node.js) From 679268874035eaa06a86f7f8d8962e7eda87c1a2 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 19:06:38 +0000 Subject: [PATCH 39/70] chore: sync cleanup.yml from Template-Generic [skip ci] --- .mokogitea/workflows/cleanup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/cleanup.yml b/.mokogitea/workflows/cleanup.yml index 0023862..2e6b6a2 100644 --- a/.mokogitea/workflows/cleanup.yml +++ b/.mokogitea/workflows/cleanup.yml @@ -6,7 +6,7 @@ # DEFGROUP: Gitea.Workflow # INGROUP: MokoStandards.Maintenance # REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards -# PATH: /.gitea/workflows/cleanup.yml +# PATH: /.mokogitea/workflows/cleanup.yml # VERSION: 01.00.00 # BRIEF: Scheduled cleanup — delete merged branches and old workflow runs From caaedfdff5fbddde7dc11f77a833bfc84e70f7ef Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 19:06:39 +0000 Subject: [PATCH 40/70] chore: sync notify.yml from Template-Generic [skip ci] --- .mokogitea/workflows/notify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/notify.yml b/.mokogitea/workflows/notify.yml index 51dfcb5..9dfa047 100644 --- a/.mokogitea/workflows/notify.yml +++ b/.mokogitea/workflows/notify.yml @@ -6,7 +6,7 @@ # DEFGROUP: Gitea.Workflow # INGROUP: MokoStandards.Notifications # REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards -# PATH: /.gitea/workflows/notify.yml +# PATH: /.mokogitea/workflows/notify.yml # VERSION: 01.00.00 # BRIEF: Push notifications via ntfy on release success or workflow failure From c5f4daa2e64e83013c8957126d0282c91d86bca8 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 19:06:40 +0000 Subject: [PATCH 41/70] chore: sync pr-check.yml from Template-Generic [skip ci] --- .mokogitea/workflows/pr-check.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index c834bf5..1652713 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -4,8 +4,8 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.CI -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform +# INGROUP: mokocli.CI +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli # PATH: /templates/workflows/universal/pr-check.yml.template # VERSION: 09.23.00 # BRIEF: PR gate — branch policy + code validation before merge From 77d93602d62e6831c6d02e783c37e412577954e2 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 19:06:41 +0000 Subject: [PATCH 42/70] chore: sync pre-release.yml from Template-Generic [skip ci] --- .mokogitea/workflows/pre-release.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index efb3d1b..cc40025 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -48,9 +48,13 @@ jobs: build: name: "Build Pre-Release (${{ inputs.stability || github.ref_name }})" runs-on: release + # Skip on template repos (Template-*) — they scaffold other repos and do not release. if: >- - github.event_name == 'workflow_dispatch' || - github.event_name == 'push' + !startsWith(github.event.repository.name, 'Template-') && + ( + github.event_name == 'workflow_dispatch' || + github.event_name == 'push' + ) steps: - name: Checkout From 406b666486bf73c65cfdd52bc429956e6baae1e1 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 19:06:42 +0000 Subject: [PATCH 43/70] chore: sync sync-on-merge.yml from Template-Generic [skip ci] --- .mokogitea/workflows/sync-on-merge.yml | 31 ++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .mokogitea/workflows/sync-on-merge.yml diff --git a/.mokogitea/workflows/sync-on-merge.yml b/.mokogitea/workflows/sync-on-merge.yml new file mode 100644 index 0000000..1b882bc --- /dev/null +++ b/.mokogitea/workflows/sync-on-merge.yml @@ -0,0 +1,31 @@ +name: Sync Workflows to Repos + +on: + push: + branches: + - main + paths: + - '.mokogitea/workflows/**' + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - name: Checkout mokocli + uses: actions/checkout@v4 + with: + repository: MokoConsulting/mokocli + token: ${{ secrets.MOKOGITEA_TOKEN }} + + - name: Setup PHP + uses: https://git.mokoconsulting.tech/MokoConsulting/.mokogitea/raw/branch/main/actions/setup-php@v1 + with: + php-version: '8.1' + + - name: Install dependencies + run: composer install --no-dev --no-interaction + + - name: Sync workflows to generic repos + run: php automation/bulk_sync.php --platform generic --org MokoConsulting --workflows-only --auto-merge --token "${{ secrets.MOKOGITEA_TOKEN }}" + env: + MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} From a808789acb71472cb6835ba5b83bbc8232a64fdf Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 4 Jul 2026 14:20:54 -0500 Subject: [PATCH 44/70] feat: add COM_MOKOJOOMBACKUP_SHORT and apply short name to views and admin menu - Add COM_MOKOJOOMBACKUP_SHORT="Backup" to the component .ini and .sys.ini (en-GB + en-US) so both view titles and the admin sidebar menu resolve it. - Prefix every admin view's toolbar title with the short brand via the constant (e.g. "Backup: Dashboard", "Backup: Records"), and tighten the view title strings that redundantly led with "Backup"/"MokoSuiteBackup". - Admin sidebar top-level menu now shows the short name. Hardcoded as "Backup" in the manifest per the package-xml convention (no language strings in package XML). --- .../language/en-GB/com_mokosuitebackup.ini | 9 +++++---- .../language/en-GB/com_mokosuitebackup.sys.ini | 1 + .../language/en-US/com_mokosuitebackup.ini | 7 ++++--- .../language/en-US/com_mokosuitebackup.sys.ini | 1 + source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../com_mokosuitebackup/src/View/Backup/HtmlView.php | 2 +- .../com_mokosuitebackup/src/View/Backups/HtmlView.php | 2 +- .../com_mokosuitebackup/src/View/Dashboard/HtmlView.php | 2 +- .../com_mokosuitebackup/src/View/Profile/HtmlView.php | 2 +- .../com_mokosuitebackup/src/View/Profiles/HtmlView.php | 2 +- .../com_mokosuitebackup/src/View/Snapshots/HtmlView.php | 2 +- 11 files changed, 18 insertions(+), 14 deletions(-) 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 c21b6d1..46c7903 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_SHORT="Backup" COM_MOKOJOOMBACKUP_CONFIGURATION="MokoSuiteBackup Options" COM_MOKOJOOMBACKUP_DESCRIPTION="Full-site backup and restore for Joomla" @@ -22,7 +23,7 @@ COM_MOKOSUITEBACKUP_ACTION_BACKUP_RESTORE="Restore Backup" COM_MOKOSUITEBACKUP_ACTION_BACKUP_RESTORE_DESC="Allows users in this group to restore the site from a backup archive. This is a destructive operation that overwrites the current site." ; Dashboard view -COM_MOKOJOOMBACKUP_DASHBOARD_TITLE="MokoSuiteBackup Dashboard" +COM_MOKOJOOMBACKUP_DASHBOARD_TITLE="Dashboard" COM_MOKOJOOMBACKUP_DASHBOARD_LAST_BACKUP="Last Backup" COM_MOKOJOOMBACKUP_DASHBOARD_NO_BACKUPS="No backups yet" COM_MOKOJOOMBACKUP_DASHBOARD_NEXT_SCHEDULED="Next Scheduled" @@ -44,14 +45,14 @@ COM_MOKOJOOMBACKUP_DASHBOARD_BACKUP_TREND="Backup Trend (30 days)" ; Backups view COM_MOKOJOOMBACKUP_BACKUPS_N_ITEMS_DELETED="%d backup records deleted." COM_MOKOJOOMBACKUP_BACKUPS_N_ITEMS_DELETED_1="%d backup record deleted." -COM_MOKOJOOMBACKUP_BACKUPS_TITLE="Backup Records" +COM_MOKOJOOMBACKUP_BACKUPS_TITLE="Records" COM_MOKOJOOMBACKUP_BACKUPS_TABLE_CAPTION="Table of backup records" COM_MOKOJOOMBACKUP_NO_BACKUPS="No backups found. Click 'Backup Now' to create your first backup." COM_MOKOJOOMBACKUP_TOOLBAR_BACKUP_NOW="Backup Now" COM_MOKOJOOMBACKUP_DOWNLOAD="Download" ; Backup detail view -COM_MOKOJOOMBACKUP_BACKUP_DETAIL="Backup Detail" +COM_MOKOJOOMBACKUP_BACKUP_DETAIL="Detail" COM_MOKOJOOMBACKUP_VIEW_LOG="Backup Log" COM_MOKOJOOMBACKUP_BROWSE_ARCHIVE="Browse Archive Contents" COM_MOKOJOOMBACKUP_BROWSE_COL_NAME="Name" @@ -75,7 +76,7 @@ COM_MOKOJOOMBACKUP_FIELD_DB_SIZE="DB Size" COM_MOKOJOOMBACKUP_FIELD_REMOTE="Remote Path" ; Profiles view -COM_MOKOJOOMBACKUP_PROFILES_TITLE="Backup Profiles" +COM_MOKOJOOMBACKUP_PROFILES_TITLE="Profiles" COM_MOKOJOOMBACKUP_PROFILES_TABLE_CAPTION="Table of backup profiles" COM_MOKOJOOMBACKUP_NO_PROFILES="No backup profiles found." COM_MOKOJOOMBACKUP_PROFILE_NEW="New Profile" diff --git a/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.sys.ini b/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.sys.ini index d9887da..b7c62b0 100644 --- a/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.sys.ini +++ b/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.sys.ini @@ -5,6 +5,7 @@ ; @license GPL-3.0-or-later COM_MOKOJOOMBACKUP="MokoSuiteBackup" +COM_MOKOJOOMBACKUP_SHORT="Backup" COM_MOKOJOOMBACKUP_DESCRIPTION="Full-site backup and restore for Joomla — database, files, and configuration." COM_MOKOJOOMBACKUP_SUBMENU_DASHBOARD="Dashboard" COM_MOKOJOOMBACKUP_SUBMENU_BACKUPS="Backup Records" 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 db5e85b..6a8d625 100644 --- a/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini +++ b/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini @@ -5,6 +5,7 @@ ; @license GPL-3.0-or-later COM_MOKOJOOMBACKUP="MokoSuiteBackup" +COM_MOKOJOOMBACKUP_SHORT="Backup" COM_MOKOJOOMBACKUP_DESCRIPTION="Full-site backup and restore for Joomla" COM_MOKOJOOMBACKUP_SUBMENU_DASHBOARD="Dashboard" COM_MOKOJOOMBACKUP_SUBMENU_BACKUPS="Backup Records" @@ -18,7 +19,7 @@ COM_MOKOSUITEBACKUP_ACTION_BACKUP_DOWNLOAD_DESC="Allows users in this group to d COM_MOKOSUITEBACKUP_ACTION_BACKUP_RESTORE="Restore Backup" COM_MOKOSUITEBACKUP_ACTION_BACKUP_RESTORE_DESC="Allows users in this group to restore the site from a backup archive. This is a destructive operation that overwrites the current site." -COM_MOKOJOOMBACKUP_DASHBOARD_TITLE="MokoSuiteBackup Dashboard" +COM_MOKOJOOMBACKUP_DASHBOARD_TITLE="Dashboard" COM_MOKOJOOMBACKUP_DASHBOARD_LAST_BACKUP="Last Backup" COM_MOKOJOOMBACKUP_DASHBOARD_NO_BACKUPS="No backups yet" COM_MOKOJOOMBACKUP_DASHBOARD_NEXT_SCHEDULED="Next Scheduled" @@ -30,8 +31,8 @@ COM_MOKOJOOMBACKUP_DASHBOARD_QUICK_ACTIONS="Quick Actions" COM_MOKOJOOMBACKUP_DASHBOARD_SCHEDULED_TASKS="Scheduled Tasks" COM_MOKOJOOMBACKUP_DASHBOARD_UPDATE_SITE="Update Site" COM_MOKOJOOMBACKUP_DASHBOARD_SYSTEM_HEALTH="System Health" -COM_MOKOJOOMBACKUP_BACKUPS_TITLE="Backup Records" -COM_MOKOJOOMBACKUP_PROFILES_TITLE="Backup Profiles" +COM_MOKOJOOMBACKUP_BACKUPS_TITLE="Records" +COM_MOKOJOOMBACKUP_PROFILES_TITLE="Profiles" COM_MOKOJOOMBACKUP_TOOLBAR_BACKUP_NOW="Backup Now" COM_MOKOJOOMBACKUP_NO_BACKUPS="No backups found. Click 'Backup Now' to create your first backup." COM_MOKOJOOMBACKUP_NO_PROFILES="No backup profiles found." diff --git a/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.sys.ini b/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.sys.ini index 23f1d10..53dd3f7 100644 --- a/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.sys.ini +++ b/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.sys.ini @@ -5,6 +5,7 @@ ; @license GPL-3.0-or-later COM_MOKOJOOMBACKUP="MokoSuiteBackup" +COM_MOKOJOOMBACKUP_SHORT="Backup" COM_MOKOJOOMBACKUP_DESCRIPTION="Full-site backup and restore for Joomla — database, files, and configuration." COM_MOKOJOOMBACKUP_SUBMENU_DASHBOARD="Dashboard" COM_MOKOJOOMBACKUP_SUBMENU_BACKUPS="Backup Records" diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index f099e1a..e14a776 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -37,7 +37,7 @@ - COM_MOKOJOOMBACKUP + Backup getIdentity(); diff --git a/source/packages/com_mokosuitebackup/src/View/Backups/HtmlView.php b/source/packages/com_mokosuitebackup/src/View/Backups/HtmlView.php index 3b45d0f..ba43de3 100644 --- a/source/packages/com_mokosuitebackup/src/View/Backups/HtmlView.php +++ b/source/packages/com_mokosuitebackup/src/View/Backups/HtmlView.php @@ -99,7 +99,7 @@ class HtmlView extends BaseHtmlView { $user = Factory::getApplication()->getIdentity(); - ToolbarHelper::title(Text::_('COM_MOKOJOOMBACKUP_BACKUPS_TITLE'), 'database'); + ToolbarHelper::title(Text::_('COM_MOKOJOOMBACKUP_SHORT') . ': ' . Text::_('COM_MOKOJOOMBACKUP_BACKUPS_TITLE'), 'database'); if ($user->authorise('mokosuitebackup.backup.restore', 'com_mokosuitebackup')) { ToolbarHelper::custom('backups.restore', 'upload', '', 'COM_MOKOJOOMBACKUP_TOOLBAR_RESTORE', true); diff --git a/source/packages/com_mokosuitebackup/src/View/Dashboard/HtmlView.php b/source/packages/com_mokosuitebackup/src/View/Dashboard/HtmlView.php index f5c9b88..454a6ae 100644 --- a/source/packages/com_mokosuitebackup/src/View/Dashboard/HtmlView.php +++ b/source/packages/com_mokosuitebackup/src/View/Dashboard/HtmlView.php @@ -52,7 +52,7 @@ class HtmlView extends BaseHtmlView protected function addToolbar(): void { - ToolbarHelper::title(Text::_('COM_MOKOJOOMBACKUP_DASHBOARD_TITLE'), 'archive'); + ToolbarHelper::title(Text::_('COM_MOKOJOOMBACKUP_SHORT') . ': ' . Text::_('COM_MOKOJOOMBACKUP_DASHBOARD_TITLE'), 'archive'); ToolbarHelper::preferences('com_mokosuitebackup'); } } diff --git a/source/packages/com_mokosuitebackup/src/View/Profile/HtmlView.php b/source/packages/com_mokosuitebackup/src/View/Profile/HtmlView.php index 315c942..0db045b 100644 --- a/source/packages/com_mokosuitebackup/src/View/Profile/HtmlView.php +++ b/source/packages/com_mokosuitebackup/src/View/Profile/HtmlView.php @@ -44,7 +44,7 @@ class HtmlView extends BaseHtmlView ? $user->authorise('core.create', 'com_mokosuitebackup') : $user->authorise('core.edit', 'com_mokosuitebackup'); - ToolbarHelper::title(Text::_($title), 'cog'); + ToolbarHelper::title(Text::_('COM_MOKOJOOMBACKUP_SHORT') . ': ' . Text::_($title), 'cog'); if ($canSave) { ToolbarHelper::apply('profile.apply'); diff --git a/source/packages/com_mokosuitebackup/src/View/Profiles/HtmlView.php b/source/packages/com_mokosuitebackup/src/View/Profiles/HtmlView.php index ce3563e..34f350c 100644 --- a/source/packages/com_mokosuitebackup/src/View/Profiles/HtmlView.php +++ b/source/packages/com_mokosuitebackup/src/View/Profiles/HtmlView.php @@ -49,7 +49,7 @@ class HtmlView extends BaseHtmlView { $user = Factory::getApplication()->getIdentity(); - ToolbarHelper::title(Text::_('COM_MOKOJOOMBACKUP_PROFILES_TITLE'), 'cog'); + ToolbarHelper::title(Text::_('COM_MOKOJOOMBACKUP_SHORT') . ': ' . Text::_('COM_MOKOJOOMBACKUP_PROFILES_TITLE'), 'cog'); if ($user->authorise('core.create', 'com_mokosuitebackup')) { ToolbarHelper::addNew('profile.add'); diff --git a/source/packages/com_mokosuitebackup/src/View/Snapshots/HtmlView.php b/source/packages/com_mokosuitebackup/src/View/Snapshots/HtmlView.php index 089e7e4..308afbe 100644 --- a/source/packages/com_mokosuitebackup/src/View/Snapshots/HtmlView.php +++ b/source/packages/com_mokosuitebackup/src/View/Snapshots/HtmlView.php @@ -38,7 +38,7 @@ class HtmlView extends BaseHtmlView { $user = Factory::getApplication()->getIdentity(); - ToolbarHelper::title(Text::_('COM_MOKOJOOMBACKUP_SNAPSHOTS_TITLE'), 'camera'); + ToolbarHelper::title(Text::_('COM_MOKOJOOMBACKUP_SHORT') . ': ' . Text::_('COM_MOKOJOOMBACKUP_SNAPSHOTS_TITLE'), 'camera'); if ($user->authorise('mokosuitebackup.snapshot.manage', 'com_mokosuitebackup')) { ToolbarHelper::custom('snapshots.create', 'plus', '', 'COM_MOKOJOOMBACKUP_SNAPSHOT_CREATE', false); From 1be16af5a4881d6cfc6176246672bd29d40e87bd Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 4 Jul 2026 19:21:24 +0000 Subject: [PATCH 45/70] chore(version): pre-release bump to 02.52.27-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- SECURITY.md | 2 +- source/packages/MokoSuiteClient | 2 +- source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/com_mokosuitebackup/sql/updates/mysql/02.52.27.sql | 1 + .../mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml | 2 +- .../packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_webservices_mokosuitebackup/mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 14 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.27.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 11958bd..1357926 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.00.00 +# VERSION: 02.52.27 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/SECURITY.md b/SECURITY.md index c72f18e..4a23ad4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.52.24 +VERSION: 02.52.27 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/MokoSuiteClient b/source/packages/MokoSuiteClient index c7e6670..7879ecb 160000 --- a/source/packages/MokoSuiteClient +++ b/source/packages/MokoSuiteClient @@ -1 +1 @@ -Subproject commit c7e66705443f74e3ee2ffdfecc08224cc40240aa +Subproject commit 7879ecbf1f91ddf3b474465a93252f6ddc2f0a3a diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index e14a776..a63a72b 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 02.52.24 + 02.52.27 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.27.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.27.sql new file mode 100644 index 0000000..1adde24 --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.27.sql @@ -0,0 +1 @@ +/* 02.52.27 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index 099fe46..b36e632 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 02.52.24 + 02.52.27 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index ced288a..2a28a92 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.52.24 + 02.52.27 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 1e8f1a8..7247bc6 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.52.24 + 02.52.27 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 7d17290..6ec6bad 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.52.24 + 02.52.27 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index e9fa579..81725cf 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.52.24 + 02.52.27 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 4cefd2c..83044c8 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.52.24 + 02.52.27 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index 4381063..5bda339 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.52.24 + 02.52.27 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index bc268fc..eb6df9b 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.52.24 + 02.52.27 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index 15b18fd..56f0ac3 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.52.24 + 02.52.27 2026-06-02 Moko Consulting hello@mokoconsulting.tech From 1ee5e6ba9b5f900a68d61a56c7925af15fe423d1 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 19:39:05 +0000 Subject: [PATCH 46/70] chore: sync branch-cleanup.yml from Template-Generic [skip ci] --- .mokogitea/workflows/branch-cleanup.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.mokogitea/workflows/branch-cleanup.yml b/.mokogitea/workflows/branch-cleanup.yml index 9d884e7..dd5ff56 100644 --- a/.mokogitea/workflows/branch-cleanup.yml +++ b/.mokogitea/workflows/branch-cleanup.yml @@ -33,7 +33,8 @@ jobs: run: | BRANCH="${{ github.event.pull_request.head.ref }}" API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches" - ENCODED=$(php -r "echo rawurlencode('${BRANCH}');") + # URL-encode the branch name's slashes (no PHP dependency on the runner) + ENCODED=$(printf '%s' "${BRANCH}" | sed 's|/|%2F|g') STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \ -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ From 6cf34ea2ed10eb770975bd96cbc7c394342458a7 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 19:39:06 +0000 Subject: [PATCH 47/70] chore: sync ci-generic.yml from Template-Generic [skip ci] --- .mokogitea/workflows/ci-generic.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.mokogitea/workflows/ci-generic.yml b/.mokogitea/workflows/ci-generic.yml index 3a942ad..f556162 100644 --- a/.mokogitea/workflows/ci-generic.yml +++ b/.mokogitea/workflows/ci-generic.yml @@ -32,6 +32,8 @@ jobs: lint: name: Lint & Validate runs-on: ubuntu-latest + # Skip on template repos (Template-*) — they hold placeholder scaffolding, not buildable source. + if: ${{ !startsWith(github.event.repository.name, 'Template-') }} steps: - name: Checkout @@ -130,6 +132,8 @@ jobs: name: Tests runs-on: ubuntu-latest needs: lint + # Skip on template repos (Template-*) — see lint job. + if: ${{ !startsWith(github.event.repository.name, 'Template-') }} steps: - name: Checkout From ece864329ea63c55d1bc9f6709a2563afe5cf76e Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 19:39:08 +0000 Subject: [PATCH 48/70] chore: sync pr-check.yml from Template-Generic [skip ci] --- .mokogitea/workflows/pr-check.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index 1652713..941e436 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -126,6 +126,8 @@ jobs: validate: name: Validate PR runs-on: ubuntu-latest + # Skip on template repos (Template-*) — no real manifest/source/changelog to validate. + if: ${{ !startsWith(github.event.repository.name, 'Template-') }} steps: - name: Checkout From 46daabc34fb38befc5097a50b3d586dd95a6fe53 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 4 Jul 2026 14:56:48 -0500 Subject: [PATCH 49/70] fix(remote-cleanup): resolve dropped-column references found in review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the legacy remote-storage removal — three consumers still referenced columns this branch drops: - 02.52.25.sql: use plain DROP COLUMN instead of DROP COLUMN IF EXISTS. IF EXISTS on DROP COLUMN is a MariaDB-only extension and errors on Oracle MySQL 8.x (which Joomla also supports); the columns always exist here, so the guard is unnecessary and the migration is now portable. - AkeebaImporter::mapToMokoProfile(): stop inserting the 19 dropped remote_storage/ftp_*/gdrive_*/s3_* columns (would fatal with "Unknown column" on Akeeba import). Remote settings now live in the remotes table and are re-added on the profile Remote tab after import. - AjaxController::browseSftpDir() + SftpPathField: remove. These were the legacy single-SFTP path picker, orphaned when the SftpPath form field was removed; they read now-dropped sftp_* columns. Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i --- .../sql/updates/mysql/02.52.25.sql | 56 ++-- .../src/Controller/AjaxController.php | 178 ------------ .../src/Engine/AkeebaImporter.php | 21 +- .../src/Field/SftpPathField.php | 253 ------------------ 4 files changed, 33 insertions(+), 475 deletions(-) delete mode 100644 source/packages/com_mokosuitebackup/src/Field/SftpPathField.php diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.25.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.25.sql index 0551d9d..550a037 100644 --- a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.25.sql +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.25.sql @@ -1,27 +1,31 @@ +-- Remove legacy single-remote storage columns (superseded by #__mokosuitebackup_remotes). +-- Plain DROP COLUMN (no IF EXISTS): all columns are created by install.mysql.sql and +-- earlier updates, so they always exist here. `DROP COLUMN IF EXISTS` is a MariaDB-only +-- extension and errors on Oracle MySQL 8.x, which Joomla also supports. ALTER TABLE `#__mokosuitebackup_profiles` - DROP COLUMN IF EXISTS `remote_storage`, - DROP COLUMN IF EXISTS `ftp_host`, - DROP COLUMN IF EXISTS `ftp_port`, - DROP COLUMN IF EXISTS `ftp_username`, - DROP COLUMN IF EXISTS `ftp_password`, - DROP COLUMN IF EXISTS `ftp_path`, - DROP COLUMN IF EXISTS `ftp_passive`, - DROP COLUMN IF EXISTS `ftp_ssl`, - DROP COLUMN IF EXISTS `sftp_host`, - DROP COLUMN IF EXISTS `sftp_port`, - DROP COLUMN IF EXISTS `sftp_username`, - DROP COLUMN IF EXISTS `sftp_auth_type`, - DROP COLUMN IF EXISTS `sftp_password`, - DROP COLUMN IF EXISTS `sftp_key_data`, - DROP COLUMN IF EXISTS `sftp_passphrase`, - DROP COLUMN IF EXISTS `sftp_path`, - DROP COLUMN IF EXISTS `gdrive_client_id`, - DROP COLUMN IF EXISTS `gdrive_client_secret`, - DROP COLUMN IF EXISTS `gdrive_refresh_token`, - DROP COLUMN IF EXISTS `gdrive_folder_id`, - DROP COLUMN IF EXISTS `s3_endpoint`, - DROP COLUMN IF EXISTS `s3_region`, - DROP COLUMN IF EXISTS `s3_access_key`, - DROP COLUMN IF EXISTS `s3_secret_key`, - DROP COLUMN IF EXISTS `s3_bucket`, - DROP COLUMN IF EXISTS `s3_path`; + DROP COLUMN `remote_storage`, + DROP COLUMN `ftp_host`, + DROP COLUMN `ftp_port`, + DROP COLUMN `ftp_username`, + DROP COLUMN `ftp_password`, + DROP COLUMN `ftp_path`, + DROP COLUMN `ftp_passive`, + DROP COLUMN `ftp_ssl`, + DROP COLUMN `sftp_host`, + DROP COLUMN `sftp_port`, + DROP COLUMN `sftp_username`, + DROP COLUMN `sftp_auth_type`, + DROP COLUMN `sftp_password`, + DROP COLUMN `sftp_key_data`, + DROP COLUMN `sftp_passphrase`, + DROP COLUMN `sftp_path`, + DROP COLUMN `gdrive_client_id`, + DROP COLUMN `gdrive_client_secret`, + DROP COLUMN `gdrive_refresh_token`, + DROP COLUMN `gdrive_folder_id`, + DROP COLUMN `s3_endpoint`, + DROP COLUMN `s3_region`, + DROP COLUMN `s3_access_key`, + DROP COLUMN `s3_secret_key`, + DROP COLUMN `s3_bucket`, + DROP COLUMN `s3_path`; diff --git a/source/packages/com_mokosuitebackup/src/Controller/AjaxController.php b/source/packages/com_mokosuitebackup/src/Controller/AjaxController.php index 264464f..c3d396f 100644 --- a/source/packages/com_mokosuitebackup/src/Controller/AjaxController.php +++ b/source/packages/com_mokosuitebackup/src/Controller/AjaxController.php @@ -1265,184 +1265,6 @@ class AjaxController extends BaseController return $config; } - /** - * Browse directories on a remote SFTP server for the path picker. - * POST: task=ajax.browseSftpDir&profile_id=1&path=/some/path - */ - public function browseSftpDir(): void - { - if (!Session::checkToken('get') && !Session::checkToken('post')) { - $this->sendJson(['error' => true, 'message' => 'Invalid token'], 403); - - return; - } - - if (!$this->app->getIdentity()->authorise('core.manage', 'com_mokosuitebackup')) { - $this->sendJson(['error' => true, 'message' => 'Access denied'], 403); - - return; - } - - $profileId = $this->input->getInt('profile_id', 0); - - if (!$profileId) { - $this->sendJson(['error' => true, 'message' => 'Missing profile_id']); - - return; - } - - /* Load the profile to get SFTP credentials */ - try { - $db = Factory::getDbo(); - $query = $db->getQuery(true) - ->select('*') - ->from($db->quoteName('#__mokosuitebackup_profiles')) - ->where($db->quoteName('id') . ' = ' . $profileId); - $db->setQuery($query); - $profile = $db->loadObject(); - } catch (\Exception $e) { - $this->sendJson(['error' => true, 'message' => 'Failed to load profile'], 500); - - return; - } - - if (!$profile) { - $this->sendJson(['error' => true, 'message' => 'Profile not found'], 404); - - return; - } - - $host = $profile->sftp_host ?? ''; - $port = (int) ($profile->sftp_port ?? 22); - $username = $profile->sftp_username ?? ''; - $keyData = $profile->sftp_key_data ?? ''; - $password = $profile->sftp_password ?? ''; - - if (empty($host) || empty($username)) { - $this->sendJson(['error' => true, 'message' => 'SFTP host and username must be configured and saved before browsing']); - - return; - } - - if (empty($keyData) && empty($password)) { - $this->sendJson(['error' => true, 'message' => 'SFTP credentials (key or password) must be configured and saved before browsing']); - - return; - } - - $requestPath = $this->input->getString('path', '/'); - - /* Sanitize: must start with / and not contain shell meta-characters */ - $requestPath = '/' . ltrim($requestPath, '/'); - - if (preg_match('/[;&|`$<>]/', $requestPath)) { - $this->sendJson(['error' => true, 'message' => 'Invalid path characters']); - - return; - } - - $keyFile = null; - - try { - /* Write temp key if using key auth (same pattern as SftpUploader) */ - if (!empty($keyData)) { - $keyContent = base64_decode($keyData, true); - - if ($keyContent === false) { - $keyContent = $keyData; - } - - $keyFile = sys_get_temp_dir() . '/mokobackup-sftp-browse-' . bin2hex(random_bytes(8)) . '.key'; - - if (file_put_contents($keyFile, $keyContent) === false) { - throw new \RuntimeException('Cannot write temporary SSH key file'); - } - - chmod($keyFile, 0600); - } - - /* Build SSH command to list directories */ - $escapedPath = escapeshellarg($requestPath); - $remoteCmd = 'ls -1pa ' . $escapedPath . ' 2>/dev/null | grep "/$"'; - - $parts = ['ssh', '-o', 'StrictHostKeyChecking=no', '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=10']; - - if ($port !== 22) { - $parts[] = '-p'; - $parts[] = (string) $port; - } - - if ($keyFile !== null) { - $parts[] = '-i'; - $parts[] = escapeshellarg($keyFile); - } - - $parts[] = escapeshellarg($username . '@' . $host); - $parts[] = escapeshellarg($remoteCmd); - - $cmd = implode(' ', $parts); - - $output = []; - $exitCode = 0; - exec($cmd . ' 2>&1', $output, $exitCode); - - /* exitCode 1 from grep means no matches (empty dir), which is OK */ - if ($exitCode !== 0 && $exitCode !== 1) { - throw new \RuntimeException('SSH command failed (exit ' . $exitCode . '): ' . implode(' ', $output)); - } - - /* Parse output: each line is a directory name ending with / */ - $dirs = []; - - foreach ($output as $line) { - $line = trim($line); - - if ($line === '' || $line === './' || $line === '../') { - continue; - } - - $dirName = rtrim($line, '/'); - - if ($dirName === '' || $dirName === '.' || $dirName === '..') { - continue; - } - - $fullPath = rtrim($requestPath, '/') . '/' . $dirName; - - $dirs[] = [ - 'name' => $dirName, - 'path' => $fullPath, - ]; - } - - usort($dirs, fn($a, $b) => strcasecmp($a['name'], $b['name'])); - - /* Parent path */ - $parent = null; - - if ($requestPath !== '/') { - $parent = \dirname($requestPath); - - if ($parent === '') { - $parent = '/'; - } - } - - $this->sendJson([ - 'error' => false, - 'current' => $requestPath, - 'parent' => $parent, - 'dirs' => $dirs, - ]); - } catch (\Throwable $e) { - $this->sendJson(['error' => true, 'message' => 'SFTP browse failed: ' . $e->getMessage()]); - } finally { - if ($keyFile !== null && is_file($keyFile)) { - unlink($keyFile); - } - } - } - /** * Send a JSON response and close the application. */ diff --git a/source/packages/com_mokosuitebackup/src/Engine/AkeebaImporter.php b/source/packages/com_mokosuitebackup/src/Engine/AkeebaImporter.php index 7020926..79dca60 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/AkeebaImporter.php +++ b/source/packages/com_mokosuitebackup/src/Engine/AkeebaImporter.php @@ -228,24 +228,9 @@ class AkeebaImporter 'exclude_dirs' => implode("\n", $filters['exclude_dirs']), 'exclude_files' => implode("\n", $filters['exclude_files']), 'exclude_tables' => implode("\n", $filters['exclude_tables']), - 'remote_storage' => $this->mapRemoteStorage($config), - 'ftp_host' => $config['engine.postproc.ftp.host'] ?? '', - 'ftp_port' => (int) ($config['engine.postproc.ftp.port'] ?? 21), - 'ftp_username' => $config['engine.postproc.ftp.user'] ?? '', - 'ftp_password' => $config['engine.postproc.ftp.pass'] ?? '', - 'ftp_path' => $config['engine.postproc.ftp.initial_directory'] ?? '/backups', - 'ftp_passive' => (int) ($config['engine.postproc.ftp.passive_mode'] ?? 1), - 'ftp_ssl' => (int) ($config['engine.postproc.ftp.ftps'] ?? 0), - 'gdrive_client_id' => $config['engine.postproc.googledrive.client_id'] ?? '', - 'gdrive_client_secret' => $config['engine.postproc.googledrive.client_secret'] ?? '', - 'gdrive_refresh_token' => $config['engine.postproc.googledrive.refresh_token'] ?? '', - 'gdrive_folder_id' => $config['engine.postproc.googledrive.directory'] ?? '', - 's3_endpoint' => $config['engine.postproc.s3.custom_endpoint'] ?? '', - 's3_region' => $config['engine.postproc.s3.region'] ?? 'us-east-1', - 's3_access_key' => $config['engine.postproc.s3.access_key'] ?? ($config['engine.postproc.s3.accesskey'] ?? ''), - 's3_secret_key' => $config['engine.postproc.s3.secret_key'] ?? ($config['engine.postproc.s3.secretkey'] ?? ''), - 's3_bucket' => $config['engine.postproc.s3.bucket'] ?? '', - 's3_path' => $config['engine.postproc.s3.directory'] ?? '/backups', + // Remote storage is no longer stored on the profile — it lives in + // #__mokosuitebackup_remotes. Akeeba remote settings are not imported; + // re-add remote destinations on the profile's Remote tab after import. 'remote_keep_local' => 1, 'include_mokorestore' => (int) (($config['akeeba.advanced.embedded_installer'] ?? 'none') !== 'none'), 'published' => 1, diff --git a/source/packages/com_mokosuitebackup/src/Field/SftpPathField.php b/source/packages/com_mokosuitebackup/src/Field/SftpPathField.php deleted file mode 100644 index b501999..0000000 --- a/source/packages/com_mokosuitebackup/src/Field/SftpPathField.php +++ /dev/null @@ -1,253 +0,0 @@ - - * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. - * @license GNU General Public License version 3 or later; see LICENSE - * - * SFTP remote path field with Browse Remote button and modal directory browser. - */ - -namespace Joomla\Component\MokoSuiteBackup\Administrator\Field; - -defined('_JEXEC') or die; - -use Joomla\CMS\Form\FormField; - -class SftpPathField extends FormField -{ - protected $type = 'SftpPath'; - - protected function getInput(): string - { - $value = htmlspecialchars($this->value ?: $this->default, ENT_QUOTES, 'UTF-8'); - $id = htmlspecialchars($this->id, ENT_QUOTES, 'UTF-8'); - $name = htmlspecialchars($this->name, ENT_QUOTES, 'UTF-8'); - - return << - - -
- - -HTML; - } -} From d3e3fd25e7ca17519342058b2acc55cfee605658 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 4 Jul 2026 20:18:21 +0000 Subject: [PATCH 50/70] chore(version): pre-release bump to 02.52.30-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- SECURITY.md | 2 +- source/packages/MokoSuiteClient | 2 +- source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/com_mokosuitebackup/sql/updates/mysql/02.52.30.sql | 1 + .../mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml | 2 +- .../packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_webservices_mokosuitebackup/mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 14 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.30.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 2b91385..829019f 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.52.25 +# VERSION: 02.52.30 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/SECURITY.md b/SECURITY.md index 73cecfe..4f0da85 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.52.25 +VERSION: 02.52.30 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/MokoSuiteClient b/source/packages/MokoSuiteClient index bb0b6ec..555376e 160000 --- a/source/packages/MokoSuiteClient +++ b/source/packages/MokoSuiteClient @@ -1 +1 @@ -Subproject commit bb0b6ecac46a5c0a14606e16411121a9d158695b +Subproject commit 555376e4a5127b08ea953e8d405bd8c5fff1c1f8 diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index 61696e8..5463fb1 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 02.52.25 + 02.52.30 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.30.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.30.sql new file mode 100644 index 0000000..4d38f7e --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.52.30.sql @@ -0,0 +1 @@ +/* 02.52.30 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index 09c6624..cf06585 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 02.52.25 + 02.52.30 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index 1c25140..fb20ce7 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.52.25 + 02.52.30 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 1612b4a..6b46622 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.52.25 + 02.52.30 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 967a767..91db104 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.52.25 + 02.52.30 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index af8383d..adb35a5 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.52.25 + 02.52.30 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 23218c4..35ffc50 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.52.25 + 02.52.30 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index 452d63d..d5a5fb9 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.52.25 + 02.52.30 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index c854bc0..7b59d6f 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.52.25 + 02.52.30 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index e89b07e..cd6dc5d 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.52.25 + 02.52.30 2026-06-02 Moko Consulting hello@mokoconsulting.tech From c492d30b1c9e7e538a4c32bd0c02f92b66c4b98b Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 4 Jul 2026 20:20:57 +0000 Subject: [PATCH 51/70] chore(release): build 02.53.00 [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 29 ++----------------- SECURITY.md | 2 +- .../com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../sql/updates/mysql/02.53.00.sql | 1 + .../mod_mokosuitebackup_cpanel.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 14 files changed, 15 insertions(+), 39 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.53.00.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 1357926..b34e35c 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.52.27 +# VERSION: 02.53.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index a2ef16d..732634c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +## [02.53.00] --- 2026-07-04 + ## [02.52.24] --- 2026-06-30 @@ -26,30 +28,3 @@ - Backups with failed remote uploads were marked as "complete", hiding the upload failure ## [02.52.18] --- 2026-06-30 - -## [01.45.00] --- 2026-06-28 - -## [01.43.35] --- 2026-06-28 - -### Added -- Customizable restore script filename per backup profile (reduces discoverability on remote servers) -- MokoRestore standalone mode: multi-ZIP selector when multiple backup archives are present -- MokoRestore preflight: Joomla installation detection warning before overwriting an existing site -- MokoRestore error handling: try/catch on fetch calls, HTTP status checks, JSON parse recovery -- Download button on individual backup record detail toolbar -- Profile column in backup records list links to the profile edit view - -### Changed -- Moved download, browse archive, and view log actions from backup list rows into the individual backup record view -- Removed "Run Backup" / "Backup Now" buttons from profiles list, profile edit toolbar, and backup records view (backups are triggered from the dashboard only) -- Removed ordering field from profiles; default sort is now by ID ascending -- MokoRestore cleanup and security messages now reference the actual script filename instead of hardcoded "restore.php" - -### Fixed -- SSH key indicator detection and missing delete language key -- Bootstrap 5 modal conversion for snapshots view (data-bs-dismiss, modal-footer, getOrCreateInstance) -- ntfy default URL changed from ntfy.sh to ntfy.mokoconsulting.tech -- Untranslated JFIELD_ORDERING_ASC / JFIELD_ORDERING_LABEL language keys replaced with component-specific keys -- Options page title now shows "MokoSuiteBackup Options" instead of raw language key -- Profile dropdown IDs in backup records and dashboard show "#ID — Title (type)" format -- MokoRestore stalling: unhandled promise rejections from network errors or non-JSON responses left UI in loading state diff --git a/SECURITY.md b/SECURITY.md index 4a23ad4..fff694a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.52.27 +VERSION: 02.53.00 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index a63a72b..aaa511c 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> MokoSuiteBackup - 02.52.27 + 02.53.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.53.00.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.53.00.sql new file mode 100644 index 0000000..d20c9cc --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.53.00.sql @@ -0,0 +1 @@ +/* 02.53.00 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index b36e632..fe2783b 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> mod_mokosuitebackup_cpanel - 02.52.27 + 02.53.00 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index 2a28a92..c133b45 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.52.27 + 02.53.00 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 7247bc6..e0b8f09 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.52.27 + 02.53.00 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 6ec6bad..c7c8954 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.52.27 + 02.53.00 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index 81725cf..e3f0fff 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.52.27 + 02.53.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 83044c8..0e8265a 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.52.27 + 02.53.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index 5bda339..29def99 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.52.27 + 02.53.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index eb6df9b..1a3ae64 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.52.27 + 02.53.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index 56f0ac3..519a233 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.52.27 + 02.53.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech From 6ff350fb1ce249c9f4a91848b09d31e6f562a90f Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 4 Jul 2026 20:21:10 +0000 Subject: [PATCH 52/70] =?UTF-8?q?chore:=20promote=20changelog=20[Unrelease?= =?UTF-8?q?d]=20=E2=86=92=20[02.53.00]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 732634c..5c1c3fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## [02.53.00] --- 2026-07-04 +## [02.53.00] --- 2026-07-04 + ## [02.52.24] --- 2026-06-30 From 84f37a8b519a1e73f1652021ba385f579d676a40 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 20:23:16 +0000 Subject: [PATCH 53/70] chore: sync ci-generic.yml from Template-Generic [skip ci] --- .mokogitea/workflows/ci-generic.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.mokogitea/workflows/ci-generic.yml b/.mokogitea/workflows/ci-generic.yml index f556162..72650d2 100644 --- a/.mokogitea/workflows/ci-generic.yml +++ b/.mokogitea/workflows/ci-generic.yml @@ -132,8 +132,9 @@ jobs: name: Tests runs-on: ubuntu-latest needs: lint - # Skip on template repos (Template-*) — see lint job. - if: ${{ !startsWith(github.event.repository.name, 'Template-') }} + # Run only when lint succeeded; always() forces evaluation so a skipped + # lint (e.g. template repos) skips this job cleanly instead of hanging. + if: ${{ always() && needs.lint.result == 'success' }} steps: - name: Checkout From 145155d06e6e4051e68516a62d390f00ca573d01 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 20:23:18 +0000 Subject: [PATCH 54/70] chore: sync issue-branch.yml from Template-Generic [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index b34e35c..11958bd 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.53.00 +# VERSION: 01.00.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" From 0df6008a3b015ae6d9323c179482c9a0d1f6edbd Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 20:23:20 +0000 Subject: [PATCH 55/70] chore: sync pr-check.yml from Template-Generic [skip ci] --- .mokogitea/workflows/pr-check.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index 941e436..c13b15e 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -494,6 +494,9 @@ jobs: name: Build RC Package runs-on: ubuntu-latest needs: [branch-policy, validate] + # Run only when both gates succeeded; always() forces evaluation so a skipped + # validate (e.g. template repos) skips this job cleanly instead of hanging. + if: ${{ always() && needs.branch-policy.result == 'success' && needs.validate.result == 'success' }} steps: - name: Trigger RC pre-release From d155958be35f7690eca461a2f042d81ecb038e96 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 4 Jul 2026 14:29:17 -0500 Subject: [PATCH 56/70] chore(manifests): hardcode names/descriptions and enforce Type - Name convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the package-XML convention, manifests must not use language strings for titles/descriptions, and must follow "Type - Name". - Component "MokoSuiteBackup" → "Component - MokoSuiteBackup" - Module "mod_mokosuitebackup_cpanel" → "Module - MokoSuiteBackup - cPanel" - Hardcode every (pkg, com, mod, all 7 plugins) with the literal text previously stored in the *_DESCRIPTION language constants - Hardcode the component admin submenu titles (Dashboard, Backup Records, Content Snapshots, Backup Profiles) Plugin values already followed the Group - Name format and are unchanged. All manifests validated as well-formed XML. --- .../packages/com_mokosuitebackup/mokosuitebackup.xml | 12 ++++++------ .../mod_mokosuitebackup_cpanel.xml | 4 ++-- .../mokosuitebackup.xml | 2 +- .../plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index aaa511c..88f791f 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -6,7 +6,7 @@ * @license GNU General Public License version 3 or later; see LICENSE --> - MokoSuiteBackup + Component - MokoSuiteBackup 02.53.00 2026-06-02 Moko Consulting @@ -14,7 +14,7 @@ https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - COM_MOKOJOOMBACKUP_DESCRIPTION + Full-site backup and restore for Joomla — database, files, and configuration. Joomla\Component\MokoSuiteBackup @@ -41,16 +41,16 @@ COM_MOKOJOOMBACKUP_SUBMENU_DASHBOARD + alt="Dashboard">Dashboard COM_MOKOJOOMBACKUP_SUBMENU_BACKUPS + alt="Backups">Backup Records COM_MOKOJOOMBACKUP_SUBMENU_SNAPSHOTS + alt="Snapshots">Content Snapshots COM_MOKOJOOMBACKUP_SUBMENU_PROFILES + alt="Profiles">Backup Profiles access.xml diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index fe2783b..567f4c8 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -7,7 +7,7 @@ * @license GNU General Public License version 3 or later; see LICENSE --> - mod_mokosuitebackup_cpanel + Module - MokoSuiteBackup - cPanel 02.53.00 2026-06-23 Moko Consulting @@ -15,7 +15,7 @@ https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - MOD_MOKOSUITEBACKUP_CPANEL_DESCRIPTION + Displays backup status, Backup Now buttons, and quick links on the admin dashboard. Joomla\Module\MokoSuiteBackupCpanel diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index c133b45..ec81674 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -14,7 +14,7 @@ https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_ACTIONLOG_MOKOJOOMBACKUP_DESCRIPTION + Logs MokoSuiteBackup actions (backup, restore, profile changes) to User Action Logs. Joomla\Plugin\Actionlog\MokoSuiteBackup diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index e0b8f09..436c872 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -14,7 +14,7 @@ https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_CONSOLE_MOKOJOOMBACKUP_DESCRIPTION + CLI commands for MokoSuiteBackup: run, list, profiles, restore, cleanup. Joomla\Plugin\Console\MokoSuiteBackup diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index c7c8954..7ac8af3 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -14,7 +14,7 @@ https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_CONTENT_MOKOJOOMBACKUP_DESCRIPTION + Automatically triggers a backup before extension installs or updates. Joomla\Plugin\Content\MokoSuiteBackup diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index e3f0fff..0e5d047 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -8,7 +8,7 @@ https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_QUICKICON_MOKOJOOMBACKUP_DESCRIPTION + Shows backup status on the administrator dashboard. Joomla\Plugin\Quickicon\MokoSuiteBackup diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 0e8265a..945cdc8 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -14,7 +14,7 @@ https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_SYSTEM_MOKOJOOMBACKUP_DESCRIPTION + Automatic cleanup of expired backup archives and scheduled backup triggers. Joomla\Plugin\System\MokoSuiteBackup diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index 29def99..008ffe8 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -14,7 +14,7 @@ https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_TASK_MOKOJOOMBACKUP_DESCRIPTION + Scheduled task plugin for MokoSuiteBackup. Run backup profiles on a schedule via Joomla's Scheduled Tasks. Joomla\Plugin\Task\MokoSuiteBackup diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index 1a3ae64..9d66392 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -14,7 +14,7 @@ https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PLG_WEBSERVICES_MOKOJOOMBACKUP_DESCRIPTION + REST API for remote backup management. Joomla\Plugin\WebServices\MokoSuiteBackup diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index 519a233..b41a721 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -15,7 +15,7 @@ https://mokoconsulting.tech Copyright (C) 2026 Moko Consulting. All rights reserved. GPL-3.0-or-later - PKG_MOKOJOOMBACKUP_DESCRIPTION + Full-site backup and restore for Joomla — database, files, and configuration. Includes admin component, system plugin, and REST API. script.php From 2e5b57fe5d0ddf77d29096b0e97c9490080f12df Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 4 Jul 2026 20:25:20 +0000 Subject: [PATCH 57/70] chore(release): build 02.54.00 [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 23 ++----------------- SECURITY.md | 2 +- .../com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../sql/updates/mysql/02.54.00.sql | 1 + .../mod_mokosuitebackup_cpanel.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- .../mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 14 files changed, 15 insertions(+), 33 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.54.00.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 11958bd..eb1f3c3 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.00.00 +# VERSION: 02.54.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c1c3fb..53a5853 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +## [02.54.00] --- 2026-07-04 + ## [02.53.00] --- 2026-07-04 ## [02.53.00] --- 2026-07-04 @@ -9,24 +11,3 @@ ## [02.52.24] --- 2026-06-30 - -## [02.52.22] --- 2026-06-30 - -### Added -- Cancel Stalled toolbar button on Backup Records view to cancel backups stuck in "running" status -- New ACL permission `mokosuitebackup.backup.cancel` for cancel stalled action -- AJAX endpoint `ajax.cancelBackup` for programmatic/API cancel -- Auto-timeout failsafe: preflight auto-cancels "running" backups older than 30 minutes -- Pre-extension-update backup progress modal (Bootstrap 5 modal with stepped AJAX progress bar) -- New `warning` backup status for records where archive succeeded but remote upload failed -- Warning-status records are downloadable, browsable, restorable, and purgeable -- Warning status filter option in Backup Records dropdown -- Yellow "Warning" badge in backup list, detail view, and cpanel module - -### Fixed -- Pre-update backup ran synchronously with no browser feedback — page hung until complete -- Stalled backups permanently blocked future backups for the same profile -- Preflight error message now directs users to Cancel Stalled action -- Backups with failed remote uploads were marked as "complete", hiding the upload failure - -## [02.52.18] --- 2026-06-30 diff --git a/SECURITY.md b/SECURITY.md index fff694a..ec3e33b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.53.00 +VERSION: 02.54.00 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index 88f791f..f72db88 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Component - MokoSuiteBackup - 02.53.00 + 02.54.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.54.00.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.54.00.sql new file mode 100644 index 0000000..79f1e75 --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.54.00.sql @@ -0,0 +1 @@ +/* 02.54.00 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index 567f4c8..f0e56e0 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> Module - MokoSuiteBackup - cPanel - 02.53.00 + 02.54.00 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index ec81674..1edf2cd 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.53.00 + 02.54.00 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 436c872..3e9f505 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.53.00 + 02.54.00 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index 7ac8af3..a110222 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.53.00 + 02.54.00 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index 0e5d047..22c10fd 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.53.00 + 02.54.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 945cdc8..1c5e7eb 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.53.00 + 02.54.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index 008ffe8..5dc3c94 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.53.00 + 02.54.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index 9d66392..5fccdbe 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.53.00 + 02.54.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index b41a721..a50be4c 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.53.00 + 02.54.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech From 4b2bb8b65587b269c257c3d22c46c05463fdf237 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 4 Jul 2026 20:25:31 +0000 Subject: [PATCH 58/70] =?UTF-8?q?chore:=20promote=20changelog=20[Unrelease?= =?UTF-8?q?d]=20=E2=86=92=20[02.54.00]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53a5853..9d1ce17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## [02.54.00] --- 2026-07-04 +## [02.54.00] --- 2026-07-04 + ## [02.53.00] --- 2026-07-04 ## [02.53.00] --- 2026-07-04 From 10248b284a5c501963f90a71e4e2220463086971 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 4 Jul 2026 13:43:58 -0500 Subject: [PATCH 59/70] fix: enforce per-profile retention and repair Purge Old Backups button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two related backup-management fixes. Retention (records/days to keep): - The retention fieldset was defined in profile.xml but never rendered in the profile editor, so retention_days/retention_count were invisible. Render the retention fieldset on the Archive tab. - retention_days/retention_count were read by nothing, so they pruned no backups. Add RetentionManager::prune(), called from completeRecord() in both BackupEngine and SteppedBackupEngine after a backup finishes. Policy: delete a completed/warning backup when EITHER it is older than retention_days OR it falls outside the newest retention_count copies (0 = unlimited for that rule). Deleting a record also removes its archive and log file. - Correct misleading language/schema text ("use global default" — no such global backup-retention setting exists) to "0 = unlimited". Purge Old Backups button: - The modal only opened via a fragile selector match on the toolbar button's inline onclick, which Joomla 6's Atum toolbar does not render, so the button did nothing. Wrap Joomla.submitbutton to open the modal for the backups.purgeModal task, keeping the selector as a fallback. --- .../language/en-GB/com_mokosuitebackup.ini | 4 +- .../language/en-US/com_mokosuitebackup.ini | 7 ++ .../com_mokosuitebackup/sql/install.mysql.sql | 4 +- .../src/Engine/BackupEngine.php | 11 ++ .../src/Engine/RetentionManager.php | 118 ++++++++++++++++++ .../src/Engine/SteppedBackupEngine.php | 7 ++ .../tmpl/backups/default.php | 32 +++-- .../com_mokosuitebackup/tmpl/profile/edit.php | 1 + 8 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/src/Engine/RetentionManager.php 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 46c7903..0e5e3c0 100644 --- a/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini +++ b/source/packages/com_mokosuitebackup/language/en-GB/com_mokosuitebackup.ini @@ -251,9 +251,9 @@ COM_MOKOJOOMBACKUP_FIELD_NOTIFY_FAILURE_DESC="Send an email when a backup fails. ; Retention COM_MOKOJOOMBACKUP_FIELDSET_RETENTION="Retention" COM_MOKOJOOMBACKUP_FIELD_RETENTION_DAYS="Keep Backups (days)" -COM_MOKOJOOMBACKUP_FIELD_RETENTION_DAYS_DESC="Delete completed backups from this profile older than this many days. Set to 0 to use the global default from component options." +COM_MOKOJOOMBACKUP_FIELD_RETENTION_DAYS_DESC="Delete completed backups from this profile older than this many days. Set to 0 for unlimited (keep by age disabled)." COM_MOKOJOOMBACKUP_FIELD_RETENTION_COUNT="Keep Backups (count)" -COM_MOKOJOOMBACKUP_FIELD_RETENTION_COUNT_DESC="Maximum number of completed backups to keep for this profile. Oldest are removed first. Set to 0 to use the global default from component options." +COM_MOKOJOOMBACKUP_FIELD_RETENTION_COUNT_DESC="Maximum number of completed backups to keep for this profile. Oldest are removed first. Set to 0 for unlimited (keep by count disabled)." COM_MOKOJOOMBACKUP_FIELD_NTFY_SPACER_DESC="Push Notifications (ntfy) — Send instant push notifications to your phone or desktop via ntfy.sh or a self-hosted ntfy server." COM_MOKOJOOMBACKUP_FIELD_NTFY_TOPIC="ntfy Topic" 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 6a8d625..1cb697a 100644 --- a/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini +++ b/source/packages/com_mokosuitebackup/language/en-US/com_mokosuitebackup.ini @@ -130,3 +130,10 @@ COM_MOKOJOOMBACKUP_STATUS_WARNING="Warning" ; ACL - Cancel COM_MOKOSUITEBACKUP_ACTION_BACKUP_CANCEL="Cancel Stalled Backup" COM_MOKOSUITEBACKUP_ACTION_BACKUP_CANCEL_DESC="Allows users to cancel backup records stuck in running status and clean up partial archive files." + +; Retention (per-profile) +COM_MOKOJOOMBACKUP_FIELDSET_RETENTION="Retention" +COM_MOKOJOOMBACKUP_FIELD_RETENTION_DAYS="Keep Backups (days)" +COM_MOKOJOOMBACKUP_FIELD_RETENTION_DAYS_DESC="Delete completed backups from this profile older than this many days. Set to 0 for unlimited (keep by age disabled)." +COM_MOKOJOOMBACKUP_FIELD_RETENTION_COUNT="Keep Backups (count)" +COM_MOKOJOOMBACKUP_FIELD_RETENTION_COUNT_DESC="Maximum number of completed backups to keep for this profile. Oldest are removed first. Set to 0 for unlimited (keep by count disabled)." diff --git a/source/packages/com_mokosuitebackup/sql/install.mysql.sql b/source/packages/com_mokosuitebackup/sql/install.mysql.sql index 705ebfd..d1c24aa 100644 --- a/source/packages/com_mokosuitebackup/sql/install.mysql.sql +++ b/source/packages/com_mokosuitebackup/sql/install.mysql.sql @@ -49,8 +49,8 @@ CREATE TABLE IF NOT EXISTS `#__mokosuitebackup_profiles` ( `notify_user_groups` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Comma-separated Joomla user group IDs', `notify_on_success` TINYINT(1) NOT NULL DEFAULT 0, `notify_on_failure` TINYINT(1) NOT NULL DEFAULT 1, - `retention_days` INT(11) NOT NULL DEFAULT 0 COMMENT '0 = use global default', - `retention_count` INT(11) NOT NULL DEFAULT 0 COMMENT '0 = use global default', + `retention_days` INT(11) NOT NULL DEFAULT 0 COMMENT 'Delete backups older than N days; 0 = unlimited', + `retention_count` INT(11) NOT NULL DEFAULT 0 COMMENT 'Keep newest N backups; 0 = unlimited', `ntfy_topic` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'ntfy topic name', `ntfy_server` VARCHAR(512) NOT NULL DEFAULT 'https://ntfy.sh' COMMENT 'ntfy server URL', `ntfy_token` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'ntfy access token (optional)', diff --git a/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php b/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php index 04b8f33..e1a818c 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php +++ b/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php @@ -403,6 +403,17 @@ class BackupEngine NotificationSender::send($profile, $update, false, "Remote upload failed — see backup log for details.\n\n" . implode("\n", $this->log)); } + // Enforce per-profile retention (age and/or copy count). + try { + $pruned = RetentionManager::prune($db, $profile); + + if ($pruned > 0) { + $this->log('Retention: pruned ' . $pruned . ' old backup(s)'); + } + } catch (\Throwable $e) { + error_log('MokoSuiteBackup: retention pass failed: ' . $e->getMessage()); + } + // Dispatch event for actionlog and other listeners $this->dispatchAfterRun(true, $recordId, $description, $profileId, $origin); diff --git a/source/packages/com_mokosuitebackup/src/Engine/RetentionManager.php b/source/packages/com_mokosuitebackup/src/Engine/RetentionManager.php new file mode 100644 index 0000000..8a611ba --- /dev/null +++ b/source/packages/com_mokosuitebackup/src/Engine/RetentionManager.php @@ -0,0 +1,118 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Component\MokoSuiteBackup\Administrator\Engine; + +defined('_JEXEC') or die; + +use Joomla\Component\MokoSuiteBackup\Administrator\Utility\BackupDirectory; + +/** + * Enforces per-profile backup retention. + * + * A profile may cap retained backups by age (retention_days) and/or by + * number of copies (retention_count). A backup is pruned when EITHER rule + * matches: it is older than retention_days OR it falls outside the newest + * retention_count copies. Deleting a record also removes its archive and + * log file, mirroring the Backup table's delete(). + */ +final class RetentionManager +{ + /** + * Prune old backups for a profile according to its retention settings. + * + * Called after a backup completes. Only 'complete' and 'warning' records + * are considered — pending/running/failed records are never pruned here. + * + * @param object $db Database driver + * @param object $profile Profile row (needs id, retention_days, retention_count) + * + * @return int Number of backup records deleted + */ + public static function prune(object $db, object $profile): int + { + $days = (int) ($profile->retention_days ?? 0); + $count = (int) ($profile->retention_count ?? 0); + + // No retention configured — nothing to do. + if ($days <= 0 && $count <= 0) { + return 0; + } + + // Newest first, so the index is the copy's position from the top. + $query = $db->getQuery(true) + ->select($db->quoteName(['id', 'absolute_path', 'backupstart'])) + ->from($db->quoteName('#__mokosuitebackup_records')) + ->where($db->quoteName('profile_id') . ' = ' . (int) $profile->id) + ->where($db->quoteName('status') . ' IN (' . implode(',', array_map([$db, 'quote'], ['complete', 'warning'])) . ')') + ->order($db->quoteName('backupstart') . ' DESC'); + $db->setQuery($query); + $records = $db->loadObjectList() ?: []; + + if (empty($records)) { + return 0; + } + + $cutoffTs = $days > 0 ? (time() - ($days * 86400)) : null; + $deleted = 0; + + foreach ($records as $index => $record) { + $tooOld = $cutoffTs !== null && strtotime((string) $record->backupstart) < $cutoffTs; + $overCount = $count > 0 && $index >= $count; + + // Delete-if-either: prune when age OR count rule is exceeded. + if (!$tooOld && !$overCount) { + continue; + } + + if (self::deleteRecord($db, $record)) { + $deleted++; + } + } + + return $deleted; + } + + /** + * Delete a single backup record and its on-disk archive + log file. + * + * The DB row is removed first; the files are only unlinked if that + * succeeds, so a failed delete never orphans the record from its files. + */ + private static function deleteRecord(object $db, object $record): bool + { + $query = $db->getQuery(true) + ->delete($db->quoteName('#__mokosuitebackup_records')) + ->where($db->quoteName('id') . ' = ' . (int) $record->id); + $db->setQuery($query); + + try { + $db->execute(); + } catch (\Throwable $e) { + error_log('MokoSuiteBackup: retention could not delete record ' . $record->id . ': ' . $e->getMessage()); + + return false; + } + + $archivePath = (string) ($record->absolute_path ?? ''); + + if ($archivePath !== '' && is_file($archivePath)) { + @unlink($archivePath); + + $logPath = BackupDirectory::logPathFromArchive($archivePath); + + if (is_file($logPath)) { + @unlink($logPath); + } + } + + return true; + } +} diff --git a/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php b/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php index ee640e7..b78eb19 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php +++ b/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php @@ -686,6 +686,13 @@ class SteppedBackupEngine if ($uploadFailed) { NotificationSender::send($profile, $record, false, "Remote upload failed — see backup log for details.\n\n" . $logContent); } + + // Enforce per-profile retention (age and/or copy count). + $pruned = RetentionManager::prune($db, $profile); + + if ($pruned > 0) { + $session->log('Retention: pruned ' . $pruned . ' old backup(s)'); + } } } catch (\Throwable $e) { error_log('MokoSuiteBackup: SteppedBackupEngine notification failed: ' . $e->getMessage()); diff --git a/source/packages/com_mokosuitebackup/tmpl/backups/default.php b/source/packages/com_mokosuitebackup/tmpl/backups/default.php index 5417461..53fe54c 100644 --- a/source/packages/com_mokosuitebackup/tmpl/backups/default.php +++ b/source/packages/com_mokosuitebackup/tmpl/backups/default.php @@ -684,19 +684,37 @@ $listDirn = $this->escape($this->state->get('list.direction')); var PURGE_TOKEN = ; var purgeCountTimer = null; - // Intercept Purge toolbar button to show the modal + // Reset modal state and show it. + function openPurgeModal() { + document.getElementById('mb-purge-date').value = ''; + document.getElementById('mb-purge-count-wrapper').style.display = 'none'; + document.getElementById('mb-purge-none-wrapper').style.display = 'none'; + document.getElementById('mb-purge-submit').disabled = true; + bootstrap.Modal.getOrCreateInstance(document.getElementById('mb-purge-modal')).show(); + } + + // Primary: wrap Joomla.submitbutton so the Purge toolbar button opens the + // modal instead of submitting the no-op backups.purgeModal task. This is + // resilient to how the Atum toolbar renders the button markup. + if (window.Joomla && typeof Joomla.submitbutton === 'function') { + var origSubmitbutton = Joomla.submitbutton; + Joomla.submitbutton = function(task) { + if (task === 'backups.purgeModal') { + openPurgeModal(); + return false; + } + return origSubmitbutton.apply(this, arguments); + }; + } + document.addEventListener('DOMContentLoaded', function() { + // Fallback: if the button still exposes an inline onclick, bind directly. var purgeBtn = document.querySelector('[onclick*="backups.purgeModal"], .button-trash'); if (purgeBtn) { purgeBtn.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); - // Reset modal state - document.getElementById('mb-purge-date').value = ''; - document.getElementById('mb-purge-count-wrapper').style.display = 'none'; - document.getElementById('mb-purge-none-wrapper').style.display = 'none'; - document.getElementById('mb-purge-submit').disabled = true; - bootstrap.Modal.getOrCreateInstance(document.getElementById('mb-purge-modal')).show(); + openPurgeModal(); return false; }, true); } diff --git a/source/packages/com_mokosuitebackup/tmpl/profile/edit.php b/source/packages/com_mokosuitebackup/tmpl/profile/edit.php index 1f6e636..a19611e 100644 --- a/source/packages/com_mokosuitebackup/tmpl/profile/edit.php +++ b/source/packages/com_mokosuitebackup/tmpl/profile/edit.php @@ -42,6 +42,7 @@ $token = Session::getFormToken();
form->renderFieldset('archive'); ?> + form->renderFieldset('retention'); ?>
From 03bf5dff7b23fa87814e0e98eaabeee599f32af2 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 20:26:04 +0000 Subject: [PATCH 60/70] chore: sync issue-branch.yml from Template-Generic [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index eb1f3c3..11958bd 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.54.00 +# VERSION: 01.00.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" From 5c5af532bb4bcf48ef1acc9f8b97be3adb137db3 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 20:26:05 +0000 Subject: [PATCH 61/70] chore: sync pr-check.yml from Template-Generic [skip ci] --- .mokogitea/workflows/pr-check.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index c13b15e..019371d 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -47,15 +47,15 @@ jobs: fi ;; fix/*|bugfix/*) - if [ "$BASE" != "dev" ]; then + if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then ALLOWED=false - REASON="Fix branches must target 'dev', not '${BASE}'" + REASON="Fix branches must target 'dev' or 'main', not '${BASE}'" fi ;; patch/*) - if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ]; then + if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ] && [ "$BASE" != "main" ]; then ALLOWED=false - REASON="Patch branches must target 'dev' or 'rc', not '${BASE}'" + REASON="Patch branches must target 'dev', 'rc', or 'main', not '${BASE}'" fi ;; hotfix/*) @@ -86,7 +86,8 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY - echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY + echo "- \`fix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY + echo "- \`patch/*\` → \`dev\`, \`rc\`, or \`main\`" >> $GITHUB_STEP_SUMMARY echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY @@ -149,11 +150,12 @@ jobs: - name: Detect platform id: platform run: | - # Read platform from XML manifest ( tag) or plain text fallback - PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1) - [ -z "$PLATFORM" ] && PLATFORM=$(cat .mokogitea/manifest.xml 2>/dev/null | tr -d '[:space:]') + # Platform comes from the MokoGitea metadata API (public GET); manifest.xml is no longer used. + API="${GITHUB_SERVER_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${GITHUB_REPOSITORY}/metadata" + PLATFORM="$(curl -sf "$API" 2>/dev/null | python3 -c "import sys, json; print(json.load(sys.stdin).get('platform') or '')" 2>/dev/null || true)" [ -z "$PLATFORM" ] && PLATFORM="generic" echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" + echo "Detected platform: $PLATFORM" - name: Setup PHP if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr' From 175869489397a8d4c62438bd89d35e8f1d4e65d7 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 4 Jul 2026 14:34:34 -0500 Subject: [PATCH 62/70] fix(installer): apply standard licensing + install-completion script pattern Adopts the Template-Joomla package-script pattern for download-key handling and post-install messaging, while preserving all MokoSuiteBackup-specific postflight steps (plugins, webcron secret, scheduled task, submenus, client_id fix, icon sync, backup_dir migration). - preflight (update): backupDownloadKey() caches the bare dlid value - postflight (update): restoreDownloadKey() re-writes extra_query as dlid= - postflight (install): installSuccessful() + warnMissingLicenseKey() - postflight (update): installSuccessful() - License nag now runs on install only (a fresh install never has a key), removing the previous always-run "is a key present?" guard - Licensing methods log via Joomla\CMS\Log\Log instead of error_log --- source/script.php | 117 ++++++++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 61 deletions(-) diff --git a/source/script.php b/source/script.php index 67668df..ebf16d0 100644 --- a/source/script.php +++ b/source/script.php @@ -12,6 +12,7 @@ defined('_JEXEC') or die; use Joomla\CMS\Factory; use Joomla\CMS\Installer\InstallerAdapter; use Joomla\CMS\Language\Text; +use Joomla\CMS\Log\Log; use Joomla\CMS\Router\Route; class Pkg_MokoSuiteBackupInstallerScript @@ -73,22 +74,25 @@ class Pkg_MokoSuiteBackupInstallerScript /* Save download key before Joomla re-registers the update site */ if ($type === 'update') { - $this->preflight_saveKey(); + $this->backupDownloadKey(); } return true; } /** - * Called before install/update to preserve the download key. - * - * Joomla re-registers update sites from the manifest on every update, - * which can reset the extra_query (download key). We save it here - * and restore it in postflight. + * The download key cached during preflight so it survives an update. */ private ?string $savedDownloadKey = null; - public function preflight_saveKey(): void + /** + * Cache the existing download key from the update sites table before update runs. + * + * Joomla re-registers update sites from the manifest on every update, which + * can reset the extra_query (download key). We save it here and restore it + * in postflight. + */ + private function backupDownloadKey(): void { try { $db = Factory::getDbo(); @@ -108,19 +112,16 @@ class Pkg_MokoSuiteBackupInstallerScript ->where($db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokosuitebackup')) ->where($db->quoteName('e.type') . ' = ' . $db->quote('package')) ->setLimit(1); - $db->setQuery($query); - $key = $db->loadResult(); - if (!empty($key)) { - $this->savedDownloadKey = $key; + $db->setQuery($query); + $extraQuery = (string) $db->loadResult(); + + if (!empty($extraQuery)) { + parse_str($extraQuery, $output); + $this->savedDownloadKey = $output['dlid'] ?? $extraQuery; } } catch (\Exception $e) { - error_log('MokoSuiteBackup: Could not save download key: ' . $e->getMessage()); - Factory::getApplication()->enqueueMessage( - 'MokoSuiteBackup could not preserve your download/license key before the update. ' - . 'Please verify your license key is still configured in System → Update Sites after this update completes.', - 'warning' - ); + Log::add('MokoSuiteBackup: Could not backup download key: ' . $e->getMessage(), Log::WARNING, 'jerror'); } } @@ -138,8 +139,8 @@ class Pkg_MokoSuiteBackupInstallerScript return; } - /* Restore download key if it was saved before update */ - if ($this->savedDownloadKey !== null) { + /* Restore the download key preserved before the update re-registered the site */ + if ($type === 'update') { $this->restoreDownloadKey(); } @@ -168,14 +169,17 @@ class Pkg_MokoSuiteBackupInstallerScript /* Sync submenu icons in #__menu (Joomla doesn't update icons on upgrades) */ $this->syncMenuIcons(); - /* Warn if no license key configured */ - $this->warnMissingLicenseKey(); - /* Migrate profiles with old default backup_dir values to [DEFAULT_DIR] placeholder */ $this->migrateDefaultBackupDir(); - /* Remind user to review backup profile settings */ + /* Install completion notice (install and update) */ + $this->installSuccessful(); + if ($type === 'install') { + /* Fresh install never carries a download key — prompt for one */ + $this->warnMissingLicenseKey(); + + /* Remind user to review backup profile settings */ $profileUrl = Route::_('index.php?option=com_mokosuitebackup&view=profiles'); Factory::getApplication()->enqueueMessage( @@ -640,66 +644,57 @@ class Pkg_MokoSuiteBackupInstallerScript ->where($db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokosuitebackup')) ->where($db->quoteName('e.type') . ' = ' . $db->quote('package')) ->setLimit(1); + $db->setQuery($query); $updateSiteId = (int) $db->loadResult(); - if ($updateSiteId > 0) { + if ($updateSiteId > 0 && !empty($this->savedDownloadKey)) { $query = $db->getQuery(true) ->update($db->quoteName('#__update_sites')) - ->set($db->quoteName('extra_query') . ' = ' . $db->quote($this->savedDownloadKey)) + ->set($db->quoteName('extra_query') . ' = ' . $db->quote('dlid=' . $this->savedDownloadKey)) ->where($db->quoteName('update_site_id') . ' = ' . $updateSiteId); + $db->setQuery($query); $db->execute(); } } catch (\Exception $e) { - error_log('MokoSuiteBackup: Could not restore download key: ' . $e->getMessage()); + Log::add('MokoSuiteBackup: Could not restore download key: ' . $e->getMessage(), Log::WARNING, 'jerror'); + Factory::getApplication()->enqueueMessage( - 'MokoSuiteBackup: Your download/license key could not be preserved during the update. ' - . 'Please re-enter it in the Update Sites configuration to continue receiving updates.', + '

MokoSuiteBackup

' + . '

Your download/license key could not be preserved during the update.

' + . '

Please re-enter it in the Update Sites manager to continue receiving updates.

', 'warning' ); } } + /** + * Show post-install license key prompt. + */ private function warnMissingLicenseKey(): void { - try - { - $db = Factory::getDbo(); - $db->setQuery( - $db->getQuery(true) - ->select([$db->quoteName('update_site_id'), $db->quoteName('extra_query')]) - ->from($db->quoteName('#__update_sites')) - ->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoSuiteBackup%') . ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoSuiteBackup%') . ')') - ->setLimit(1) - ); - $site = $db->loadObject(); - - if ($site) - { - $eq = (string) ($site->extra_query ?? ''); - if (!empty($eq) && strpos($eq, 'dlid=') !== false) { parse_str($eq, $p); if (!empty($p['dlid'])) { return; } } - $editUrl = 'index.php?option=com_installer&task=updatesite.edit&update_site_id=' . (int) $site->update_site_id; - } - else - { - $editUrl = 'index.php?option=com_installer&view=updatesites'; - } - + try { Factory::getApplication()->enqueueMessage( - 'Moko Consulting License Key Required — ' - . 'No download key is configured. Updates will not be available until a valid license key is entered. ' - . 'Enter License Key', + '

MokoSuiteBackup License Key Required

' + . '

A download/license key (DLID) is required to receive updates.

' + . '

Enter your key in the Update Sites manager ' + . 'or contact Moko Consulting Support to obtain one.

', 'warning' ); - } - catch (\Exception $e) { - error_log('MokoSuiteBackup: License key check failed: ' . $e->getMessage()); + } catch (\Exception $e) {} + } + + /** + * Show install successful prompt. + */ + private function installSuccessful(): void + { + try { Factory::getApplication()->enqueueMessage( - 'MokoSuiteBackup could not verify your license key status. ' - . 'Please check System → Update Sites to ensure a valid license key is configured.', - 'warning' + '

MokoSuiteBackup installed successfully!

', + 'info' ); - } + } catch (\Exception $e) {} } } From 772f2b75a1fc5001179ca71682477fcfda0613d5 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 4 Jul 2026 20:27:20 +0000 Subject: [PATCH 63/70] chore(release): build 02.54.00 [skip ci] --- CHANGELOG.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d1ce17..eeda661 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,8 @@ ## [02.54.00] --- 2026-07-04 -## [02.53.00] --- 2026-07-04 +## [02.54.00] --- 2026-07-04 ## [02.53.00] --- 2026-07-04 -## [02.52.24] --- 2026-06-30 - - -## [02.52.24] --- 2026-06-30 +## [02.53.00] --- 2026-07-04 From 4dc8bc69122c3c44e0f873cc4825231b832e4191 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 4 Jul 2026 20:28:01 +0000 Subject: [PATCH 64/70] chore(release): build 02.55.00 [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 4 ++-- SECURITY.md | 2 +- source/packages/com_mokosuitebackup/mokosuitebackup.xml | 2 +- .../com_mokosuitebackup/sql/updates/mysql/02.55.00.sql | 1 + .../mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml | 2 +- .../plg_actionlog_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_console_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_content_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_quickicon_mokosuitebackup/mokosuitebackup.xml | 2 +- .../packages/plg_system_mokosuitebackup/mokosuitebackup.xml | 2 +- source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml | 2 +- .../plg_webservices_mokosuitebackup/mokosuitebackup.xml | 2 +- source/pkg_mokosuitebackup.xml | 2 +- 14 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 source/packages/com_mokosuitebackup/sql/updates/mysql/02.55.00.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 11958bd..93398bb 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.00.00 +# VERSION: 02.55.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index eeda661..27c8aff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +## [02.55.00] --- 2026-07-04 + ## [02.54.00] --- 2026-07-04 ## [02.54.00] --- 2026-07-04 @@ -8,5 +10,3 @@ ## [02.54.00] --- 2026-07-04 ## [02.53.00] --- 2026-07-04 - -## [02.53.00] --- 2026-07-04 diff --git a/SECURITY.md b/SECURITY.md index ec3e33b..c3466da 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 02.54.00 +VERSION: 02.55.00 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitebackup/mokosuitebackup.xml b/source/packages/com_mokosuitebackup/mokosuitebackup.xml index f72db88..9784a0e 100644 --- a/source/packages/com_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/com_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Component - MokoSuiteBackup - 02.54.00 + 02.55.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitebackup/sql/updates/mysql/02.55.00.sql b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.55.00.sql new file mode 100644 index 0000000..5278158 --- /dev/null +++ b/source/packages/com_mokosuitebackup/sql/updates/mysql/02.55.00.sql @@ -0,0 +1 @@ +/* 02.55.00 — no schema changes */ diff --git a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml index f0e56e0..439582f 100644 --- a/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml +++ b/source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.xml @@ -8,7 +8,7 @@ --> Module - MokoSuiteBackup - cPanel - 02.54.00 + 02.55.00 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml index 1edf2cd..0028425 100644 --- a/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Action Log - MokoSuiteBackup - 02.54.00 + 02.55.00 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml index 3e9f505..d3dd271 100644 --- a/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Console - MokoSuiteBackup - 02.54.00 + 02.55.00 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml index a110222..d9b0bbd 100644 --- a/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Content - MokoSuiteBackup - 02.54.00 + 02.55.00 2026-06-04 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml index 22c10fd..c7f0749 100644 --- a/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml @@ -1,7 +1,7 @@ Quick Icon - MokoSuiteBackup - 02.54.00 + 02.55.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml index 1c5e7eb..0a0470b 100644 --- a/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> System - MokoSuiteBackup - 02.54.00 + 02.55.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml index 5dc3c94..c5448a1 100644 --- a/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Task - MokoSuiteBackup - 02.54.00 + 02.55.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml index 5fccdbe..319fd0b 100644 --- a/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml +++ b/source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml @@ -7,7 +7,7 @@ --> Web Services - MokoSuiteBackup - 02.54.00 + 02.55.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitebackup.xml b/source/pkg_mokosuitebackup.xml index a50be4c..9f782dc 100644 --- a/source/pkg_mokosuitebackup.xml +++ b/source/pkg_mokosuitebackup.xml @@ -8,7 +8,7 @@ Package - MokoSuiteBackup mokosuitebackup - 02.54.00 + 02.55.00 2026-06-02 Moko Consulting hello@mokoconsulting.tech From 56154680428040a5c38e47b14fe120a63ba83d2d Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 4 Jul 2026 20:28:12 +0000 Subject: [PATCH 65/70] =?UTF-8?q?chore:=20promote=20changelog=20[Unrelease?= =?UTF-8?q?d]=20=E2=86=92=20[02.55.00]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27c8aff..9786647 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## [02.55.00] --- 2026-07-04 +## [02.55.00] --- 2026-07-04 + ## [02.54.00] --- 2026-07-04 ## [02.54.00] --- 2026-07-04 From 278c572e0f1faba10091df35b97d920900dd69d6 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 20:28:29 +0000 Subject: [PATCH 66/70] chore: sync issue-branch.yml from Template-Generic [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 93398bb..11958bd 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.55.00 +# VERSION: 01.00.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" From eecb8c8c7208dc4579f8448a809e4218f6ed97eb Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 4 Jul 2026 20:37:39 +0000 Subject: [PATCH 67/70] chore(release): build 02.55.00 [skip ci] --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9786647..d28b279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,8 @@ ## [02.55.00] --- 2026-07-04 -## [02.54.00] --- 2026-07-04 +## [02.55.00] --- 2026-07-04 ## [02.54.00] --- 2026-07-04 ## [02.54.00] --- 2026-07-04 - -## [02.53.00] --- 2026-07-04 From 0578f2deedd6ee939b826586c42dab34981a51c9 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 4 Jul 2026 20:37:49 +0000 Subject: [PATCH 68/70] =?UTF-8?q?chore:=20promote=20changelog=20[Unrelease?= =?UTF-8?q?d]=20=E2=86=92=20[02.55.00]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d28b279..c1f99df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ ## [02.55.00] --- 2026-07-04 +## [02.55.00] --- 2026-07-04 + ## [02.54.00] --- 2026-07-04 ## [02.54.00] --- 2026-07-04 From 882cb2c48cab21ffb6f57599e38df2c84ae48db5 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 4 Jul 2026 20:40:14 +0000 Subject: [PATCH 69/70] chore: sync issue-branch.yml from Template-Generic [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 2b91385..11958bd 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 02.52.25 +# VERSION: 01.00.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" From 0c9d9a633630a5eb5ae86013c528aa452e27e177 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 4 Jul 2026 15:51:56 -0500 Subject: [PATCH 70/70] =?UTF-8?q?docs(changelog):=20document=2002.53.00?= =?UTF-8?q?=E2=80=9302.55.00=20and=20pending=20remote-storage=20removal=20?= =?UTF-8?q?[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Populate the empty CI-generated version sections and dedupe headers: - 02.53.00: short-name branding - 02.54.00: per-profile retention enforcement, Purge button fix, manifest naming convention - 02.55.00: installer licensing + install-completion pattern - Unreleased: legacy single-remote storage removal (#204) Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i --- CHANGELOG.md | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1f99df..4667a52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,55 @@ # Changelog + +All notable changes to this project are documented in this file. The format is +based on [Keep a Changelog](https://keepachangelog.com/); versions use +zero-padded `MAJOR.MINOR.PATCH`. + ## [Unreleased] ## [02.55.00] --- 2026-07-04 -## [02.55.00] --- 2026-07-04 +### Removed +- Legacy single-remote storage: the per-profile `remote_storage` column and all + FTP/SFTP/S3/Google Drive credential columns. Remote destinations are now sourced + exclusively from the `#__mokosuitebackup_remotes` table. +- Orphaned `SftpPath` form field and the `ajax.browseSftpDir` endpoint (leftovers + of the removed single-SFTP path picker). -## [02.55.00] --- 2026-07-04 +### Changed +- Package installer script adopts the standard licensing and install-completion + pattern: the download key is preserved across updates (backed up in preflight, + restored in postflight), an "installed successfully" notice shows on install and + update, and the license-key prompt shows on fresh install only. +- Akeeba import no longer copies legacy remote settings onto the profile; re-add + remote destinations on the profile's Remote tab after importing. -## [02.55.00] --- 2026-07-04 +### Fixed +- Schema migration `02.52.25.sql` uses plain `DROP COLUMN` (portable to both + Oracle MySQL 8.x and MariaDB) instead of the MariaDB-only `DROP COLUMN IF EXISTS`. ## [02.54.00] --- 2026-07-04 -## [02.54.00] --- 2026-07-04 +### Added +- Per-profile backup retention is now enforced. After each backup, completed + backups older than `retention_days` or beyond the newest `retention_count` + copies are pruned, along with their archive and log files (`0` = unlimited for + either rule). The retention fields are now shown on the profile editor's + Archive tab. + +### Fixed +- "Purge Old Backups" toolbar button now opens the purge modal under the Joomla 6 + Atum toolbar (it was bound to markup the toolbar no longer renders). + +### Changed +- All Joomla package manifests hardcode their titles and descriptions (no language + strings), and `` follows the `Type - Name` convention (e.g. + `Component - MokoSuiteBackup`, `Module - MokoSuiteBackup - cPanel`). + +## [02.53.00] --- 2026-07-04 + +### Added +- `COM_MOKOJOOMBACKUP_SHORT` short-name language constant. + +### Changed +- Admin views and the administrator sidebar menu use the short "Backup" name + (e.g. "Backup: Dashboard", "Backup: Records").