Compare commits

...

6 Commits

Author SHA1 Message Date
e314a6af98 Release 0.12.3 2026-08-21 10:55:45 +02:00
03ff3795b2 Datum + Uhrzeit: Uhrzeit ging in Text- und DateTime-Feldern verloren
Das Token "Datum + Uhrzeit der E-Mail" lieferte nur das Datum. Drei Ursachen:

1. setFieldValue schrieb ein Date in ein TEXT-Feld ueber toDateInput, also nur
   "2026-08-04" - die Uhrzeit war weg, obwohl ein Textfeld sie problemlos traegt.
   Beim Upload wurde daraus String(dateObjekt), also die JS-Standardausgabe
   "Mon Aug 04 2026 16:28:51 GMT+0200 (...)". Neu: DocuWare.dateToText() liefert
   "04.08.2026 16:28:51", bei exakter Mitternacht nur "04.08.2026". Gilt fuer
   Text-, Memo- und Keyword-Felder und fuer Anzeige wie Upload gleichermassen.

2. TOKEN_FN.date war identisch mit dateTime (beide m.date). In einem Datums- oder
   DateTime-Feld fiel das nicht auf, in einem Textfeld schon: "Datum" haette dort
   ebenfalls eine Uhrzeit getragen. date liefert jetzt via dayOnly() den
   Kalendertag (lokale Mitternacht), wie archiveDate es schon tat.

3. Das datetime-local-Control kannte ohne step="1" nur Minuten und schnitt die
   Sekunden ab; fmtTime liefert sie jetzt mit.

Ausserdem geklaert: DateTime geht als LOKALE Wanduhrzeit ohne Zeitzonen-Suffix
raus ("2026-08-04T16:28:51") statt als toISOString() mit "Z". Ob der Server das
"Z" zurueckrechnet, haengt von dessen Zeitzone ab (Cloud meist UTC) - Zeiten
erschienen verschoben, und lokale Mitternacht kippte als UTC auf den VORTAG
22:00. Ohne Suffix ist der Wert eindeutig. Date-Felder bleiben unveraendert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 10:54:22 +02:00
d9ec2537e2 Release 0.12.2 2026-08-21 10:23:56 +02:00
f6c7761182 Feld-Zuordnung: Feldname wieder als Auswahl - und die Liste bleibt gefuellt
0.12.1 hatte den Feldnamen auf eine freie Texteingabe umgestellt. Das behob zwar
die Blockade, nahm aber die Auswahl weg - eigentlich war nur die Liste leer.

Jetzt wieder ein <select>, dessen Liste sich aus drei Quellen speist und damit
nie leer ist: Felder des gewaehlten Archivs, die Namen aus DEFAULT_FIELD_MAP und
alles im Formular bereits Vergebene. Ein Eintrag "Anderer Feldname" blendet eine
freie Eingabe ein, fuer Felder die in keiner Liste stehen. Ein gespeicherter Name,
den kein Archiv kennt, wird ins Dropdown aufgenommen statt verloren zu gehen.

Kern des Problems war die Herkunft der Liste: sie kam ausschliesslich vom Server
und setzte damit gecachte Schrankliste + Passwort dieser Sitzung + Archivwahl
voraus. Die Feldnamen werden nun je Archiv in storage.local unter
"fieldsByCabinet" gemerkt und beim Oeffnen VOR dem Aufbau der Zeilen gelesen -
die Auswahl steht also sofort und auch ohne Anmeldung. Ist ein Token da, wird im
Hintergrund aufgefrischt; schlaegt das fehl, bleibt die gemerkte Liste nutzbar.

Bei Server-Wechsel wird fieldsByCabinet mitgeloescht - die Feldlisten haengen an
Schrank-GUIDs des alten Servers (gleiche Logik wie fuer cabinets/defaultCabinetId).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 10:23:36 +02:00
ecbf1fcf33 Release 0.12.1 2026-08-21 10:08:13 +02:00
2e6a1a58e5 Feld-Zuordnung: Feldname wieder frei eingebbar
Die Spalte "DocuWare-Feldname" war ein starres <select>, dessen Optionen
ausschliesslich aus availableFields kamen - der Feldliste eines Archivs. Die ist
aber erst gefuellt, wenn Schrankliste gecacht, Passwort dieser Sitzung eingegeben
und ein Archiv gewaehlt ist. Bis dahin enthielt jedes Dropdown nur
"- Feld waehlen -": eine neue Zeile hatte null auswaehlbare Felder, und weil
collectMapping() Zeilen ohne Feldnamen verwirft, verschwand sie beim Speichern
kommentarlos wieder.

Jetzt wieder freie Texteingabe mit Vorschlagsliste (<input list>), wie der
veraltete Kommentar an mapCabinet es schon beschrieb. Die Vorschlaege kommen aus
drei Quellen statt einer: Felder des gewaehlten Archivs, die Namen aus
DEFAULT_FIELD_MAP und alles bereits Eingetragene - damit stehen sofort nach dem
Oeffnen Vorschlaege bereit, auch ohne Anmeldung.

fillFieldSelect entfaellt; refreshFieldSelects baut nur noch die gemeinsame
<datalist> auf und wird zusaetzlich am Ende von loadMapping aufgerufen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 10:07:41 +02:00
6 changed files with 189 additions and 37 deletions

View File

@ -37,7 +37,10 @@ const TOKEN_FN = {
bcc: (m) => m.bcc,
subject: (m) => m.subject,
body: (m) => m.bodyText,
date: (m) => m.date,
// Nur der Kalendertag: auf lokale Mitternacht gesetzt, damit „Datum" auch in
// einem DateTime- oder Textfeld keine Uhrzeit trägt (gleiche Konvention wie
// archiveDate). Den echten Zeitpunkt liefert dateTime.
date: (m) => dayOnly(m.date),
direction: (m) => m.direction,
size: (m) => m.sizeBytes,
account: (m) => m.account,
@ -49,7 +52,7 @@ const TOKEN_FN = {
time: (m) => fmtTime(m.date),
// archiveDate ist bewusst auf Mitternacht gesetzt: so bleibt es „nur Datum",
// auch wenn es auf ein DateTime-Feld gemappt wird. archiveDateTime trägt die Uhrzeit.
archiveDate: () => { const n = new Date(); return new Date(n.getFullYear(), n.getMonth(), n.getDate()); },
archiveDate: () => dayOnly(new Date()),
archiveDateTime: () => new Date(),
// Dokumentbezogen
@ -500,6 +503,8 @@ function buildInput(f) {
// DateTime-Feld: Tag UND Uhrzeit erfassbar (vor dem reinen date-Zweig prüfen).
el = document.createElement("input");
el.type = "datetime-local";
// Ohne step="1" kennt das Control nur Minuten und schneidet die Sekunden ab.
el.step = "1";
} else if (t.includes("date")) {
el = document.createElement("input");
el.type = "date";
@ -564,7 +569,10 @@ function setFieldValue(name, f, val) {
}
el.value = String(val);
} else {
el.value = val instanceof Date ? toDateInput(val) : String(val);
// Textfeld: Datum lesbar ausschreiben ("04.08.2026 16:28:51"). Früher lief
// hier toDateInput – das warf die Uhrzeit weg, obwohl ein Textfeld sie
// problemlos trägt (genau der Fall „Datum + Uhrzeit" ohne Uhrzeit).
el.value = val instanceof Date ? DocuWare.dateToText(val) : String(val);
}
wrap.classList.add("prefilled");
}
@ -859,10 +867,21 @@ const toDateInput = (d) => {
// Lokal als YYYY-MM-DDTHH:MM für <input type="datetime-local">.
const toDateTimeInput = (d) => `${toDateInput(d)}T${fmtTime(d)}`;
// Uhrzeit HH:MM lokal (auch als eigenständiger Text-Token nutzbar).
// Kalendertag ohne Uhrzeit (lokale Mitternacht). Funktionsdeklaration, damit
// TOKEN_FN sie oben im Modul schon verwenden kann.
function dayOnly(d) {
const dt = d instanceof Date ? d : new Date(d);
if (isNaN(dt)) return dt;
return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
}
// Uhrzeit inkl. Sekunden ("16:28:51"). Die Sekunden braucht auch
// toDateTimeInput – ein datetime-local mit step="1" nimmt sie entgegen.
function fmtTime(d) {
const dt = d instanceof Date ? d : new Date(d);
if (isNaN(dt)) return "";
return `${String(dt.getHours()).padStart(2, "0")}:${String(dt.getMinutes()).padStart(2, "0")}`;
const p = (n) => String(n).padStart(2, "0");
return `${p(dt.getHours())}:${p(dt.getMinutes())}:${p(dt.getSeconds())}`;
}
// Dateiname, den die E-Mail selbst beim Upload bekäme – für die Vorschau im

View File

@ -427,22 +427,47 @@ const DocuWare = {
return out;
},
/**
* Date-Objekt als deutscher Text: "04.08.2026 16:28:51". Ohne Uhrzeit (exakt
* Mitternacht) nur "04.08.2026" – so tragen reine Datums-Tokens kein
* künstliches " 00:00:00". Gebraucht, sobald ein Datum in einem TEXT-Feld
* landet: String(dateObjekt) lieferte sonst die JS-Standardausgabe
* ("Mon Aug 04 2026 16:28:51 GMT+0200 (Mitteleuropäische Sommerzeit)").
* Bewusst lokale Zeit ohne UTC-Umrechnung – der Text soll zeigen, was der
* Nutzer in Thunderbird sieht.
*/
dateToText(value) {
const d = value instanceof Date ? value : new Date(value);
if (isNaN(d)) return String(value);
const p = (n) => String(n).padStart(2, "0");
const day = `${p(d.getDate())}.${p(d.getMonth() + 1)}.${d.getFullYear()}`;
if (!d.getHours() && !d.getMinutes() && !d.getSeconds()) return day;
return `${day} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
},
_field(name, value, type) {
const t = (type || "Text").toLowerCase();
// Datum in einem Text-/Memo-Feld lesbar machen (s. dateToText).
const asText = (v) => (v instanceof Date ? this.dateToText(v) : String(v));
// WICHTIG: "datetime" zuerst prüfen – "DateTime".includes("date") ist true und
// würde sonst die Uhrzeit auf Mitternacht normalisieren (Datenverlust).
if (t.includes("datetime")) {
const d = value instanceof Date ? value : new Date(value);
if (isNaN(d)) return { FieldName: name, Item: String(value), ItemElementName: "DateTime" };
// Mit Uhrzeit: echter Zeitpunkt, daher unverändert als ISO (UTC) senden.
// LOKALE Wanduhrzeit ohne Zeitzonen-Suffix ("2026-08-04T16:28:51").
//
// UNGEPRÜFT gegen den Server: ob DocuWare das "Z" auswertet (dann stimmt die
// Uhrzeit) oder die ISO-Zeichenkette als lokale Wanduhrzeit übernimmt (dann
// erscheint sie um den UTC-Versatz verschoben, in Wien -2 h). Reine Date-Felder
// sind davon nicht betroffen, die werden auf UTC-Mitternacht normalisiert.
// Falls im Testarchiv verschoben: hier stattdessen die lokale Wanduhrzeit ohne
// Zeitzone senden, also `${toLocalIso(d)}` statt d.toISOString().
return { FieldName: name, Item: d.toISOString(), ItemElementName: "DateTime" };
// Vorher ging hier d.toISOString() raus, also UTC mit "Z". Ob DocuWare das
// "Z" in Serverzeit zurückrechnet, hängt vom Server ab (Cloud-Instanzen
// laufen meist auf UTC) – die Uhrzeit erschien dann um den Versatz
// verschoben, in Wien -2 h. Schlimmer beim Kalendertag: lokale Mitternacht
// wird als UTC zum VORTAG 22:00.
// Ohne Suffix ist der Wert eindeutig: der Server übernimmt ihn so, wie der
// Nutzer die Zeit in Thunderbird sieht. Reine Date-Felder bleiben unberührt.
const p = (n) => String(n).padStart(2, "0");
const iso =
`${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}` +
`T${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
return { FieldName: name, Item: iso, ItemElementName: "DateTime" };
}
if (t.includes("date")) {
// DocuWare erwartet /Date(ms)/ oder ISO; ISO funktioniert bei Platform.
@ -461,13 +486,13 @@ const DocuWare = {
return { FieldName: name, Item: Number(value), ItemElementName: "Int" };
}
if (t.includes("memo")) {
return { FieldName: name, Item: String(value), ItemElementName: "Memo" };
return { FieldName: name, Item: asText(value), ItemElementName: "Memo" };
}
if (t.includes("keyword")) {
const arr = Array.isArray(value) ? value : [String(value)];
const arr = Array.isArray(value) ? value : [asText(value)];
return { FieldName: name, Item: arr, ItemElementName: "Keywords" };
}
return { FieldName: name, Item: String(value), ItemElementName: "String" };
return { FieldName: name, Item: asText(value), ItemElementName: "String" };
},
_isSystemField(dbName) {

View File

@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "DocuWare Ablage",
"description": "Legt markierte E-Mails (als .eml, PDF und mit Anhängen) in DocuWare ab.",
"version": "0.12.0",
"version": "0.12.3",
"author": "l.lingler",
"applications": {
"gecko": {

View File

@ -108,7 +108,8 @@
.map-row.has-const .map-left { flex: 0 0 250px; }
.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-right input { width: 100%; padding: 7px 9px; }
.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 {
@ -227,6 +228,8 @@
<button id="mapAdd" class="ghost" type="button">+ Zeile</button>
<button id="mapReset" class="ghost" type="button">Auf Standard zurücksetzen</button>
</div>
<div class="hint" style="margin-top:8px">Steht ein Feld nicht in der Liste,
„<strong>✎ Anderer Feldname</strong>" wählen und den Namen eintippen.</div>
<div class="hint" style="margin-top:8px">Als Mail-Attribut „<strong>Fester Wert</strong>"
wählen, um einen festen Text (z. B. <code>NEU</code>) ins Feld zu schreiben.</div>

View File

@ -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,23 +199,53 @@ function mapValueState(value) {
return { sel: "", konst: "" };
}
// Befüllt ein Feldname-Dropdown mit den Archiv-Feldern + dem aktuellen Wert (bleibt erhalten).
// 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-row")].map(rowFieldName);
const all = [...(availableFields || []), ...Object.keys(DEFAULT_FIELD_MAP), ...used];
return [...new Set(all.filter(Boolean))].sort();
}
// 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([...(availableFields || []), current].filter(Boolean))].sort().forEach((n) => {
[...new Set([...fieldSuggestions(), current].filter(Boolean))].sort().forEach((n) => {
const o = document.createElement("option");
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 || "";
}
// Aktualisiert alle Feldname-Dropdowns auf die aktuelle Archiv-Feldliste (Auswahl bleibt).
// Alle Dropdowns auf die aktuelle Liste bringen (Auswahl bleibt). Zeilen mit
// freier Eingabe bleiben unangetastet.
function refreshFieldSelects() {
document.querySelectorAll("#mapBody .map-field").forEach((sel) => fillFieldSelect(sel, sel.value));
document.querySelectorAll("#mapBody .map-field-sel").forEach((sel) => {
if (sel.value === CUSTOM_OPT) return;
fillFieldSelect(sel, sel.value);
});
}
function addMapRow(field = "", value = "") {
@ -196,11 +253,33 @@ function addMapRow(field = "", value = "") {
const row = document.createElement("div");
row.className = "map-row" + (st.sel === CONST_OPT ? " has-const" : "");
// Links gestapelt: Feldname (Dropdown) über Mail-Attribut.
// Links gestapelt: Feldname (Dropdown, bei Bedarf freie Eingabe) über Mail-Attribut.
const left = document.createElement("div"); left.className = "map-left";
const fieldSel = document.createElement("select"); fieldSel.className = "map-field";
// Feldname wird ausgewählt. Die Liste ist immer gefüllt (Archiv-Cache +
// Standardnamen), und „✎ Anderer Feldname" blendet eine freie Eingabe ein –
// für Felder, die in keiner Liste stehen.
const fieldSel = document.createElement("select");
fieldSel.className = "map-field-sel";
const own = document.createElement("input");
own.type = "text"; own.className = "map-field-custom";
own.placeholder = "Eigener Feldname, z. B. EML_SUBJECT";
own.hidden = true;
fillFieldSelect(fieldSel, field);
if (field && fieldSel.value !== field) {
// Sicherheitsnetz: Name nicht im Dropdown gelandet -> als freie Eingabe zeigen,
// statt ihn stillschweigend zu verlieren.
fieldSel.value = CUSTOM_OPT; own.value = field; own.hidden = false;
}
fieldSel.addEventListener("change", () => {
const isOwn = fieldSel.value === CUSTOM_OPT;
own.hidden = !isOwn;
if (isOwn) own.focus();
});
own.addEventListener("change", refreshFieldSelects);
const src = document.createElement("select"); src.className = "map-source";
const none = document.createElement("option"); none.value = ""; none.textContent = "— nichts —";
@ -216,7 +295,7 @@ function addMapRow(field = "", value = "") {
if (st.sel === CONST_OPT) co.selected = true;
src.appendChild(co);
left.append(fieldSel, src);
left.append(fieldSel, own, src);
// Rechts (breit): fester Wert – nur sichtbar bei „Fester Wert".
const right = document.createElement("div"); right.className = "map-right";
@ -242,12 +321,13 @@ function addMapRow(field = "", value = "") {
function loadMapping(map) {
$("mapBody").innerHTML = "";
Object.entries(map || {}).forEach(([f, v]) => addMapRow(f, v));
refreshFieldSelects();
}
function collectMapping() {
const map = {};
document.querySelectorAll("#mapBody .map-row").forEach((row) => {
const f = row.querySelector(".map-field").value.trim();
const f = rowFieldName(row);
if (!f) return; // ohne Feldname ignorieren
const sel = row.querySelector(".map-source").value;
if (sel === CONST_OPT) {
@ -355,19 +435,32 @@ $("mappingPull").addEventListener("click", async () => {
}
});
// Archiv wählen -> dessen Indexfelder als Vorschläge (datalist) für alle Feldnamen.
// Archiv wählen -> dessen Indexfelder als Vorschläge für alle Feldname-Eingaben.
$("mapCabinet").addEventListener("change", async () => {
const id = $("mapCabinet").value;
const st = $("mapFieldStatus");
if (!id) { availableFields = []; refreshFieldSelects(); st.textContent = ""; return; }
st.textContent = "Lade Felder …";
// Gemerkte Liste sofort anzeigen – dann ist die Auswahl auch ohne Anmeldung da.
if (Array.isArray(fieldsByCabinet[id])) {
availableFields = fieldsByCabinet[id];
refreshFieldSelects();
st.textContent = `${availableFields.length} Felder zur Auswahl (gemerkt).`;
} else {
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; }
if (!ok) {
if (!availableFields.length) st.textContent = "Erst „Verbindung testen“ (Passwort nötig).";
return; // gemerkte Liste bleibt stehen
}
availableFields = (await DocuWare.cabinetFieldNames(s, id))
.filter((n) => n && !/^DW[A-Z]/.test(n)) // DW*-Systemfelder ausblenden
.sort();
await rememberFields(id, availableFields);
refreshFieldSelects();
st.textContent = `${availableFields.length} Felder zur Auswahl geladen.`;
} catch (e) {

View File

@ -45,6 +45,18 @@
{
"version": "0.12.0",
"update_link": "https://sylyx.xyz/sylyx/thunderbird2docuware/releases/download/v0.12.0/docuware-ablage-0.12.0.xpi"
},
{
"version": "0.12.1",
"update_link": "https://sylyx.xyz/sylyx/thunderbird2docuware/releases/download/v0.12.1/docuware-ablage-0.12.1.xpi"
},
{
"version": "0.12.2",
"update_link": "https://sylyx.xyz/sylyx/thunderbird2docuware/releases/download/v0.12.2/docuware-ablage-0.12.2.xpi"
},
{
"version": "0.12.3",
"update_link": "https://sylyx.xyz/sylyx/thunderbird2docuware/releases/download/v0.12.3/docuware-ablage-0.12.3.xpi"
}
]
}