/** * options.js - Einstellungsseite. * * Beim Speichern wird zusätzlich das Zugriffsrecht für genau die * eingetragene Adresse angefordert. Es steht bewusst nicht pauschal im * Manifest: Eine Erweiterung, die von Haus aus jede Adresse erreichen * darf, ist eine unnötig grosse Angriffsfläche. */ import { ladeEinstellungen, speichereEinstellungen } from "../lib/settings.js"; import { uebersetzeSeite, ruf, fehlerAnzeigen, meldungAnzeigen } from "./gemeinsam.js"; const $ = (id) => document.getElementById(id); const FELDER = ["baseUrl", "token", "versionLabel", "shareDays", "shareTag", "ignorierteKleiner"]; document.addEventListener("DOMContentLoaded", async () => { uebersetzeSeite(); const s = await ladeEinstellungen(); for (const f of FELDER) if ($(f)) $(f).value = s[f] ?? ""; $("speichern").addEventListener("click", speichern); $("pruefen").addEventListener("click", pruefen); await rechtAnzeigen(); }); /** * Zustand des Zugriffsrechts anzeigen. * * Es ist die häufigste Ursache für ein scheiterndes fetch, und ohne * Anzeige merkt man erst beim ersten Ablegen, dass es fehlt. */ async function rechtAnzeigen() { // Diese Anzeige ist Beiwerk. Fehlt das Element - etwa weil eine ältere // options.html geladen ist -, darf das nicht das Speichern verhindern. const ziel = $("recht"); if (!ziel) return; const url = $("baseUrl").value.trim(); if (!url) { ziel.textContent = ""; return; } let origin; try { origin = new URL(url).origin + "/*"; } catch { ziel.className = "fehler"; ziel.textContent = browser.i18n.getMessage("errBadUrl") || "Ungültige Adresse."; return; } const erteilt = await browser.permissions.contains({ origins: [origin] }); ziel.className = erteilt ? "hinweis" : "fehler"; ziel.textContent = erteilt ? `Zugriffsrecht erteilt für ${origin}` : `Zugriffsrecht fehlt für ${origin} — auf Speichern klicken.`; } function gelesen() { return { baseUrl: $("baseUrl").value.trim().replace(/\/+$/, ""), token: $("token").value.trim(), versionLabel: $("versionLabel").value.trim() || "Thunderbird {datum}", shareDays: Number($("shareDays").value || 0), shareTag: $("shareTag").value.trim(), ignorierteKleiner: Number($("ignorierteKleiner").value || 0) }; } /** * Einstellungen speichern und das Zugriffsrecht anfordern. * * WICHTIG: permissions.request MUSS als erstes stehen, noch vor jedem * await. Der Aufruf ist nur aus einer Benutzeraktion heraus zulässig, und * diese Eigenschaft geht beim ersten await verloren - die Folge wäre * "permissions.request may only be called from a user input handler". * * Ein vorgeschaltetes permissions.contains() ist deshalb ausgeschlossen. * Das ist auch nicht nötig: request() löst keine Abfrage aus, wenn das * Recht bereits erteilt ist, sondern liefert sofort true. */ function speichern() { const werte = gelesen(); let origin; try { origin = new URL(werte.baseUrl).origin + "/*"; } catch { fehlerAnzeigen(browser.i18n.getMessage("errBadUrl") || "Ungültige Adresse. Erwartet wird https://rechner oder http://rechner."); return; } // Kein await davor. Die Auswertung läuft in der Zusage. browser.permissions.request({ origins: [origin] }) .then(async (erteilt) => { if (!erteilt) { fehlerAnzeigen(browser.i18n.getMessage("errNoPermission") || "Ohne Zugriffsrecht auf die Adresse kann die Erweiterung nicht arbeiten."); await rechtAnzeigen(); return; } await speichereEinstellungen(werte); meldungAnzeigen(browser.i18n.getMessage("msgSaved") || "Gespeichert."); // Anzeige und Prüfung sind Beiwerk; ein Fehler darin soll nicht // aussehen, als wäre das Speichern gescheitert. try { await rechtAnzeigen(); } catch { /* egal */ } try { await pruefen(); } catch { /* egal */ } }) .catch((err) => fehlerAnzeigen(err.message)); } async function pruefen() { $("status").className = "hinweis"; $("status").textContent = "…"; try { await speichereEinstellungen(gelesen()); const treffer = await ruf("suchen", { query: "" }); $("status").textContent = browser.i18n.getMessage("msgConnectionOk", [String(treffer.length)]) || `Verbindung steht (${treffer.length} Dokumente sichtbar).`; } catch (err) { $("status").className = "fehler"; $("status").textContent = String(err.message).slice(0, 200); } }