// Logik der Einstellungsseite. const $ = (id) => document.getElementById(id); // Passwort ist bewusst NICHT dabei – es wird nie gespeichert, nur zum Testen/Login // transient verwendet (siehe "Verbindung testen"). const FIELDS = ["serverUrl", "organization", "username"]; const CHECKS = ["storeAttachments", "tagOnSuccess"]; function setStatus(msg, kind) { const el = $("status"); el.textContent = msg; el.className = kind || ""; } async function load() { const s = await Settings.get(); FIELDS.forEach((f) => ($(f).value = s[f] || "")); CHECKS.forEach((c) => ($(c).checked = !!s[c])); setMailFormat(s.mailFormat === "pdf" ? "pdf" : "eml"); $("mappingUrl").value = s.mappingUrl || ""; loadMapping(s.fieldMapping && Object.keys(s.fieldMapping).length ? s.fieldMapping : DEFAULT_FIELD_MAP); // Schrankliste, falls vorher schon getestet (im Storage gecached) const cached = (await browser.storage.local.get("cabinets")).cabinets || []; fillCabinets(cached, s.defaultCabinetId); } function setMailFormat(v) { const el = document.querySelector(`input[name="mailFormat"][value="${v}"]`); if (el) el.checked = true; } function getMailFormat() { const el = document.querySelector('input[name="mailFormat"]:checked'); return el ? el.value : "eml"; } function fillCabinets(cabinets, selectedId) { // Standard-Ziel (Archive + Briefkörbe) const sel = $("defaultCabinet"); sel.innerHTML = ''; const addGroup = (label, list) => { if (!list.length) return; const g = document.createElement("optgroup"); g.label = label; list.forEach((c) => { const o = document.createElement("option"); o.value = c.id; o.textContent = c.name; if (String(c.id) === String(selectedId)) o.selected = true; g.appendChild(o); }); sel.appendChild(g); }; addGroup("Archive", cabinets.filter((c) => !c.isBasket)); addGroup("Briefkörbe", cabinets.filter((c) => c.isBasket)); // Mapping-Helfer: nur Archive (Briefkörbe haben keine Indexfelder). const mc = $("mapCabinet"); const keep = mc.value; mc.innerHTML = ''; cabinets.filter((c) => !c.isBasket).forEach((c) => { const o = document.createElement("option"); o.value = c.id; o.textContent = c.name; mc.appendChild(o); }); if (keep) mc.value = keep; } // Besorgt einen Sitzungs-Token: vorhandenen vom Broker nutzen, sonst mit dem // (transienten) Passwortfeld anmelden. Liefert false, wenn kein Passwort da ist. async function ensureToken() { const st = await browser.runtime.sendMessage({ type: "auth:status" }); if (st && st.hasToken && st.token) { Auth.setToken(st.token); return true; } const pw = $("password").value; if (!pw) return false; const res = await browser.runtime.sendMessage({ type: "auth:logon", password: pw }); if (!res || !res.ok) throw new Error(res && res.error ? res.error : "Anmeldung fehlgeschlagen."); Auth.setToken(res.token); $("password").value = ""; // Passwort nicht behalten return true; } // --- Feld-Mapping-Editor --------------------------------------------------- const CONST_OPT = "__const__"; // Sentinel im Quelle-Dropdown für „Fester Wert" // Zerlegt einen Mapping-Wert (Token-String ODER { const } / { token }) in den UI-Zustand. function mapValueState(value) { if (value && typeof value === "object") { if (value.const != null) return { sel: CONST_OPT, konst: String(value.const) }; if (value.token) return { sel: value.token, konst: "" }; } if (typeof value === "string" && value) return { sel: value, konst: "" }; return { sel: "", konst: "" }; } function addMapRow(field = "", value = "") { const st = mapValueState(value); const tr = document.createElement("tr"); const inp = document.createElement("input"); inp.type = "text"; inp.className = "map-field"; inp.value = field; inp.placeholder = "EML_SUBJECT"; inp.setAttribute("list", "fieldNames"); // Vorschläge aus gewähltem Archiv const tdF = document.createElement("td"); tdF.appendChild(inp); const sel = document.createElement("select"); sel.className = "map-source"; const none = document.createElement("option"); none.value = ""; none.textContent = "— nichts —"; sel.appendChild(none); MAIL_TOKENS.forEach((t) => { const o = document.createElement("option"); o.value = t.key; o.textContent = t.label; if (t.key === st.sel) o.selected = true; sel.appendChild(o); }); const co = document.createElement("option"); co.value = CONST_OPT; co.textContent = "★ Fester Wert"; if (st.sel === CONST_OPT) co.selected = true; sel.appendChild(co); const konst = document.createElement("input"); konst.type = "text"; konst.className = "map-const"; konst.placeholder = "z. B. NEU"; konst.value = st.konst; konst.style.marginTop = "6px"; konst.style.display = st.sel === CONST_OPT ? "" : "none"; sel.addEventListener("change", () => { konst.style.display = sel.value === CONST_OPT ? "" : "none"; if (sel.value === CONST_OPT) konst.focus(); }); const tdT = document.createElement("td"); tdT.appendChild(sel); tdT.appendChild(konst); const del = document.createElement("button"); del.type = "button"; del.className = "icon-btn"; del.textContent = "✕"; del.title = "Zeile entfernen"; del.addEventListener("click", () => tr.remove()); const tdX = document.createElement("td"); tdX.className = "del"; tdX.appendChild(del); tr.append(tdF, tdT, tdX); $("mapBody").appendChild(tr); } function loadMapping(map) { $("mapBody").innerHTML = ""; Object.entries(map || {}).forEach(([f, v]) => addMapRow(f, v)); } function collectMapping() { const map = {}; document.querySelectorAll("#mapBody tr").forEach((tr) => { const f = tr.querySelector(".map-field").value.trim(); if (!f) return; // ohne Feldname ignorieren const sel = tr.querySelector(".map-source").value; if (sel === CONST_OPT) { const c = tr.querySelector(".map-const").value; if (c !== "") map[f] = { const: c }; // fester Wert } else if (sel) { map[f] = sel; // Mail-Token (als String, abwärtskompatibel) } }); return map; } function collect() { const partial = {}; FIELDS.forEach((f) => (partial[f] = $(f).value.trim())); CHECKS.forEach((c) => (partial[c] = $(c).checked)); partial.defaultCabinetId = $("defaultCabinet").value; partial.mailFormat = getMailFormat(); partial.fieldMapping = collectMapping(); partial.mappingUrl = $("mappingUrl").value.trim(); return partial; } // --- Events ---------------------------------------------------------------- $("save").addEventListener("click", async () => { await Settings.set(collect()); setStatus("Gespeichert.", "ok"); }); $("test").addEventListener("click", async () => { setStatus("Verbinde …"); try { const s = await Settings.set(collect()); // erst speichern (ohne Passwort), dann testen const ok = await ensureToken(); if (!ok) { setStatus("Bitte Passwort eingeben.", "err"); return; } const cabinets = await DocuWare.listCabinets(s); await browser.storage.local.set({ cabinets }); fillCabinets(cabinets, s.defaultCabinetId); const arch = cabinets.filter((c) => !c.isBasket).length; const bk = cabinets.filter((c) => c.isBasket).length; setStatus(`Verbindung ok – ${arch} Archive, ${bk} Briefkörbe gefunden.`, "ok"); } catch (e) { setStatus(`Fehler: ${e.message}`, "err"); } }); $("mapAdd").addEventListener("click", () => addMapRow()); $("mapReset").addEventListener("click", () => loadMapping(DEFAULT_FIELD_MAP)); // --- Mapping teilen: JSON anzeigen / kopieren / anwenden / von URL laden ---- $("mappingExport").addEventListener("click", () => { $("mappingJson").value = JSON.stringify(collectMapping(), null, 2); setStatus("Aktuelles Mapping als JSON angezeigt.", "ok"); }); $("mappingCopy").addEventListener("click", async () => { let txt = $("mappingJson").value.trim(); if (!txt) { txt = JSON.stringify(collectMapping(), null, 2); $("mappingJson").value = txt; } try { await navigator.clipboard.writeText(txt); setStatus("In Zwischenablage kopiert.", "ok"); } catch (_) { $("mappingJson").select(); document.execCommand("copy"); setStatus("In Zwischenablage kopiert.", "ok"); } }); $("mappingApply").addEventListener("click", () => { try { const obj = JSON.parse($("mappingJson").value); if (!obj || typeof obj !== "object" || Array.isArray(obj)) throw new Error("kein JSON-Objekt"); loadMapping(obj); setStatus("JSON ins Mapping übernommen – mit „Speichern“ sichern.", "ok"); } catch (e) { setStatus(`Ungültiges JSON: ${e.message}`, "err"); } }); $("mappingPull").addEventListener("click", async () => { const url = $("mappingUrl").value.trim(); if (!url) { setStatus("Bitte eine URL eingeben.", "err"); return; } setStatus("Lade Mapping …"); try { const res = await fetch(url, { cache: "no-store" }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const obj = await res.json(); if (!obj || typeof obj !== "object" || Array.isArray(obj)) throw new Error("kein JSON-Objekt"); loadMapping(obj); await Settings.set({ mappingUrl: url }); // URL gleich merken setStatus("Mapping von URL geladen – mit „Speichern“ übernehmen.", "ok"); } catch (e) { setStatus(`Laden fehlgeschlagen: ${e.message}`, "err"); } }); // Archiv wählen -> dessen Indexfelder als Vorschläge (datalist) für alle Feldnamen. $("mapCabinet").addEventListener("change", async () => { const id = $("mapCabinet").value; const dl = $("fieldNames"); const st = $("mapFieldStatus"); if (!id) { dl.innerHTML = ""; st.textContent = ""; return; } st.textContent = "Lade Felder …"; try { const s = await Settings.get(); const ok = await ensureToken(); if (!ok) { st.textContent = "Erst „Verbindung testen“ (Passwort nötig)."; return; } const names = (await DocuWare.cabinetFieldNames(s, id)) .filter((n) => n && !/^DW[A-Z]/.test(n)); // DW*-Systemfelder ausblenden dl.innerHTML = ""; names.sort().forEach((n) => { const o = document.createElement("option"); o.value = n; dl.appendChild(o); }); st.textContent = `${names.length} Felder als Vorschlag geladen.`; } catch (e) { st.textContent = `Fehler: ${e.message}`; } }); load();