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
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
8.8 KiB
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 validatedreturnurl(flaggedis_backed_up=1so 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 standalonerestore.phpis 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
RemoteUploaderFactoryreplaces thecreateUploaderFromParams()copy that was duplicated inBackupEngineandSteppedBackupEngine. RetentionManageris now the single retention authority — it takes the globalmax_age_days/max_backupsfallback and gainedpruneOrphans(); the system plugin's hourly cleanup delegates to it and its duplicatedeleteBackupRecord()logic is removed.- The backend controller, Web Services API controller, and legacy
cli/mokosuitebackup.phpnow run backups through the sharedBackupRunner(gaining the normalized complete/warning/fail status) instead of instantiatingBackupEnginedirectly.
- New
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 installedinstall.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(andignore_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 hostjoomla.invalid; the resolver now treats that (like empty/localhost) as unusable and falls back to the configuredlive_sitehost, 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 astr_replace()call (the "Backup Archive" pre-check rewrite) that was missing its$phpsubject argument, so every standalone-mode backup fatally errored while "Generating standalone restore.php…" — the archive still finalized and uploaded, but norestore.phpwas 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-fluidand give it a 1rem padding frame via the WAM stylesheet (Bootstrap's.modal-contenthas 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-mokosuitebackupis 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 constantCOM_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) usedPREPARE/EXECUTE/DEALLOCATE, which Joomla's installer rejects; the drop now runs in the package installer script via an INFORMATION_SCHEMA-gated plainALTER(portable across MariaDB and MySQL 8). (#213 update path)
[02.57.00] --- 2026-07-05
Added
BackupRunnerservice — 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 toBackupEngine; 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
postflightnow removes the orphanedcom_component-mokosuitebackupregistration (stray extension record, admin menu items, schema rows, andadministrator/folder) left behind by the old "Type - Name" component<name>— self-healing on update so affected sites reconcile to the realcom_mokosuitebackup. (#213 follow-up)
[02.56.08] --- 2026-07-05
Fixed
- Component manifest
<name>reverted to the element-safeMokoSuiteBackup. The "Type - Name" convention derived elementcom_component-mokosuitebackup, registering a duplicate component and leaving the realcom_mokosuitebackuporphaned 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 vialistRemotes. (#169) - Stop the API single-item view (
Backups/JsonapiView) leaking the serverabsolute_path. (#187) - SFTP uploader uses
StrictHostKeyChecking=accept-newinstead ofno. (#182)
Fixed
DatabaseDumperwrites a real newline afterDROP TABLE(was a literal\n). (#179)- Profile-picker forms order by
idinstead of the droppedorderingcolumn. (#180) uninstall.mysql.sqlnow drops the snapshots table. (#181)- CLI snapshot delete uses the
data_filecolumn (was non-existentfile_path). (#184) - Remove the duplicate pre-update backup (content plugin no longer subscribes to
onExtensionBeforeUpdate). (#186) DatabaseImportercollects non-fatal statement errors (getErrors()/hasErrors()). (#188)