diff --git a/options/options.html b/options/options.html index 5deb398..c660e16 100644 --- a/options/options.html +++ b/options/options.html @@ -109,6 +109,7 @@ .map-right { display: none; flex: 1 1 auto; min-width: 0; } .map-row.has-const .map-right { display: flex; align-items: center; } .map-left select, .map-left input, .map-right input { width: 100%; padding: 7px 9px; } + .map-left input[hidden] { display: none; } .map-hint { font-size: 11px; letter-spacing: .4px; text-transform: uppercase; color: var(--muted); font-weight: 600; margin: 2px 2px 8px; } .icon-btn { @@ -222,13 +223,13 @@
DocuWare-Feld → Mail-Attribut · fester Wert
- -
+
Steht ein Feld nicht in der Liste, + „✎ Anderer Feldname" wählen und den Namen eintippen.
Als Mail-Attribut „Fester Wert" wählen, um einen festen Text (z. B. NEU) ins Feld zu schreiben.
diff --git a/options/options.js b/options/options.js index aefc7d7..c9de665 100644 --- a/options/options.js +++ b/options/options.js @@ -58,7 +58,12 @@ async function invalidateSession(s) { let next = s; if (server) { next = await Settings.set({ defaultCabinetId: "", dialogByCabinet: {} }); + // Gemerkte Feldlisten hängen an Schrank-GUIDs des alten Servers -> weg damit. + fieldsByCabinet = {}; + await browser.storage.local.remove("fieldsByCabinet").catch(() => {}); + availableFields = []; fillCabinets([], ""); // Dropdowns leeren – die alte Liste gilt nicht mehr + refreshFieldSelects(); } snapshotAuth(next); return next; @@ -71,13 +76,23 @@ async function load() { 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); - // Best-effort: Felder des Standard-Archivs gleich laden – nur wenn schon ein - // Sitzungs-Token da ist (sonst wählt der Nutzer das Archiv manuell). + // Gemerkte Schrank- und Feldlisten VOR dem Aufbau der Zeilen lesen, damit die + // Feldname-Dropdowns schon beim ersten Rendern gefüllt sind. + const local = await browser.storage.local.get(["cabinets", "fieldsByCabinet"]); + fieldsByCabinet = local.fieldsByCabinet || {}; + if (s.defaultCabinetId && Array.isArray(fieldsByCabinet[s.defaultCabinetId])) { + availableFields = fieldsByCabinet[s.defaultCabinetId]; + } + + loadMapping(s.fieldMapping && Object.keys(s.fieldMapping).length ? s.fieldMapping : DEFAULT_FIELD_MAP); + fillCabinets(local.cabinets || [], s.defaultCabinetId); + if (availableFields.length) { + $("mapFieldStatus").textContent = `${availableFields.length} Felder zur Auswahl (gemerkt).`; + } + + // Best-effort auffrischen – nur wenn schon ein Sitzungs-Token da ist. Ohne + // Token bleibt es bei der gemerkten Liste, die Auswahl funktioniert trotzdem. if (s.defaultCabinetId && [...$("mapCabinet").options].some((o) => o.value === s.defaultCabinetId)) { $("mapCabinet").value = s.defaultCabinetId; try { @@ -86,10 +101,11 @@ async function load() { Auth.setToken(stt.token); availableFields = (await DocuWare.cabinetFieldNames(s, s.defaultCabinetId)) .filter((n) => n && !/^DW[A-Z]/.test(n)).sort(); + await rememberFields(s.defaultCabinetId, availableFields); refreshFieldSelects(); $("mapFieldStatus").textContent = `${availableFields.length} Felder zur Auswahl geladen.`; } - } catch (_) { /* egal – Archiv kann manuell gewählt werden */ } + } catch (_) { /* egal – gemerkte Liste bleibt nutzbar */ } } } @@ -159,8 +175,19 @@ async function ensureToken() { // --- Feld-Mapping-Editor --------------------------------------------------- -const CONST_OPT = "__const__"; // Sentinel im Quelle-Dropdown für „Fester Wert" -let availableFields = []; // Feldnamen aus dem zuletzt gewählten Archiv +const CONST_OPT = "__const__"; // Sentinel im Quelle-Dropdown für „Fester Wert" +const CUSTOM_OPT = "__custom__"; // Sentinel im Feldname-Dropdown für „eigener Name" +let availableFields = []; // Feldnamen des zuletzt gewählten Archivs + +// Gemerkte Feldnamen je Archiv (storage.local, Schlüssel "fieldsByCabinet"). +// Ohne diesen Cache war die Auswahlliste bei jedem Thunderbird-Start wieder leer: +// sie kam nur vom Server und setzte damit Passworteingabe + Archivwahl voraus. +let fieldsByCabinet = {}; + +async function rememberFields(cabinetId, names) { + fieldsByCabinet = { ...fieldsByCabinet, [cabinetId]: names }; + await browser.storage.local.set({ fieldsByCabinet }).catch(() => {}); +} // Zerlegt einen Mapping-Wert (Token-String ODER { const } / { token }) in den UI-Zustand. function mapValueState(value) { @@ -172,25 +199,52 @@ function mapValueState(value) { return { sel: "", konst: "" }; } -// Vorschläge für die Feldname-Eingaben: Felder des gewählten Archivs, dazu die -// Standard-Feldnamen und alles schon Eingetragene. Der Archiv-Teil ist erst nach -// Anmeldung + Archivwahl da – ohne die beiden anderen Quellen stünde man vorher -// vor einer leeren Liste. +// Feldname einer Zeile – aus dem Dropdown oder, bei „Anderer Feldname", aus der +// daneben eingeblendeten Eingabe. +function rowFieldName(row) { + const sel = row.querySelector(".map-field-sel"); + if (!sel) return ""; + if (sel.value === CUSTOM_OPT) { + const inp = row.querySelector(".map-field-custom"); + return inp ? inp.value.trim() : ""; + } + return sel.value.trim(); +} + +// Auswahlliste für die Feldname-Dropdowns. Drei Quellen, damit sie NIE leer ist: +// Felder des gewählten Archivs (auch aus dem Cache, also ohne Anmeldung), die +// Standard-Feldnamen und alles im Formular bereits Vergebene. function fieldSuggestions() { - const used = [...document.querySelectorAll("#mapBody .map-field")].map((i) => i.value); + const used = [...document.querySelectorAll("#mapBody .map-row")].map(rowFieldName); const all = [...(availableFields || []), ...Object.keys(DEFAULT_FIELD_MAP), ...used]; return [...new Set(all.filter(Boolean))].sort(); } -// Baut die gemeinsame neu auf (alle Zeilen hängen daran). -function refreshFieldSelects() { - const dl = $("fieldNameList"); - if (!dl) return; - dl.innerHTML = ""; - fieldSuggestions().forEach((n) => { +// Ein Feldname-Dropdown neu aufbauen. Der aktuelle Wert steht immer mit drin, +// auch wenn das Archiv ihn nicht (mehr) kennt – sonst ginge er beim Neuaufbau +// still verloren. +function fillFieldSelect(sel, current) { + sel.innerHTML = ""; + const empty = document.createElement("option"); + empty.value = ""; empty.textContent = "— Feld wählen —"; + sel.appendChild(empty); + [...new Set([...fieldSuggestions(), current].filter(Boolean))].sort().forEach((n) => { const o = document.createElement("option"); - o.value = n; - dl.appendChild(o); + o.value = n; o.textContent = n; + sel.appendChild(o); + }); + const own = document.createElement("option"); + own.value = CUSTOM_OPT; own.textContent = "✎ Anderer Feldname …"; + sel.appendChild(own); + sel.value = current || ""; +} + +// Alle Dropdowns auf die aktuelle Liste bringen (Auswahl bleibt). Zeilen mit +// freier Eingabe bleiben unangetastet. +function refreshFieldSelects() { + document.querySelectorAll("#mapBody .map-field-sel").forEach((sel) => { + if (sel.value === CUSTOM_OPT) return; + fillFieldSelect(sel, sel.value); }); } @@ -199,19 +253,33 @@ function addMapRow(field = "", value = "") { const row = document.createElement("div"); row.className = "map-row" + (st.sel === CONST_OPT ? " has-const" : ""); - // Links gestapelt: Feldname (Eingabe mit Vorschlägen) über Mail-Attribut. + // Links gestapelt: Feldname (Dropdown, bei Bedarf freie Eingabe) über Mail-Attribut. const left = document.createElement("div"); left.className = "map-left"; - // Freie Eingabe mit Vorschlagsliste – bewusst KEIN