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
- install.mysql.sql: rename `config` → `params` and drop `keep_local` from remotes
table to match update file 01.41.00 and RemoteTable.php code (fixes Joomla
database maintenance "one problem")
- install.mysql.sql: fix idx_enabled index to use composite (profile_id, enabled)
- install.mysql.sql: add restore_script_name column to profiles table
- 01.43.22.sql: ALTER TABLE to add restore_script_name for existing installs
- DashboardModel: order profile dropdown by ID instead of ordering column
- SteppedBackupEngine: add stack trace logging around MokoRestore standalone
generation to debug str_replace FATAL on SFTP profiles
New #__mokosuitebackup_remotes table stores remote destinations with
JSON params per type (SFTP/S3/GDrive/FTP). Each profile can have
multiple enabled destinations — the engine uploads to all of them.
Database:
- New table with profile_id FK, type, enabled, params JSON, ordering
- Migration auto-converts existing profile remote columns to new table
- RemoteTable, RemoteModel, RemotesModel classes
Engine:
- BackupEngine: loadRemoteDestinations() + createUploaderFromParams()
iterates all enabled remotes, falls back to legacy columns
- SteppedBackupEngine: one upload step per remote destination, persisted
via session.remoteDestinations + remoteIndex
- Local copy only deleted when ALL uploads succeed
UI:
- Profile edit: "Remote Destinations" linked table with AJAX CRUD
- Add/edit modal with type selector showing dynamic fields
- Toggle enabled/disabled, delete with confirmation
- Legacy fields hidden when remotes configured, shown as fallback
- Secrets masked in responses, merged from DB on save
Closes#97
New "Data Sanitization" fieldset on profile form with four options:
- Sanitize User Passwords: replaces all bcrypt hashes with invalid sentinel
- Preserve Super Admin: keeps Super Users group passwords intact
- Sanitize User Emails: replaces with user123@sanitized.example.com
- Clear Session Data: excludes #__session table data (default: on)
DatabaseDumper sanitizes rows inline during dump — both in-memory
and file-streaming paths. Super admin detection uses group_id=8
from #__user_usergroup_map with static caching.
Use cases: sharing backups, creating demo/staging sites, GDPR compliance.
Partial #129 (Part 2 — restore script password reset — tracked separately)
All placeholders changed from lowercase to UPPERCASE:
[host] → [HOST], [site_name] → [SITE_NAME], [date] → [DATE],
[datetime] → [DATETIME], [profile_id] → [PROFILE_ID], etc.
[HOME] and [DEFAULT_DIR] were already uppercase — now consistent.
SQL migration 01.39.01 updates existing profile data in the database.
Resolution display prefixed with "EXAMPLE:" to clarify these are
example values resolved at backup time.
13 files updated across engines, fields, forms, templates, and SQL.
The column was TINYINT(1) which can only store 0/1. The new
'standalone' mode value causes MySQL to truncate the string to 0,
breaking profile save. Changed to VARCHAR(20) to support all three
modes: '0' (none), '1' (wrapped), 'standalone'.
SFTP UX improvements:
- SshKeyField: file upload button (FileReader → base64 → hidden field),
key never displayed as readable text, __KEEP_EXISTING__ sentinel
preserves DB value on re-save without re-uploading
- Auth type dropdown: password / key file / key file + passphrase
with conditional field visibility via showon
- Required field markers on host, username, path, password
- Remove insecure FTP option from remote storage dropdown
Security:
- Private key stored base64-encoded in database
- SftpUploader decodes base64 before writing temp file
- ProfileTable::store() handles sentinel to prevent key leakage
- Key content never rendered in HTML form output
Add content snapshot system for lightweight article/category/module
versioning independent of full backups. Snapshots store as JSON files
with replace or merge restore modes, wrapped in DB transactions.
- SnapshotEngine: dumps articles, categories, modules + related tables
(workflow_associations, tag maps, frontpage) to JSON
- SnapshotRestoreEngine: replace (clean slate) or merge (upsert) mode
- Full MVC: controller, models, view, template with create/restore modals
- New ACL permission: mokosuitebackup.snapshot.manage
- Submenu entry with camera icon, upgrade SQL for snapshots table
Improve full-site restore UI with confirmation modal offering options
for files, database, preserve config, and encryption password.
Config improvements:
- WebcronSecretField: CSPRNG generator, strength meter, rejects weak
patterns (password, admin, secret), enforces min 16 chars
- IpWhitelistField: table-based management, current IP detection with
one-click "Add my IP" button
- Default profile shows "Title (#ID)" format
- Default backup dir uses [DEFAULT_DIR] placeholder
- Install script generates random 32-char webcron secret
- Dashboard quick actions: full-width dropdown with button below
Each profile can now set its own retention_days and retention_count.
A value of 0 means use the global default from component options.
Cleanup logic refactored to iterate per-profile with individual
retention thresholds. Also cleans up orphaned records where the
parent profile was deleted. Log files alongside archives are now
removed during cleanup.
Extracted deleteBackupRecord() helper for consistent file+DB cleanup.
- getDefaultAbsolute() now returns JPATH_ROOT/backups instead of
the old admin component directory
- SQL default, form default, and install migration all use [DEFAULT_DIR]
- portablize() converts ./backups and old literal paths back to [DEFAULT_DIR]
- Users see [DEFAULT_DIR] in the field, resolved path shown in status
- Component: MokoSuiteBackup
- Plugins: System/Task/Console/Content/QuickIcon/ActionLog/WebServices - MokoSuiteBackup
- Default backup_dir changed to ./backups (relative to site root)
- Auto-migrate old defaults on upgrade
- Default backup_dir is now ../backups (relative to JPATH_ROOT),
which resolves outside public_html on most hosting setups
- Added BackupDirectory::normalizePath() to resolve ../ segments
without requiring the path to exist on disk
- Added BackupDirectory::portablize() to auto-detect absolute paths
and replace them with portable placeholders ([HOME], ../backups)
- ProfileTable::check() auto-normalizes backup_dir on save
- Install postflight auto-migrates old in-webroot defaults to ../backups
- Dashboard warning now checks resolved path instead of string matching
- .htaccess protection only applied when directory is inside web root
- browseDir() now resolves paths via BackupDirectory::resolve() before
permission check, fixing "access denied" when browsing from [DEFAULT_DIR]
- SQL default for backup_dir changed to [DEFAULT_DIR] placeholder
- Added help button (?) next to Browse that opens a modal listing all
available placeholders with examples and recommended paths
Renames all sub-extensions from mokojoombackup to mokosuitebackup
(package, component, 7 plugins, language files, manifests).
Adds [HOME] placeholder to BackupDirectory and PlaceholderResolver
so users can set backup_dir to [HOME]/backups (outside web root).
Fixes folder browser "access denied" on PHP-FPM shared hosting
where getenv('HOME') returns empty by adding POSIX and JPATH_ROOT
fallback detection.