feat: Content snapshots, restore UI, and config hardening (v01.25.00) #69

Merged
jmiller merged 9 commits from feature/47-backup-status-helper into main 2026-06-21 22:34:53 +00:00
Owner

Summary

  • Content Snapshots: New lightweight snapshot system for articles, categories, and modules — independent of full backups. JSON-based storage with replace or merge restore modes, wrapped in DB transactions.
  • Restore UI: Full-site restore now has a confirmation modal with options for files, database, preserve config, and encryption password.
  • Config hardening: Webcron secret field with CSPRNG generator + strength meter, IP whitelist with table UI + current IP detection, profile dropdown shows name + ID.

New Files (12)

File Purpose
SnapshotEngine.php Dumps articles/categories/modules + related tables to JSON
SnapshotRestoreEngine.php Replace (clean slate) or merge (upsert) restore with transactions
SnapshotsController.php Create, restore, delete actions
SnapshotsModel.php / SnapshotModel.php List + single record models
View/Snapshots/HtmlView.php View with toolbar
tmpl/snapshots/default.php List + create modal + restore modal
WebcronSecretField.php Random generator, strength meter, rejects weak patterns
IpWhitelistField.php Table UI, current IP detection, one-click "Add my IP"
01.25.00.sql Upgrade SQL for snapshots table

Modified Files (9)

  • install.mysql.sql — snapshots table added
  • access.xmlmokosuitebackup.snapshot.manage ACL
  • mokosuitebackup.xml — version bump + Snapshots submenu
  • config.xml — custom fields, profile name+ID, fixed default dir
  • tmpl/backups/default.php — restore confirmation modal
  • tmpl/dashboard/default.php — full-width quick actions layout
  • com_mokosuitebackup.ini — 40+ new language strings
  • script.php — random webcron secret on install, snapshots submenu
  • pkg_mokosuitebackup.xml — version bump

Snapshot Tables Captured

Type Tables
Articles #__content, #__content_frontpage, #__workflow_associations, #__contentitem_tag_map
Categories #__categories (com_content only)
Modules #__modules, #__modules_menu

Test Plan

  • Fresh install creates snapshots table and generates webcron secret
  • Create snapshot with all types — verify JSON file created in backup dir
  • Create snapshot with single type (articles only) — verify only article tables captured
  • Restore snapshot in replace mode — verify existing content replaced
  • Restore snapshot in merge mode — verify existing content preserved, missing items added
  • Restore modal on backups view — verify options (files, DB, config, password) passed correctly
  • WebcronSecretField — generate button creates 32-char secret, strength meter rejects weak patterns
  • IpWhitelistField — shows current IP, "Add my IP" works, table add/remove works
  • Default profile dropdown shows "Title (#1)" format
  • Dashboard quick actions — dropdown full-width, button below
## Summary - **Content Snapshots**: New lightweight snapshot system for articles, categories, and modules — independent of full backups. JSON-based storage with replace or merge restore modes, wrapped in DB transactions. - **Restore UI**: Full-site restore now has a confirmation modal with options for files, database, preserve config, and encryption password. - **Config hardening**: Webcron secret field with CSPRNG generator + strength meter, IP whitelist with table UI + current IP detection, profile dropdown shows name + ID. ## New Files (12) | File | Purpose | |------|---------| | `SnapshotEngine.php` | Dumps articles/categories/modules + related tables to JSON | | `SnapshotRestoreEngine.php` | Replace (clean slate) or merge (upsert) restore with transactions | | `SnapshotsController.php` | Create, restore, delete actions | | `SnapshotsModel.php` / `SnapshotModel.php` | List + single record models | | `View/Snapshots/HtmlView.php` | View with toolbar | | `tmpl/snapshots/default.php` | List + create modal + restore modal | | `WebcronSecretField.php` | Random generator, strength meter, rejects weak patterns | | `IpWhitelistField.php` | Table UI, current IP detection, one-click "Add my IP" | | `01.25.00.sql` | Upgrade SQL for snapshots table | ## Modified Files (9) - `install.mysql.sql` — snapshots table added - `access.xml` — `mokosuitebackup.snapshot.manage` ACL - `mokosuitebackup.xml` — version bump + Snapshots submenu - `config.xml` — custom fields, profile name+ID, fixed default dir - `tmpl/backups/default.php` — restore confirmation modal - `tmpl/dashboard/default.php` — full-width quick actions layout - `com_mokosuitebackup.ini` — 40+ new language strings - `script.php` — random webcron secret on install, snapshots submenu - `pkg_mokosuitebackup.xml` — version bump ## Snapshot Tables Captured | Type | Tables | |------|--------| | Articles | `#__content`, `#__content_frontpage`, `#__workflow_associations`, `#__contentitem_tag_map` | | Categories | `#__categories` (com_content only) | | Modules | `#__modules`, `#__modules_menu` | ## Test Plan - [ ] Fresh install creates snapshots table and generates webcron secret - [ ] Create snapshot with all types — verify JSON file created in backup dir - [ ] Create snapshot with single type (articles only) — verify only article tables captured - [ ] Restore snapshot in **replace** mode — verify existing content replaced - [ ] Restore snapshot in **merge** mode — verify existing content preserved, missing items added - [ ] Restore modal on backups view — verify options (files, DB, config, password) passed correctly - [ ] WebcronSecretField — generate button creates 32-char secret, strength meter rejects weak patterns - [ ] IpWhitelistField — shows current IP, "Add my IP" works, table add/remove works - [ ] Default profile dropdown shows "Title (#1)" format - [ ] Dashboard quick actions — dropdown full-width, button below
jmiller added the component: enginecomponent: admin labels 2026-06-21 20:30:23 +00:00
jmiller added 2 commits 2026-06-21 20:30:24 +00:00
feat: content snapshots, restore UI, and config hardening (v01.25.00)
Universal: Auto Version Bump / Version Bump (push) Successful in 10s
ef31713029
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
jmiller added 4 commits 2026-06-21 22:22:18 +00:00
The truncateFiltered() method ran unfiltered DELETE FROM #__modules
in replace mode, which would wipe ALL site modules (admin toolbar,
login, menus) — not just the ones in the snapshot. Now scoped to
only delete modules whose IDs exist in the snapshot data.

Also scopes #__modules_menu delete to snapshot module IDs, and adds
defense-in-depth validation of restore_mode in the controller.
fix: address PR review findings — error handling and safety
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 4s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: Auto Version Bump / Version Bump (push) Successful in 14s
Generic: Project CI / Lint & Validate (pull_request) Successful in 44s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 49s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 51s
d5421738b7
Fixes from code review and silent failure audit:

- SnapshotRestoreEngine: catch only duplicate key errors (MySQL 1062)
  in merge mode, re-throw all other exceptions instead of swallowing
- SnapshotRestoreEngine: add json_last_error() check for better error
  messages on corrupt snapshot files
- SnapshotRestoreEngine: log warnings when set_time_limit/ini_set fail
- SnapshotEngine: use strlen($json) instead of filesize() to avoid
  race conditions; catch \Exception instead of \Throwable
- SnapshotsController: remove @unlink suppression, add try-catch
  around delete loop with partial failure reporting
- script.php: add user-facing warning when webcron secret generation
  fails (was silently swallowed, inconsistent with other catch blocks)
jmiller added 1 commit 2026-06-21 22:22:42 +00:00
jmiller added 2 commits 2026-06-21 22:33:55 +00:00
# Conflicts:
#	.mokogitea/workflows/issue-branch.yml
#	.mokogitea/workflows/pr-check.yml
#	README.md
#	mokosuitebackup.xml
#	source/packages/com_mokosuitebackup/mokosuitebackup.xml
#	source/packages/plg_actionlog_mokosuitebackup/mokosuitebackup.xml
#	source/packages/plg_console_mokosuitebackup/mokosuitebackup.xml
#	source/packages/plg_content_mokosuitebackup/mokosuitebackup.xml
#	source/packages/plg_quickicon_mokosuitebackup/mokosuitebackup.xml
#	source/packages/plg_system_mokosuitebackup/mokosuitebackup.xml
#	source/packages/plg_task_mokosuitebackup/mokosuitebackup.xml
#	source/packages/plg_webservices_mokosuitebackup/mokosuitebackup.xml
#	source/pkg_mokosuitebackup.xml
Merge remote-tracking branch 'origin/feature/47-backup-status-helper' into feature/47-backup-status-helper
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 9s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 12s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 2s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 3s
Universal: Auto Version Bump / Version Bump (push) Successful in 8s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 15s
0a4f896f31
jmiller merged commit 0f88aa0055 into main 2026-06-21 22:34:53 +00:00
jmiller deleted branch feature/47-backup-status-helper 2026-06-21 22:34:54 +00:00
Sign in to join this conversation.
No Reviewers
Priority -
Type -
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: MokoConsulting/MokoSuiteBackup#69