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=<key>
- 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
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.
Per the package-XML convention, manifests must not use language strings for
titles/descriptions, and <name> must follow "Type - Name".
- Component <name> "MokoSuiteBackup" → "Component - MokoSuiteBackup"
- Module <name> "mod_mokosuitebackup_cpanel" → "Module - MokoSuiteBackup - cPanel"
- Hardcode every <description> (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 <name> values already followed the Group - Name format and are
unchanged. All manifests validated as well-formed XML.
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
- 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).
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
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
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
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
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
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
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
The ordering column was unused in the profiles UI — profiles sort by ID.
Drops the column via migration, removes all references from model,
config query, importer, and install SQL.
Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG
CI version_bump was creating duplicate <version> lines in all
sub-extension manifests. Also AjaxController still referenced the old
`config` column and removed `keep_local` column on the remotes table.
Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG
The CI version_bump wrote 't' instead of a tab before <version> in
pkg_mokosuitebackup.xml, and appended a duplicate <version> line in
mokosuitebackup.xml instead of replacing the existing one.
Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG
- Add MokoSuiteClient as git submodule under source/packages/
- Add pkg_mokosuiteclient entry to pkg_mokosuitebackup.xml
- Fix duplicate <version> tag in package manifest