Commit Graph

92 Commits

Author SHA1 Message Date
jmiller 186a75681a @
fix: require PHP 8.3 (Joomla 6) across code, composer, docs

Joomla 6 requires PHP 8.3+, and the MokoGitea repo metadata php_minimum
is 8.3, but the extension still enforced/declared 8.1:
- DashboardModel health check: PHP_VERSION >= 8.1.0 -> 8.3.0
- MokoRestore preflight check: 8.1 -> 8.3 (hint now "Joomla 6 requires PHP 8.3+")
- composer.json require php: >=8.1 -> >=8.3
- .mokogitea/CLAUDE.md: PHP 8.1+ -> 8.3+ (both places)

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
@
2026-07-12 20:46:35 -05:00
jmiller f62b1ec72e @
Generic: Project CI / Lint & Validate (pull_request) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Universal: PR Check / Require Docs Update (pull_request) Has been cancelled
Universal: PR Check / Wiki Update Reminder (pull_request) Has been cancelled
Universal: PR Check / Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
RC Revert / Rename rc/ back to dev/ (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
docs(ajax): correct preupdateAck docblock to one-shot skip semantics

The docblock still described the removed 10-minute throttle ("arms BOTH
keys"). It now sets a one-shot, action-keyed skip flag consumed once by
the system plugin. Comment-only; no behaviour change.

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
@
2026-07-12 18:28:54 -05:00
jmiller 3eb2562abd @
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 18s
fix: pre-action backup fires for every extension, not one per 10 min

Root cause of "pre-update backup only fires on the backup extension":
a coarse 600s session throttle. Whenever ANY pre-action backup ran
(including updating MokoSuiteBackup itself, or a core Joomla update), the
key mokosuitebackup.preaction_backup_before_update was armed for 10
minutes. onBeforeCompileHead read it as `recentBackup` and DISABLED the
client-side interceptor for all extensions, and runPreActionBackup was
throttled by the same window. So after updating one extension, every
other extension update silently skipped its backup for 10 minutes.

Replace the time window with "one backup per Update action":
- runPreActionBackup dedupes per-request (a batch update fires the event
  once per extension in a single request -> back up once) via instance
  flags, and consumes a ONE-SHOT session skip flag for the re-fired
  client update/uninstall (skip once, next distinct action backs up).
- ajax.preupdateAck sets the one-shot flag for the SPECIFIC action
  (update|uninstall), passed from installer-backup.js via &msb_action and
  surfaced by Runbackup HtmlView -> CFG.action. A core update passes no
  action, so it never suppresses an extension backup.
- onBeforeCompileHead drops the recentBackup time-suppression (the
  re-fire loop is already guarded client-side by window.__msbResuming).
- The core Joomla update path uses its own key
  (mokosuitebackup.core_update_backed_up), decoupled from extensions.

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
@
2026-07-12 17:48:09 -05:00
jmiller f9c1c6b186 @
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 16s
Generic: Project CI / Lint & Validate (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Universal: PR Check / Require Docs Update (pull_request) Has been cancelled
Universal: PR Check / Wiki Update Reminder (pull_request) Has been cancelled
Universal: PR Check / Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Has been cancelled
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(review): open-redirect, btoa UTF-8 safety, honest duplicate-mode txn

Pre-release review findings on dev -> main:

- Return-URL open-redirect (MED): a base64 returnurl like "/\evil.com"
  passed the root-relative allow-list (raw[1] is "\", not "/"), and a
  browser normalises leading "/\" to "//", yielding a protocol-relative
  redirect off-site. Reject any backslash or control char in the decoded
  return URL outright — legitimate Joomla admin URLs never contain them.

- installer-backup.js btoa() (LOW): btoa() throws on non-Latin1 input
  (e.g. a UTF-8 filter value in the list query string), and the throw was
  uncaught after preventDefault(), leaving the toolbar button dead. Build
  the redirect (UTF-8-safe base64) BEFORE swallowing the click; if it
  still fails, bail without preventing so Joomla proceeds normally.

- Snapshot duplicate-mode transaction (HIGH): restore() wrapped duplicate
  mode in transactionStart()/Rollback(), but duplicate mode writes via
  Joomla Table API whose Nested tables (categories/tags) issue LOCK
  TABLES, which implicitly COMMITS the open transaction in MySQL — so the
  rollback promise was illusory and a mid-inject failure left a slave
  half-updated. Run only the raw-DB modes (replace/create/merge) inside
  the transaction; duplicate mode runs outside it, matching its existing
  per-item defensive design (bad item skipped + logged, never fatal).

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
@
2026-07-12 17:14:10 -05:00
jmiller 4e07eabc0f feat(#237): implement duplicate snapshot mode (insert as new, remapped IDs)
Universal: Auto Version Bump / Version Bump (push) Successful in 7s
Finishes the deferred 'duplicate' conflict mode. Inserts snapshot content
as brand-new records with remapped IDs, leaving existing content
untouched — for master->slave 'push as new copies'.

- Uses Joomla's Table API (bootComponent MVCFactory) for Article,
  Category and Module so assets, UCM entries, category/tag nested-sets
  and alias uniqueness are handled by core rather than hand-rolled.
- Remaps category parent_id (parents first), article catid, module menu
  assignments, and custom-field-value item_id; re-features articles.
- Tags are reused/created by title (resolveTagIds) and assigned via the
  article table's newTags so UCM/tag-map stay correct.
- uniqueAlias() avoids alias collisions per scope.
- DEFENSIVE: every item is wrapped in try/catch — a bad item is skipped
  and logged, never fatal — so an untested/partial import degrades
  gracefully instead of corrupting content.

NOTE: still needs a real test pass on a dev site before relying on it.

Refs #237

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-10 12:00:45 -05:00
jmiller 49fcd5e63c feat(#237): portable snapshot transfer + master->slave injection API
Universal: Auto Version Bump / Version Bump (push) Successful in 16s
Adds the first increment of snapshot transfer:

- SnapshotPortable engine: package a snapshot into a portable .msbsnap
  (zip of manifest.json + snapshot.json + sha256), and ingest one back
  (from a zip upload or an inline payload) by materialising a local
  snapshot record.
- Admin: per-row 'Download portable snapshot' + an Import modal
  (upload .msbsnap) on the Snapshots page.
- API: POST /snapshot/inject — master pushes a snapshot payload into a
  slave's Web Services API; gated by a new 'Allow Snapshot Injection'
  option. Route added to the webservices plugin.
- Restore engine: new 'create' mode (skip existing, insert only new) and
  mode-name normalisation (overwrite->replace, create, duplicate). The
  'duplicate' (insert-as-new-with-remapped-IDs) mode returns a clear
  'not yet available' — deferred to a dedicated, tested pass.
- Options: 'Snapshot Transfer' fieldset — default conflict mode +
  allow-inject toggle. Language keys added (en-GB fallback).

Reuses the existing mokosuitebackup.snapshot.manage ACL. NOTE: untested
end-to-end (no dev build this session) — verify download/import/inject
on a dev build.

Refs #237

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-10 11:40:51 -05:00
jmiller 7a0636606e feat: full-screen backup for extension update/uninstall + view-record button
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 18s
Extend the full-screen backup screen beyond core Joomla updates:

- Extension updates/uninstalls (com_installer): update.update and
  manage.remove are POST + checkToken with a checked cid[] list, so a
  server-side GET redirect can't resume them. New client-side interceptor
  (installer-backup.js, injected via onBeforeCompileHead on the update and
  manage views) wraps Joomla.submitbutton, stores the selection, sends the
  browser to view=runbackup, and on return restores the selection and
  re-submits the real POST form. Gated by backup_before_update /
  backup_before_uninstall, super-user only, skipped within the throttle
  window.

- runbackup: on manual 'Backup Now' completion, show a 'View backup
  record' button (links to view=backup&id=<record_id>); not shown for the
  pre-update/uninstall flow (which hands back to Joomla). SteppedBackup
  Engine now returns record_id in its step result.

- runbackup arms the pre-action throttles on success (ajax.preupdateAck,
  now arming both update+uninstall keys) so the following server-side
  onExtensionBefore(Update|Uninstall) backup is skipped (no duplicate).

- Plugin manifest re-registers media/js.

NOTE: untested end-to-end (no dev build available this session) — verify
the com_installer resume flow on a dev build before relying on it.

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-10 10:53:46 -05:00
jmiller 8cfa46d985 feat: full-screen backup screen for pre-update + Backup Now (Akeeba-style)
Universal: PR Check / Wiki Update Reminder (pull_request) Has been skipped
Universal: PR Check / Require Docs Update (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: PR Check / Secret Scan (pull_request) Successful in 7s
Generic: Project CI / Lint & Validate (pull_request) Successful in 12s
Universal: PR Check / Validate PR (pull_request) Failing after 13s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 49s
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
Replace the popup-modal pre-update approach with a dedicated full-page
backup screen, modelled on Akeeba Backup-on-Update, and fix the white
screen caused by running a backup synchronously inside the update request.

- New view=runbackup (View/Runbackup + tmpl/runbackup): full-screen
  progress page that auto-starts the stepped backup (ajax.init -> loop
  ajax.step), then redirects to a validated returnurl on completion or
  shows a completion/retry panel. Auto-resolved by MVCFactory.
- System plugin: on com_joomlaupdate task=update.install (when
  backup_before_update is on), redirect to the full-screen backup screen
  with returnurl back to update.install&is_backed_up=1; on return, arm the
  throttle so onExtensionBeforeUpdate does not run a duplicate backup. No
  synchronous backup in the update request.
- Dashboard 'Backup Now' navigates to the full-screen screen instead of
  the inline modal; the inline modal + stepped JS are removed.
- Retire the popup modal: delete media/js/update-backup.js, remove the
  plugin <media> element and the preupdate_auto_intercept param.
- Dashboard: warn when Global Config live_site is empty (CLI/scheduled
  backup naming + URL generation depend on it).

Refs #196

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-05 22:55:28 -05:00
jmiller 5ffcbcc2f1 fix: CLI backups no longer named joomla.invalid (host placeholder)
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: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 40s
Universal: PR Check / Secret Scan (pull_request) Successful in 9s
Generic: Project CI / Lint & Validate (pull_request) Successful in 17s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 16s
Universal: PR Check / Validate PR (pull_request) Failing after 50s
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's console fills $_SERVER['HTTP_HOST'] with the reserved sentinel
'joomla.invalid', which the [HOST] placeholder resolver used verbatim
because its unusable-host check only knew about empty/localhost. Treat
'joomla.invalid' as unusable too, falling back to live_site then the
system hostname, so CLI/console/scheduled backups get a real name.

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-05 22:02:46 -05:00
jmiller 8dc4eaa384 feat: pre-update backup notice + live-progress modal on update pages
Universal: PR Check / Wiki Update Reminder (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Require Docs Update (pull_request) Has been skipped
Universal: PR Check / Secret Scan (pull_request) Successful in 10s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Generic: Project CI / Lint & Validate (pull_request) Successful in 15s
Universal: PR Check / Validate PR (pull_request) Failing after 12s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 48s
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
The server-side onExtensionBeforeUpdate backup runs synchronously and
cannot drive a browser modal, so nothing ever 'popped'. Add a client
notice + on-demand live-progress modal on com_joomlaupdate and
com_installer(view=update):

- plg_system_mokosuitebackup gains onBeforeCompileHead, which injects a
  'back up before you update' notice + Back up now button on the update
  pages (gated by show_update_notice + backup_before_update), plus a new
  media JS asset that runs the same stepped backup as the dashboard
  (ajax.init then a loop of ajax.step) with a dependency-free progress
  overlay.
- New AjaxController::preupdateAck() arms the same 10-min throttle key
  runPreActionBackup() checks, so proceeding with Joomla's update after a
  modal backup does not run a duplicate server-side backup.
- Plugin manifest registers the new media/js folder; language keys added.

Phase-1 baseline (notice + on-demand). Auto-intercepting the Update
button is the planned phase-2 follow-up (#196).

Refs #196

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-05 20:56:50 -05:00
jmiller 447f7b572e feat: prune remote backups on retention + consolidate backup helpers
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 10s
Universal: PR Check / Validate PR (pull_request) Failing after 14s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 40s
Generic: Project CI / Lint & Validate (pull_request) Successful in 52s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 1m8s
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
Retention (RetentionManager and the plugin's hourly cleanup) only ever
deleted local files; remote archives grew unbounded. Add an idempotent
delete() to RemoteUploaderInterface + all four uploaders and have
RetentionManager remove each pruned archive from the profile's enabled
remotes (best-effort; failures logged). The shared restore.php is left
in place since every backup overwrites it. (#229)

Consolidate duplicated plumbing (#230):
- New RemoteUploaderFactory replaces the createUploaderFromParams copy
  duplicated in BackupEngine and SteppedBackupEngine.
- RetentionManager becomes the single retention authority (global-default
  fallback + pruneOrphans()); the system plugin delegates to it and its
  duplicate doCleanup()/deleteBackupRecord() logic is removed.
- Backend controller, API controller and legacy cli/mokosuitebackup.php
  now run through the shared BackupRunner instead of BackupEngine directly.

Refs #229 #230

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-05 20:34:04 -05:00
jmiller 0ae9bfd93a fix: restore missing subject arg in MokoRestore str_replace (standalone gen)
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 30s
MokoRestore::generateStandaloneScript() rewrites the 'Backup Archive'
pre-check via str_replace(ORIG, REPL) but was missing the third
$php subject argument, so every standalone-mode backup fatally errored
with 'str_replace() expects at least 3 arguments, 2 given' at the
'Generating standalone restore.php...' step. The archive still finalized
and uploaded, but no restore.php was produced -- the underlying cause of
#226 (restore script missing on the remote).

Refs #226

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-05 20:15:30 -05:00
jmiller bf91611b97 fix(engine): check + log restore-script remote upload (no longer silent)
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 21s
The standalone restore script was uploaded to each remote with the result
discarded and nothing logged, so a failed second upload left the restore script
missing from the remote with no warning while the archive still recorded
'complete'. Capture the result: log success, and on failure set uploadFailed
(-> warning status) and log the reason.

Fixes #226

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-05 19:39:37 -05:00
jmiller 307067580c feat: BackupRunner helper — unify backup run/status/notification
Every non-interactive backup trigger used a slightly different path, so
final status and notification gating behaved inconsistently depending on
what started the backup (this is why pre-update backups did not gate
notifications the way "Backup Now" does).

Add src/Service/BackupRunner.php as the single synchronous entry point.
It delegates to BackupEngine (which already sends notifications gated by
the profile's notify_on_success/notify_on_failure and applies retention —
so there is exactly ONE notification layer), and normalises the result,
including the real complete/warning/fail status the engine records but did
not return. BackupRunner self-loads BackupEngine so callers require one file.

Routed through BackupRunner:
- plg_system  runPreActionBackup() (preaction) + web-cron handler (webcron)
- plg_content triggerAutoBackup() (pre-install)
- plg_task    scheduled task run
- plg_console RunCommand (cli)

The dashboard AJAX/stepped "Backup Now" flow is unchanged (it is the
interactive equivalent). The installer progress popup is deferred — the
installer request cannot drive the AJAX modal (TODO(#196) noted in code).

php -l clean on all changed files.

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-05 17:28:59 -05:00
jmiller 99b844e04a fix: security + data-integrity fixes (P0/P1)
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Wiki Update Reminder (pull_request) Successful in 2s
Universal: PR Check / Require Docs Update (pull_request) Failing after 8s
Universal: PR Check / Secret Scan (pull_request) Successful in 9s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 13s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 12s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 28s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 46s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 50s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
Security (P0):
- #169 mask FTP password in AjaxController maskSecrets()/mergeExistingSecrets()
- #187 stop leaking absolute_path in API item view (JsonapiView)
- #182 SftpUploader StrictHostKeyChecking=no -> accept-new (MITM)

Data integrity / correctness (P1):
- #179 DatabaseDumper: real newline after DROP TABLE (was literal backslash-n)
- #180 profile-picker forms ORDER BY id (the 'ordering' column is dropped on
  upgraded sites, breaking the query) — run_profile.xml + plg_content xml
- #181 uninstall.mysql.sql: add missing DROP for snapshots table
- #184 CLI snapshot delete uses data_file column (file_path does not exist,
  so snapshot files were never deleted)
- #186 remove duplicate pre-update backup: content plugin no longer subscribes
  to onExtensionBeforeUpdate (owned by the system plugin); keeps pre-install
- #188 DatabaseImporter now collects non-fatal statement errors
  (getErrors()/hasErrors()) so restores are no longer silent and callers can
  surface a warning status

Also folds in the .mokogitea/CLAUDE.md mokoplatform -> mokocli fix (stale repo
name after the rename).

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-05 16:32:56 -05:00
jmiller 6cef8fa9f7 Merge remote-tracking branch 'origin/main' into fix/remote-storage-cleanup
# Conflicts:
#	SECURITY.md
#	source/packages/MokoSuiteClient
#	source/packages/com_mokosuitebackup/mokosuitebackup.xml
#	source/packages/mod_mokosuitebackup_cpanel/mod_mokosuitebackup_cpanel.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
#	source/script.php
2026-07-04 15:34:41 -05:00
jmiller 10248b284a fix: enforce per-profile retention and repair Purge Old Backups button
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Generic: Project CI / Lint & Validate (pull_request) Successful in 12s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 9s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 27s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 39s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 30s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 4m0s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
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.
2026-07-04 15:25:43 -05:00
jmiller 46daabc34f fix(remote-cleanup): resolve dropped-column references found in review
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
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: PR Check / Secret Scan (pull_request) Successful in 10s
Generic: Project CI / Lint & Validate (pull_request) Successful in 13s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 27s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 40s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 41s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
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
2026-07-04 15:17:54 -05:00
jmiller a808789acb feat: add COM_MOKOJOOMBACKUP_SHORT and apply short name to views and admin menu
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 23s
- 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).
2026-07-04 14:20:54 -05:00
jmiller 68605ffc05 refactor(remote): remove legacy single-remote storage in favor of remotes table
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 25s
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
2026-07-04 13:22:44 -05:00
jmiller aefa46e0c4 fix: use warning status when backup succeeds but remote upload fails
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 19s
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
2026-06-30 14:14:06 -05:00
jmiller b26a21820b fix: auto-cancel stalled backups after 30 min timeout
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 29s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Failing after 14s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 13s
Generic: Project CI / Lint & Validate (pull_request) Successful in 43s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 47s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
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
2026-06-30 13:50:18 -05:00
jmiller 1eb1c18bdf fix: add cancel stalled backup action with ACL permission
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 18s
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
2026-06-30 13:48:51 -05:00
jmiller 4093267984 fix: add verbose error_log throughout restore script
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 8s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 7s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Generic: Project CI / Lint & Validate (pull_request) Successful in 16s
Universal: PR Check / Secret Scan (pull_request) Successful in 11s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 30s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 41s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
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
2026-06-30 12:59:59 -05:00
jmiller 20ce945e73 fix: recreate security file if missing while verification is pending
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Secret Scan (pull_request) Successful in 9s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 11s
Generic: Project CI / Lint & Validate (pull_request) Successful in 13s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 22s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 34s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
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
2026-06-30 12:53:36 -05:00
jmiller 3a6bb1c783 fix: remote upload prefix mismatch and restore security file visibility
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 23s
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
2026-06-30 12:50:02 -05:00
jmiller 1b481f2e2c fix: remove ordering column from profiles table
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 18s
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
2026-06-28 13:45:01 -05:00
jmiller 288baf41d3 fix: remove duplicate version tags from 8 manifests, align AjaxController to params column
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: PR Check / Secret Scan (pull_request) Successful in 5s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 13s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 30s
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
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
2026-06-26 21:19:21 -05:00
jmiller c508fcc8d5 fix: align remotes table schema, add restore_script_name column, profile ordering
- 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
2026-06-26 21:18:47 -05:00
jmiller 93f0fa0a47 fix: SSH key indicator detection and missing delete language key
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 9s
- SshKeyField: detect base64-encoded keys from DB so the "Key loaded"
  badge displays correctly after initial upload
- Add COM_MOKOJOOMBACKUP_BACKUPS_N_ITEMS_DELETED language keys for
  Joomla's AdminController delete feedback message
2026-06-25 12:17:45 -05:00
jmiller 07fb4dcc24 fix: remove run/backup buttons, move actions to detail view, custom restore script name, version bump 01.43.11-dev
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 10s
- Remove Run Backup / Backup Now buttons from profiles list, profile edit toolbar, and backup records view
- Move download, browse archive, and view log from backup list rows into individual backup record detail view
- Add download button to backup detail toolbar
- Link profile column in backup records list to profile edit
- Complete restore script filename customization across BackupEngine, SteppedBackupEngine, and MokoRestore
- Remove ordering field from profiles, default sort by ID ascending
- Fix untranslated JFIELD language keys
- Bump all manifests to 01.43.11-dev
2026-06-25 10:54:35 -05:00
jmiller 836d1bc8b7 fix(mokorestore): add Joomla detection warning, multi-zip selector, and standalone backup scan
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 13s
- Preflight now detects existing Joomla installation (configuration.php / Version.php)
  and shows a yellow warning — does not block, but alerts the user
- Standalone mode: backup archive check scans for all ZIPs instead of hardcoded name
- Multi-zip selector integrated into extract step with radio buttons
- Selected backup file passed through to extract action
- Added warn-style CSS class (yellow) for preflight warnings
2026-06-25 10:00:07 -05:00
jmiller 3742477aef fix: convert inline modals to Bootstrap 5, fix language keys, ntfy default, and MokoRestore error handling
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 19s
Universal: PR Check / Secret Scan (pull_request) Successful in 9s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 29s
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
- Convert 10 inline CSS modals to Bootstrap 5 (backups: 7, snapshots: 3)
- Replace style.display show/hide with Bootstrap Modal API
- Fix JFIELD_ORDERING_LABEL_ASC → JFIELD_ORDERING_ASC in profile filter
- Add COM_MOKOJOOMBACKUP_CONFIGURATION key for Options page title
- Change ntfy default server to ntfy.mokoconsulting.tech
- Add profile ID to dropdown labels across backups, dashboard, cpanel module
- Add error handling to MokoRestore post() and runPreflight() to prevent UI stalling
- Remove outdated SSH auth pattern references from field descriptions
2026-06-25 08:35:40 -05:00
Jonathan Miller ff7418721d fix: review findings — key desc, missing changelog, [HOST] domain resolution
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
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 4s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 7s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 11s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 14s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
- Language: "encrypted" → "base64-encoded" for SSH key description
- CHANGELOG: added 3 missing bug fix entries (fields_values scope, CSRF
  token on Run Backup, SFTP showon/required)
- [HOST] placeholder: resolve domain from Joomla live_site config when
  HTTP_HOST is unavailable (CLI), instead of falling back to system
  hostname (joomla.invalid). Applied to both PlaceholderResolver and
  FolderPickerField.
2026-06-23 17:20:05 -05:00
Jonathan Miller dae30161ae feat: multi-remote storage — multiple destinations per profile (#97)
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 8s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 5s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 41s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 22s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
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
2026-06-23 16:53:08 -05:00
Jonathan Miller 8e5913d706 fix: enforce correct ACL permissions across all controllers (#137)
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 7s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 11s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Secret Scan (pull_request) Successful in 10s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 33s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 5s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 54s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
13 ACL fixes across 5 files:
- BackupsController: purge() uses backup.purge (was core.delete)
- SnapshotsController: delete() uses snapshot.manage (was core.delete)
- AjaxController: restoreInit/Step use backup.restore (was backup.run),
  browseArchive uses backup.browse (was core.manage),
  countPurge uses backup.purge (was core.delete),
  compareBackups uses backup.compare (was core.manage)
- API SnapshotsController: displayList/download use snapshot.manage
  (was core.manage)
- HtmlView: verify gated by core.manage, compare by backup.compare,
  purge separated from delete with backup.purge

Closes #137
2026-06-23 14:16:54 -05:00
Jonathan Miller 899a33bc58 feat: purge, CPanel module, 7z format, SFTP browser (#119, #105, #122, #98)
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Failing after 10s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 3s
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 4s
Universal: PR Check / Validate PR (pull_request) Failing after 3s
Universal: PR Check / Secret Scan (pull_request) Successful in 5s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 13s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 4m50s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
#119: Manual purge — toolbar button opens modal with date picker,
AJAX count preview, confirmation before bulk delete.

#105: CPanel admin dashboard module (mod_mokosuitebackup_cpanel) —
backup status, quick action buttons per profile, next scheduled,
stats, and quick links. Registered in package manifest.

#122: 7z archive format via system 7za/7z CLI binary with optional
password encryption. New SevenZipArchiver engine class.

#98: SFTP remote file browser — custom SftpPathField with "Browse
Remote" button, modal directory listing via SSH ls, click to navigate,
double-click to select.

Also: CHANGELOG updated, wiki Home updated, #121 verified (encryption
field already visible in Archive Settings tab).

Closes #119, closes #105, closes #122, closes #98, closes #121
2026-06-23 13:05:42 -05:00
Jonathan Miller 7e404b0246 fix: MokoRestore review findings + update README
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
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 4s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 13s
Universal: PR Check / Secret Scan (pull_request) Successful in 10s
Universal: Build & Release / Promote to RC (pull_request) Failing after 10s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 42s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 50s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Review fixes:
- data-only mode uses REPLACE INTO (was INSERT INTO, broke on dupes)
- temporary password is random 16-char hex (was hardcoded "changeme")

README rewritten with all features: snapshots, SFTP, MokoRestore
wizard, sanitization, dashboard, CLI, API.
2026-06-23 12:49:17 -05:00
Jonathan Miller 0dc0eb1bef feat: MokoRestore post-restore resets + per-table conflict resolution (#131, #132)
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Universal: Build & Release / Promote to RC (pull_request) Failing after 13s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 53s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 1m0s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
#131: Post-restore actions step in MokoRestore wizard:
- Reset all passwords to temporary "changeme" with requireReset flag
- Reset article hit counters to zero
- Clear content versions (#__history)
- Clear sessions (#__session)
- Clear cache tables and filesystem cache
- Auto-detect sanitized password hashes and prompt for reset

#132: Per-table conflict resolution during database import:
- New "Tables" step shows all tables from database.sql
- Per-table dropdown: Replace / Skip / Merge / Data Only
- Preset buttons: All Replace, All Skip, Everything except users
- Skip mode skips all statements for that table
- Merge mode uses INSERT IGNORE instead of INSERT INTO
- Data Only skips DROP/CREATE, inserts data only

Wizard now has 9 steps: Pre-check → Extract → Tables → Database →
Config → Admin → Post-Restore → Provisioning → Complete

Closes #131, closes #132
2026-06-23 12:33:18 -05:00
Jonathan Miller 5698c074da feat: data sanitization — passwords, emails, sessions (#129)
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Secret Scan (pull_request) Successful in 7s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 9s
Branch Cleanup / Delete merged branch (pull_request) Successful in 3s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 6s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 54s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 33s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
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)
2026-06-23 12:06:19 -05:00
Jonathan Miller 12c832d7fe feat: comprehensive help modal for backup directory field
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 3s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 11s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Universal: PR Check / Secret Scan (pull_request) Successful in 4s
Universal: Build & Release / Promote to RC (pull_request) Failing after 8s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 43s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Expanded the help modal with:
- Absolute paths: starts with / or drive letter, used as-is
- Relative paths: ./backups, ../backups, ../../backups with table
  showing URL-style conventions and resolved examples
- Placeholder paths: detailed descriptions of each placeholder
  with current server values
- Recommended configurations table: single site, multi-site,
  date-organized, per-profile, shared hosting
- Security warnings for web-root-accessible paths
- Help button uses JS click handler with Bootstrap 5 fallback
  (fixes non-working tooltip icon)
2026-06-23 11:58:20 -05:00
Jonathan Miller de632e9c5c feat: uppercase all placeholders + EXAMPLE prefix in resolution display
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
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 20s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 10s
Universal: PR Check / Validate PR (pull_request) Failing after 11s
Universal: PR Check / Secret Scan (pull_request) Successful in 12s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 14s
Universal: Build & Release / Promote to RC (pull_request) Failing after 10s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 47s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
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.
2026-06-23 11:52:52 -05:00
Jonathan Miller e415e701cd fix: resolve [site_name] and other placeholders in checkDir AJAX
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 3s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 15s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 8s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Universal: PR Check / Secret Scan (pull_request) Successful in 9s
Universal: Build & Release / Promote to RC (pull_request) Failing after 15s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 46s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
BackupDirectory::resolve() only handles [HOME] and [DEFAULT_DIR].
The checkDir AJAX endpoint now uses PlaceholderResolver to also
resolve [site_name], [host], [profile_id], [date], etc. before
checking if the directory exists. This makes the "Resolves to"
display accurate for all placeholder types.
2026-06-23 11:49:21 -05:00
Jonathan Miller fc41e1801a fix: placeholder resolution display + CSRF token on Run Backup button
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 26s
FolderPickerField: shows resolved placeholder values below input as
badges (e.g. [HOME]=/home/user, [host]=example.com), plus full
resolved path. Updates live as user types.

BackupsController::start(): accept CSRF token from both GET and POST
so the "Run Backup Now" link button on profile edit works without
triggering "security token did not match" error.
2026-06-23 11:35:48 -05:00
Jonathan Miller e62dba8f40 feat: standalone restore script — separate file that scans for ZIPs (#107)
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 7s
Universal: PR Check / Branch Policy (pull_request) Failing after 3s
Universal: PR Check / Secret Scan (pull_request) Successful in 9s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Access control (pull_request) Successful in 3s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 16s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 53s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 31s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 8m41s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
New MokoRestore mode: 'standalone' generates restore.php as a separate
file that scans its directory for ZIP backup archives and lets the user
choose which one to restore. Unlike 'wrapped' mode which bundles
restore.php inside the backup ZIP, standalone mode keeps both files
separate — ideal for remote servers where you SCP the backup.

Changes:
- MokoRestore::generateStandalone() — writes restore.php with ZIP scanner
- Profile form: include_mokorestore now a dropdown (none/wrapped/standalone)
- BackupEngine: standalone mode writes restore.php + uploads to remote
- Restore script uses safe DOM methods (no innerHTML with user data)

Closes #107
2026-06-23 11:20:23 -05:00
Jonathan Miller 64ffbb9d61 feat: profiles UI, snapshot detail, progress warning, action logs (#100, #104, #108, #110)
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 4s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 10s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 39s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 18s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 4m10s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
#100: Run Backup button on profiles list (per-row) and edit toolbar,
backup count badge linking to filtered backups view, View Backups
toolbar button on profile edit.

#101: Profile → filtered backup list link (included in #100).

#104: Snapshot browse modal now shows tabbed view (Articles,
Categories, Modules) with item counts. AjaxController returns
all content types. Categories show indented hierarchy.

#108: "Do not navigate away or close this window" warning banner
added to both backup and restore progress modals.

#110: Joomla Action Logs integration — RestoreEngine, SnapshotEngine,
and SnapshotRestoreEngine now dispatch events that the actionlog
plugin logs to #__action_logs.

Closes #100, closes #101, closes #104, closes #108, closes #110
2026-06-23 11:03:13 -05:00
Jonathan Miller 456e744d81 feat: clickable placeholder pills for backup dir and archive name fields
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Branch Policy (pull_request) Failing after 4s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 12s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Secret Scan (pull_request) Successful in 10s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 14s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 26s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 9m59s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Both Backup Directory and Archive Name Format fields now show clickable
placeholder tags below the input. Clicking a tag inserts the placeholder
at the current cursor position using selectionStart/End.

- FolderPickerField: pills for [HOME], [DEFAULT_DIR], [host], [site_name],
  [date], [profile_id], [profile_name], [type]
- PlaceholderTextField: new custom field type used by archive_name_format,
  configurable placeholders via XML attribute
- Cursor position preserved after insert, input event dispatched for
  live status updates
2026-06-23 10:47:45 -05:00
Jonathan Miller d6b3e8cff0 feat: SFTP key file upload, auth type dropdown, security hardening
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 6s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 23s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Universal: PR Check / Secret Scan (pull_request) Successful in 9s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 13s
Universal: Build & Release / Promote to RC (pull_request) Failing after 11s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 34s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
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
2026-06-23 08:51:49 -05:00
Jonathan Miller f66100f74f feat: SFTP remote storage with key file auth + CLI restore options
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 7s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 13s
Universal: PR Check / Secret Scan (pull_request) Successful in 9s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 9s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 1s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 34s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 3s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 27s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
SFTP support:
- SftpUploader uses system scp/ssh binaries with key file auth
- Private key stored as MEDIUMTEXT in profile table (sftp_key_data)
- Key written to temp file (0600) at upload time, deleted after
- Profile form: host, port, username, password, key textarea,
  passphrase, remote path — all with showon="remote_storage:sftp"
- SQL migration for 7 new SFTP columns
- Wired into BackupEngine, SteppedBackupEngine, PreflightCheck
- API credential masking includes SFTP fields

CLI restore options:
- --files-only: restore files without touching database
- --db-only: restore database without touching files
- --no-preserve-config: overwrite configuration.php
- --password: decryption password for encrypted archives
2026-06-23 08:21:10 -05:00
Jonathan Miller ee21f7a373 feat: dashboard snapshot widget, backup trend chart, storage breakdown (#61)
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 7s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 15s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 42s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 25s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 3m18s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Add three new dashboard widgets:
- Snapshot widget: latest snapshot info, type badges, item counts,
  link to snapshots view, total count
- Backup trend: CSS bar chart showing daily backup sizes over 30 days,
  red bars for days with failures, tooltips with details
- Storage breakdown: horizontal bars showing space used per profile
  with color coding and backup counts

Closes #61
2026-06-23 07:22:04 -05:00