Firefox ESR, Office aus eigenem Pfad, Warnung bei ungespeicherten Blaupausen
- Katalog: Mozilla Firefox ESR (DE) - Office-Modus "Offline aus eigenem Pfad" (Netzlaufwerk/Ordner) mit Pruefung auf Office\Data und SourcePath in der XML-Vorschau - Seitenleiste: Speichern-Button + Hinweis bei ungespeicherten Aenderungen; Rueckfrage vor Laden einer anderen Blaupause, Beenden und Schliessen Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
+35
-8
@@ -37,7 +37,7 @@ const DEFAULTS = () => ({
|
||||
exclude: ['Groove', 'Lync', 'Bing'], extraProducts: [], pidKey: '', company: '',
|
||||
updates: true, removeMsi: true, migrateArch: true, sharedComputer: false, disableVba: true, ooxmlDefault: true, showUi: false
|
||||
},
|
||||
officeMode: 'online',
|
||||
officeMode: 'online', officeSource: '',
|
||||
shortcuts: [],
|
||||
system: { computerName: '', timeZone: '', showExtensions: true, disableConsumer: true },
|
||||
security: { defender: true, firewall: true, smartScreen: true, uac: false, disableSmb1: true, disableAutorun: true, lsaProtection: false, telemetryMin: false, rdp: 'keep', localAdminName: '', localAdminPassword: '' },
|
||||
@@ -61,7 +61,22 @@ function deepMerge(base, add) {
|
||||
}
|
||||
const getPath = p => p.split('.').reduce((o, k) => o?.[k], S);
|
||||
function setPath(p, v) { const ks = p.split('.'); const last = ks.pop(); ks.reduce((o, k) => o[k] ??= {}, S)[last] = v; }
|
||||
function persist() { try { localStorage.setItem('kdsState', JSON.stringify(S)); } catch { } renderSummary(); }
|
||||
function persist() { try { localStorage.setItem('kdsState', JSON.stringify(S)); } catch { } renderSummary(); updateDirty(); }
|
||||
|
||||
// ---- Ungespeicherte Änderungen erkennen (Vergleich mit dem zuletzt gespeicherten/geladenen Stand)
|
||||
const snap = o => JSON.stringify(o);
|
||||
let savedSnap = null;
|
||||
try { savedSnap = localStorage.getItem('kdsSaved'); } catch { }
|
||||
if (savedSnap == null) savedSnap = snap(S);
|
||||
const isDirty = () => snap(S) !== savedSnap;
|
||||
function markSaved() { savedSnap = snap(S); try { localStorage.setItem('kdsSaved', savedSnap); } catch { } updateDirty(); }
|
||||
function updateDirty() {
|
||||
const d = isDirty();
|
||||
$('#dirtyWarn')?.classList.toggle('hidden', !d);
|
||||
$('#btnSideSave')?.classList.toggle('pulse', d);
|
||||
}
|
||||
const confirmDiscard = () => !isDirty() || confirm('Es gibt ungespeicherte Änderungen an der aktuellen Blaupause. Trotzdem fortfahren und Änderungen verwerfen?');
|
||||
window.addEventListener('beforeunload', e => { if (isDirty()) { e.preventDefault(); e.returnValue = ''; } });
|
||||
|
||||
// generische Formularbindung: data-bind="pfad" und data-bind-list="pfad" (Checkbox-Gruppen)
|
||||
function bindForms() {
|
||||
@@ -87,12 +102,14 @@ function fillForms() {
|
||||
$$('input', box).forEach(i => i.checked = v.includes(i.value));
|
||||
});
|
||||
$$('#steps input').forEach(i => i.checked = arr(S.steps).includes(i.value));
|
||||
$('#officeSourceRow').classList.toggle('hidden', S.officeMode !== 'custom');
|
||||
$('#profileName').textContent = 'Blaupause: ' + ([S.customer, S.name].filter(Boolean).join(' / ') || '(ungespeichert)');
|
||||
$$('.pinonly').forEach(e => e.classList.toggle('hidden', !S.bitlocker.usePin));
|
||||
}
|
||||
function onChange(path) {
|
||||
if (path.startsWith('office')) { if (path === 'office.product') { renderOfficeChannels(); renderOfficeExtras(); } updateOfficeXml(); }
|
||||
if (path === 'bitlocker.usePin') fillForms();
|
||||
if (path === 'officeMode' || path === 'officeSource') { fillForms(); updateOfficeXml(); }
|
||||
if (path === 'bloatware.recommended') renderBloat();
|
||||
}
|
||||
function loadState(obj) { S = deepMerge(DEFAULTS(), obj); persist(); fillForms(); renderAll(); }
|
||||
@@ -288,12 +305,13 @@ $('#officeExtras').onchange = () => { S.office.extraProducts = $$('#officeExtras
|
||||
let xmlTimer;
|
||||
function updateOfficeXml() {
|
||||
clearTimeout(xmlTimer);
|
||||
xmlTimer = setTimeout(() => guard(async () => { $('#officeXml').textContent = await api('POST', '/api/office/xml', S.office, true); }), 250);
|
||||
xmlTimer = setTimeout(() => guard(async () => { $('#officeXml').textContent = await api('POST', '/api/office/xml', { ...S.office, sourcePath: S.officeMode === 'custom' ? S.officeSource : '' }, true); }), 250);
|
||||
}
|
||||
$('#btnXmlDownload').onclick = () => download('configuration.xml', $('#officeXml').textContent, 'application/xml');
|
||||
$('#btnXmlCopy').onclick = () => navigator.clipboard.writeText($('#officeXml').textContent).then(() => toast('XML kopiert'));
|
||||
$('#btnOffice').onclick = () => guard(async () => {
|
||||
jobStarted((await api('POST', '/api/office/install', { office: S.office, mode: S.officeMode, name: S.name })).job);
|
||||
if (S.officeMode === 'custom' && !S.officeSource) return toast('Pfad zur Office-Quelle angeben', true);
|
||||
jobStarted((await api('POST', '/api/office/install', { office: S.office, mode: S.officeMode, sourcePath: S.officeSource, name: S.name })).job);
|
||||
});
|
||||
async function loadOfficeBuilds() {
|
||||
const b = arr(await api('GET', '/api/office/configs').catch(() => []));
|
||||
@@ -670,8 +688,9 @@ function renderQuickSelect() {
|
||||
}
|
||||
$('#bpFilter').oninput = renderBlueprints;
|
||||
async function loadBlueprint(id) {
|
||||
if (!confirmDiscard()) { renderQuickSelect(); return; }
|
||||
const data = await api('GET', '/api/blueprints/' + encodeURIComponent(id));
|
||||
loadState({ ...data, id });
|
||||
loadState({ ...data, id }); markSaved();
|
||||
renderBlueprints(); renderQuickSelect();
|
||||
$('#bpCustomer').value = S.customer || ''; $('#profileSaveName').value = S.name || ''; $('#bpDescription').value = S.description || '';
|
||||
toast(`Blaupause "${[S.customer, S.name].filter(Boolean).join(' / ')}" geladen`);
|
||||
@@ -700,6 +719,7 @@ async function saveBlueprint(asNew) {
|
||||
await api('DELETE', '/api/blueprints/' + encodeURIComponent(S.id));
|
||||
Object.assign(S, { id, name, customer, description: $('#bpDescription').value.trim() }); persist();
|
||||
await api('POST', '/api/blueprints/' + encodeURIComponent(id), JSON.stringify(S, null, 2));
|
||||
markSaved();
|
||||
toast(`Blaupause "${name}" gespeichert`); fillForms(); loadProfiles();
|
||||
}
|
||||
$('#btnProfileSave').onclick = () => guard(() => saveBlueprint(false));
|
||||
@@ -709,7 +729,14 @@ $('#importFile').onchange = async e => {
|
||||
const f = e.target.files[0]; e.target.value = '';
|
||||
if (f) guard(async () => { const d = JSON.parse(await f.text()); d.id = ''; loadState(d); loadProfiles(); toast('Importiert – zum Übernehmen „Speichern“ klicken'); });
|
||||
};
|
||||
$('#btnReset').onclick = () => { if (confirm('Alle Einstellungen auf Standard zurücksetzen?')) loadState(DEFAULTS()); };
|
||||
$('#btnReset').onclick = () => { if (confirm('Alle Einstellungen auf Standard zurücksetzen?')) { loadState(DEFAULTS()); markSaved(); } };
|
||||
// Speichern-Button in der Seitenleiste
|
||||
$('#btnSideSave').onclick = () => guard(async () => {
|
||||
if (!S.name) { showTab('profiles'); $('#profileSaveName').focus(); return toast('Bitte Kunde und Namen für die neue Blaupause angeben', true); }
|
||||
if (!blueprints.length) await loadProfiles();
|
||||
$('#bpCustomer').value = S.customer || ''; $('#profileSaveName').value = S.name; $('#bpDescription').value = S.description || '';
|
||||
await saveBlueprint(false);
|
||||
});
|
||||
|
||||
function download(name, text, type) {
|
||||
const a = document.createElement('a'); a.href = URL.createObjectURL(new Blob([text], { type })); a.download = name; a.click();
|
||||
@@ -759,13 +786,13 @@ async function waitJob(id) {
|
||||
}
|
||||
}
|
||||
|
||||
$('#btnShutdown').onclick = () => { if (confirm('Agent beenden? Laufende Aufgaben werden abgebrochen.')) api('POST', '/api/shutdown', {}).finally(() => { document.body.innerHTML = '<main><h1>Agent beendet.</h1><p>Das Fenster kann geschlossen werden.</p></main>'; }); };
|
||||
$('#btnShutdown').onclick = () => { if (confirmDiscard() && confirm('Agent beenden? Laufende Aufgaben werden abgebrochen.')) api('POST', '/api/shutdown', {}).finally(() => { document.body.innerHTML = '<main><h1>Agent beendet.</h1><p>Das Fenster kann geschlossen werden.</p></main>'; }); };
|
||||
|
||||
// ---------------------------------------------------------------- Start
|
||||
function renderAll() { renderOffice(); renderSelectedApps(); renderPlanned(); renderWifi(); renderSummary(); }
|
||||
(async () => {
|
||||
if (!TOKEN) { document.body.innerHTML = '<main><h1>Kein Zugriffstoken</h1><p>Bitte die vollständige URL (mit ?token=…) aus dem Agent-Fenster öffnen.</p></main>'; return; }
|
||||
bindForms(); renderAll(); fillForms(); scTypeUi();
|
||||
bindForms(); renderAll(); fillForms(); scTypeUi(); updateDirty();
|
||||
await guard(loadStatus);
|
||||
await guard(loadCatalog);
|
||||
await guard(async () => {
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
</nav>
|
||||
<div class="side-foot">
|
||||
<div id="profileName" class="muted">Blaupause: (ungespeichert)</div>
|
||||
<div id="dirtyWarn" class="dirty hidden">⚠ Ungespeicherte Änderungen</div>
|
||||
<button class="primary" id="btnSideSave">💾 Blaupause speichern</button>
|
||||
<button class="ghost" id="btnShutdown">Agent beenden</button>
|
||||
</div>
|
||||
</aside>
|
||||
@@ -163,7 +165,10 @@
|
||||
<option value="online">Online installieren (direkt von Microsoft)</option>
|
||||
<option value="download">Nur Build erstellen (Dateien auf den Stick laden)</option>
|
||||
<option value="offline">Vom Stick installieren (Build wird bei Bedarf erstellt)</option>
|
||||
<option value="custom">Offline aus eigenem Pfad (Netzlaufwerk / Ordner)</option>
|
||||
</select></label>
|
||||
<label class="f" id="officeSourceRow">Pfad zur Office-Quelle
|
||||
<input data-bind="officeSource" placeholder="z.B. \\server\software\Office365 oder D:\Office – Ordner, der Office\Data enthält"></label>
|
||||
<p class="muted small">setup.exe des Office Deployment Tools wird bei Bedarf automatisch von Microsoft geladen und unter <code>office\</code> abgelegt. Builds liegen in <code>office\source\</code>.</p>
|
||||
<div id="officeBuilds" class="muted small"></div>
|
||||
<button class="primary" id="btnOffice">Ausführen</button>
|
||||
|
||||
@@ -101,3 +101,6 @@ dialog { border: 1px solid var(--line); border-radius: 12px; background: var(--c
|
||||
dialog::backdrop { background: rgba(0,0,0,.45); }
|
||||
.okline { color: var(--ok); font-weight: 600; } .errline { color: var(--err); font-weight: 600; }
|
||||
.checks label input { flex: none; }
|
||||
.dirty { background: rgba(178,106,0,.18); color: #ffb648; border: 1px solid rgba(255,182,72,.4); border-radius: 6px; padding: 6px 10px; font-size: 12px; font-weight: 600; }
|
||||
#btnSideSave.pulse { animation: pulse 1.6s ease-in-out infinite; }
|
||||
@keyframes pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(76,149,255,.6); } 50% { box-shadow: 0 0 0 6px rgba(76,149,255,0); } }
|
||||
|
||||
Reference in New Issue
Block a user