thunderbird2docuware/background/background.js
sylyx 73693fc381 Feature: „bereits abgelegt"-Erkennung + Dedup über Message-ID (v0.10.0)
Erster C2O-Parität-Schritt. Die RFC822-Message-ID wird normalisiert ins Feld
EML_ID geschrieben und zur Dubletten-Erkennung genutzt.

- mail.js: rfcMessageId aus den Mail-Headern
- docuware.js: normalizeMessageId, cabinetFieldNames, getSearchDialogId,
  findByMessageId (DialogExpression, Format gem. Postman-Collection), documentViewUrl
- background.js: ✓-Badge am Nachrichten-Button beim Öffnen (Suche im Standard-
  schrank, Cache, archive:done-Refresh); lädt dafür lib/docuware.js
- dialog: EML_ID beim Upload mitschreiben; „bereits abgelegt"-Banner mit
  „Dokument öffnen"; Dubletten-Warnung beim Ablegen (warnen, „Trotzdem ablegen")

UX-Entscheidung: Prüfung beim Öffnen (1 Schrank) + Duplikat warnen/erlauben.
Schema bestätigt (EML_ID existiert). TB-Runtime-Test steht noch aus.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 09:03:48 +02:00

170 lines
6.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Hintergrund-Skript: Button + Kontextmenü, öffnet den Ablage-Dialog.
// Zusätzlich Auth-Broker: hält den OAuth-Token nur im Speicher dieser TB-Sitzung
// (siehe auth.js). Das Passwort wird einmalig zum Login durchgereicht, daraus ein
// Token geholt und sofort verworfen es wird NIE gespeichert.
const DIALOG_URL = "dialog/dialog.html";
// Auth-Broker: Dialog-/Optionsfenster fragen hier den Sitzungs-Token an bzw.
// melden sich mit dem Passwort an. So muss das Passwort nur 1x pro Sitzung getippt
// werden und liegt nirgends auf der Platte.
browser.runtime.onMessage.addListener((msg) => {
if (!msg || !msg.type) return undefined;
if (msg.type === "auth:status") {
return Promise.resolve({ hasToken: Auth.hasValidToken(), token: Auth.currentToken() });
}
if (msg.type === "auth:logon") {
return (async () => {
try {
const settings = await Settings.get();
if (!settings.serverUrl) throw new Error("Keine DocuWare-Server-URL konfiguriert.");
Auth.reset();
const r = await Auth.logon({ ...settings, password: msg.password || "" });
// Passwort wird hier verworfen nur der Token bleibt im Speicher.
return { ok: true, mode: r.mode, token: Auth.currentToken() };
} catch (e) {
return { ok: false, error: String((e && e.message) || e) };
}
})();
}
if (msg.type === "auth:logout") {
Auth.reset();
return Promise.resolve({ ok: true });
}
// Dialog meldet eine erfolgreiche Ablage -> Badge-Cache aktualisieren, damit
// die Mail beim nächsten Anzeigen sofort als „abgelegt" erscheint.
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 });
}
return Promise.resolve({ ok: true });
}
return undefined;
});
// --- „Bereits abgelegt"-Badge am Nachrichten-Button ------------------------
// Beim Anzeigen einer Mail im Standard-Schrank nach der Message-ID suchen und das
// Ergebnis als Badge (✓) am messageDisplayAction-Button zeigen. Pro Message-ID +
// Schrank wird das Ergebnis im Speicher gecacht (eine Suche pro Mail).
const ARCHIVED_CACHE = new Map(); // "messageId|cabinetId" -> { found, docId }
async function rfcMessageIdOf(messageId) {
try {
const full = await browser.messages.getFull(messageId);
const h = full && full.headers && full.headers["message-id"];
if (Array.isArray(h) && h.length) return String(h[0]).trim();
} catch (_) {}
return "";
}
function setBadge(tabId, found) {
try {
browser.messageDisplayAction.setBadgeText({ tabId, text: found ? "✓" : "" });
browser.messageDisplayAction.setTitle({
tabId,
title: found
? "Bereits in DocuWare abgelegt klicken zum Prüfen/Ablegen"
: "In DocuWare ablegen",
});
if (found && browser.messageDisplayAction.setBadgeBackgroundColor) {
browser.messageDisplayAction.setBadgeBackgroundColor({ tabId, color: "#0e8a8a" });
}
} catch (_) {}
}
async function updateArchivedBadge(tab, message) {
const tabId = tab.id;
try {
if (!Auth.hasValidToken()) return setBadge(tabId, false); // ohne Login keine Suche
const settings = await Settings.get();
if (!settings.serverUrl || !settings.defaultCabinetId) return setBadge(tabId, false);
const mid = DocuWare.normalizeMessageId(await rfcMessageIdOf(message.id));
if (!mid) return setBadge(tabId, false);
const key = `${mid}|${settings.defaultCabinetId}`;
let info = ARCHIVED_CACHE.get(key);
if (!info) {
const hit = await DocuWare.findByMessageId(settings, settings.defaultCabinetId, mid).catch(
() => null
);
info = { found: !!hit, docId: hit ? hit.id : null };
ARCHIVED_CACHE.set(key, info);
}
setBadge(tabId, info.found);
} catch (_) {
setBadge(tabId, false);
}
}
browser.messageDisplay.onMessagesDisplayed.addListener((tab, displayed) => {
const msgs = Array.isArray(displayed) ? displayed : (displayed && displayed.messages) || [];
if (msgs.length === 1) updateArchivedBadge(tab, msgs[0]);
else setBadge(tab.id, false); // bei Mehrfachauswahl keine eindeutige Aussage
});
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,
});
});
}
// Button in der geöffneten Nachricht
browser.messageDisplayAction.onClicked.addListener(async (tab) => {
const msg = await browser.messageDisplay.getDisplayedMessage(tab.id);
if (msg) openDialog([msg.id]);
});
// Kontextmenü-Eintrag in der Nachrichtenliste
browser.menus.create({
id: "docuware-archive",
title: "In DocuWare ablegen",
contexts: ["message_list"],
});
browser.menus.onClicked.addListener((info) => {
if (info.menuItemId !== "docuware-archive") return;
const msgs = info.selectedMessages && info.selectedMessages.messages;
if (msgs && msgs.length > 0) openDialog(msgs.map((m) => m.id));
});
// Tastenkürzel (Strg+Alt+I): angezeigte bzw. alle markierten Nachrichten ablegen.
browser.commands.onCommand.addListener(async (command) => {
if (command !== "open-archive-dialog") return;
const ids = await currentMessageIds();
if (ids.length) openDialog(ids);
});
// Ermittelt die "aktiven" Nachrichten: erst die im Reader angezeigte, sonst
// alle im Nachrichtenlisten-Tab markierten.
async function currentMessageIds() {
try {
const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
if (tab) {
const shown = await browser.messageDisplay
.getDisplayedMessage(tab.id)
.catch(() => null);
if (shown) return [shown.id];
}
const sel = await browser.mailTabs.getSelectedMessages().catch(() => null);
if (sel && sel.messages && sel.messages.length) return sel.messages.map((m) => m.id);
} catch (_) {}
return [];
}