Compare commits

...

4 Commits

Author SHA1 Message Date
gitea-actions[bot] 950f4bb58c chore(version): pre-release bump to 01.43.07-dev [skip ci] 2026-06-25 14:56:27 +00:00
jmiller dbbacc98a7 chore: retrigger CI
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 10s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 15s
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
2026-06-25 09:56:08 -05:00
gitea-actions[bot] 6b42fefe09 chore(version): pre-release bump to 01.43.06-dev [skip ci] 2026-06-25 14:51:17 +00:00
jmiller a3dbd1f89a 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 15s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 27s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Universal: PR Check / Secret Scan (pull_request) Successful in 10s
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
- 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 09:50:44 -05:00
12 changed files with 145 additions and 70 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Automation
# VERSION: 01.43.02
# VERSION: 01.43.07
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
@@ -7,7 +7,7 @@
-->
<extension type="component" method="upgrade">
<name>MokoSuiteBackup</name>
<version>01.43.02</version>
<version>01.43.07</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -165,7 +165,38 @@ SCANNER;
$php
);
/* Modify the pre-checks to use getSelectedBackupFile() */
/* Replace the backup archive check with one that scans for ZIPs
(must run BEFORE the blanket file_exists replacement below) */
$php = str_replace(
<<<'ORIG'
$checks[] = [
'label' => 'Backup Archive',
'value' => file_exists(BACKUP_FILE) ? number_format(filesize(BACKUP_FILE) / 1048576, 2) . ' MB' : 'Not found',
'ok' => file_exists(BACKUP_FILE),
'hint' => 'site-backup.zip must be in the same directory as restore.php',
];
ORIG,
<<<'REPL'
$availableBackups = scanForBackups();
$backupCount = count($availableBackups);
$selectedFile = getSelectedBackupFile();
if ($selectedFile && file_exists($selectedFile)) {
$archiveValue = basename($selectedFile) . ' (' . number_format(filesize($selectedFile) / 1048576, 2) . ' MB)';
} elseif ($backupCount > 0) {
$archiveValue = $backupCount . ' ZIP file(s) found';
} else {
$archiveValue = 'No ZIP files found';
}
$checks[] = [
'label' => 'Backup Archive',
'value' => $archiveValue,
'ok' => $backupCount > 0,
'hint' => 'Place one or more backup ZIP files in the same directory as restore.php',
];
REPL
);
/* Modify remaining pre-checks to use getSelectedBackupFile() */
$php = str_replace(
"file_exists(BACKUP_FILE)",
"(getSelectedBackupFile() !== '' || file_exists(BACKUP_FILE))",
@@ -174,65 +205,83 @@ SCANNER;
$html = self::generateFrontend();
/* Add backup file selector to the frontend before the extract step */
/* Inject backup file selector into the extract step (panel2) */
$selectorHtml = <<<'SELECTOR'
<!-- Backup File Selector (standalone mode) -->
<div id="mr-step-select" class="mr-step" style="display:none;">
<h2 class="mr-step-title">Select Backup File</h2>
<p class="mr-desc">Choose which backup archive to restore from.</p>
<div id="mr-backup-list"></div>
<input type="hidden" name="backup_file" id="mr-backup-file" value="">
</div>
<script>
(function() {
var backups = <?php echo json_encode(scanForBackups()); ?>;
var list = document.getElementById('mr-backup-list');
var hiddenInput = document.getElementById('mr-backup-file');
<div id="mr-backup-selector" class="mb-3">
<label class="mr-field-label" style="font-weight:600;margin-bottom:8px;display:block;">Backup Archive</label>
<div id="mr-backup-list"></div>
<input type="hidden" name="backup_file" id="mr-backup-file" value="">
</div>
<script>
(function() {
var backups = <?php echo json_encode(scanForBackups()); ?>;
var list = document.getElementById('mr-backup-list');
var hiddenInput = document.getElementById('mr-backup-file');
if (backups.length === 0) {
var alert = document.createElement('div');
alert.className = 'mr-alert mr-alert-danger';
alert.textContent = 'No ZIP files found in this directory. Upload a backup archive first.';
list.appendChild(alert);
} else if (backups.length === 1) {
hiddenInput.value = backups[0].name;
var found = document.createElement('div');
found.className = 'mr-alert mr-alert-success';
var strong = document.createElement('strong');
strong.textContent = backups[0].name;
found.appendChild(document.createTextNode('Found: '));
found.appendChild(strong);
found.appendChild(document.createTextNode(' (' + (backups[0].size / 1048576).toFixed(1) + ' MB)'));
list.appendChild(found);
} else {
var group = document.createElement('div');
group.className = 'mr-field-group';
backups.forEach(function(b) {
var label = document.createElement('label');
label.style.cssText = 'display:block; padding:8px; margin:4px 0; border:1px solid #ddd; border-radius:4px; cursor:pointer;';
var radio = document.createElement('input');
radio.type = 'radio';
radio.name = 'backup_choice';
radio.value = b.name;
radio.style.marginRight = '8px';
radio.addEventListener('change', function() { hiddenInput.value = this.value; });
label.appendChild(radio);
var nameStrong = document.createElement('strong');
nameStrong.textContent = b.name;
label.appendChild(nameStrong);
label.appendChild(document.createTextNode(' \u2014 ' + (b.size / 1048576).toFixed(1) + ' MB \u2014 ' + b.date));
group.appendChild(label);
});
list.appendChild(group);
}
})();
</script>
if (backups.length === 0) {
var alert = document.createElement('div');
alert.style.cssText = 'padding:12px;background:#fef2f2;border:1px solid #fecaca;border-radius:6px;color:#dc2626;';
alert.textContent = 'No ZIP files found in this directory. Upload a backup archive first.';
list.appendChild(alert);
} else if (backups.length === 1) {
hiddenInput.value = backups[0].name;
var found = document.createElement('div');
found.style.cssText = 'padding:12px;background:#dcfce7;border:1px solid #bbf7d0;border-radius:6px;color:#16a34a;';
var strong = document.createElement('strong');
strong.textContent = backups[0].name;
found.appendChild(document.createTextNode('Found: '));
found.appendChild(strong);
found.appendChild(document.createTextNode(' (' + (backups[0].size / 1048576).toFixed(1) + ' MB)'));
list.appendChild(found);
} else {
var hint = document.createElement('div');
hint.style.cssText = 'padding:8px 12px;background:#eff6ff;border:1px solid #bfdbfe;border-radius:6px;color:#1d4ed8;margin-bottom:8px;font-size:0.9em;';
hint.textContent = 'Multiple backup archives found \u2014 select which one to restore:';
list.appendChild(hint);
backups.forEach(function(b, i) {
var label = document.createElement('label');
label.style.cssText = 'display:flex;align-items:center;padding:10px 12px;margin:4px 0;border:1px solid #e2e8f0;border-radius:6px;cursor:pointer;transition:background 0.15s;';
label.onmouseover = function() { this.style.background = '#f8fafc'; };
label.onmouseout = function() { this.style.background = ''; };
var radio = document.createElement('input');
radio.type = 'radio';
radio.name = 'backup_choice';
radio.value = b.name;
radio.style.marginRight = '10px';
if (i === 0) { radio.checked = true; hiddenInput.value = b.name; }
radio.addEventListener('change', function() { hiddenInput.value = this.value; });
label.appendChild(radio);
var info = document.createElement('div');
var nameStrong = document.createElement('strong');
nameStrong.textContent = b.name;
info.appendChild(nameStrong);
var meta = document.createElement('div');
meta.style.cssText = 'font-size:0.85em;color:#64748b;margin-top:2px;';
meta.textContent = (b.size / 1048576).toFixed(1) + ' MB \u2014 ' + b.date;
info.appendChild(meta);
label.appendChild(info);
list.appendChild(label);
});
}
})();
</script>
SELECTOR;
/* Insert the selector before the extract step in the HTML */
/* Insert the selector into the extract panel */
$html = str_replace(
'<!-- Step: Extract -->',
$selectorHtml . "\n<!-- Step: Extract -->",
'<p class="mr-desc">Extract site-backup.zip into the current directory.</p>',
'<p class="mr-desc">Select a backup archive and extract it into the current directory.</p>' . "\n" . $selectorHtml,
$html
);
/* Pass selected backup file to the extract action */
$html = str_replace(
"const r = await post('extract', pw ? { archive_password: pw } : {});",
"var extraParams = {};\n" .
" if (pw) extraParams.archive_password = pw;\n" .
" var sel = document.getElementById('mr-backup-file');\n" .
" if (sel && sel.value) extraParams.backup_file = sel.value;\n" .
" const r = await post('extract', extraParams);",
$html
);
@@ -462,15 +511,31 @@ function actionPreflight(): array
'hint' => 'Informational',
];
$joomlaExists = file_exists(RESTORE_DIR . '/configuration.php')
|| file_exists(RESTORE_DIR . '/libraries/src/Version.php');
$checks[] = [
'label' => 'Existing Installation',
'value' => $joomlaExists ? 'Joomla detected' : 'Clean directory',
'ok' => true,
'warn' => $joomlaExists,
'hint' => $joomlaExists
? 'WARNING: A Joomla installation already exists in this directory. Restoring will overwrite it.'
: 'No existing installation found — safe to proceed',
];
$allOk = true;
$warnings = [];
foreach ($checks as $c) {
if (!$c['ok']) {
$allOk = false;
}
if (!empty($c['warn'])) {
$warnings[] = $c['hint'];
}
}
return ['success' => $allOk, 'checks' => $checks];
return ['success' => $allOk, 'checks' => $checks, 'warnings' => $warnings];
}
function actionExtract(array $data): array
@@ -1425,6 +1490,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica N
.mr-checks li:last-child{border-bottom:none}
.mr-check-icon{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:0.75rem;font-weight:700;flex-shrink:0}
.mr-check-ok{background:#dcfce7;color:#16a34a}
.mr-check-warn{background:#fef9c3;color:#a16207}
.mr-check-fail{background:#fef2f2;color:#dc2626}
.mr-check-info{background:#e0f2fe;color:#0284c7}
.mr-check-label{flex:1;font-weight:500}
@@ -1877,8 +1943,10 @@ async function runPreflight() {
r.checks.forEach(function(c) {
const li = document.createElement('li');
const icon = document.createElement('span');
icon.className = 'mr-check-icon ' + (c.ok ? 'mr-check-ok' : 'mr-check-fail');
icon.textContent = c.ok ? '\u2713' : '\u2717';
var iconClass = c.ok ? 'mr-check-ok' : 'mr-check-fail';
if (c.warn) iconClass = 'mr-check-warn';
icon.className = 'mr-check-icon ' + iconClass;
icon.textContent = c.warn ? '\u26a0' : (c.ok ? '\u2713' : '\u2717');
const label = document.createElement('span');
label.className = 'mr-check-label';
@@ -1891,9 +1959,16 @@ async function runPreflight() {
li.appendChild(icon);
li.appendChild(label);
li.appendChild(val);
if (c.warn && c.hint) {
var hint = document.createElement('div');
hint.style.cssText = 'font-size:0.85em;color:#a16207;margin-top:4px;padding:4px 8px;background:#fef9c3;border-radius:4px;';
hint.textContent = c.hint;
li.appendChild(hint);
}
list.appendChild(li);
log(' ' + (c.ok ? 'OK' : 'FAIL') + ': ' + c.label + ' = ' + c.value);
var logPrefix = c.warn ? 'WARN' : (c.ok ? 'OK' : 'FAIL');
log(' ' + logPrefix + ': ' + c.label + ' = ' + c.value);
});
setBtnLoading(btn, false);
@@ -8,7 +8,7 @@
-->
<extension type="module" client="administrator" method="upgrade">
<name>mod_mokosuitebackup_cpanel</name>
<version>01.43.02</version>
<version>01.43.07</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>01.43.02</version>
<version>01.43.07</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>01.43.02</version>
<version>01.43.07</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>01.43.02</version>
<version>01.43.07</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>01.43.02</version>
<version>01.43.07</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -7,7 +7,7 @@
-->
<extension type="plugin" group="system" method="upgrade">
<name>System - MokoSuiteBackup</name>
<version>01.43.02</version>
<version>01.43.07</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -7,7 +7,7 @@
-->
<extension type="plugin" group="task" method="upgrade">
<name>Task - MokoSuiteBackup</name>
<version>01.43.02</version>
<version>01.43.07</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>01.43.02</version>
<version>01.43.07</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>01.43.02</version>
<version>01.43.07</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>