Compare commits

...

8 Commits

Author SHA1 Message Date
mokogitea-actions[bot] 120cca5594 chore(version): pre-release bump to 02.64.04-dev [skip ci] 2026-07-15 03:33:52 +00:00
jmiller 536a9b0c78 Merge pull request 'feat: lock admin UI while a backup is running' (#277) from feat/lock-admin-during-backup into dev
Universal: Auto Version Bump / Version Bump (push) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 49s
2026-07-15 03:33:15 +00:00
jmiller 257003fb74 feat: lock the admin UI while a backup is actively running
Generic: Project CI / Lint & Validate (pull_request) Successful in 13s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Secret Scan (pull_request) Successful in 7s
Universal: PR Check / Validate PR (pull_request) Failing after 9s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 10s
Branch Cleanup / Delete merged branch (pull_request) Successful in 3s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
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
New "Lock admin during backup" option (on by default). While a stepped
backup is running, every admin page load except the full-screen backup
screen and the login page is replaced by a "backup in progress"
interstitial that auto-refreshes, so no other window can navigate or change
anything mid-backup.

Detection uses the stepped-backup session file heartbeat
(JPATH_ROOT/tmp/mokosuitebackup-sessions/mb_*.json is rewritten every step
and deleted on completion) — a file with an mtime within ~45s means a
backup is live. A crashed/abandoned backup goes stale and releases the lock
on its own, so the admin is never permanently locked. Implemented in the
system plugin's onAfterRoute (admin, HTML page loads only; AJAX/assets and
the backup's own step requests are unaffected).

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-14 22:33:07 -05:00
mokogitea-actions[bot] 254bf18c45 chore(version): pre-release bump to 02.64.03-dev [skip ci] 2026-07-15 03:17:23 +00:00
jmiller 6fc57eb163 Merge pull request 'fix(task): scheduled-task fieldset label renders raw string' (#276) from fix/scheduler-fieldset-label into dev
Universal: Auto Version Bump / Version Bump (push) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 24s
2026-07-15 03:16:58 +00:00
jmiller 9e7ebfe89e fix(task): scheduled-task fieldset label renders raw string
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Generic: Project CI / Lint & Validate (pull_request) Successful in 11s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 10s
Universal: PR Check / Validate PR (pull_request) Failing after 11s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 10s
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
com_scheduler's task edit view renders a params fieldset legend as
Text::_($fieldset->label ?: 'COM_SCHEDULER_FIELDSET_'.$name). The
run_profile / run_snapshot fieldsets had no `label` attribute, so it fell
back to an undefined generated key (COM_SCHEDULER_RUN_PROFILE_FIELDSET_LABEL)
and showed the raw constant. Give each fieldset a label attribute pointing
to a plugin-defined constant, and define them in en-GB/en-US.

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-14 22:16:47 -05:00
mokogitea-actions[bot] 8df658c5da chore(version): pre-release bump to 02.64.02-dev [skip ci] 2026-07-14 20:40:49 +00:00
gitea-actions[bot] 6c79443be0 chore(version): auto-bump patch 02.64.01-dev [skip ci] 2026-07-14 20:40:32 +00:00
27 changed files with 137 additions and 15 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION
# DEFGROUP: MokoGitea.Workflow
# INGROUP: mokocli.Automation
# VERSION: 02.64.00
# VERSION: 02.64.04
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
+1 -1
View File
@@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla
INGROUP: Template-Joomla.Documentation
REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla
PATH: /SECURITY.md
VERSION: 02.64.00
VERSION: 02.64.04
BRIEF: Security vulnerability reporting and handling policy
-->
@@ -162,6 +162,17 @@
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field
name="lock_admin_during_backup"
type="radio"
label="COM_MOKOJOOMBACKUP_CONFIG_LOCK_ADMIN"
description="COM_MOKOJOOMBACKUP_CONFIG_LOCK_ADMIN_DESC"
default="1"
class="btn-group"
>
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
</fieldset>
<fieldset name="cleanup" label="COM_MOKOJOOMBACKUP_CONFIG_CLEANUP">
@@ -335,6 +335,8 @@ COM_MOKOJOOMBACKUP_CONFIG_PREUPDATE_AUTO_INTERCEPT="Auto-run on Update click"
COM_MOKOJOOMBACKUP_CONFIG_PREUPDATE_AUTO_INTERCEPT_DESC="When enabled, clicking the Update button on the Joomla Update or Extensions update page first pops the backup progress modal, runs the pre-update backup, then automatically continues the update. When disabled, a notice with a manual 'Back up now' button is shown instead."
COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UNINSTALL="Backup Before Extension Uninstall"
COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UNINSTALL_DESC="Automatically run a full backup before any extension is uninstalled. Uses the default profile. Throttled to once per 10 minutes."
COM_MOKOJOOMBACKUP_CONFIG_LOCK_ADMIN="Lock admin during backup"
COM_MOKOJOOMBACKUP_CONFIG_LOCK_ADMIN_DESC="While a backup is actively running, show a \"backup in progress\" screen on all other admin pages until it finishes, so nothing changes mid-backup. The backup screen itself and the login page stay accessible. Auto-releases if a backup stops responding."
; Full-screen backup progress view (runbackup)
COM_MOKOJOOMBACKUP_RUNBACKUP_TITLE="Backing Up"
@@ -57,6 +57,8 @@ COM_MOKOJOOMBACKUP_CONFIG_PREUPDATE_AUTO_INTERCEPT="Auto-run on Update click"
COM_MOKOJOOMBACKUP_CONFIG_PREUPDATE_AUTO_INTERCEPT_DESC="When enabled, clicking the Update button on the Joomla Update or Extensions update page first pops the backup progress modal, runs the pre-update backup, then automatically continues the update. When disabled, a notice with a manual 'Back up now' button is shown instead."
COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UNINSTALL="Backup Before Extension Uninstall"
COM_MOKOJOOMBACKUP_CONFIG_BACKUP_BEFORE_UNINSTALL_DESC="Automatically run a full backup before any extension is uninstalled. Uses the default profile. Throttled to once per 10 minutes."
COM_MOKOJOOMBACKUP_CONFIG_LOCK_ADMIN="Lock admin during backup"
COM_MOKOJOOMBACKUP_CONFIG_LOCK_ADMIN_DESC="While a backup is actively running, show a \"backup in progress\" screen on all other admin pages until it finishes, so nothing changes mid-backup. The backup screen itself and the login page stay accessible. Auto-releases if a backup stops responding."
; Full-screen backup progress view (runbackup)
COM_MOKOJOOMBACKUP_RUNBACKUP_TITLE="Backing Up"
@@ -17,7 +17,7 @@
display label there.
-->
<name>MokoSuiteBackup</name>
<version>02.64.00</version>
<version>02.64.04</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -0,0 +1 @@
/* 02.64.01 — no schema changes */
@@ -0,0 +1 @@
/* 02.64.02 — no schema changes */
@@ -0,0 +1 @@
/* 02.64.03 — no schema changes */
@@ -0,0 +1 @@
/* 02.64.04 — no schema changes */
@@ -8,7 +8,7 @@
-->
<extension type="module" client="administrator" method="upgrade">
<name>Module - MokoSuiteBackup - cPanel</name>
<version>02.64.00</version>
<version>02.64.04</version>
<creationDate>2026-06-23</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -7,7 +7,7 @@
-->
<extension type="plugin" group="actionlog" method="upgrade">
<name>Action Log - MokoSuiteBackup</name>
<version>02.64.00</version>
<version>02.64.04</version>
<creationDate>2026-06-04</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -7,7 +7,7 @@
-->
<extension type="plugin" group="console" method="upgrade">
<name>Console - MokoSuiteBackup</name>
<version>02.64.00</version>
<version>02.64.04</version>
<creationDate>2026-06-04</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -7,7 +7,7 @@
-->
<extension type="plugin" group="content" method="upgrade">
<name>Content - MokoSuiteBackup</name>
<version>02.64.00</version>
<version>02.64.04</version>
<creationDate>2026-06-04</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="quickicon" method="upgrade">
<name>Quick Icon - MokoSuiteBackup</name>
<version>02.64.00</version>
<version>02.64.04</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -15,3 +15,5 @@ PLG_SYSTEM_MOKOJOOMBACKUP_BACKUP_COMPLETE="Backup complete — safe to update."
PLG_SYSTEM_MOKOJOOMBACKUP_BACKUP_FAILED="Backup failed"
PLG_SYSTEM_MOKOJOOMBACKUP_BACKUP_STARTING="Starting backup…"
PLG_SYSTEM_MOKOSUITEBACKUP_UPDATE_RUNNING="Backup complete. The update is now running — please wait and do not close this window…"
PLG_SYSTEM_MOKOSUITEBACKUP_LOCK_TITLE="Backup in progress"
PLG_SYSTEM_MOKOSUITEBACKUP_LOCK_MESSAGE="A MokoSuiteBackup backup is currently running in another window. The administrator area is locked until it finishes — this page will refresh automatically. Please do not make changes while the backup runs."
@@ -15,3 +15,5 @@ PLG_SYSTEM_MOKOJOOMBACKUP_BACKUP_COMPLETE="Backup complete — safe to update."
PLG_SYSTEM_MOKOJOOMBACKUP_BACKUP_FAILED="Backup failed"
PLG_SYSTEM_MOKOJOOMBACKUP_BACKUP_STARTING="Starting backup…"
PLG_SYSTEM_MOKOSUITEBACKUP_UPDATE_RUNNING="Backup complete. The update is now running — please wait and do not close this window…"
PLG_SYSTEM_MOKOSUITEBACKUP_LOCK_TITLE="Backup in progress"
PLG_SYSTEM_MOKOSUITEBACKUP_LOCK_MESSAGE="A MokoSuiteBackup backup is currently running in another window. The administrator area is locked until it finishes — this page will refresh automatically. Please do not make changes while the backup runs."
@@ -7,7 +7,7 @@
-->
<extension type="plugin" group="system" method="upgrade">
<name>System - MokoSuiteBackup</name>
<version>02.64.00</version>
<version>02.64.04</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -133,6 +133,10 @@ final class MokoSuiteBackup extends CMSPlugin implements SubscriberInterface
return;
}
// While a backup is actively running, lock the admin UI so no other
// window/page can navigate or change anything mid-backup. May close().
$this->maybeLockAdminDuringBackup();
// Pre-update: send Joomla core-update installs through the full-screen
// backup page BEFORE the update runs (may not return from here).
$this->maybeRedirectForUpdate();
@@ -155,6 +159,97 @@ final class MokoSuiteBackup extends CMSPlugin implements SubscriberInterface
$this->cleanupOldSnapshots();
}
/**
* Detect whether a stepped backup is actively running right now.
*
* The stepped-backup engine rewrites its session JSON file
* (JPATH_ROOT/tmp/mokosuitebackup-sessions/mb_*.json) on every step and
* deletes it on completion, so a file whose mtime is within the last ~45s
* is a reliable, cross-session heartbeat. A crashed/abandoned backup goes
* stale and stops locking on its own — the admin is never permanently locked.
*/
private function backupIsActive(): bool
{
$dir = JPATH_ROOT . '/tmp/mokosuitebackup-sessions';
if (!is_dir($dir)) {
return false;
}
$cutoff = time() - 45;
foreach (glob($dir . '/mb_*.json') ?: [] as $file) {
if (@filemtime($file) >= $cutoff) {
return true;
}
}
return false;
}
/**
* While a backup is actively running, lock the admin UI: every admin page
* load (except the full-screen backup screen and the login/logout page)
* gets a "backup in progress" interstitial that auto-refreshes, so no other
* window can navigate or change anything mid-backup. Gated by the
* lock_admin_during_backup option; auto-released when the backup ends.
*/
private function maybeLockAdminDuringBackup(): void
{
if (!(int) ComponentHelper::getParams('com_mokosuitebackup')->get('lock_admin_during_backup', 1)) {
return;
}
$app = $this->getApplication();
$input = $app->getInput();
$option = $input->getCmd('option', '');
$view = $input->getCmd('view', '');
// Never lock the full-screen backup screen (it IS the backup) nor login/logout.
if (($option === 'com_mokosuitebackup' && $view === 'runbackup') || $option === 'com_login') {
return;
}
if (!$this->backupIsActive()) {
return;
}
$this->renderBackupLockPage();
$app->close();
}
/**
* Emit a self-contained "backup in progress" interstitial that refreshes
* itself (meta refresh) until the backup finishes and the lock releases.
*/
private function renderBackupLockPage(): void
{
$app = $this->getApplication();
$title = Text::_('PLG_SYSTEM_MOKOSUITEBACKUP_LOCK_TITLE');
$message = Text::_('PLG_SYSTEM_MOKOSUITEBACKUP_LOCK_MESSAGE');
$t = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
$m = htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
$html = '<!doctype html><html lang="en"><head><meta charset="utf-8">'
. '<meta name="viewport" content="width=device-width, initial-scale=1">'
. '<meta http-equiv="refresh" content="8">'
. '<title>' . $t . '</title><style>'
. 'html,body{height:100%;margin:0}'
. 'body{background:#000;color:#fff;font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;'
. 'display:flex;align-items:center;justify-content:center;text-align:center}'
. '.w{max-width:34rem;padding:2rem}'
. '.s{width:3rem;height:3rem;margin:0 auto 1.5rem;border:.35rem solid rgba(255,255,255,.2);'
. 'border-top-color:#fff;border-radius:50%;animation:sp 1s linear infinite}'
. 'h1{font-size:1.5rem;margin:.5rem 0}p{opacity:.85;line-height:1.55}'
. '@keyframes sp{to{transform:rotate(360deg)}}'
. '</style></head><body><div class="w"><div class="s"></div>'
. '<h1>' . $t . '</h1><p>' . $m . '</p></div></body></html>';
$app->setHeader('Content-Type', 'text/html; charset=utf-8', true);
$app->sendHeaders();
echo $html;
}
/**
* Inject the client-side interceptor on the Extensions → Update and
* Extensions → Manage pages, so an EXTENSION update / uninstall also runs
@@ -5,7 +5,7 @@
* "MokoSuiteBackup: Run Backup Profile" task.
-->
<form>
<fieldset name="run_profile">
<fieldset name="run_profile" label="PLG_TASK_MOKOJOOMBACKUP_FIELDSET_RUN_PROFILE">
<field
name="profile_id"
type="sql"
@@ -5,7 +5,7 @@
* "MokoSuiteBackup: Run Content Snapshot" task.
-->
<form>
<fieldset name="run_snapshot">
<fieldset name="run_snapshot" label="PLG_TASK_MOKOJOOMBACKUP_FIELDSET_RUN_SNAPSHOT">
<field name="content_types" type="checkboxes" label="Content Types" default="articles,categories,modules">
<option value="articles">Articles</option>
<option value="categories">Categories</option>
@@ -8,5 +8,7 @@ PLG_TASK_MOKOJOOMBACKUP_TASK_RUN_PROFILE_DESC="Run a MokoSuiteBackup backup usin
; Task form fields
PLG_TASK_MOKOJOOMBACKUP_FIELD_PROFILE="Backup Profile"
PLG_TASK_MOKOJOOMBACKUP_FIELDSET_RUN_PROFILE="Backup Profile"
PLG_TASK_MOKOJOOMBACKUP_FIELDSET_RUN_SNAPSHOT="Content Snapshot"
PLG_TASK_MOKOJOOMBACKUP_FIELD_PROFILE_DESC="Select which backup profile to run. Each profile defines backup type (full/database/files), exclusion filters, and storage settings."
PLG_TASK_MOKOJOOMBACKUP_SELECT_PROFILE="- Select Profile -"
@@ -4,5 +4,7 @@ PLG_TASK_MOKOJOOMBACKUP_DESCRIPTION="Scheduled task plugin for MokoSuiteBackup.
PLG_TASK_MOKOJOOMBACKUP_TASK_RUN_PROFILE_TITLE="MokoSuiteBackup: Run Backup Profile"
PLG_TASK_MOKOJOOMBACKUP_TASK_RUN_PROFILE_DESC="Run a MokoSuiteBackup backup using the selected profile. Create multiple tasks with different profiles for different backup schedules."
PLG_TASK_MOKOJOOMBACKUP_FIELD_PROFILE="Backup Profile"
PLG_TASK_MOKOJOOMBACKUP_FIELDSET_RUN_PROFILE="Backup Profile"
PLG_TASK_MOKOJOOMBACKUP_FIELDSET_RUN_SNAPSHOT="Content Snapshot"
PLG_TASK_MOKOJOOMBACKUP_FIELD_PROFILE_DESC="Select which backup profile to run."
PLG_TASK_MOKOJOOMBACKUP_SELECT_PROFILE="- Select Profile -"
@@ -7,7 +7,7 @@
-->
<extension type="plugin" group="task" method="upgrade">
<name>Task - MokoSuiteBackup</name>
<version>02.64.00</version>
<version>02.64.04</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -7,7 +7,7 @@
-->
<extension type="plugin" group="webservices" method="upgrade">
<name>Web Services - MokoSuiteBackup</name>
<version>02.64.00</version>
<version>02.64.04</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
+1 -1
View File
@@ -8,7 +8,7 @@
<extension type="package" method="upgrade">
<name>Package - MokoSuiteBackup</name>
<packagename>mokosuitebackup</packagename>
<version>02.64.00</version>
<version>02.64.04</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>