diff --git a/dist/urbm-2026.06.15.r018-x86_64-1.txz.sha256 b/dist/urbm-2026.06.15.r018-x86_64-1.txz.sha256 deleted file mode 100644 index d4cf75f..0000000 --- a/dist/urbm-2026.06.15.r018-x86_64-1.txz.sha256 +++ /dev/null @@ -1 +0,0 @@ -56f07ac0a18bf9067c8cb38ef6e5cbf69d52a2313e37f32f87b619268a47d6e8 urbm-2026.06.15.r018-x86_64-1.txz diff --git a/dist/urbm-2026.06.15.r018-x86_64-1.txz b/dist/urbm-2026.06.15.r019-x86_64-1.txz similarity index 56% rename from dist/urbm-2026.06.15.r018-x86_64-1.txz rename to dist/urbm-2026.06.15.r019-x86_64-1.txz index a1f9b46..5603746 100644 Binary files a/dist/urbm-2026.06.15.r018-x86_64-1.txz and b/dist/urbm-2026.06.15.r019-x86_64-1.txz differ diff --git a/dist/urbm-2026.06.15.r019-x86_64-1.txz.sha256 b/dist/urbm-2026.06.15.r019-x86_64-1.txz.sha256 new file mode 100644 index 0000000..0a6d7da --- /dev/null +++ b/dist/urbm-2026.06.15.r019-x86_64-1.txz.sha256 @@ -0,0 +1 @@ +89f13497e0503b91602af00343fb15cd897e775189e63fbd6af4e0c302ff06d4 urbm-2026.06.15.r019-x86_64-1.txz diff --git a/dist/urbm.plg b/dist/urbm.plg index 965a834..d5a3893 100644 --- a/dist/urbm.plg +++ b/dist/urbm.plg @@ -2,13 +2,17 @@ - + - + ]> +### 2026.06.15.r019 +- Add a clearly labeled manual-only schedule mode for every backup and Rsync job. +- Keep manual-only jobs enabled for one-click starts while excluding them from scheduled and catch-up runs. + ### 2026.06.15.r018 - Run a write-free Rsync preflight to estimate the actual transfer size before copying. - Abort Rsync jobs when the estimated transfer exceeds the free destination capacity. diff --git a/plugin/urbm.plg b/plugin/urbm.plg index fb30532..5aa8e73 100644 --- a/plugin/urbm.plg +++ b/plugin/urbm.plg @@ -2,13 +2,17 @@ - + ]> +### 2026.06.15.r019 +- Add a clearly labeled manual-only schedule mode for every backup and Rsync job. +- Keep manual-only jobs enabled for one-click starts while excluding them from scheduled and catch-up runs. + ### 2026.06.15.r018 - Run a write-free Rsync preflight to estimate the actual transfer size before copying. - Abort Rsync jobs when the estimated transfer exceeds the free destination capacity. diff --git a/webgui/URBM.page b/webgui/URBM.page index f4d847e..ac6e754 100644 --- a/webgui/URBM.page +++ b/webgui/URBM.page @@ -9,12 +9,12 @@ Tag="URBM Unraid Restic Backup Manager backup snapshots restore" - +
- -

URBM 2026.06.15.r018

Restic Backup Manager für Unraid

+ +

URBM 2026.06.15.r019

Restic Backup Manager für Unraid

Verbindung wird hergestellt...
@@ -32,4 +32,4 @@ $pluginRoot = '/plugins/urbm';
- + diff --git a/webgui/assets/urbm.css b/webgui/assets/urbm.css index e1ae66d..3291fdf 100644 --- a/webgui/assets/urbm.css +++ b/webgui/assets/urbm.css @@ -92,6 +92,7 @@ .bu-schedule-grid { display: grid; gap: 15px; grid-template-columns: minmax(240px, 2fr) minmax(150px, 1fr); } .bu-weekdays { align-items: center; display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; } .bu-weekdays[hidden] { display: none; } +.bu-schedule-time[hidden], .bu-schedule-manual[hidden] { display: none !important; } .bu-weekdays > span { font-weight: 700; margin-right: 4px; } #urbm-app .bu-weekdays label { display: block; } #urbm-app .bu-weekdays label span { background: var(--bu-panel-alt); border: 1px solid var(--bu-border-strong); border-radius: 5px; cursor: pointer; display: block; min-width: 46px; padding: 9px 11px; text-align: center; } diff --git a/webgui/assets/urbm.js b/webgui/assets/urbm.js index dba8a5a..d1d3f31 100644 --- a/webgui/assets/urbm.js +++ b/webgui/assets/urbm.js @@ -193,7 +193,9 @@ }; const weekDays = [{value:'1',short:'Mo',name:'Montag'},{value:'2',short:'Di',name:'Dienstag'},{value:'3',short:'Mi',name:'Mittwoch'},{value:'4',short:'Do',name:'Donnerstag'},{value:'5',short:'Fr',name:'Freitag'},{value:'6',short:'Sa',name:'Samstag'},{value:'0',short:'So',name:'Sonntag'}]; function parseSchedule(cron = '0 2 * * *') { - const match = String(cron).trim().match(/^(\d{1,2})\s+(\d{1,2})\s+\*\s+\*\s+(.+)$/); + const value=String(cron).trim(); + if(!value) return {mode:'manual',time:'02:00',days:[],cron:''}; + const match = value.match(/^(\d{1,2})\s+(\d{1,2})\s+\*\s+\*\s+(.+)$/); if (!match || Number(match[1]) > 59 || Number(match[2]) > 23) return {mode:'custom',time:'02:00',days:[],cron}; const time = `${String(match[2]).padStart(2,'0')}:${String(match[1]).padStart(2,'0')}`; if (match[3] === '*') return {mode:'daily',time,days:[],cron}; @@ -204,6 +206,7 @@ } function scheduleCron(form) { const mode = form.get('scheduleMode'); + if (mode === 'manual') return ''; if (mode === 'custom') return form.get('customCron'); const [hour, minute] = String(form.get('scheduleTime') || '02:00').split(':'); const dayField = mode === 'daily' ? '*' : mode === 'weekdays' ? '1-5' : form.getAll('scheduleDays').join(','); @@ -212,6 +215,7 @@ } function scheduleLabel(cron) { const schedule = parseSchedule(cron); + if (schedule.mode === 'manual') return 'Nur manuell'; if (schedule.mode === 'custom') return cron || 'Manuell'; if (schedule.mode === 'daily') return `Täglich um ${schedule.time} Uhr`; if (schedule.mode === 'weekdays') return `Mo–Fr um ${schedule.time} Uhr`; @@ -219,10 +223,11 @@ return `${labels} um ${schedule.time} Uhr`; } function scheduleFields(job = {}) { - const schedule = parseSchedule(job.schedule?.cron || '0 2 * * *'); - const modes = [['daily','Täglich'],['weekdays','Montag bis Freitag'],['selected','Ausgewählte Wochentage']]; + const cron=job.schedule&&Object.prototype.hasOwnProperty.call(job.schedule,'cron')?job.schedule.cron:'0 2 * * *'; + const schedule = parseSchedule(cron); + const modes = [['manual','Nur manuell starten'],['daily','Täglich'],['weekdays','Montag bis Freitag'],['selected','Ausgewählte Wochentage']]; if (schedule.mode === 'custom') modes.push(['custom','Bestehenden Cron-Zeitplan beibehalten']); - return `
Backup-Zeitplan
Wochentage${weekDays.map(day=>``).join('')}
${schedule.mode==='custom'?``:''}
`; + return `
Backup-Zeitplan
Dieser Job wird nicht automatisch gestartet. Er kann jederzeit in der Job-Liste über Starten ausgeführt werden.
Wochentage${weekDays.map(day=>``).join('')}
${schedule.mode==='custom'?``:''}
`; } function lineChart(runs, field, formatter, emptyText) { @@ -723,6 +728,10 @@ if (e.target.name === 'scheduleMode') { const weekdays=e.target.closest('.bu-schedule')?.querySelector('.bu-weekdays'); if (weekdays) weekdays.hidden=e.target.value!=='selected'; + const time=e.target.closest('.bu-schedule')?.querySelector('.bu-schedule-time'); + if(time) time.hidden=e.target.value==='manual'; + const manual=e.target.closest('.bu-schedule')?.querySelector('.bu-schedule-manual'); + if(manual) manual.hidden=e.target.value!=='manual'; } if (e.target.name === 'type' && e.target.closest('#bu-job-form')) { state.backupSelections=[]; state.workloadSelections=[];