Feld-Zuordnung: Dateiname, Datum+Uhrzeit, Benutzer und weitere Werte
MAIL_TOKENS wächst von 11 auf 22 auswählbare Werte:
- Dokument: fileName (je Dokument), attachmentCount, rfcMessageId
- Datum/Zeit: dateTime, time, archiveDate, archiveDateTime
- Wer/Woher: dwUser, dwOrganization, accountEmail, folder
fileName kann nicht wie die übrigen Tokens einmal je Mail aufgelöst werden: die
.eml/PDF trägt den Mailnamen, jeder Anhang seinen eigenen. Darum tragen TOKEN_FN
jetzt ein zweites Argument ctx {fileName, attachmentCount}, und Felder, die auf
ein Token aus PER_FILE_TOKENS gemappt sind, werden über applyPerFileFields() vor
JEDEM Upload neu gesetzt. Im Formular steht nur eine Vorschau (Tooltip erklärt
das), die beim Umschalten .eml <-> PDF nachzieht.
Fix DateTime-Felder: _field prüfte t.includes("date") zuerst, und "DateTime"
enthält "date" — jedes DocuWare-DateTime-Feld wurde damit auf UTC-Mitternacht
normalisiert und verlor die Uhrzeit. Das betraf auch bestehende Zuordnungen.
Jetzt wird datetime zuerst geprüft; der Dialog rendert solche Felder als
datetime-local und liest sie lokal zurück. Date-Felder bleiben unverändert auf
dem lokalen Kalendertag als UTC-Mitternacht.
Offen und im Code vermerkt: ob DocuWare bei DateTime das "Z" auswertet oder die
ISO-Zeichenkette als lokale Wanduhrzeit liest, ist nicht gegen einen Server
verifiziert.
Mail.getMeta liefert zusätzlich folder und accountEmail; _accountForMessage
wurde zu _accountInfo (Name + Identitäts-Adresse).
DEFAULT_FIELD_MAP: DOC_FILE_NAME -> fileName.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
6ca1e01b46
commit
7e351a818a
140
dialog/dialog.js
140
dialog/dialog.js
@ -27,6 +27,8 @@ let STATE = {
|
|||||||
|
|
||||||
// Mail-Token -> Funktion, die den Wert aus den Mail-Metadaten liefert.
|
// Mail-Token -> Funktion, die den Wert aus den Mail-Metadaten liefert.
|
||||||
// Welches DocuWare-Feld welchen Token bekommt, steht im Mapping (s. effectiveFieldMap).
|
// Welches DocuWare-Feld welchen Token bekommt, steht im Mapping (s. effectiveFieldMap).
|
||||||
|
// Zweites Argument ctx trägt Werte, die nicht an der Mail hängen, sondern am
|
||||||
|
// konkreten Upload: { fileName, attachmentCount }.
|
||||||
const TOKEN_FN = {
|
const TOKEN_FN = {
|
||||||
senderEmail: (m) => m.senderEmail,
|
senderEmail: (m) => m.senderEmail,
|
||||||
senderName: (m) => m.senderName,
|
senderName: (m) => m.senderName,
|
||||||
@ -39,8 +41,34 @@ const TOKEN_FN = {
|
|||||||
direction: (m) => m.direction,
|
direction: (m) => m.direction,
|
||||||
size: (m) => m.sizeBytes,
|
size: (m) => m.sizeBytes,
|
||||||
account: (m) => m.account,
|
account: (m) => m.account,
|
||||||
|
|
||||||
|
// Datum/Zeit. date liefert den Kalendertag, dateTime den echten Zeitpunkt –
|
||||||
|
// in DocuWare braucht letzteres ein Feld vom Typ DateTime, sonst schneidet
|
||||||
|
// der Server die Uhrzeit ab.
|
||||||
|
dateTime: (m) => m.date,
|
||||||
|
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()); },
|
||||||
|
archiveDateTime: () => new Date(),
|
||||||
|
|
||||||
|
// Dokumentbezogen
|
||||||
|
fileName: (m, ctx) => (ctx && ctx.fileName) || "",
|
||||||
|
attachmentCount: (m, ctx) => (ctx && ctx.attachmentCount) || 0,
|
||||||
|
rfcMessageId: (m) => DocuWare.normalizeMessageId(m.rfcMessageId),
|
||||||
|
|
||||||
|
// Wer/Woher – aus den Einstellungen bzw. dem Thunderbird-Konto.
|
||||||
|
dwUser: () => (STATE.settings && STATE.settings.username) || "",
|
||||||
|
dwOrganization: () => (STATE.settings && STATE.settings.organization) || "",
|
||||||
|
accountEmail: (m) => m.accountEmail || "",
|
||||||
|
folder: (m) => m.folder || "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Tokens, deren Wert vom einzelnen hochgeladenen Dokument abhängt. Felder, die
|
||||||
|
// darauf gemappt sind, werden je Upload neu gesetzt – sonst trüge jeder Anhang
|
||||||
|
// den Dateinamen der E-Mail.
|
||||||
|
const PER_FILE_TOKENS = new Set(["fileName"]);
|
||||||
|
|
||||||
// Aktives Feld-Mapping: eigenes (aus den Einstellungen) wenn gesetzt, sonst Default.
|
// Aktives Feld-Mapping: eigenes (aus den Einstellungen) wenn gesetzt, sonst Default.
|
||||||
function effectiveFieldMap() {
|
function effectiveFieldMap() {
|
||||||
const m = STATE.settings && STATE.settings.fieldMapping;
|
const m = STATE.settings && STATE.settings.fieldMapping;
|
||||||
@ -49,16 +77,23 @@ function effectiveFieldMap() {
|
|||||||
|
|
||||||
// Vorbefüllwert für ein DocuWare-Feld (oder undefined). Das Mapping kann je Feld
|
// 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.
|
// entweder einen Mail-Token (String) ODER einen festen Wert ({ const: "NEU" }) tragen.
|
||||||
function prefillFor(fieldName, meta) {
|
function prefillFor(fieldName, meta, ctx) {
|
||||||
const m = effectiveFieldMap()[fieldName];
|
const m = effectiveFieldMap()[fieldName];
|
||||||
if (!m) return undefined;
|
if (!m) return undefined;
|
||||||
if (typeof m === "object") {
|
if (typeof m === "object") {
|
||||||
if (m.const !== undefined && m.const !== null && m.const !== "") return m.const;
|
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);
|
if (m.token && TOKEN_FN[m.token]) return TOKEN_FN[m.token](meta, ctx);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const fn = TOKEN_FN[m];
|
const fn = TOKEN_FN[m];
|
||||||
return fn ? fn(meta) : undefined;
|
return fn ? fn(meta, ctx) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auf welchen Token ist ein Feld gemappt? (String-Form oder { token }.)
|
||||||
|
function tokenOf(fieldName) {
|
||||||
|
const m = effectiveFieldMap()[fieldName];
|
||||||
|
if (typeof m === "string") return m;
|
||||||
|
return m && typeof m === "object" ? m.token : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function status(msg, kind) {
|
function status(msg, kind) {
|
||||||
@ -461,6 +496,10 @@ function buildInput(f) {
|
|||||||
// Nur Listenwerte erlaubt -> starres Dropdown.
|
// Nur Listenwerte erlaubt -> starres Dropdown.
|
||||||
el = document.createElement("select");
|
el = document.createElement("select");
|
||||||
el.innerHTML = '<option value=""></option>';
|
el.innerHTML = '<option value=""></option>';
|
||||||
|
} else if (t.includes("datetime")) {
|
||||||
|
// DateTime-Feld: Tag UND Uhrzeit erfassbar (vor dem reinen date-Zweig prüfen).
|
||||||
|
el = document.createElement("input");
|
||||||
|
el.type = "datetime-local";
|
||||||
} else if (t.includes("date")) {
|
} else if (t.includes("date")) {
|
||||||
el = document.createElement("input");
|
el = document.createElement("input");
|
||||||
el.type = "date";
|
el.type = "date";
|
||||||
@ -493,10 +532,18 @@ function applyFieldDefaults() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function prefillFields() {
|
function prefillFields() {
|
||||||
|
const ctx = formCtx();
|
||||||
STATE.fields.forEach((f) => {
|
STATE.fields.forEach((f) => {
|
||||||
const val = prefillFor(f.name, STATE.meta);
|
const val = prefillFor(f.name, STATE.meta, ctx);
|
||||||
if (val === undefined || val === null || val === "") return;
|
if (val === undefined || val === null || val === "") return;
|
||||||
setFieldValue(f.name, f, val);
|
setFieldValue(f.name, f, val);
|
||||||
|
// Dateiname-Felder sind nur eine Vorschau: beim Ablegen trägt jeder Anhang
|
||||||
|
// seinen eigenen Namen. Im Tooltip erklären, statt still zu überschreiben.
|
||||||
|
if (PER_FILE_TOKENS.has(tokenOf(f.name))) {
|
||||||
|
const wrap = document.querySelector(`.field[data-name="${cssEsc(f.name)}"]`);
|
||||||
|
const el = wrap && wrap.querySelector("[data-role='value']");
|
||||||
|
if (el) el.title = "Wird beim Ablegen je Dokument gesetzt (Anhänge tragen ihren eigenen Dateinamen).";
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,9 +552,9 @@ function setFieldValue(name, f, val) {
|
|||||||
const wrap = document.querySelector(`.field[data-name="${cssEsc(name)}"]`);
|
const wrap = document.querySelector(`.field[data-name="${cssEsc(name)}"]`);
|
||||||
const el = wrap && wrap.querySelector("[data-role='value']");
|
const el = wrap && wrap.querySelector("[data-role='value']");
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
if (el.type === "date") {
|
if (el.type === "date" || el.type === "datetime-local") {
|
||||||
const d = val instanceof Date ? val : new Date(val);
|
const d = val instanceof Date ? val : new Date(val);
|
||||||
if (!isNaN(d)) el.value = toDateInput(d);
|
if (!isNaN(d)) el.value = el.type === "date" ? toDateInput(d) : toDateTimeInput(d);
|
||||||
} else if (el.tagName === "SELECT") {
|
} else if (el.tagName === "SELECT") {
|
||||||
let opt = Array.from(el.options).find((o) => o.value === String(val));
|
let opt = Array.from(el.options).find((o) => o.value === String(val));
|
||||||
if (!opt) {
|
if (!opt) {
|
||||||
@ -576,8 +623,12 @@ function collectFieldValues() {
|
|||||||
}
|
}
|
||||||
if (String(raw).trim() !== "") {
|
if (String(raw).trim() !== "") {
|
||||||
// Date-Input als LOKALE Mitternacht parsen (raw+"T00:00:00"), sonst
|
// Date-Input als LOKALE Mitternacht parsen (raw+"T00:00:00"), sonst
|
||||||
// interpretiert new Date("YYYY-MM-DD") als UTC.
|
// interpretiert new Date("YYYY-MM-DD") als UTC. datetime-local ist bereits
|
||||||
const value = el.type === "date" ? new Date(`${raw}T00:00:00`) : raw;
|
// lokal und wird direkt geparst.
|
||||||
|
const value =
|
||||||
|
el.type === "date" ? new Date(`${raw}T00:00:00`)
|
||||||
|
: el.type === "datetime-local" ? new Date(raw)
|
||||||
|
: raw;
|
||||||
result[name] = { value, type };
|
result[name] = { value, type };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -643,7 +694,8 @@ async function submit() {
|
|||||||
mid === STATE.messageId ? STATE.attachments : await Mail.listAttachments(mid);
|
mid === STATE.messageId ? STATE.attachments : await Mail.listAttachments(mid);
|
||||||
|
|
||||||
// Indexfelder: manuelle Werte für alle gleich; gemappte Felder je Mail neu.
|
// Indexfelder: manuelle Werte für alle gleich; gemappte Felder je Mail neu.
|
||||||
const fields = STATE.isBasket ? {} : fieldsForMessage(result, meta, multi);
|
const msgCtx = { attachmentCount: atts.length };
|
||||||
|
const fields = STATE.isBasket ? {} : fieldsForMessage(result, meta, multi, msgCtx);
|
||||||
// Message-ID für Dedup/„bereits abgelegt" mitschreiben (wenn Schrank EML_ID hat).
|
// Message-ID für Dedup/„bereits abgelegt" mitschreiben (wenn Schrank EML_ID hat).
|
||||||
if (!STATE.isBasket && meta.rfcMessageId && fieldNames.includes("EML_ID")) {
|
if (!STATE.isBasket && meta.rfcMessageId && fieldNames.includes("EML_ID")) {
|
||||||
fields.EML_ID = { value: DocuWare.normalizeMessageId(meta.rfcMessageId), type: "Text" };
|
fields.EML_ID = { value: DocuWare.normalizeMessageId(meta.rfcMessageId), type: "Text" };
|
||||||
@ -654,14 +706,16 @@ async function submit() {
|
|||||||
await uploadOnce(`${mid}|eml`, async () => {
|
await uploadOnce(`${mid}|eml`, async () => {
|
||||||
status(`${prefix}E-Mail …`);
|
status(`${prefix}E-Mail …`);
|
||||||
const eml = await Mail.getEmlFile(mid, meta.subject);
|
const eml = await Mail.getEmlFile(mid, meta.subject);
|
||||||
await DocuWare.uploadDocument(STATE.settings, STATE.cabinetId, eml, eml.name, fields);
|
const ef = applyPerFileFields(fields, meta, { ...msgCtx, fileName: eml.name });
|
||||||
|
await DocuWare.uploadDocument(STATE.settings, STATE.cabinetId, eml, eml.name, ef);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (wantPdf) {
|
if (wantPdf) {
|
||||||
await uploadOnce(`${mid}|pdf`, async () => {
|
await uploadOnce(`${mid}|pdf`, async () => {
|
||||||
status(`${prefix}PDF …`);
|
status(`${prefix}PDF …`);
|
||||||
const pdf = Pdf.fromMail(meta);
|
const pdf = Pdf.fromMail(meta);
|
||||||
await DocuWare.uploadDocument(STATE.settings, STATE.cabinetId, pdf, pdf.name, fields);
|
const pf = applyPerFileFields(fields, meta, { ...msgCtx, fileName: pdf.name });
|
||||||
|
await DocuWare.uploadDocument(STATE.settings, STATE.cabinetId, pdf, pdf.name, pf);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (wantAtt) {
|
if (wantAtt) {
|
||||||
@ -673,7 +727,14 @@ async function submit() {
|
|||||||
await uploadOnce(`${mid}|att:${a.partName}`, async () => {
|
await uploadOnce(`${mid}|att:${a.partName}`, async () => {
|
||||||
status(`${prefix}Anhang ${a.name} …`);
|
status(`${prefix}Anhang ${a.name} …`);
|
||||||
const file = await Mail.getAttachmentFile(mid, a.partName);
|
const file = await Mail.getAttachmentFile(mid, a.partName);
|
||||||
const af = STATE.isBasket ? {} : { ...fields, DOC_FILE_NAME: { value: a.name, type: "Text" } };
|
// Anhang trägt seinen EIGENEN Dateinamen – sowohl in gemappten
|
||||||
|
// fileName-Feldern als auch im festen DOC_FILE_NAME.
|
||||||
|
const af = STATE.isBasket
|
||||||
|
? {}
|
||||||
|
: {
|
||||||
|
...applyPerFileFields(fields, meta, { ...msgCtx, fileName: a.name }),
|
||||||
|
DOC_FILE_NAME: { value: a.name, type: "Text" },
|
||||||
|
};
|
||||||
await DocuWare.uploadDocument(STATE.settings, STATE.cabinetId, file, a.name, af);
|
await DocuWare.uploadDocument(STATE.settings, STATE.cabinetId, file, a.name, af);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -720,17 +781,32 @@ async function uploadOnce(part, doUpload) {
|
|||||||
|
|
||||||
// Indexfelder für eine konkrete Nachricht: gemappte Felder (EML_*/generisch)
|
// Indexfelder für eine konkrete Nachricht: gemappte Felder (EML_*/generisch)
|
||||||
// werden je Mail aus deren Metadaten neu berechnet, manuelle Werte bleiben.
|
// werden je Mail aus deren Metadaten neu berechnet, manuelle Werte bleiben.
|
||||||
function fieldsForMessage(baseResult, meta, multi) {
|
function fieldsForMessage(baseResult, meta, multi, ctx) {
|
||||||
if (!multi) return baseResult; // Einzelmail: manuelle Edits respektieren
|
if (!multi) return baseResult; // Einzelmail: manuelle Edits respektieren
|
||||||
const result = { ...baseResult };
|
const result = { ...baseResult };
|
||||||
STATE.fields.forEach((f) => {
|
STATE.fields.forEach((f) => {
|
||||||
const val = prefillFor(f.name, meta);
|
const val = prefillFor(f.name, meta, ctx);
|
||||||
if (val === undefined || val === null || val === "") return;
|
if (val === undefined || val === null || val === "") return;
|
||||||
result[f.name] = { value: val, type: f.type };
|
result[f.name] = { value: val, type: f.type };
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Felder, die auf ein dokumentabhängiges Token (fileName) gemappt sind, für
|
||||||
|
// GENAU dieses Dokument setzen – auch bei Einzelmail, weil ein Formularwert
|
||||||
|
// nicht gleichzeitig für .eml und jeden Anhang stimmen kann.
|
||||||
|
function applyPerFileFields(fields, meta, ctx) {
|
||||||
|
if (STATE.isBasket) return fields; // Briefkorb: keine Indexfelder
|
||||||
|
const out = { ...fields };
|
||||||
|
STATE.fields.forEach((f) => {
|
||||||
|
if (!PER_FILE_TOKENS.has(tokenOf(f.name))) return;
|
||||||
|
const val = prefillFor(f.name, meta, ctx);
|
||||||
|
if (val === undefined || val === null || val === "") return;
|
||||||
|
out[f.name] = { value: val, type: f.type };
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
// Sticky: zuletzt genutztes Format + Anhänge-Wahl in den Einstellungen merken.
|
// Sticky: zuletzt genutztes Format + Anhänge-Wahl in den Einstellungen merken.
|
||||||
async function saveOptionPrefs(mailFormat, storeAttachments) {
|
async function saveOptionPrefs(mailFormat, storeAttachments) {
|
||||||
try {
|
try {
|
||||||
@ -780,6 +856,27 @@ const toDateInput = (d) => {
|
|||||||
const day = String(d.getDate()).padStart(2, "0");
|
const day = String(d.getDate()).padStart(2, "0");
|
||||||
return `${y}-${m}-${day}`;
|
return `${y}-${m}-${day}`;
|
||||||
};
|
};
|
||||||
|
// 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).
|
||||||
|
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")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dateiname, den die E-Mail selbst beim Upload bekäme – für die Vorschau im
|
||||||
|
// Formular. Beim tatsächlichen Upload gilt der echte Name (siehe submit()).
|
||||||
|
function previewFileName() {
|
||||||
|
const subj = (STATE.meta && STATE.meta.subject) || "";
|
||||||
|
const base = subj.replace(/[\\/:*?"<>|]+/g, "_").slice(0, 80).trim() || "email";
|
||||||
|
return `${base}.${getMailFormat() === "pdf" ? "pdf" : "eml"}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kontext für die Token-Auflösung im Formular (Vorschauwerte).
|
||||||
|
function formCtx() {
|
||||||
|
return { fileName: previewFileName(), attachmentCount: STATE.attachments.length };
|
||||||
|
}
|
||||||
// Sicheres CSS-Escaping für Attribut-Selektoren (Feldnamen aus DocuWare).
|
// Sicheres CSS-Escaping für Attribut-Selektoren (Feldnamen aus DocuWare).
|
||||||
const cssEsc = (s) =>
|
const cssEsc = (s) =>
|
||||||
window.CSS && CSS.escape ? CSS.escape(String(s)) : String(s).replace(/["\\\]]/g, "\\$&");
|
window.CSS && CSS.escape ? CSS.escape(String(s)) : String(s).replace(/["\\\]]/g, "\\$&");
|
||||||
@ -811,8 +908,21 @@ $("cancel").addEventListener("click", () => window.close());
|
|||||||
|
|
||||||
// Sticky: Optionen sofort bei Änderung merken (nicht erst beim Ablegen).
|
// Sticky: Optionen sofort bei Änderung merken (nicht erst beim Ablegen).
|
||||||
document.querySelectorAll('input[name="mailFormat"]').forEach((r) =>
|
document.querySelectorAll('input[name="mailFormat"]').forEach((r) =>
|
||||||
r.addEventListener("change", () => saveOptionPrefs(getMailFormat(), $("optAtt").checked))
|
r.addEventListener("change", () => {
|
||||||
|
refreshPerFilePreview(); // .eml ↔ .pdf: Dateiname-Vorschau nachziehen
|
||||||
|
saveOptionPrefs(getMailFormat(), $("optAtt").checked);
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Aktualisiert die Vorschauwerte dokumentabhängiger Felder (Dateiname).
|
||||||
|
function refreshPerFilePreview() {
|
||||||
|
const ctx = formCtx();
|
||||||
|
STATE.fields.forEach((f) => {
|
||||||
|
if (!PER_FILE_TOKENS.has(tokenOf(f.name))) return;
|
||||||
|
const val = prefillFor(f.name, STATE.meta, ctx);
|
||||||
|
if (val !== undefined && val !== null && val !== "") setFieldValue(f.name, f, val);
|
||||||
|
});
|
||||||
|
}
|
||||||
$("optAtt").addEventListener("change", () => {
|
$("optAtt").addEventListener("change", () => {
|
||||||
const show = !isMulti() && $("optAtt").checked && STATE.attachments.length > 0;
|
const show = !isMulti() && $("optAtt").checked && STATE.attachments.length > 0;
|
||||||
$("attList").style.display = show ? "" : "none";
|
$("attList").style.display = show ? "" : "none";
|
||||||
|
|||||||
@ -429,6 +429,21 @@ const DocuWare = {
|
|||||||
|
|
||||||
_field(name, value, type) {
|
_field(name, value, type) {
|
||||||
const t = (type || "Text").toLowerCase();
|
const t = (type || "Text").toLowerCase();
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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" };
|
||||||
|
}
|
||||||
if (t.includes("date")) {
|
if (t.includes("date")) {
|
||||||
// DocuWare erwartet /Date(ms)/ oder ISO; ISO funktioniert bei Platform.
|
// DocuWare erwartet /Date(ms)/ oder ISO; ISO funktioniert bei Platform.
|
||||||
const d = value instanceof Date ? value : new Date(value);
|
const d = value instanceof Date ? value : new Date(value);
|
||||||
|
|||||||
19
lib/mail.js
19
lib/mail.js
@ -16,9 +16,11 @@ const Mail = {
|
|||||||
const bodyText = this._extractText(full);
|
const bodyText = this._extractText(full);
|
||||||
|
|
||||||
const sender = this._parseAddress(header.author);
|
const sender = this._parseAddress(header.author);
|
||||||
const account = await this._accountForMessage(messageId);
|
const acc = await this._accountInfo(messageId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
folder: (header.folder && (header.folder.name || header.folder.path)) || "",
|
||||||
|
accountEmail: acc.email,
|
||||||
rfcMessageId: this._header(full, "message-id"), // RFC822 Message-ID (Dedup-Schlüssel)
|
rfcMessageId: this._header(full, "message-id"), // RFC822 Message-ID (Dedup-Schlüssel)
|
||||||
subject: header.subject || "",
|
subject: header.subject || "",
|
||||||
senderEmail: sender.email,
|
senderEmail: sender.email,
|
||||||
@ -28,7 +30,7 @@ const Mail = {
|
|||||||
bcc: (header.bccList || []).join(", "),
|
bcc: (header.bccList || []).join(", "),
|
||||||
date: header.date instanceof Date ? header.date : new Date(header.date),
|
date: header.date instanceof Date ? header.date : new Date(header.date),
|
||||||
bodyText,
|
bodyText,
|
||||||
account,
|
account: acc.name,
|
||||||
direction: this._direction(header),
|
direction: this._direction(header),
|
||||||
sizeBytes: header.size || 0,
|
sizeBytes: header.size || 0,
|
||||||
};
|
};
|
||||||
@ -128,16 +130,23 @@ const Mail = {
|
|||||||
return { name: "", email: str.trim() };
|
return { name: "", email: str.trim() };
|
||||||
},
|
},
|
||||||
|
|
||||||
async _accountForMessage(messageId) {
|
/**
|
||||||
|
* Konto-Name und eigene E-Mail-Adresse (erste Identität) zur Nachricht.
|
||||||
|
* @returns {Promise<{name:string, email:string}>}
|
||||||
|
*/
|
||||||
|
async _accountInfo(messageId) {
|
||||||
try {
|
try {
|
||||||
const msg = await browser.messages.get(messageId);
|
const msg = await browser.messages.get(messageId);
|
||||||
const folder = msg.folder;
|
const folder = msg.folder;
|
||||||
if (folder && folder.accountId) {
|
if (folder && folder.accountId) {
|
||||||
const acc = await browser.accounts.get(folder.accountId);
|
const acc = await browser.accounts.get(folder.accountId);
|
||||||
return acc ? acc.name : "";
|
if (acc) {
|
||||||
|
const ident = (acc.identities || [])[0];
|
||||||
|
return { name: acc.name || "", email: (ident && ident.email) || "" };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
return "";
|
return { name: "", email: "" };
|
||||||
},
|
},
|
||||||
|
|
||||||
_direction(header) {
|
_direction(header) {
|
||||||
|
|||||||
22
lib/store.js
22
lib/store.js
@ -32,6 +32,27 @@ const MAIL_TOKENS = [
|
|||||||
{ key: "direction", label: "Richtung (Ein-/Ausgang)" },
|
{ key: "direction", label: "Richtung (Ein-/Ausgang)" },
|
||||||
{ key: "size", label: "Größe (Bytes)" },
|
{ key: "size", label: "Größe (Bytes)" },
|
||||||
{ key: "account", label: "Konto" },
|
{ key: "account", label: "Konto" },
|
||||||
|
|
||||||
|
// --- Datum/Zeit ---------------------------------------------------------
|
||||||
|
// "date" liefert nur den Kalendertag. Für DocuWare-Felder vom Typ DateTime
|
||||||
|
// gibt es "dateTime" (Tag + Uhrzeit); "time" nur die Uhrzeit als Text.
|
||||||
|
{ key: "dateTime", label: "Datum + Uhrzeit der E-Mail" },
|
||||||
|
{ key: "time", label: "Uhrzeit der E-Mail" },
|
||||||
|
{ key: "archiveDate", label: "Ablagedatum (heute)" },
|
||||||
|
{ key: "archiveDateTime", label: "Ablagezeitpunkt (jetzt)" },
|
||||||
|
|
||||||
|
// --- Dokument -----------------------------------------------------------
|
||||||
|
// fileName wird JE HOCHGELADENEM DOKUMENT gesetzt: .eml/PDF tragen den
|
||||||
|
// Mailnamen, jeder Anhang seinen eigenen Dateinamen.
|
||||||
|
{ key: "fileName", label: "Dateiname (je Dokument)" },
|
||||||
|
{ key: "attachmentCount", label: "Anzahl Anhänge" },
|
||||||
|
{ key: "rfcMessageId", label: "Message-ID (RFC822)" },
|
||||||
|
|
||||||
|
// --- Wer/Woher ----------------------------------------------------------
|
||||||
|
{ key: "dwUser", label: "DocuWare-Benutzer (Mitarbeiter)" },
|
||||||
|
{ key: "dwOrganization", label: "DocuWare-Organisation" },
|
||||||
|
{ key: "accountEmail", label: "Eigene E-Mail-Adresse" },
|
||||||
|
{ key: "folder", label: "Ordner" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Standard-Zuordnung DocuWare-Feldname -> Mail-Token. Greift, solange der Nutzer
|
// Standard-Zuordnung DocuWare-Feldname -> Mail-Token. Greift, solange der Nutzer
|
||||||
@ -53,6 +74,7 @@ const DEFAULT_FIELD_MAP = {
|
|||||||
EML_SIZE: "size",
|
EML_SIZE: "size",
|
||||||
EML_ACCOUNT: "account",
|
EML_ACCOUNT: "account",
|
||||||
DOC_DATE: "date",
|
DOC_DATE: "date",
|
||||||
|
DOC_FILE_NAME: "fileName", // .eml/PDF: Mailname, Anhang: eigener Dateiname
|
||||||
SUBJECT: "subject",
|
SUBJECT: "subject",
|
||||||
BETREFF: "subject",
|
BETREFF: "subject",
|
||||||
EMAIL: "senderEmail",
|
EMAIL: "senderEmail",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user