This commit is contained in:
2026-01-17 04:05:22 +01:00
parent 4e9e9a6ef0
commit 3752a531f1
3 changed files with 77 additions and 7 deletions

View File

@@ -1,5 +1,11 @@
# Mailadmin Email Template System (R17 modular) # Mailadmin Email Template System (R17 modular)
Stand: 2025-08-31 Stand: 2026-01-17
## Kurzuebersicht
- Admin UI fuer Templates, Sections und Snippets
- GrapesJS Editor lokal eingebunden (kein CDN)
- Modularer Bridge-Core fuer Blöcke, Kategorien und Editor-Erweiterungen
- JSON API mit Dual-DB und Prefix-Support
## Struktur ## Struktur
mailadmin/ mailadmin/
@@ -23,8 +29,12 @@ mailadmin/
ui-editor.js # Editor-Dialog + Handshake ui-editor.js # Editor-Dialog + Handshake
editor/ editor/
editor-core.php # LÄDT NUR LOKAL (kein CDN) editor-core.php # LÄDT NUR LOKAL (kein CDN)
bridge-core.js bridge-core.js # Loader + Guards + Editor-Orchestrierung
config.js # Bibliothek in Block-Leiste config.js # Bibliothek in Block-Leiste
public/assets/js/bridge/
rte-editor.js # RichText-Editor (Modal)
table-builder.js # Table Builder (Modal)
blocks-*.js # Block-Definitionen
## Vendor (lokal bereitstellen) ## Vendor (lokal bereitstellen)
Lege die GrapesJS-Dateien lokal ab (kein CDN): Lege die GrapesJS-Dateien lokal ab (kein CDN):
@@ -36,10 +46,16 @@ Lege die GrapesJS-Dateien lokal ab (kein CDN):
Nutze `schema.sql` (inkl. *NULLfähiger* Fremdschlüssel & ON DELETE SET NULL). Nutze `schema.sql` (inkl. *NULLfähiger* Fremdschlüssel & ON DELETE SET NULL).
Prefix standardmäßig: `emailtemplate_`. Prefix standardmäßig: `emailtemplate_`.
## Editor-Flags (window.BridgeParts)
- ENABLE_EDITOR_EXTENSIONS
- ENABLE_EDITOR_BEHAVIOR
- ENABLE_PLACEHOLDERS
- ENABLE_TABLE_BUILDER
- ENABLE_RTE
## Schnellstart ## Schnellstart
1) `mailadmin/inc/config.php` anlegen (siehe `config.example.php`). 1) `mailadmin/inc/config.php` anlegen (siehe `config.example.php`).
2) `schema.sql` in deiner Templates-Datenbank ausführen. 2) `schema.sql` in deiner Templates-Datenbank ausführen.
3) Vendor-Dateien in `public/vendor/...` kopieren. 3) Vendor-Dateien in `public/vendor/...` kopieren.
4) `public/tools/config-doctor.php` & `public/api.php?action=health` prüfen. 4) `public/tools/config-doctor.php` & `public/api.php?action=health` prüfen.
5) `public/index.php` öffnen → „Neu …“, „Vorschau“, „Im EMailEditor öffnen“ etc. 5) `public/index.php` öffnen → „Neu …“, „Vorschau“, „Im EMailEditor öffnen“ etc.

View File

@@ -1 +1,3 @@
This archive contains the R17 modular Email Template System. Fill inc/config.php and vendor libs. Mailadmin ist ein modulares Email-Template-System mit Admin-UI und lokal eingebundenem GrapesJS-Editor.
Es verwaltet Templates, Sections und Snippets, nutzt eine JSON-API mit Prefix-Support
und laesst sich ueber Bridge-Module (Blocks, RTE, Table Builder) erweitern.

View File

@@ -32,13 +32,17 @@
window.__bridgeModalGuardsInstalled = true; window.__bridgeModalGuardsInstalled = true;
window.__bridgeModalAllowClose = false; window.__bridgeModalAllowClose = false;
const allowCloseOnce = () => {
window.__bridgeModalAllowClose = true;
setTimeout(() => { window.__bridgeModalAllowClose = false; }, 0);
};
document.addEventListener('click', (evt) => { document.addEventListener('click', (evt) => {
const closeHit = evt.target && evt.target.closest const closeHit = evt.target && evt.target.closest
? evt.target.closest('.gjs-mdl-btn-close,[data-bridge-modal-close="1"]') ? evt.target.closest('.gjs-mdl-btn-close,[data-bridge-modal-close="1"]')
: null; : null;
if (closeHit) { if (closeHit) {
window.__bridgeModalAllowClose = true; allowCloseOnce();
setTimeout(() => { window.__bridgeModalAllowClose = false; }, 0);
} }
const container = evt.target && evt.target.closest const container = evt.target && evt.target.closest
? evt.target.closest('.gjs-mdl-container') ? evt.target.closest('.gjs-mdl-container')
@@ -53,6 +57,15 @@
} }
}, true); }, true);
document.addEventListener('mousedown', (evt) => {
const closeHit = evt.target && evt.target.closest
? evt.target.closest('.gjs-mdl-btn-close,[data-bridge-modal-close="1"]')
: null;
if (closeHit) {
allowCloseOnce();
}
}, true);
document.addEventListener('keydown', (evt) => { document.addEventListener('keydown', (evt) => {
if (evt.key !== 'Escape') return; if (evt.key !== 'Escape') return;
const hasModal = document.querySelector('.gjs-mdl-container'); const hasModal = document.querySelector('.gjs-mdl-container');
@@ -603,9 +616,9 @@
try { try {
const modalEl = ed.Modal && ed.Modal.el; const modalEl = ed.Modal && ed.Modal.el;
if (!modalEl) return; if (!modalEl) return;
if (modalEl.querySelector('[data-bridge-viewcode-close]')) return;
const contentEl = modalEl.querySelector('.gjs-mdl-content'); const contentEl = modalEl.querySelector('.gjs-mdl-content');
if (!contentEl) return; if (!contentEl) return;
if (modalEl.querySelector('[data-bridge-viewcode-close]')) return;
const footer = document.createElement('div'); const footer = document.createElement('div');
footer.style.display = 'flex'; footer.style.display = 'flex';
footer.style.justifyContent = 'flex-end'; footer.style.justifyContent = 'flex-end';
@@ -629,6 +642,45 @@
} catch {} } catch {}
}); });
} }
if (ed && ed.Modal && ed.Modal.getModel && !ed.Modal.__bridgeModelHooked) {
ed.Modal.__bridgeModelHooked = true;
const mdl = ed.Modal.getModel();
if (mdl && typeof mdl.on === 'function') {
mdl.on('change:open', () => {
if (!mdl.get('open')) return;
setTimeout(() => {
try {
const modalEl = ed.Modal && ed.Modal.el;
if (!modalEl) return;
const contentEl = modalEl.querySelector('.gjs-mdl-content');
if (!contentEl) return;
if (modalEl.querySelector('[data-bridge-viewcode-close]')) return;
const footer = document.createElement('div');
footer.style.display = 'flex';
footer.style.justifyContent = 'flex-end';
footer.style.paddingTop = '12px';
const btn = document.createElement('button');
btn.type = 'button';
btn.textContent = 'Schließen';
btn.setAttribute('data-bridge-viewcode-close', '1');
btn.setAttribute('data-bridge-modal-close', '1');
btn.style.padding = '6px 12px';
btn.style.border = '1px solid #cbd5f5';
btn.style.borderRadius = '4px';
btn.style.background = '#f8fafc';
btn.style.cursor = 'pointer';
btn.addEventListener('click', () => {
if (B.allowModalCloseOnce) B.allowModalCloseOnce();
if (ed.Modal && ed.Modal.close) ed.Modal.close();
});
footer.appendChild(btn);
contentEl.appendChild(footer);
} catch {}
}, 0);
});
}
}
                 
        // 🛑 KRITISCHE KORREKTUR 1: Explizite Erstellung aller konfigurierten Kategorien         // 🛑 KRITISCHE KORREKTUR 1: Explizite Erstellung aller konfigurierten Kategorien
        ensureConfiguredCategories(ed);          ensureConfiguredCategories(ed);