Compare commits
2 Commits
d02228e4c9
...
6ca1e01b46
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ca1e01b46 | |||
| e201e6c654 |
@ -30,8 +30,19 @@ browser.runtime.onMessage.addListener((msg) => {
|
||||
})();
|
||||
}
|
||||
|
||||
// Sitzung verwerfen. Immer: Token + Badge-Cache (der Cache ist serverspezifisch
|
||||
// und würde sonst ein ✓ aus der alten Verbindung weiterzeigen).
|
||||
// scope:"server" = die Server-URL hat sich geändert -> zusätzlich die gecachte
|
||||
// Schrankliste wegwerfen, denn deren GUIDs gehören zum alten Server.
|
||||
if (msg.type === "auth:logout") {
|
||||
Auth.reset();
|
||||
ARCHIVED_CACHE.clear();
|
||||
if (msg.scope === "server") {
|
||||
return browser.storage.local
|
||||
.remove("cabinets")
|
||||
.catch(() => {})
|
||||
.then(() => ({ ok: true }));
|
||||
}
|
||||
return Promise.resolve({ ok: true });
|
||||
}
|
||||
|
||||
@ -40,7 +51,7 @@ browser.runtime.onMessage.addListener((msg) => {
|
||||
if (msg.type === "archive:done") {
|
||||
const mid = DocuWare.normalizeMessageId(msg.rfcMessageId);
|
||||
if (mid && msg.cabinetId) {
|
||||
ARCHIVED_CACHE.set(`${mid}|${msg.cabinetId}`, { found: true, docId: msg.docId || null });
|
||||
cacheArchived(`${mid}|${msg.cabinetId}`, { found: true, docId: msg.docId || null });
|
||||
}
|
||||
return Promise.resolve({ ok: true });
|
||||
}
|
||||
@ -54,6 +65,16 @@ browser.runtime.onMessage.addListener((msg) => {
|
||||
// Schrank wird das Ergebnis im Speicher gecacht (eine Suche pro Mail).
|
||||
|
||||
const ARCHIVED_CACHE = new Map(); // "messageId|cabinetId" -> { found, docId }
|
||||
const ARCHIVED_CACHE_MAX = 500; // Obergrenze: Thunderbird-Sitzungen laufen wochenlang
|
||||
|
||||
// Schreibt in den Badge-Cache und hält ihn klein (ältester Eintrag fliegt raus –
|
||||
// Map-Iteration ist Einfügereihenfolge).
|
||||
function cacheArchived(key, info) {
|
||||
if (ARCHIVED_CACHE.size >= ARCHIVED_CACHE_MAX && !ARCHIVED_CACHE.has(key)) {
|
||||
ARCHIVED_CACHE.delete(ARCHIVED_CACHE.keys().next().value);
|
||||
}
|
||||
ARCHIVED_CACHE.set(key, info);
|
||||
}
|
||||
|
||||
async function rfcMessageIdOf(messageId) {
|
||||
try {
|
||||
@ -95,7 +116,7 @@ async function updateArchivedBadge(tab, message) {
|
||||
() => null
|
||||
);
|
||||
info = { found: !!hit, docId: hit ? hit.id : null };
|
||||
ARCHIVED_CACHE.set(key, info);
|
||||
cacheArchived(key, info);
|
||||
}
|
||||
setBadge(tabId, info.found);
|
||||
} catch (_) {
|
||||
@ -110,19 +131,15 @@ browser.messageDisplay.onMessagesDisplayed.addListener((tab, displayed) => {
|
||||
});
|
||||
|
||||
function openDialog(messageIds) {
|
||||
const ids = Array.isArray(messageIds) ? messageIds : [messageIds];
|
||||
// Pro Nachricht ein eigenes Ablagefenster (leicht versetzt gestapelt).
|
||||
ids.forEach((id, i) => {
|
||||
const url = browser.runtime.getURL(`${DIALOG_URL}?messageId=${id}`);
|
||||
browser.windows.create({
|
||||
url,
|
||||
type: "popup",
|
||||
width: 720,
|
||||
height: 820,
|
||||
left: 100 + i * 28,
|
||||
top: 80 + i * 28,
|
||||
});
|
||||
});
|
||||
const ids = (Array.isArray(messageIds) ? messageIds : [messageIds])
|
||||
.map((id) => parseInt(id, 10))
|
||||
.filter((n) => !isNaN(n));
|
||||
if (!ids.length) return;
|
||||
// EIN Ablagefenster für die gesamte Auswahl – dialog.js arbeitet die Liste
|
||||
// nacheinander ab (Fortschritt „Mail 3/20"). Früher wurde pro Nachricht ein
|
||||
// eigenes Fenster geöffnet, was bei größerer Auswahl unbrauchbar war.
|
||||
const url = browser.runtime.getURL(`${DIALOG_URL}?messageIds=${ids.join(",")}`);
|
||||
browser.windows.create({ url, type: "popup", width: 720, height: 820, left: 100, top: 80 });
|
||||
}
|
||||
|
||||
// Button in der geöffneten Nachricht
|
||||
|
||||
@ -15,7 +15,14 @@ let STATE = {
|
||||
fields: [], // Felddefinitionen des Store-Dialogs
|
||||
existingDoc: null, // Treffer der Dedup-Suche im gewählten Schrank (oder null)
|
||||
cabinetFieldsByCab: {}, // Cache: cabinetId -> [Feldnamen] (für EML_ID-Erkennung)
|
||||
uploadedIds: new Set(), // bereits erfolgreich abgelegte Nachrichten (für Retry-Schutz)
|
||||
// Vollständig abgelegte Nachrichten je Ziel ("<cab>|<mid>") – Tag/Badge nur einmal.
|
||||
// Ziel im Schlüssel, damit dieselbe Mail bewusst in ein zweites Archiv abgelegt
|
||||
// werden kann, ohne den Dialog neu zu öffnen.
|
||||
uploadedIds: new Set(),
|
||||
// Bereits hochgeladene EINZELTEILE, damit ein Retry nach einem Teilfehler nichts
|
||||
// doppelt hochlädt. Schlüssel inkl. Ziel-Schrank ("<cab>|<mid>|eml"), damit ein
|
||||
// Wechsel des Ziels nach einem Fehlversuch wieder alles hochlädt.
|
||||
uploadedParts: new Set(),
|
||||
};
|
||||
|
||||
// Mail-Token -> Funktion, die den Wert aus den Mail-Metadaten liefert.
|
||||
@ -88,10 +95,12 @@ async function init() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (STATE.messageIds.length > 1) {
|
||||
if (isMulti()) {
|
||||
$("multiNote").textContent =
|
||||
`${STATE.messageIds.length} E-Mails werden abgelegt. ` +
|
||||
"Eingetragene Werte gelten für alle; Absender/Betreff/Datum werden je E-Mail einzeln übernommen.";
|
||||
"Eingetragene Werte gelten für alle; zugeordnete Felder (Absender/Betreff/Datum …) " +
|
||||
"werden je E-Mail einzeln übernommen. Anhänge werden – falls aktiviert – je E-Mail " +
|
||||
"vollständig mit abgelegt. Angezeigt sind die Werte der ersten E-Mail.";
|
||||
}
|
||||
|
||||
renderAttachments();
|
||||
@ -165,10 +174,21 @@ function askPassword(errMsg) {
|
||||
function applyDefaults() {
|
||||
// Mail-Format (.eml ODER PDF) aus den Einstellungen.
|
||||
setMailFormat(STATE.settings.mailFormat === "pdf" ? "pdf" : "eml");
|
||||
// Anhänge-Haken nur sinnvoll, wenn die Mail überhaupt Anhänge hat.
|
||||
const hasAtt = STATE.attachments.length > 0;
|
||||
const att = $("optAtt");
|
||||
att.checked = hasAtt && STATE.settings.storeAttachments !== false;
|
||||
const want = STATE.settings.storeAttachments !== false;
|
||||
if (isMulti()) {
|
||||
// Mehrfachauswahl: STATE.attachments gehört nur zur ERSTEN Mail. Daraus darf
|
||||
// nicht geschlossen werden, ob die übrigen Anhänge haben – sonst bliebe der
|
||||
// Haken deaktiviert und die Anhänge aller anderen Mails fielen still weg.
|
||||
// Ob es etwas hochzuladen gibt, entscheidet submit() je Mail einzeln.
|
||||
att.checked = want;
|
||||
att.disabled = false;
|
||||
$("attList").style.display = "none";
|
||||
return;
|
||||
}
|
||||
// Einzelmail: Haken nur sinnvoll, wenn die Mail überhaupt Anhänge hat.
|
||||
const hasAtt = STATE.attachments.length > 0;
|
||||
att.checked = hasAtt && want;
|
||||
att.disabled = !hasAtt;
|
||||
$("attList").style.display = hasAtt && att.checked ? "" : "none";
|
||||
}
|
||||
@ -186,6 +206,10 @@ function getMailFormat() {
|
||||
function renderAttachments() {
|
||||
const ul = $("attList");
|
||||
ul.innerHTML = "";
|
||||
// Die Einzelauswahl der Anhänge gilt nur für EINE Mail. Bei Mehrfachauswahl
|
||||
// würden hier die Anhänge der ersten Mail stehen, die Haken aber nichts tun
|
||||
// (submit() legt dort je Mail alle Anhänge ab) -> Liste bleibt leer/verborgen.
|
||||
if (isMulti()) return;
|
||||
STATE.attachments.forEach((a, i) => {
|
||||
// Bewusst per DOM-API (nicht innerHTML): der Anhang-Name stammt aus der Mail
|
||||
// und ist angreiferkontrolliert – als Text einsetzen, nie als HTML.
|
||||
@ -304,6 +328,9 @@ async function onCabinetChange() {
|
||||
async function checkExisting() {
|
||||
STATE.existingDoc = null;
|
||||
hideArchivedBanner();
|
||||
// Bei Mehrfachauswahl nicht prüfen: der Banner bezöge sich nur auf die erste
|
||||
// Mail, würde aber wie eine Aussage über die ganze Auswahl wirken.
|
||||
if (isMulti()) return;
|
||||
if (STATE.isBasket || !STATE.cabinetId || !STATE.meta || !STATE.meta.rfcMessageId) return;
|
||||
try {
|
||||
const names = await cabinetFieldNames(STATE.cabinetId);
|
||||
@ -577,7 +604,7 @@ async function submit() {
|
||||
|
||||
// Dubletten-Warnung (nur Einzelmail): liegt die Mail im Ziel schon, warnen –
|
||||
// „Trotzdem ablegen" bleibt aber möglich.
|
||||
if (STATE.messageIds.length === 1 && !STATE.isBasket && STATE.meta.rfcMessageId) {
|
||||
if (!isMulti() && !STATE.isBasket && STATE.meta.rfcMessageId) {
|
||||
let hit = STATE.existingDoc;
|
||||
if (!hit) {
|
||||
const names = await cabinetFieldNames(STATE.cabinetId);
|
||||
@ -598,16 +625,18 @@ async function submit() {
|
||||
await saveOptionPrefs(fmt, wantAttScope); // Sticky: Format + Anhänge-Wahl merken
|
||||
|
||||
$("submit").disabled = true;
|
||||
const multi = STATE.messageIds.length > 1;
|
||||
const multi = isMulti();
|
||||
let tagFailed = false;
|
||||
let current = ""; // zuletzt bearbeitete Mail – für eine brauchbare Fehlermeldung
|
||||
try {
|
||||
const fieldNames = STATE.isBasket ? [] : await cabinetFieldNames(STATE.cabinetId);
|
||||
let mi = 0;
|
||||
for (const mid of STATE.messageIds) {
|
||||
mi++;
|
||||
// Retry-Schutz: nach einem Teilfehler nicht erneut hochladen (sonst Dubletten).
|
||||
if (STATE.uploadedIds.has(mid)) continue;
|
||||
// Retry-Schutz: in DIESES Ziel bereits vollständig abgelegt -> überspringen.
|
||||
if (STATE.uploadedIds.has(`${STATE.cabinetId}|${mid}`)) continue;
|
||||
const prefix = multi ? `Mail ${mi}/${STATE.messageIds.length}: ` : "";
|
||||
current = multi ? `Mail ${mi}/${STATE.messageIds.length}` : "";
|
||||
// Meta/Anhänge der jeweiligen Nachricht.
|
||||
const meta = mid === STATE.messageId ? STATE.meta : await Mail.getMeta(mid);
|
||||
const atts =
|
||||
@ -622,14 +651,18 @@ async function submit() {
|
||||
const wantAtt = wantAttScope && atts.length > 0;
|
||||
|
||||
if (wantEml) {
|
||||
status(`${prefix}E-Mail …`);
|
||||
const eml = await Mail.getEmlFile(mid, meta.subject);
|
||||
await DocuWare.uploadDocument(STATE.settings, STATE.cabinetId, eml, eml.name, fields);
|
||||
await uploadOnce(`${mid}|eml`, async () => {
|
||||
status(`${prefix}E-Mail …`);
|
||||
const eml = await Mail.getEmlFile(mid, meta.subject);
|
||||
await DocuWare.uploadDocument(STATE.settings, STATE.cabinetId, eml, eml.name, fields);
|
||||
});
|
||||
}
|
||||
if (wantPdf) {
|
||||
status(`${prefix}PDF …`);
|
||||
const pdf = Pdf.fromMail(meta);
|
||||
await DocuWare.uploadDocument(STATE.settings, STATE.cabinetId, pdf, pdf.name, fields);
|
||||
await uploadOnce(`${mid}|pdf`, async () => {
|
||||
status(`${prefix}PDF …`);
|
||||
const pdf = Pdf.fromMail(meta);
|
||||
await DocuWare.uploadDocument(STATE.settings, STATE.cabinetId, pdf, pdf.name, fields);
|
||||
});
|
||||
}
|
||||
if (wantAtt) {
|
||||
// Bei Einzelmail: angehakte Anhänge. Bei Mehrfach: alle Anhänge je Mail.
|
||||
@ -637,10 +670,12 @@ async function submit() {
|
||||
for (const i of indices) {
|
||||
const a = atts[i];
|
||||
if (!a) continue;
|
||||
status(`${prefix}Anhang ${a.name} …`);
|
||||
const file = await Mail.getAttachmentFile(mid, a.partName);
|
||||
const af = STATE.isBasket ? {} : { ...fields, DOC_FILE_NAME: { value: a.name, type: "Text" } };
|
||||
await DocuWare.uploadDocument(STATE.settings, STATE.cabinetId, file, a.name, af);
|
||||
await uploadOnce(`${mid}|att:${a.partName}`, async () => {
|
||||
status(`${prefix}Anhang ${a.name} …`);
|
||||
const file = await Mail.getAttachmentFile(mid, a.partName);
|
||||
const af = STATE.isBasket ? {} : { ...fields, DOC_FILE_NAME: { value: a.name, type: "Text" } };
|
||||
await DocuWare.uploadDocument(STATE.settings, STATE.cabinetId, file, a.name, af);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -656,19 +691,33 @@ async function submit() {
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
// Diese Nachricht ist vollständig abgelegt -> bei einem späteren Retry überspringen.
|
||||
STATE.uploadedIds.add(mid);
|
||||
// In diesem Ziel vollständig abgelegt -> bei einem späteren Retry überspringen.
|
||||
STATE.uploadedIds.add(`${STATE.cabinetId}|${mid}`);
|
||||
}
|
||||
|
||||
const base = multi ? `${STATE.messageIds.length} E-Mails abgelegt.` : "Erfolgreich abgelegt.";
|
||||
status(tagFailed ? `${base} (Markierung nicht möglich)` : base, "ok");
|
||||
setTimeout(() => window.close(), tagFailed ? 2500 : 1200);
|
||||
} catch (e) {
|
||||
status(`Fehler beim Ablegen: ${e.message}`, "err");
|
||||
// Bei Mehrfachauswahl sagen, WO es hakte – und dass ein erneuter Klick die
|
||||
// bereits übertragenen E-Mails/Teile überspringt (kein Dublettenrisiko).
|
||||
const where = current ? ` (${current})` : "";
|
||||
const hint = multi ? " Bereits abgelegtes wird bei erneutem Klick übersprungen." : "";
|
||||
status(`Fehler beim Ablegen${where}: ${e.message}${hint}`, "err");
|
||||
$("submit").disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Lädt einen Teil (eml/pdf/Anhang) genau EINMAL hoch. Schlägt ein späterer Teil
|
||||
// fehl und der Nutzer klickt erneut auf „Ablegen", werden die bereits erfolgreich
|
||||
// übertragenen Teile übersprungen – sonst entstünden Dubletten in DocuWare.
|
||||
async function uploadOnce(part, doUpload) {
|
||||
const key = `${STATE.cabinetId}|${part}`;
|
||||
if (STATE.uploadedParts.has(key)) return;
|
||||
await doUpload();
|
||||
STATE.uploadedParts.add(key);
|
||||
}
|
||||
|
||||
// Indexfelder für eine konkrete Nachricht: gemappte Felder (EML_*/generisch)
|
||||
// werden je Mail aus deren Metadaten neu berechnet, manuelle Werte bleiben.
|
||||
function fieldsForMessage(baseResult, meta, multi) {
|
||||
@ -720,6 +769,8 @@ async function tagMessage(messageId) {
|
||||
}
|
||||
|
||||
// --- Helfer ---
|
||||
// Mehrfachauswahl? Steuert Anhang-Liste, Dubletten-Banner und Feldberechnung.
|
||||
const isMulti = () => STATE.messageIds.length > 1;
|
||||
const isMemo = (f) => /memo/i.test(f.type) || f.name === "EML_BODY";
|
||||
// Lokales Datum als YYYY-MM-DD (NICHT toISOString -> das rechnet auf UTC um und
|
||||
// kippt eine Uhrzeit nahe Mitternacht auf den Vortag).
|
||||
@ -763,7 +814,7 @@ document.querySelectorAll('input[name="mailFormat"]').forEach((r) =>
|
||||
r.addEventListener("change", () => saveOptionPrefs(getMailFormat(), $("optAtt").checked))
|
||||
);
|
||||
$("optAtt").addEventListener("change", () => {
|
||||
const show = $("optAtt").checked && STATE.attachments.length > 0;
|
||||
const show = !isMulti() && $("optAtt").checked && STATE.attachments.length > 0;
|
||||
$("attList").style.display = show ? "" : "none";
|
||||
saveOptionPrefs(getMailFormat(), $("optAtt").checked);
|
||||
});
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
// DocuWare Platform REST-Client.
|
||||
// Alle Pfade relativ zu {serverUrl}/DocuWare/Platform.
|
||||
// Auth läuft per Session-Cookie (siehe auth.js); daher überall credentials:"include".
|
||||
// Auth primär per Bearer-Token (Identity Service) über Auth.authHeaders(); das
|
||||
// credentials:"include" überall ist für den Cookie-Fallback alter On-Prem-Server
|
||||
// nötig (siehe auth.js) und stört im Token-Modus nicht.
|
||||
|
||||
const DocuWare = {
|
||||
async _get(settings, path) {
|
||||
@ -10,13 +12,32 @@ const DocuWare = {
|
||||
credentials: "include",
|
||||
headers: { Accept: "application/json", ...Auth.authHeaders() },
|
||||
});
|
||||
if (!res.ok) {
|
||||
const t = await res.text().catch(() => "");
|
||||
throw new Error(`GET ${path} -> HTTP ${res.status}. ${t}`.trim());
|
||||
}
|
||||
if (!res.ok) throw await this._httpError(res, `GET ${path}`);
|
||||
return res.json();
|
||||
},
|
||||
|
||||
/**
|
||||
* Baut aus einer fehlgeschlagenen Antwort einen sprechenden Fehler.
|
||||
* 401/403 sind der häufigste Fall (Token abgelaufen oder Zugangsdaten/Server
|
||||
* gewechselt) – der nackte Statuscode hilft dem Anwender dabei nicht weiter.
|
||||
*/
|
||||
async _httpError(res, what) {
|
||||
const body = (await res.text().catch(() => "")).slice(0, 300);
|
||||
if (res.status === 401) {
|
||||
return new Error(
|
||||
"Nicht angemeldet (HTTP 401). Die DocuWare-Sitzung ist abgelaufen oder die " +
|
||||
"Zugangsdaten stimmen nicht. Bitte das Ablagefenster neu öffnen bzw. in den " +
|
||||
"Einstellungen „Verbindung testen“."
|
||||
);
|
||||
}
|
||||
if (res.status === 403) {
|
||||
return new Error(
|
||||
"Keine Berechtigung (HTTP 403). Der Benutzer darf diese Aktion in DocuWare nicht ausführen."
|
||||
);
|
||||
}
|
||||
return new Error(`${what} -> HTTP ${res.status}. ${body}`.trim());
|
||||
},
|
||||
|
||||
/**
|
||||
* Liste der Ablageziele: Archive UND Briefkörbe.
|
||||
* @returns {Promise<Array<{id, name, color, isBasket}>>}
|
||||
@ -304,10 +325,7 @@ const DocuWare = {
|
||||
headers: { Accept: "application/json", ...Auth.authHeaders() },
|
||||
body: form,
|
||||
});
|
||||
if (!res.ok) {
|
||||
const t = await res.text().catch(() => "");
|
||||
throw new Error(`Upload fehlgeschlagen (HTTP ${res.status}). ${t}`.trim());
|
||||
}
|
||||
if (!res.ok) throw await this._httpError(res, "Upload fehlgeschlagen");
|
||||
const doc = await res.json().catch(() => ({}));
|
||||
return { id: doc.Id, raw: doc };
|
||||
},
|
||||
|
||||
@ -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.10.8",
|
||||
"version": "0.11.0",
|
||||
"author": "l.lingler",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
|
||||
@ -20,9 +20,54 @@ function insecureWarning(url) {
|
||||
: "";
|
||||
}
|
||||
|
||||
// Schnappschuss der auth-relevanten Felder (Server/Org/Benutzer) zum Zeitpunkt
|
||||
// des letzten Speicherns/Ladens. Ändert sich davon etwas, ist ein evtl. noch im
|
||||
// Broker liegender Token ungültig (er gehört zu den alten Daten) -> verwerfen.
|
||||
let authSnapshot = { serverUrl: "", organization: "", username: "" };
|
||||
const AUTH_FIELDS = ["serverUrl", "organization", "username"];
|
||||
|
||||
// Server-URLs nur für den Vergleich vereinheitlichen, damit ein rein kosmetischer
|
||||
// Edit (Schrägstrich am Ende, Groß-/Kleinschreibung) nicht als Wechsel zählt.
|
||||
const normUrl = (u) => String(u || "").trim().replace(/\/+$/, "").toLowerCase();
|
||||
|
||||
function snapshotAuth(s) {
|
||||
authSnapshot = { serverUrl: s.serverUrl || "", organization: s.organization || "", username: s.username || "" };
|
||||
}
|
||||
function authChanged(s) {
|
||||
return AUTH_FIELDS.some((k) => (s[k] || "") !== (authSnapshot[k] || ""));
|
||||
}
|
||||
// Nur ein echter Server-Wechsel entwertet die Schrank-GUIDs (Schrankliste,
|
||||
// Standardziel, gemerkte Ablagedialoge). Org-/Benutzer-Wechsel tut das nicht.
|
||||
function serverChanged(s) {
|
||||
return normUrl(s.serverUrl) !== normUrl(authSnapshot.serverUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nach dem Speichern aufräumen, wenn sich Zugangsdaten geändert haben:
|
||||
* Token immer verwerfen; bei Server-Wechsel zusätzlich alles wegwerfen, was
|
||||
* Schrank-GUIDs des alten Servers enthält (sonst zeigt der Ablage-Dialog fremde
|
||||
* Archive bzw. legt gegen eine nicht mehr existierende GUID ab).
|
||||
* @returns {Promise<object>} die ggf. bereinigten Einstellungen
|
||||
*/
|
||||
async function invalidateSession(s) {
|
||||
if (!authChanged(s)) { snapshotAuth(s); return s; }
|
||||
const server = serverChanged(s);
|
||||
await browser.runtime
|
||||
.sendMessage({ type: "auth:logout", scope: server ? "server" : "auth" })
|
||||
.catch(() => {});
|
||||
let next = s;
|
||||
if (server) {
|
||||
next = await Settings.set({ defaultCabinetId: "", dialogByCabinet: {} });
|
||||
fillCabinets([], ""); // Dropdowns leeren – die alte Liste gilt nicht mehr
|
||||
}
|
||||
snapshotAuth(next);
|
||||
return next;
|
||||
}
|
||||
|
||||
async function load() {
|
||||
const s = await Settings.get();
|
||||
FIELDS.forEach((f) => ($(f).value = s[f] || ""));
|
||||
snapshotAuth(s);
|
||||
CHECKS.forEach((c) => ($(c).checked = !!s[c]));
|
||||
setMailFormat(s.mailFormat === "pdf" ? "pdf" : "eml");
|
||||
$("mappingUrl").value = s.mappingUrl || "";
|
||||
@ -93,15 +138,23 @@ function fillCabinets(cabinets, selectedId) {
|
||||
// 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 pw = $("password").value;
|
||||
// Wurde ein Passwort eingegeben, IMMER frisch anmelden: ein evtl. noch im Broker
|
||||
// liegender Token könnte zu früheren Zugangsdaten/Server gehören und würde sonst
|
||||
// zu „GET /FileCabinets -> HTTP 401" führen (bis zum Thunderbird-Neustart).
|
||||
// Der Broker (background.js) ruft bei auth:logon zuerst Auth.reset() und liest die
|
||||
// gerade gespeicherten Einstellungen neu.
|
||||
if (pw) {
|
||||
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;
|
||||
}
|
||||
// Kein Passwort getippt: vorhandenen Sitzungs-Token vom Broker übernehmen.
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- Feld-Mapping-Editor ---------------------------------------------------
|
||||
@ -222,15 +275,22 @@ function collect() {
|
||||
|
||||
$("save").addEventListener("click", async () => {
|
||||
const data = collect();
|
||||
await Settings.set(data);
|
||||
const saved = await Settings.set(data);
|
||||
// Server/Org/Benutzer geändert? Dann gehört ein evtl. noch im Broker liegender
|
||||
// Token zu den ALTEN Daten -> verwerfen, damit Ablage/Test sich neu anmelden.
|
||||
const wasServerChange = serverChanged(saved); // vor dem Neu-Schnappschuss prüfen
|
||||
await invalidateSession(saved);
|
||||
const warn = insecureWarning(data.serverUrl);
|
||||
setStatus("Gespeichert." + warn, warn ? "err" : "ok");
|
||||
const note = wasServerChange ? " Neuer Server – bitte Verbindung testen und Standardziel neu wählen." : "";
|
||||
setStatus("Gespeichert." + note + warn, warn ? "err" : "ok");
|
||||
});
|
||||
|
||||
$("test").addEventListener("click", async () => {
|
||||
setStatus("Verbinde …");
|
||||
try {
|
||||
const s = await Settings.set(collect()); // erst speichern (ohne Passwort), dann testen
|
||||
// Erst speichern (ohne Passwort), dann testen. Bei geänderten Zugangsdaten
|
||||
// vorher den alten Broker-Token verwerfen (sonst HTTP 401).
|
||||
const s = await invalidateSession(await Settings.set(collect()));
|
||||
const ok = await ensureToken();
|
||||
if (!ok) { setStatus("Bitte Passwort eingeben.", "err"); return; }
|
||||
const cabinets = await DocuWare.listCabinets(s);
|
||||
|
||||
@ -37,6 +37,10 @@
|
||||
{
|
||||
"version": "0.10.8",
|
||||
"update_link": "https://sylyx.xyz/sylyx/thunderbird2docuware/releases/download/v0.10.8/docuware-ablage-0.10.8.xpi"
|
||||
},
|
||||
{
|
||||
"version": "0.11.0",
|
||||
"update_link": "https://sylyx.xyz/sylyx/thunderbird2docuware/releases/download/v0.11.0/docuware-ablage-0.11.0.xpi"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user