Files
MokoSuiteBackup/CHANGELOG.md
T
jmiller 6b4f98869a
Universal: PR Check / Require Docs Update (pull_request) Has been skipped
Universal: PR Check / Wiki Update Reminder (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 37s
Generic: Project CI / Lint & Validate (pull_request) Successful in 33s
Universal: PR Check / Validate PR (pull_request) Failing after 28s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 32s
Generic: Project CI / Tests (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
fix: installer postflight honest about partial/failed install
Joomla logs a failed child extension but still runs the package
postflight, so the 'installed successfully' message could appear on a
broken install. In postflight, before the success/next-steps messages:

- verify every bundled child declared in the package manifest actually
  registered in #__extensions (match element+type, and folder=group for
  plugins), and
- verify the component's schema tables (parsed dynamically from the
  installed install.mysql.sql) exist in the DB.

If anything is missing, enqueue an error listing it and RETURN before the
success message. Fail-open (try/catch -> nothing missing) so a transient
manifest/DB/IO glitch can't fail a good install. Unconditional
housekeeping (download-key restore, menu fixes) still runs regardless.
Names escaped with htmlspecialchars; table/child lists derived
dynamically, never hard-coded.

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-06 10:49:49 -05:00

8.8 KiB

Changelog

[Unreleased]

Added

  • Full-screen backup screen (view=runbackup) for both pre-update and manual backups, modelled on Akeeba's Backup-on-Update — replaces the earlier popup-modal approach. Clicking Joomla's Install the update now redirects (server-side, Akeeba-style) to a dedicated full-page backup screen that runs the stepped backup with a real progress bar and then automatically continues the update via a validated returnurl (flagged is_backed_up=1 so the backup isn't repeated). Crucially, no backup runs synchronously inside the update request — which is what white-screened large sites. The dashboard Backup Now now opens the same full-screen screen instead of an inline modal. (#196)
  • Retention now prunes remote copies too: when a backup is pruned by age/count, its archive is deleted from every enabled remote destination (SFTP / FTP / S3 / Google Drive), not just the local copy. Each uploader gained an idempotent delete() method (already-absent file = success), and removal is best-effort — a failing destination is logged but never blocks local pruning. The shared standalone restore.php is intentionally left in place (every backup overwrites it, so newer backups still depend on it). (#229)

Changed

  • Consolidated backup plumbing into shared helpers (#230):
    • New RemoteUploaderFactory replaces the createUploaderFromParams() copy that was duplicated in BackupEngine and SteppedBackupEngine.
    • RetentionManager is now the single retention authority — it takes the global max_age_days/max_backups fallback and gained pruneOrphans(); the system plugin's hourly cleanup delegates to it and its duplicate deleteBackupRecord() logic is removed.
    • The backend controller, Web Services API controller, and legacy cli/mokosuitebackup.php now run backups through the shared BackupRunner (gaining the normalized complete/warning/fail status) instead of instantiating BackupEngine directly.

Fixed

  • Package installer is now honest about success: the postflight no longer prints "installed successfully" / next-steps when the install actually failed or only partially completed. Joomla logs a failed child extension but still runs the package postflight, so the postflight now (a) verifies every bundled child declared in the manifest actually registered in #__extensions (matched by element + type, and folder/group for plugins) and (b) verifies the component's schema tables — derived dynamically from the installed install.mysql.sql — actually exist. If anything is missing it enqueues an error listing what's missing and stops before the success message. Fail-open: any manifest/DB/IO glitch is treated as "nothing missing" so a transient error never turns a good install into a false failure. Unconditional housekeeping (e.g. download-key restore) still runs regardless.
  • Pre-update/uninstall backup no longer white-screens the update on large sites. The synchronous backup that runs inside the extension update/uninstall request now raises max_execution_time/memory_limit (and ignore_user_abort) like the web-cron path, so it can't exhaust the request's default limits mid-backup. (Core Joomla updates additionally get a full-screen backup screen — see below.)
  • Archive names for CLI/console-triggered backups no longer come out as joomla.invalid_…. The [HOST] placeholder took $_SERVER['HTTP_HOST'] verbatim, but Joomla's console fills that with the reserved sentinel host joomla.invalid; the resolver now treats that (like empty/localhost) as unusable and falls back to the configured live_site host, then the system hostname. (Set the site's live_site to get the exact domain in CLI-built names.)
  • Standalone restore script generation no longer aborts backups with str_replace() expects at least 3 arguments, 2 given. MokoRestore::generateStandaloneScript() had a str_replace() call (the "Backup Archive" pre-check rewrite) that was missing its $php subject argument, so every standalone-mode backup fatally errored while "Generating standalone restore.php…" — the archive still finalized and uploaded, but no restore.php was ever produced (the true root cause behind #226). (#226)
  • Remote upload: the standalone restore script upload is no longer silent — its result is now checked and logged, and a failed restore-script upload marks the backup as warning (previously the result was discarded, so a missing restore script on the remote went unreported while the archive still showed success). (#226)

[02.58.00] --- 2026-07-06

Added

  • SFTP destinations: upload an SSH private key file in the Add/Edit Destination modal instead of pasting it (reads the file into the key field client-side; pasting still works).

Changed

  • Add/Edit Destination modal: wrap the content in a Bootstrap .container-fluid and give it a 1rem padding frame via the WAM stylesheet (Bootstrap's .modal-content has no padding of its own).

Fixed

  • Pre-update backup now shows an admin notification on every outcome (success / warning / failure) — previously a successful pre-update backup fired nothing, so it looked like the notification wasn't working. (#192)

Changed

  • Component admin CSS now loads via the Joomla Web Asset Manager (media/com_mokosuitebackup/ + joomla.asset.json) instead of an inline <style> block.
  • "Keep local copy" is configured per remote destination (in the Add/Edit Destination modal); the redundant profile-level field and its "Remote" tab are removed.
  • Add/Edit Destination modal: cleaned up the cramped padding/margins.

Fixed

  • Admin menu / duplicate-component fallout: (a) the orphaned com_component-mokosuitebackup is now removed in preflight too, so its "Backup" menu no longer collides on install ("The alias backup is already being used"); (b) ensureSubmenuItems() recreates the top-level "Backup" menu if it was deleted, instead of giving up — so Backup no longer disappears from the admin menu (and the cPanel module / MokoSuiteClient can find it again); (c) the component menu label now uses the short constant COM_MOKOJOOMBACKUP_SHORT. (#213 fallout)
  • Installer no longer aborts on MySQL 8 with "This command is not supported in the prepared statement protocol yet" (error 1295). The legacy remote-column purge migration (02.56.01.sql) used PREPARE/EXECUTE/DEALLOCATE, which Joomla's installer rejects; the drop now runs in the package installer script via an INFORMATION_SCHEMA-gated plain ALTER (portable across MariaDB and MySQL 8). (#213 update path)

[02.57.00] --- 2026-07-05

Added

  • BackupRunner service — a single synchronous entry point for backups that unifies final status and notification gating across the pre-update, web-cron, pre-install, scheduled-task and CLI triggers (delegates to BackupEngine; no double-notify). Returns a normalized result including the real complete/warning/fail status. Dashboard "Backup Now" (AJAX) unchanged; installer progress popup deferred to #196. (#214)

Fixed

  • Release automation: the changelog-promote step is now idempotent — it only inserts a ## [version] header when that version isn't already present, preventing the empty, duplicated version headers that repeated/same-version builds were producing. (workflow)

[02.56.11] --- 2026-07-05

Fixed

  • Package postflight now removes the orphaned com_component-mokosuitebackup registration (stray extension record, admin menu items, schema rows, and administrator/ folder) left behind by the old "Type - Name" component <name> — self-healing on update so affected sites reconcile to the real com_mokosuitebackup. (#213 follow-up)

[02.56.08] --- 2026-07-05

Fixed

  • Component manifest <name> reverted to the element-safe MokoSuiteBackup. The "Type - Name" convention derived element com_component-mokosuitebackup, registering a duplicate component and leaving the real com_mokosuitebackup orphaned on the old version. (#213)

[02.56.07] --- 2026-07-05

Security

  • Mask the FTP password in AjaxController::maskSecrets()/mergeExistingSecrets() — stored FTP remotes no longer leak their password via listRemotes. (#169)
  • Stop the API single-item view (Backups/JsonapiView) leaking the server absolute_path. (#187)
  • SFTP uploader uses StrictHostKeyChecking=accept-new instead of no. (#182)

Fixed

  • DatabaseDumper writes a real newline after DROP TABLE (was a literal \n). (#179)
  • Profile-picker forms order by id instead of the dropped ordering column. (#180)
  • uninstall.mysql.sql now drops the snapshots table. (#181)
  • CLI snapshot delete uses the data_file column (was non-existent file_path). (#184)
  • Remove the duplicate pre-update backup (content plugin no longer subscribes to onExtensionBeforeUpdate). (#186)
  • DatabaseImporter collects non-fatal statement errors (getErrors()/hasErrors()). (#188)