Compare commits
2 Commits
2194cc2d56
...
1a358a96ef
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a358a96ef | |||
| 81964c76d3 |
@ -39,10 +39,17 @@ function effectiveFieldMap() {
|
||||
return m && Object.keys(m).length ? m : DEFAULT_FIELD_MAP;
|
||||
}
|
||||
|
||||
// Vorbefüllwert für ein DocuWare-Feld aus den Mail-Metadaten (oder undefined).
|
||||
// Vorbefüllwert für ein DocuWare-Feld (oder undefined). Das Mapping kann je Feld
|
||||
// entweder einen Mail-Token (String) ODER einen festen Wert ({ const: "NEU" }) tragen.
|
||||
function prefillFor(fieldName, meta) {
|
||||
const token = effectiveFieldMap()[fieldName];
|
||||
const fn = token && TOKEN_FN[token];
|
||||
const m = effectiveFieldMap()[fieldName];
|
||||
if (!m) return undefined;
|
||||
if (typeof m === "object") {
|
||||
if (m.const !== undefined && m.const !== null && m.const !== "") return m.const;
|
||||
if (m.token && TOKEN_FN[m.token]) return TOKEN_FN[m.token](meta);
|
||||
return undefined;
|
||||
}
|
||||
const fn = TOKEN_FN[m];
|
||||
return fn ? fn(meta) : undefined;
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,10 @@ const DEFAULTS = {
|
||||
mailFormat: "eml", // E-Mail wird als "eml" ODER "pdf" abgelegt (nie beides)
|
||||
storeAttachments: true,
|
||||
tagOnSuccess: true,
|
||||
fieldMapping: {}, // DocuWare-Feldname -> Mail-Attribut (Token). Leer = DEFAULT_FIELD_MAP.
|
||||
// DocuWare-Feldname -> Wert: entweder ein Mail-Token ("subject") ODER ein fester
|
||||
// Wert ({ const: "NEU" }). Leer = DEFAULT_FIELD_MAP.
|
||||
fieldMapping: {},
|
||||
mappingUrl: "", // optional: raw-JSON-URL (Gitea/GitHub) zum Nachladen des Mappings.
|
||||
};
|
||||
|
||||
// Mögliche Mail-Attribute für die Vorbefüllung (Token + Anzeigename).
|
||||
|
||||
@ -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.4",
|
||||
"version": "0.10.5",
|
||||
"author": "l.lingler",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
|
||||
@ -110,6 +110,22 @@
|
||||
}
|
||||
.icon-btn:hover { color: var(--err); border-color: var(--err); }
|
||||
|
||||
/* Teilen / JSON */
|
||||
details.share { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 10px; }
|
||||
details.share summary {
|
||||
cursor: pointer; font-size: 13px; font-weight: 600; color: var(--accent);
|
||||
list-style: none; user-select: none; padding: 2px 0;
|
||||
}
|
||||
details.share summary::-webkit-details-marker { display: none; }
|
||||
details.share summary::before { content: "▸ "; color: var(--muted); }
|
||||
details.share[open] summary::before { content: "▾ "; }
|
||||
textarea {
|
||||
width: 100%; padding: 10px 11px; color: var(--text); background: var(--surface-2);
|
||||
border: 1px solid var(--border-strong); border-radius: 8px; box-sizing: border-box;
|
||||
font: 12px/1.45 ui-monospace, "Cascadia Code", Consolas, monospace; resize: vertical; outline: none;
|
||||
}
|
||||
textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-weak); }
|
||||
|
||||
/* Buttons */
|
||||
button { font: inherit; cursor: pointer; border-radius: 8px; }
|
||||
button.primary {
|
||||
@ -206,6 +222,28 @@
|
||||
<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">Als Mail-Attribut „<strong>Fester Wert</strong>"
|
||||
wählen, um einen festen Text (z. B. <code>NEU</code>) ins Feld zu schreiben.</div>
|
||||
|
||||
<details class="share">
|
||||
<summary>Mapping teilen / übernehmen</summary>
|
||||
|
||||
<label for="mappingUrl">Aus Repo/URL laden</label>
|
||||
<div class="map-toolbar">
|
||||
<input type="url" id="mappingUrl" class="grow" placeholder="https://gitea.example/…/mapping.json" />
|
||||
<button id="mappingPull" class="ghost" type="button">Von URL laden</button>
|
||||
</div>
|
||||
<div class="hint">Eine raw-JSON-Datei aus einem Gitea/GitHub-Repo. Lädt das Mapping in
|
||||
den Editor – mit „Speichern" übernehmen. (Lesen/Pull; Zurückschreiben ins Repo nicht.)</div>
|
||||
|
||||
<label for="mappingJson">JSON (kopieren / einfügen)</label>
|
||||
<textarea id="mappingJson" rows="6" placeholder='{ "EML_SUBJECT": "subject", "STATUS": { "const": "NEU" } }'></textarea>
|
||||
<div class="map-toolbar">
|
||||
<button id="mappingExport" class="ghost" type="button">Aktuelles als JSON</button>
|
||||
<button id="mappingCopy" class="ghost" type="button">Kopieren</button>
|
||||
<button id="mappingApply" class="ghost" type="button">JSON anwenden</button>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ async function load() {
|
||||
FIELDS.forEach((f) => ($(f).value = s[f] || ""));
|
||||
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 || [];
|
||||
@ -81,7 +82,20 @@ async function ensureToken() {
|
||||
|
||||
// --- Feld-Mapping-Editor ---------------------------------------------------
|
||||
|
||||
function addMapRow(field = "", token = "") {
|
||||
const CONST_OPT = "__const__"; // Sentinel im Quelle-Dropdown für „Fester Wert"
|
||||
|
||||
// Zerlegt einen Mapping-Wert (Token-String ODER { const } / { token }) in den UI-Zustand.
|
||||
function mapValueState(value) {
|
||||
if (value && typeof value === "object") {
|
||||
if (value.const != null) return { sel: CONST_OPT, konst: String(value.const) };
|
||||
if (value.token) return { sel: value.token, konst: "" };
|
||||
}
|
||||
if (typeof value === "string" && value) return { sel: value, konst: "" };
|
||||
return { sel: "", konst: "" };
|
||||
}
|
||||
|
||||
function addMapRow(field = "", value = "") {
|
||||
const st = mapValueState(value);
|
||||
const tr = document.createElement("tr");
|
||||
|
||||
const inp = document.createElement("input");
|
||||
@ -90,16 +104,31 @@ function addMapRow(field = "", token = "") {
|
||||
inp.setAttribute("list", "fieldNames"); // Vorschläge aus gewähltem Archiv
|
||||
const tdF = document.createElement("td"); tdF.appendChild(inp);
|
||||
|
||||
const sel = document.createElement("select"); sel.className = "map-token";
|
||||
const sel = document.createElement("select"); sel.className = "map-source";
|
||||
const none = document.createElement("option"); none.value = ""; none.textContent = "— nichts —";
|
||||
sel.appendChild(none);
|
||||
MAIL_TOKENS.forEach((t) => {
|
||||
const o = document.createElement("option");
|
||||
o.value = t.key; o.textContent = t.label;
|
||||
if (t.key === token) o.selected = true;
|
||||
if (t.key === st.sel) o.selected = true;
|
||||
sel.appendChild(o);
|
||||
});
|
||||
const tdT = document.createElement("td"); tdT.appendChild(sel);
|
||||
const co = document.createElement("option");
|
||||
co.value = CONST_OPT; co.textContent = "★ Fester Wert";
|
||||
if (st.sel === CONST_OPT) co.selected = true;
|
||||
sel.appendChild(co);
|
||||
|
||||
const konst = document.createElement("input");
|
||||
konst.type = "text"; konst.className = "map-const"; konst.placeholder = "z. B. NEU";
|
||||
konst.value = st.konst; konst.style.marginTop = "6px";
|
||||
konst.style.display = st.sel === CONST_OPT ? "" : "none";
|
||||
|
||||
sel.addEventListener("change", () => {
|
||||
konst.style.display = sel.value === CONST_OPT ? "" : "none";
|
||||
if (sel.value === CONST_OPT) konst.focus();
|
||||
});
|
||||
|
||||
const tdT = document.createElement("td"); tdT.appendChild(sel); tdT.appendChild(konst);
|
||||
|
||||
const del = document.createElement("button");
|
||||
del.type = "button"; del.className = "icon-btn"; del.textContent = "✕"; del.title = "Zeile entfernen";
|
||||
@ -112,15 +141,21 @@ function addMapRow(field = "", token = "") {
|
||||
|
||||
function loadMapping(map) {
|
||||
$("mapBody").innerHTML = "";
|
||||
Object.entries(map || {}).forEach(([f, t]) => addMapRow(f, t));
|
||||
Object.entries(map || {}).forEach(([f, v]) => addMapRow(f, v));
|
||||
}
|
||||
|
||||
function collectMapping() {
|
||||
const map = {};
|
||||
document.querySelectorAll("#mapBody tr").forEach((tr) => {
|
||||
const f = tr.querySelector(".map-field").value.trim();
|
||||
const t = tr.querySelector(".map-token").value;
|
||||
if (f && t) map[f] = t; // leere Feldnamen/Token ignorieren
|
||||
if (!f) return; // ohne Feldname ignorieren
|
||||
const sel = tr.querySelector(".map-source").value;
|
||||
if (sel === CONST_OPT) {
|
||||
const c = tr.querySelector(".map-const").value;
|
||||
if (c !== "") map[f] = { const: c }; // fester Wert
|
||||
} else if (sel) {
|
||||
map[f] = sel; // Mail-Token (als String, abwärtskompatibel)
|
||||
}
|
||||
});
|
||||
return map;
|
||||
}
|
||||
@ -132,6 +167,7 @@ function collect() {
|
||||
partial.defaultCabinetId = $("defaultCabinet").value;
|
||||
partial.mailFormat = getMailFormat();
|
||||
partial.fieldMapping = collectMapping();
|
||||
partial.mappingUrl = $("mappingUrl").value.trim();
|
||||
return partial;
|
||||
}
|
||||
|
||||
@ -162,6 +198,53 @@ $("test").addEventListener("click", async () => {
|
||||
$("mapAdd").addEventListener("click", () => addMapRow());
|
||||
$("mapReset").addEventListener("click", () => loadMapping(DEFAULT_FIELD_MAP));
|
||||
|
||||
// --- Mapping teilen: JSON anzeigen / kopieren / anwenden / von URL laden ----
|
||||
|
||||
$("mappingExport").addEventListener("click", () => {
|
||||
$("mappingJson").value = JSON.stringify(collectMapping(), null, 2);
|
||||
setStatus("Aktuelles Mapping als JSON angezeigt.", "ok");
|
||||
});
|
||||
|
||||
$("mappingCopy").addEventListener("click", async () => {
|
||||
let txt = $("mappingJson").value.trim();
|
||||
if (!txt) { txt = JSON.stringify(collectMapping(), null, 2); $("mappingJson").value = txt; }
|
||||
try {
|
||||
await navigator.clipboard.writeText(txt);
|
||||
setStatus("In Zwischenablage kopiert.", "ok");
|
||||
} catch (_) {
|
||||
$("mappingJson").select(); document.execCommand("copy");
|
||||
setStatus("In Zwischenablage kopiert.", "ok");
|
||||
}
|
||||
});
|
||||
|
||||
$("mappingApply").addEventListener("click", () => {
|
||||
try {
|
||||
const obj = JSON.parse($("mappingJson").value);
|
||||
if (!obj || typeof obj !== "object" || Array.isArray(obj)) throw new Error("kein JSON-Objekt");
|
||||
loadMapping(obj);
|
||||
setStatus("JSON ins Mapping übernommen – mit „Speichern“ sichern.", "ok");
|
||||
} catch (e) {
|
||||
setStatus(`Ungültiges JSON: ${e.message}`, "err");
|
||||
}
|
||||
});
|
||||
|
||||
$("mappingPull").addEventListener("click", async () => {
|
||||
const url = $("mappingUrl").value.trim();
|
||||
if (!url) { setStatus("Bitte eine URL eingeben.", "err"); return; }
|
||||
setStatus("Lade Mapping …");
|
||||
try {
|
||||
const res = await fetch(url, { cache: "no-store" });
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const obj = await res.json();
|
||||
if (!obj || typeof obj !== "object" || Array.isArray(obj)) throw new Error("kein JSON-Objekt");
|
||||
loadMapping(obj);
|
||||
await Settings.set({ mappingUrl: url }); // URL gleich merken
|
||||
setStatus("Mapping von URL geladen – mit „Speichern“ übernehmen.", "ok");
|
||||
} catch (e) {
|
||||
setStatus(`Laden fehlgeschlagen: ${e.message}`, "err");
|
||||
}
|
||||
});
|
||||
|
||||
// Archiv wählen -> dessen Indexfelder als Vorschläge (datalist) für alle Feldnamen.
|
||||
$("mapCabinet").addEventListener("change", async () => {
|
||||
const id = $("mapCabinet").value;
|
||||
|
||||
@ -21,6 +21,10 @@
|
||||
{
|
||||
"version": "0.10.4",
|
||||
"update_link": "https://sylyx.xyz/sylyx/thunderbird2docuware/releases/download/v0.10.4/docuware-ablage-0.10.4.xpi"
|
||||
},
|
||||
{
|
||||
"version": "0.10.5",
|
||||
"update_link": "https://sylyx.xyz/sylyx/thunderbird2docuware/releases/download/v0.10.5/docuware-ablage-0.10.5.xpi"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user