Compare commits
6 Commits
02776311c3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
730d32ab29 | ||
|
|
3f792a6434 | ||
|
|
8057870f8b | ||
|
|
2b6e2267c2 | ||
|
|
169fc68f18 | ||
|
|
adddef75ac |
108
README.md
108
README.md
@@ -1,19 +1,46 @@
|
|||||||
# HPS Vorlagen & Signaturen
|
# HPS Vorlagen & Signaturen
|
||||||
|
|
||||||
Thunderbird-Plugin (v2.2.0) zur zentralen Verwaltung von E-Mail-Vorlagen und Signaturen für Hotel Park Soltau. Vorlagen und Signaturen werden über ein Gitea/Forgejo-Repository synchronisiert und stehen so allen Mitarbeitern zur Verfügung.
|
Thunderbird-MailExtension zur zentralen Verwaltung von E-Mail-Vorlagen und Signaturen für
|
||||||
|
Hotel Park Soltau. Vorlagen, Signaturen und Schlagwörter werden über ein Gitea/Forgejo-Repository
|
||||||
|
synchronisiert und stehen so allen Mitarbeitern zur Verfügung. (Aktuelle Version: siehe
|
||||||
|
`manifest.json`.)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **E-Mail-Vorlagen** erstellen, bearbeiten und per Klick in Compose-Fenster einfügen
|
- **E-Mail-Vorlagen** erstellen, bearbeiten und per Klick ins Compose-Fenster einfügen
|
||||||
- **3 Sichtbarkeitsstufen** pro Vorlage:
|
- **3 Sichtbarkeitsstufen** pro Vorlage:
|
||||||
- **Persönlich** — nur für den eigenen Account, gesynct in `_benutzer/{email}/`
|
- **Persönlich** — nur für den eigenen Account, gesynct in `_benutzer/{email}/`
|
||||||
- **Abteilung** — für alle in der Abteilung, gesynct in den Abteilungsordner
|
- **Abteilung** — für alle in der Abteilung, gesynct in den Abteilungsordner
|
||||||
- **Alle Abteilungen** — firmenweit, gesynct in `_gemeinsam/`
|
- **Alle Abteilungen** — firmenweit, gesynct in `_gemeinsam/`
|
||||||
- **Signaturen-Verwaltung** mit persönlichem Kopfbereich + gemeinsamer Fußzeile pro Abteilung
|
- **Signaturen-Verwaltung** mit persönlichem Kopfbereich + gemeinsamer Fußzeile pro Abteilung
|
||||||
|
- **QuickMove** — Button in der Nachrichtenansicht: markiert die E-Mail mit deinem Schlagwort
|
||||||
|
und verschiebt sie in einen festgelegten Ordner. Mehrere Aktionen → Auswahlmenü.
|
||||||
|
- **Schlagwörter-Sync** — Benutzer-Tags werden zentral über `_config/schlagwoerter.json`
|
||||||
|
verwaltet, in Thunderbird angelegt und nie gelöscht (dauerhaft nachvollziehbar)
|
||||||
- **Git-Sync** über Gitea/Forgejo API (Pull + Push, automatisch alle 15 Min.)
|
- **Git-Sync** über Gitea/Forgejo API (Pull + Push, automatisch alle 15 Min.)
|
||||||
- **Auto-Erkennung** von Abteilung und Benutzer via `_config/abteilungen.json`
|
- **Auto-Erkennung** von Abteilung und Benutzer via `_config/abteilungen.json`
|
||||||
|
- **Abteilungsverwaltung** — neue Abteilungen direkt aus den Einstellungen anlegen
|
||||||
- **WYSIWYG-Editor** mit Schriftart, Farben, Listen, Bildern, Links
|
- **WYSIWYG-Editor** mit Schriftart, Farben, Listen, Bildern, Links
|
||||||
- **Sichtbarkeit direkt änderbar** per klickbarem Badge in der Vorlagenliste
|
- **Sichtbarkeit direkt änderbar** per klickbarem Badge in der Vorlagenliste
|
||||||
|
- **Auto-Update** über Gitea (siehe unten) — installierte Clients aktualisieren sich selbst
|
||||||
|
- **Toolbar-Button** öffnet direkt die Einstellungen
|
||||||
|
- **Erzwungene Thunderbird-Einstellungen** — setzt bei jedem Start bestimmte `about:config`-Prefs
|
||||||
|
(z.B. Startseite aus, Kartenansicht, „nicht automatisch als gelesen", Signatur-/Antwort-Verhalten
|
||||||
|
pro Konto); pro Einstellung im Sync-Tab abschaltbar
|
||||||
|
- **Client-Versions-Übersicht** — jeder Client meldet Plugin- und Thunderbird-Version; im
|
||||||
|
Web-Editor unter *Verwaltung → User & Versionen* einsehbar (veraltete Clients markiert)
|
||||||
|
- **[Web-Editor](web-editor/)** als optionales Web-Gegenstück (gleiche Gitea-Quelle)
|
||||||
|
|
||||||
|
## Bedienoberfläche (Einstellungsseite)
|
||||||
|
|
||||||
|
Vier Tabs:
|
||||||
|
|
||||||
|
| Tab | Funktion |
|
||||||
|
|---|---|
|
||||||
|
| **Vorlagen** | Vorlagen anlegen/bearbeiten, Sichtbarkeit, manuelles Push/Pull |
|
||||||
|
| **Signaturen** | Persönlicher Kopfbereich + Fußzeile (gemeinsam / Abteilung) |
|
||||||
|
| **QuickMove** | Aktionen konfigurieren (Schlagwort + Zielordner) |
|
||||||
|
| **Sync** | Server-Verbindung, Benutzer & Abteilung, Sync auslösen, erzwungene Einstellungen (Opt-out) |
|
||||||
|
|
||||||
## Repository-Struktur (Gitea)
|
## Repository-Struktur (Gitea)
|
||||||
|
|
||||||
@@ -25,14 +52,18 @@ repo/
|
|||||||
│ ├── max@hotel-park-soltau.de/
|
│ ├── max@hotel-park-soltau.de/
|
||||||
│ └── anna@hotel-park-soltau.de/
|
│ └── anna@hotel-park-soltau.de/
|
||||||
├── _config/
|
├── _config/
|
||||||
│ └── abteilungen.json # E-Mail → Abteilung Mapping
|
│ ├── abteilungen.json # E-Mail → Abteilung Mapping
|
||||||
|
│ └── schlagwoerter.json # Benutzer-Schlagwörter (Name + Farbe)
|
||||||
|
├── _clients/ # Status-Meldungen: Plugin-/TB-Version pro User
|
||||||
|
│ └── max@hotel-park-soltau.de.json
|
||||||
├── Rezeption/ # Abteilungsvorlagen
|
├── Rezeption/ # Abteilungsvorlagen
|
||||||
├── IT/
|
├── IT/
|
||||||
├── signatures/
|
├── signatures/
|
||||||
│ ├── headers/ # Persönliche Signatur-Köpfe
|
│ ├── headers/ # Persönliche Signatur-Köpfe
|
||||||
│ │ └── max@hotel.de.max-mustermann.html
|
│ │ └── max@hotel.de.max-mustermann.html
|
||||||
│ └── footers/ # Gemeinsame Fußbereiche
|
│ └── footers/ # Fußbereiche
|
||||||
│ └── Rezeption.html
|
│ ├── _default.html # gemeinsame Standard-Fußzeile
|
||||||
|
│ └── Rezeption.html # Abteilungs-Fußzeile
|
||||||
```
|
```
|
||||||
|
|
||||||
### `_config/abteilungen.json`
|
### `_config/abteilungen.json`
|
||||||
@@ -48,17 +79,36 @@ Mapping von Abteilungs-E-Mail-Adressen zu Ordnernamen. Wird vom Plugin gelesen,
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `_config/schlagwoerter.json`
|
||||||
|
|
||||||
|
Liste der Benutzer-Schlagwörter (für QuickMove/Markierung). Wird beim Sync automatisch um neue
|
||||||
|
Benutzer ergänzt und nie geleert, damit Markierungen dauerhaft nachvollziehbar bleiben:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{ "name": "Max Mustermann", "color": "#4a7c59" },
|
||||||
|
{ "name": "Anna Beispiel", "color": "#2874a6" }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## Plugin-Aufbau
|
## Plugin-Aufbau
|
||||||
|
|
||||||
| Datei | Funktion |
|
| Datei | Funktion |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `manifest.json` | Extension-Manifest (Thunderbird WebExtension v2) |
|
| `manifest.json` | Extension-Manifest (Thunderbird WebExtension v2) |
|
||||||
| `background.js` | Template-Insertion ins Compose-Fenster |
|
| `background.js` | Template-Insertion + QuickMove-Aktionen (Tag setzen, Nachricht verschieben) |
|
||||||
| `popup.html` / `popup.js` | Popup beim Klick auf "Vorlagen" im Compose |
|
| `popup.html` / `popup.js` | Compose-Popup ("Vorlagen" beim Schreiben) |
|
||||||
| `lib/gitea-sync.js` | Gitea-API-Client + Sync-Manager |
|
| `message_popup.html` / `message_popup.js` | QuickMove-Popup in der Nachrichtenansicht |
|
||||||
| `lib/mdi/` | Material Design Icons (Subset) |
|
| `toolbar_popup.html` | Toolbar-Button → öffnet die Einstellungen |
|
||||||
| `templates_options/` | Einstellungsseite (Vorlagen, Signaturen, Verbindung) |
|
| `templates_options/` | Einstellungsseite (Tabs: Vorlagen, Signaturen, QuickMove, Sync) |
|
||||||
|
| `lib/gitea-sync.js` | Gitea-API-Client + Sync-Manager (inkl. Schlagwörter-Sync + Client-Status-Meldung) |
|
||||||
|
| `lib/forced-prefs-list.js` | Liste der erzwungenen `about:config`-Prefs (geteilt: Background + Optionsseite) |
|
||||||
|
| `experiments/prefs/` | Privilegierte Experiment-API zum Setzen von `about:config`-Prefs |
|
||||||
|
| `lib/mdi/` | Material Design Icons (Subset) für den Editor |
|
||||||
| `defaults.local.json` | Optionale vorkonfigurierte Verbindungsdaten (gitignored) |
|
| `defaults.local.json` | Optionale vorkonfigurierte Verbindungsdaten (gitignored) |
|
||||||
|
| `updates.json` | Update-Manifest für Thunderbird-Auto-Update |
|
||||||
|
| `release.sh` | Release-Skript (Build hashen, Gitea-Release anlegen, updates.json pflegen) |
|
||||||
|
| `web-editor/` | Optionaler Node/Docker-Web-Editor (eigenständig, gleiche Gitea-Quelle) |
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -71,17 +121,25 @@ Mapping von Abteilungs-E-Mail-Adressen zu Ordnernamen. Wird vom Plugin gelesen,
|
|||||||
|
|
||||||
### XPI bauen
|
### XPI bauen
|
||||||
|
|
||||||
```bash
|
`zip` ist nicht zwingend vorhanden, `7z` reicht. Immer **ohne** `defaults.local.json` bauen —
|
||||||
# Ohne vorkonfigurierte Verbindungsdaten:
|
die Datei enthält den Gitea-Token und darf nicht in eine veröffentlichte `.xpi`:
|
||||||
7z a templates-reply-hotel.xpi manifest.json background.js popup.html popup.js lib/ templates_options/ icons/
|
|
||||||
|
|
||||||
# Mit vorkonfigurierten Verbindungsdaten (für Deployment):
|
```bash
|
||||||
7z a templates-reply-hotel.xpi manifest.json background.js popup.html popup.js lib/ templates_options/ icons/ defaults.local.json
|
rm -f templates-reply-hotel.xpi
|
||||||
|
7z a -tzip templates-reply-hotel.xpi . \
|
||||||
|
-xr'!.git' -xr'!node_modules' -xr'!web-editor' -xr'!.claude' \
|
||||||
|
-xr'!defaults.local.json' -xr'!*.xpi' -xr'!release.sh' -xr'!*.md'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> Eine **vorkonfigurierte** `.xpi` (mit `defaults.local.json`) ist nur für die interne
|
||||||
|
> Erstinstallation auf einer neuen Maschine gedacht und darf **niemals** als öffentliches
|
||||||
|
> Release hochgeladen werden — sonst wäre der Token auslesbar.
|
||||||
|
|
||||||
### Vorkonfigurierte Verbindungsdaten (`defaults.local.json`)
|
### Vorkonfigurierte Verbindungsdaten (`defaults.local.json`)
|
||||||
|
|
||||||
Wenn eine `defaults.local.json` im Plugin-Root existiert und in die XPI eingebaut wird, werden die Verbindungsdaten beim ersten Start automatisch gesetzt. Der User muss dann nur noch "Verbindung speichern" klicken.
|
Wenn eine `defaults.local.json` im Plugin-Root existiert und in die XPI eingebaut wird, werden
|
||||||
|
die Verbindungsdaten **beim allerersten Start** automatisch gesetzt. Updates brauchen die Datei
|
||||||
|
nicht — bestehende Installationen behalten ihre Konfiguration in `storage.local`.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -107,17 +165,25 @@ Installierte Add-ons aktualisieren sich automatisch über `updates.json` in dies
|
|||||||
|
|
||||||
Neue Version veröffentlichen:
|
Neue Version veröffentlichen:
|
||||||
|
|
||||||
1. `version` in `manifest.json` hochzählen, `.xpi` **ohne** `defaults.local.json` neu bauen.
|
1. **`version` in `manifest.json` hochzählen** (gleiche Version = Clients updaten nicht!).
|
||||||
2. `GITEA_TOKEN=… ./release.sh` — hasht die `.xpi`, aktualisiert `updates.json`, legt das
|
2. `.xpi` **ohne** `defaults.local.json` neu bauen (siehe oben).
|
||||||
|
3. `GITEA_TOKEN=… ./release.sh` — hasht die `.xpi`, aktualisiert `updates.json`, legt das
|
||||||
Gitea-Release an und lädt die `.xpi` als Asset hoch.
|
Gitea-Release an und lädt die `.xpi` als Asset hoch.
|
||||||
3. `updates.json` + `manifest.json` committen & pushen.
|
4. `updates.json` + `manifest.json` committen & pushen.
|
||||||
|
|
||||||
|
Bestehende Installationen ziehen das Update dann automatisch (Prüfung ~1×/Tag, oder manuell
|
||||||
|
über Add-ons → Zahnrad → „Auf Updates prüfen").
|
||||||
|
|
||||||
## Einrichtung
|
## Einrichtung
|
||||||
|
|
||||||
1. **Verbindung konfigurieren**: Einstellungen-Tab (⚙) → Server-URL, Repository, Token eingeben → Verbindung speichern (entfällt bei vorkonfigurierter XPI)
|
1. **Verbindung konfigurieren**: Sync-Tab → Server-URL, Repository, Token eingeben →
|
||||||
2. **Abteilung wählen** (oder automatisch erkannt via `abteilungen.json`)
|
Verbindung speichern (entfällt bei vorkonfigurierter XPI)
|
||||||
|
2. **Abteilung wählen** (oder automatisch erkannt via `abteilungen.json`; neue Abteilungen
|
||||||
|
lassen sich per **+** direkt anlegen)
|
||||||
3. **Vorlagen erstellen**: Vorlagen-Tab → Neue Vorlage → Sichtbarkeit wählen → Speichern
|
3. **Vorlagen erstellen**: Vorlagen-Tab → Neue Vorlage → Sichtbarkeit wählen → Speichern
|
||||||
4. **Signaturen einrichten**: Signaturen-Tab → Identität wählen → Kopfbereich bearbeiten → Speichern
|
4. **Signaturen einrichten**: Signaturen-Tab → Identität wählen → Kopfbereich bearbeiten → Speichern
|
||||||
|
5. **QuickMove einrichten** (optional): QuickMove-Tab → Aktion hinzufügen (Name + Zielordner) →
|
||||||
|
Speichern. Der Button erscheint dann in der Nachrichtenansicht.
|
||||||
|
|
||||||
## Voraussetzungen
|
## Voraussetzungen
|
||||||
|
|
||||||
|
|||||||
@@ -276,31 +276,50 @@ async function handleInsertTemplate(msg) {
|
|||||||
const newBody = msg.text + (old ? '\n' + old : '');
|
const newBody = msg.text + (old ? '\n' + old : '');
|
||||||
await browser.compose.setComposeDetails(tab.id, { plainTextBody: newBody });
|
await browser.compose.setComposeDetails(tab.id, { plainTextBody: newBody });
|
||||||
} else {
|
} else {
|
||||||
// HTML mode: use insertText API to go through the editor's rendering pipeline
|
// HTML mode: wrap the template in a marker element (id=hps-vorlage) and
|
||||||
|
// replace any previously inserted one — so durchswitchen ersetzt statt stapelt.
|
||||||
const htmlContent = isHtmlTemplate ? msg.text : msg.text.replace(/\n/g, '<br>');
|
const htmlContent = isHtmlTemplate ? msg.text : msg.text.replace(/\n/g, '<br>');
|
||||||
|
|
||||||
// Move cursor to beginning of body first via script injection
|
// Directly manipulate the editor DOM: remove the old template block (if any),
|
||||||
await browser.tabs.executeScript(tab.id, {
|
// then insert the new one at the top of the body. Returns true on success.
|
||||||
|
const injected = await browser.tabs.executeScript(tab.id, {
|
||||||
code: `
|
code: `
|
||||||
|
(function () {
|
||||||
const editor = document.getElementById('messageEditor');
|
const editor = document.getElementById('messageEditor');
|
||||||
const editorDoc = editor ? editor.contentDocument : document;
|
const editorDoc = editor ? editor.contentDocument : document;
|
||||||
const body = editorDoc.body;
|
const body = editorDoc && editorDoc.body;
|
||||||
if (body) {
|
if (!body) return false;
|
||||||
|
|
||||||
|
// Remove a previously inserted template block, so switching replaces it
|
||||||
|
const prev = editorDoc.getElementById('hps-vorlage');
|
||||||
|
if (prev) prev.remove();
|
||||||
|
|
||||||
|
// Wrap the new template in a marker element with a stable id
|
||||||
|
const wrapper = editorDoc.createElement('div');
|
||||||
|
wrapper.id = 'hps-vorlage';
|
||||||
|
wrapper.innerHTML = ${JSON.stringify(htmlContent)};
|
||||||
|
|
||||||
|
// Insert at the very top of the body (above quote/signature/typed text)
|
||||||
|
body.insertBefore(wrapper, body.firstChild);
|
||||||
|
|
||||||
|
// Place the cursor right after the inserted block
|
||||||
const range = editorDoc.createRange();
|
const range = editorDoc.createRange();
|
||||||
range.setStart(body, 0);
|
range.setStartAfter(wrapper);
|
||||||
range.collapse(true);
|
range.collapse(true);
|
||||||
const sel = editorDoc.getSelection();
|
const sel = editorDoc.getSelection();
|
||||||
sel.removeAllRanges();
|
sel.removeAllRanges();
|
||||||
sel.addRange(range);
|
sel.addRange(range);
|
||||||
}
|
return true;
|
||||||
|
})();
|
||||||
`
|
`
|
||||||
}).catch(() => {
|
}).then(r => (Array.isArray(r) ? r[0] : r)).catch(() => false);
|
||||||
// Fallback: some TB versions structure the editor differently
|
|
||||||
});
|
|
||||||
|
|
||||||
// Insert HTML at cursor position - this goes through the editor's render pipeline
|
if (!injected) {
|
||||||
|
// Fallback: editor DOM not reachable (TB version differences) — use the
|
||||||
|
// insertText pipeline. Note: this variant cannot replace, only prepend.
|
||||||
await browser.compose.insertText(tab.id, htmlContent + '<br>', { insertAsText: false });
|
await browser.compose.insertText(tab.id, htmlContent + '<br>', { insertAsText: false });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('background.js error:', e);
|
console.error('background.js error:', e);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "HPS Vorlagen & Signaturen",
|
"name": "HPS Vorlagen & Signaturen",
|
||||||
"version": "2.5.0",
|
"version": "2.6.0",
|
||||||
"description": "Vorlagen- und Signaturverwaltung für Hotel Park Soltau mit Git-Sync",
|
"description": "Vorlagen- und Signaturverwaltung für Hotel Park Soltau mit Git-Sync",
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
|
|||||||
Binary file not shown.
20
updates.json
20
updates.json
@@ -2,6 +2,26 @@
|
|||||||
"addons": {
|
"addons": {
|
||||||
"it@hotel-park-soltau.de": {
|
"it@hotel-park-soltau.de": {
|
||||||
"updates": [
|
"updates": [
|
||||||
|
{
|
||||||
|
"version": "2.6.0",
|
||||||
|
"update_link": "https://git.hotel-park-soltau.de/hps/hps-thunderbird-templates/releases/download/v2.6.0/templates-reply-hotel.xpi",
|
||||||
|
"update_hash": "sha256:563e65f7686db7be30712fbc2be934f51c9ce8f4d35bb37f07046d8da40b562d",
|
||||||
|
"applications": {
|
||||||
|
"gecko": {
|
||||||
|
"strict_min_version": "109.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.5.0",
|
||||||
|
"update_link": "https://git.hotel-park-soltau.de/hps/hps-thunderbird-templates/releases/download/v2.5.0/templates-reply-hotel.xpi",
|
||||||
|
"update_hash": "sha256:97f9006a88d565e6ec524841687b851c4f2f8f02b313b2effd5b05b68e141599",
|
||||||
|
"applications": {
|
||||||
|
"gecko": {
|
||||||
|
"strict_min_version": "109.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "2.3.6",
|
"version": "2.3.6",
|
||||||
"update_link": "https://git.hotel-park-soltau.de/hps/hps-thunderbird-templates/releases/download/v2.3.6/templates-reply-hotel.xpi",
|
"update_link": "https://git.hotel-park-soltau.de/hps/hps-thunderbird-templates/releases/download/v2.3.6/templates-reply-hotel.xpi",
|
||||||
|
|||||||
@@ -364,7 +364,9 @@
|
|||||||
valid_elements: '*[*]', extended_valid_elements: '*[*]', valid_children: '+body[style]',
|
valid_elements: '*[*]', extended_valid_elements: '*[*]', valid_children: '+body[style]',
|
||||||
verify_html: false, convert_urls: false,
|
verify_html: false, convert_urls: false,
|
||||||
content_style: 'body{font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#1f2a30;line-height:1.6;padding:10px 12px;} img{max-width:100%;height:auto;} table{border-collapse:collapse;}',
|
content_style: 'body{font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#1f2a30;line-height:1.6;padding:10px 12px;} img{max-width:100%;height:auto;} table{border-collapse:collapse;}',
|
||||||
paste_data_images: true, automatic_uploads: false, file_picker_types: 'image',
|
// Chrome/Edge/Safari entfernen sonst beim Einfügen aus externen Quellen (E-Mails)
|
||||||
|
// ALLE inline-Styles inkl. Farben ('paste_webkit_styles' default 'none'). 'all' behält sie.
|
||||||
|
paste_data_images: true, paste_webkit_styles: 'all', automatic_uploads: false, file_picker_types: 'image',
|
||||||
file_picker_callback: function (cb) {
|
file_picker_callback: function (cb) {
|
||||||
const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*';
|
const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*';
|
||||||
input.onchange = function () { const file = input.files[0]; if (!file) return; const r = new FileReader(); r.onload = function () { cb(r.result, { title: file.name }); }; r.readAsDataURL(file); };
|
input.onchange = function () { const file = input.files[0]; if (!file) return; const r = new FileReader(); r.onload = function () { cb(r.result, { title: file.name }); }; r.readAsDataURL(file); };
|
||||||
@@ -374,6 +376,38 @@
|
|||||||
ed = editor;
|
ed = editor;
|
||||||
editor.on('init', function () { edReady = true; });
|
editor.on('init', function () { edReady = true; });
|
||||||
editor.on('input ExecCommand Undo Redo SetContent paste', function () { if (!suppressDirty) markDirty(); });
|
editor.on('input ExecCommand Undo Redo SetContent paste', function () { if (!suppressDirty) markDirty(); });
|
||||||
|
// Robustes Einfügen aus externen Quellen (z.B. E-Mails). TinyMCEs eigener
|
||||||
|
// Fallback (verstecktes „Pastebin"-Feld) schlägt bei manchen Quellen still
|
||||||
|
// fehl → „nichts passiert". Wir lesen die Zwischenablage direkt aus und
|
||||||
|
// fügen HTML mitsamt Formatierung ein. Interner Copy/Paste (funktioniert
|
||||||
|
// bereits) und Bild-Paste bleiben dem Standard-Handler überlassen.
|
||||||
|
editor.on('paste', function (e) {
|
||||||
|
const cd = e.clipboardData;
|
||||||
|
if (!cd) return; // keine Zwischenablage → Standard-Handler versuchen lassen
|
||||||
|
let html = '';
|
||||||
|
try { html = cd.getData('text/html') || ''; } catch (_) {}
|
||||||
|
// TinyMCE-interner Inhalt trägt diesen Marker → Standard-Pfad übernehmen.
|
||||||
|
if (html.indexOf('x-tinymce/html') !== -1) return;
|
||||||
|
let text = '';
|
||||||
|
try { text = cd.getData('text/plain') || ''; } catch (_) {}
|
||||||
|
// Reines Bild (Screenshot o.ä.) → TinyMCEs Bildverarbeitung übernehmen.
|
||||||
|
const hasImage =
|
||||||
|
(cd.types && Array.prototype.indexOf.call(cd.types, 'Files') !== -1) ||
|
||||||
|
(cd.items && Array.prototype.some.call(cd.items, function (it) { return it.kind === 'file' && /^image\//.test(it.type); }));
|
||||||
|
if (!html && !text && hasImage) return;
|
||||||
|
if (!html && !text) return; // nichts Brauchbares → Standard-Fallback lassen
|
||||||
|
e.preventDefault(); // unterdrückt den fragilen TinyMCE-Standard-Handler
|
||||||
|
let content;
|
||||||
|
if (html) {
|
||||||
|
// Office/Outlook markieren die eigentliche Auswahl per Fragment-Kommentar.
|
||||||
|
const frag = /<!--\s*StartFragment\s*-->([\s\S]*?)<!--\s*EndFragment\s*-->/i.exec(html);
|
||||||
|
content = frag ? frag[1] : html;
|
||||||
|
} else {
|
||||||
|
content = editor.dom.encode(text).replace(/\r\n?|\n/g, '<br>');
|
||||||
|
}
|
||||||
|
editor.insertContent(content);
|
||||||
|
markDirty();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
}).then(() => { edReady = true; });
|
}).then(() => { edReady = true; });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user