Align backup and snapshot browsers
This commit is contained in:
@@ -1 +0,0 @@
|
||||
7f2bb1ba2b35b4c507680885a1284769fe4261c74d58c611c79d5eb10bf6782c backupper-2026.06.14-x86_64-1.txz
|
||||
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
||||
29655e71df818c71341058d031570f204d423eea63de4e199190c44b217343cc backupper-2026.06.14.1-x86_64-1.txz
|
||||
Vendored
+5
-2
@@ -2,13 +2,16 @@
|
||||
<!DOCTYPE PLUGIN [
|
||||
<!ENTITY name "backupper">
|
||||
<!ENTITY author "Backupper Project">
|
||||
<!ENTITY version "2026.06.14">
|
||||
<!ENTITY version "2026.06.14.1">
|
||||
<!ENTITY pluginURL "https://git.casaderoll.de/michael/BackUpper/raw/branch/main/dist/backupper.plg">
|
||||
<!ENTITY packageURL "https://git.casaderoll.de/michael/BackUpper/raw/branch/main/dist/backupper-&version;-x86_64-1.txz">
|
||||
<!ENTITY packageSHA256 "7f2bb1ba2b35b4c507680885a1284769fe4261c74d58c611c79d5eb10bf6782c">
|
||||
<!ENTITY packageSHA256 "29655e71df818c71341058d031570f204d423eea63de4e199190c44b217343cc">
|
||||
]>
|
||||
<PLUGIN name="&name;" author="&author;" version="&version;" pluginURL="&pluginURL;" min="7.0.0" support="https://git.casaderoll.de/michael/BackUpper/issues">
|
||||
<CHANGES>
|
||||
### 2026.06.14.1
|
||||
- Match the backup source browser layout and controls to the snapshot file browser.
|
||||
|
||||
### 2026.06.14
|
||||
- Switch to lexicographically sortable date versions for reliable Unraid update detection.
|
||||
- Add checkbox folder browsers for backup sources and persistent multi-selection for restores.
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
<!DOCTYPE PLUGIN [
|
||||
<!ENTITY name "backupper">
|
||||
<!ENTITY author "Backupper Project">
|
||||
<!ENTITY version "2026.06.14">
|
||||
<!ENTITY version "2026.06.14.1">
|
||||
<!ENTITY pluginURL "https://git.casaderoll.de/michael/BackUpper/raw/branch/main/dist/backupper.plg">
|
||||
<!ENTITY packageURL "https://git.casaderoll.de/michael/BackUpper/raw/branch/main/dist/backupper-&version;-x86_64-1.txz">
|
||||
<!ENTITY packageSHA256 "REPLACE_DURING_RELEASE">
|
||||
]>
|
||||
<PLUGIN name="&name;" author="&author;" version="&version;" pluginURL="&pluginURL;" min="7.0.0" support="https://git.casaderoll.de/michael/BackUpper/issues">
|
||||
<CHANGES>
|
||||
### 2026.06.14.1
|
||||
- Match the backup source browser layout and controls to the snapshot file browser.
|
||||
|
||||
### 2026.06.14
|
||||
- Switch to lexicographically sortable date versions for reliable Unraid update detection.
|
||||
- Add checkbox folder browsers for backup sources and persistent multi-selection for restores.
|
||||
|
||||
@@ -9,10 +9,10 @@ Tag="backup restic snapshots restore"
|
||||
<?php
|
||||
$pluginRoot = '/plugins/backupper';
|
||||
?>
|
||||
<link rel="stylesheet" href="<?= $pluginRoot ?>/assets/backupper.css?v=20260614">
|
||||
<link rel="stylesheet" href="<?= $pluginRoot ?>/assets/backupper.css?v=202606141">
|
||||
<div id="backupper-app" data-api="<?= $pluginRoot ?>/api.php">
|
||||
<header class="bu-header">
|
||||
<div><h1>Backupper <span class="bu-version">2026.06.14</span></h1><p>Encrypted Restic backups for Unraid</p></div>
|
||||
<div><h1>Backupper <span class="bu-version">2026.06.14.1</span></h1><p>Encrypted Restic backups for Unraid</p></div>
|
||||
<div id="bu-health" class="bu-health" tabindex="0" data-tooltip="Zeigt, ob die Backupper-Hintergrundkomponente erreichbar ist. Beispiel: 'Daemon online' bedeutet, dass Jobs gestartet werden können.">Connecting...</div>
|
||||
</header>
|
||||
<nav class="bu-tabs" aria-label="Backupper sections">
|
||||
@@ -29,4 +29,4 @@ $pluginRoot = '/plugins/backupper';
|
||||
<div id="bu-tooltip" role="tooltip" aria-hidden="true"></div>
|
||||
</div>
|
||||
<script>window.BACKUPPER_CSRF = <?= json_encode($var['csrf_token'] ?? '') ?>;</script>
|
||||
<script src="<?= $pluginRoot ?>/assets/backupper-2026.06.14.js"></script>
|
||||
<script src="<?= $pluginRoot ?>/assets/backupper-2026.06.14.1.js"></script>
|
||||
|
||||
@@ -63,11 +63,12 @@
|
||||
.bu-table-actions { align-items: flex-start; min-width: 310px; }
|
||||
.bu-table-actions .bu-button { flex: 0 0 auto; }
|
||||
.bu-warning { color: var(--bu-bad) !important; display: block; font-size: 13px; font-weight: 700; margin-top: 5px; }
|
||||
.bu-browser { border: 1px solid var(--bu-border); border-radius: 6px; margin-top: 10px; max-height: 430px; overflow: auto; padding: 12px; }
|
||||
.bu-browser-head { align-items: center; display: flex; gap: 10px; justify-content: space-between; margin-bottom: 10px; }
|
||||
.bu-browser-card { border: 1px solid var(--bu-border); border-radius: 6px; margin-top: 10px; padding: 14px; }
|
||||
.bu-browser-scroll { max-height: 430px; overflow: auto; }
|
||||
.bu-selection-list { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0; }
|
||||
.bu-selection { background: var(--bu-panel-alt); border: 1px solid var(--bu-border); border-radius: 999px; color: var(--bu-text); font-size: 12px; padding: 4px 8px; }
|
||||
.bu-browser input[type="checkbox"], .bu-table input[type="checkbox"] { accent-color: var(--bu-accent); height: 20px; width: 20px; }
|
||||
.bu-browser-card input[type="checkbox"], .bu-table input[type="checkbox"] { accent-color: var(--bu-accent); height: 20px; width: 20px; }
|
||||
.bu-count { align-items: center; border: 1px solid var(--bu-border); border-radius: 999px; display: inline-flex; font-size: 13px; font-weight: 700; padding: 7px 11px; }
|
||||
|
||||
#backupper-app .bu-button {
|
||||
appearance: none;
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
'cancel-form': 'Verwirft ungespeicherte Eingaben und kehrt zur Übersicht zurück.',
|
||||
'browser-up': 'Öffnet im Snapshot-Browser das übergeordnete Verzeichnis.',
|
||||
'source-up': 'Öffnet im Ordner-Browser das übergeordnete Verzeichnis.',
|
||||
'select-current-source': 'Wählt den aktuell geöffneten Ordner vollständig als Backup-Quelle aus.',
|
||||
'select-current-restore': 'Wählt den aktuell geöffneten Snapshot-Ordner vollständig für die Wiederherstellung aus.',
|
||||
};
|
||||
|
||||
@@ -226,7 +227,7 @@
|
||||
<label>Repository<select name="repositoryId" required>${options}</select></label><label>Schedule (cron)<input name="cron" value="${esc(job.schedule?.cron || '0 2 * * *')}"></label>
|
||||
<label>Consistency<select name="consistency"><option value="live">Live</option><option value="stop" ${job.consistency?.mode==='stop'?'selected':''}>Controlled stop</option></select></label>
|
||||
<label>Compression<select name="compression">${['auto','off','max'].map(v=>`<option ${v===(job.compression||'auto')?'selected':''}>${v}</option>`).join('')}</select></label>
|
||||
<div class="wide"><h3>Backup folders</h3><div class="bu-muted">Select one or more folders. Open navigates into a folder without selecting it.</div><div id="bu-source-browser" class="bu-browser"></div></div>
|
||||
<div class="wide"><h3>Backup folders</h3><div class="bu-muted">Select folders exactly like files and folders in the snapshot browser.</div><div id="bu-source-browser"></div></div>
|
||||
<label class="wide">Additional manual paths or workload IDs<textarea name="sources">${esc(source)}</textarea></label>
|
||||
<label class="wide">Exclude patterns, one per line<textarea name="excludes">${esc((job.excludes||[]).join('\n'))}</textarea></label>
|
||||
<label>Shutdown timeout (seconds)<input name="shutdownSecs" type="number" min="1" value="${job.shutdownTimeoutSeconds || 120}"></label>
|
||||
@@ -240,9 +241,9 @@
|
||||
const host = document.getElementById('bu-source-browser');
|
||||
if (!host) return;
|
||||
const selected = new Set(state.backupSelections);
|
||||
const rows = state.sourceDirectories.map(item => `<tr><td><input type="checkbox" class="bu-source-select" value="${esc(item.path)}" ${selected.has(item.path)?'checked':''}></td><td><strong>${esc(item.name)}</strong><br><span class="bu-muted">${esc(item.path)}</span></td><td>${button('Open',`open-source-dir:${encodeURIComponent(item.path)}`)}</td></tr>`).join('');
|
||||
const rows = state.sourceDirectories.map(item => `<tr><td><input type="checkbox" class="bu-source-select" value="${esc(item.path)}" ${selected.has(item.path)?'checked':''}></td><td>dir</td><td>${esc(item.name)}</td><td>${esc(item.path)}</td><td>${button('Open',`open-source-dir:${encodeURIComponent(item.path)}`)}</td></tr>`).join('');
|
||||
const chosen = state.backupSelections.length ? state.backupSelections.map(path=>`<span class="bu-selection">${esc(path)}</span>`).join('') : '<span class="bu-muted">No folders selected.</span>';
|
||||
host.innerHTML = `<div class="bu-browser-head"><div><strong>Folder: ${esc(state.sourceBrowserPath)}</strong></div><div>${state.sourceBrowserPath!=='/'?button('Up','source-up'):''}</div></div><div class="bu-selection-list">${chosen}</div><table class="bu-table"><thead><tr><th>Select</th><th>Folder</th><th></th></tr></thead><tbody>${rows||'<tr><td colspan="3" class="bu-empty">No subfolders found.</td></tr>'}</tbody></table>`;
|
||||
host.innerHTML = `<section class="bu-browser-card"><div class="bu-toolbar"><div><h3>Backup source browser</h3><span class="bu-muted">${esc(state.sourceBrowserPath)}</span></div><div class="bu-actions">${state.sourceBrowserPath!=='/'?button('Select current folder','select-current-source'):''}${state.sourceBrowserPath!=='/'?button('Up','source-up'):''}<span class="bu-count">Selected: ${state.backupSelections.length}</span></div></div><div class="bu-selection-list">${chosen}</div><div class="bu-browser-scroll"><table class="bu-table"><thead><tr><th>Select</th><th>Type</th><th>Name</th><th>Path</th><th></th></tr></thead><tbody>${rows||'<tr><td colspan="5" class="bu-empty">No subfolders found.</td></tr>'}</tbody></table></div></section>`;
|
||||
enhanceTooltips(host);
|
||||
}
|
||||
|
||||
@@ -335,6 +336,7 @@
|
||||
if (name === 'browse') { state.selectedSnapshot=a; state.browserPath='/'; state.restoreIncludes=[]; state.files=await api(`/v1/repositories/${state.snapshotRepo}/snapshots/${a}/files?path=${encodeURIComponent('/')}`); return render(); }
|
||||
if (name === 'open-source-dir') { return loadSourceDirectories(decodeURIComponent(a)); }
|
||||
if (name === 'source-up') { const parent=state.sourceBrowserPath==='/'?'/':state.sourceBrowserPath.split('/').slice(0,-1).join('/')||'/'; return loadSourceDirectories(parent==='/mnt'?'/':parent); }
|
||||
if (name === 'select-current-source') { if(!state.backupSelections.includes(state.sourceBrowserPath)) state.backupSelections.push(state.sourceBrowserPath); return renderSourceBrowser(); }
|
||||
if (name === 'open-dir') { state.browserPath=decodeURIComponent(a); state.files=await api(`/v1/repositories/${state.snapshotRepo}/snapshots/${state.selectedSnapshot}/files?path=${encodeURIComponent(state.browserPath)}`); return render(); }
|
||||
if (name === 'browser-up') { state.browserPath=state.browserPath.replace(/\/$/,'').split('/').slice(0,-1).join('/')||'/'; state.files=await api(`/v1/repositories/${state.snapshotRepo}/snapshots/${state.selectedSnapshot}/files?path=${encodeURIComponent(state.browserPath)}`); return render(); }
|
||||
if (name === 'select-current-restore') { if(!state.restoreIncludes.includes(state.browserPath)) state.restoreIncludes.push(state.browserPath); return render(); }
|
||||
|
||||
Reference in New Issue
Block a user