Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e314a6af98 | |||
| 03ff3795b2 | |||
| d9ec2537e2 | |||
| f6c7761182 |
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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.1",
|
||||
"version": "0.12.3",
|
||||
"author": "l.lingler",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
|
||||
@ -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 @@
|
||||
|
||||
<div class="map-hint">DocuWare-Feld → Mail-Attribut · fester Wert</div>
|
||||
<div id="mapBody" class="map"></div>
|
||||
<!-- Vorschlagsliste fuer alle Feldname-Eingaben (siehe refreshFieldSelects). -->
|
||||
<datalist id="fieldNameList"></datalist>
|
||||
|
||||
<div class="map-toolbar" style="margin-top:10px">
|
||||
<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>
|
||||
|
||||
|
||||
@ -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 <datalist> 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 <select>: die Archiv-Feldliste
|
||||
// gibt es erst nach Anmeldung, ein starres Dropdown wäre bis dahin leer und die
|
||||
// Zeile unbenutzbar (ohne Feldnamen wirft collectMapping sie beim Speichern weg).
|
||||
const fieldSel = document.createElement("input");
|
||||
fieldSel.type = "text";
|
||||
fieldSel.className = "map-field";
|
||||
fieldSel.setAttribute("list", "fieldNameList");
|
||||
fieldSel.placeholder = "DocuWare-Feldname, z. B. EML_SUBJECT";
|
||||
fieldSel.value = field || "";
|
||||
fieldSel.addEventListener("change", refreshFieldSelects);
|
||||
// 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 —";
|
||||
@ -227,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";
|
||||
@ -259,7 +327,7 @@ function loadMapping(map) {
|
||||
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) {
|
||||
@ -372,14 +440,27 @@ $("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) {
|
||||
|
||||
@ -49,6 +49,14 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user