/* /assets/js/bridge/blocks-custom.js (FINAL & LOG-KONTROLLIERT) */ (function () { const PluginName = 'blocks-custom'; const B = window.BridgeParts || (window.BridgeParts = {}); // ---------------------------------------------------------------------- // 🎯 NEU: LOKALE LOG-KONFIGURATION UND WRAPPER // ---------------------------------------------------------------------- // Setzen Sie dies auf 'false' in der config.js oder hier, um alle Logs NUR für dieses Plugin zu deaktivieren. if (B.LOG_CONFIG && B.LOG_CONFIG.PLUGINS) { B.LOG_CONFIG.PLUGINS[PluginName] = false; // <-- HIER IST IHR SCHALTER } // NEUER LOKALER WRAPPER, der die zentrale B.log Funktion verwendet: const log = (type, message, color = '#FFD700', logType = 'info', force = false) => { if (typeof B.log === 'function') { B.log(PluginName, `[${type}] ${message}`, color, logType, force); } else if (logType === 'error') { // Fallback für kritische Fehler, wenn B.log fehlt console.error(`%c[${PluginName} - ${type}] %c${message}`, `color:red; font-weight:bold;`, 'color:inherit;'); } }; // ---------------------------------------------------------------------- log('FILE CHECK', 'Datei-IIFE startet.'); // NEU: Kontrollierbarer Start-Log if (window.__CUSTOM_BLOCKS_LOADED) return; window.__CUSTOM_BLOCKS_LOADED = true; const TARGET_CAT_ID = 'bausteine'; const ALL_CUSTOM_BLOCK_IDS = []; function addOnce(bm, id, def) { // Hinzufügen des Blocks und Sicherstellen der Kategorie-Zuweisung try { bm.add(id, { ...def, category: TARGET_CAT_ID }); ALL_CUSTOM_BLOCK_IDS.push(id); log('BLOCK ADD', `Block '${id}' erfolgreich hinzugefügt.`, '#B8860B'); } catch (e) { log('BLOCK ERROR', `Fehler beim Hinzufügen von Block '${id}': ${e.message}`, 'red', 'error'); } } const css = o => Object.entries(o).map(([k,v]) => `${k}:${v}`).join(';'); const PLACEHOLDER_COMPONENT = 'placeholder-block'; const placeholderSchemaStore = { promise: null, tables: [], status: null, statusPromise: null, }; const ensureBridgeAvailability = () => { if (placeholderSchemaStore.status !== null) { return Promise.resolve(placeholderSchemaStore.status); } if (placeholderSchemaStore.statusPromise) { return placeholderSchemaStore.statusPromise; } const base = B.API_KERNEL_URL || '/api.php'; const sep = base.includes('?') ? '&' : '?'; const url = `${base}${sep}action=placeholders.status`; placeholderSchemaStore.statusPromise = fetch(url, { credentials: 'include' }) .then(res => { if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json(); }) .then(data => { const available = !!(data && (data.available || (data.settings && data.settings.available))); placeholderSchemaStore.status = available; placeholderSchemaStore.statusPromise = null; if (!available) { log('PLACEHOLDER INFO', 'Bridge-Placeholders nicht konfiguriert – DB-Funktionen deaktiviert.', '#64748b'); } return available; }) .catch(err => { placeholderSchemaStore.status = false; placeholderSchemaStore.statusPromise = null; log('PLACEHOLDER WARN', `Bridge-Status konnte nicht geprüft werden: ${err && err.message ? err.message : err}`, '#b45309'); return false; }); return placeholderSchemaStore.statusPromise; }; const fetchPlaceholderSchema = () => { if (placeholderSchemaStore.promise) return placeholderSchemaStore.promise; placeholderSchemaStore.promise = ensureBridgeAvailability().then(isAvailable => { if (!isAvailable) throw new Error('Bridge not available'); const base = B.API_KERNEL_URL || '/api.php'; const sep = base.includes('?') ? '&' : '?'; const url = `${base}${sep}action=placeholders.schema`; return fetch(url, { credentials: 'include' }) .then(res => { if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json(); }) .then(data => { const tbls = data && Array.isArray(data.tables) ? data.tables : []; placeholderSchemaStore.tables = tbls; return placeholderSchemaStore.tables; }); }).catch(err => { placeholderSchemaStore.tables = []; placeholderSchemaStore.promise = null; const msg = err && err.message ? err.message : err; if (msg === 'Bridge not available') { log('PLACEHOLDER INFO', 'Schema-Abfrage übersprungen (keine Bridge verfügbar).', '#64748b'); } else { log('PLACEHOLDER ERROR', `Schema konnte nicht geladen werden: ${msg}`, 'red', 'error'); } throw err; }); return placeholderSchemaStore.promise; }; const getTraitByName = (model, name) => { if (typeof model.getTrait === 'function') return model.getTrait(name); const traits = model.get('traits'); if (!traits) return null; if (typeof traits.where === 'function') { const found = traits.where({ name }); return found && found[0]; } if (Array.isArray(traits.models)) { return traits.models.find(t => t.get && t.get('name') === name) || null; } return null; }; const ensurePlaceholderComponent = (editor) => { const domc = editor.DomComponents; if (domc.getType(PLACEHOLDER_COMPONENT)) return; const baseType = domc.getType('text') || domc.getType('default') || {}; const BaseModel = baseType.model || editor.DomComponents.Component; const BaseView = baseType.view || editor.DomComponents.View; const placeholderDefaults = {}; if (BaseModel.prototype && BaseModel.prototype.defaults) { for (const key in BaseModel.prototype.defaults) { placeholderDefaults[key] = BaseModel.prototype.defaults[key]; } } placeholderDefaults.name = 'Placeholder'; placeholderDefaults.tagName = 'span'; placeholderDefaults.droppable = false; placeholderDefaults.attributes = { 'data-placeholder-type': 'custom', 'data-placeholder-key': 'UEBERSCHRIFT', }; placeholderDefaults.traits = [ { type: 'select', name: 'data-placeholder-type', label: 'Typ', options: [ { id: 'custom', label: 'Allgemein' }, { id: 'database', label: 'Datenbank' }, ], changeProp: true, }, { type: 'text', name: 'data-placeholder-key', label: 'Bezeichner', placeholder: 'UEBERSCHRIFT', changeProp: true, }, { type: 'select', name: 'data-placeholder-table', label: 'Tabelle', options: [], changeProp: true, }, { type: 'select', name: 'data-placeholder-column', label: 'Feld', options: [], changeProp: true, }, ]; const PlaceholderModel = BaseModel.extend({ init() { this.listenTo(this, 'change:attributes', this.updatePlaceholderState); this.updatePlaceholderState(); fetchPlaceholderSchema() .then(() => this.updateSchemaTraits()) .catch(() => this.updateSchemaTraits([])); }, updatePlaceholderState() { const attrs = this.getAttributes(); const type = attrs['data-placeholder-type'] || 'custom'; if (type === 'database' && placeholderSchemaStore.tables.length === 0) { this.addAttributes({ 'data-placeholder-type': 'custom' }); } this.updateTraitVisibility(); this.updateSchemaTraits(); this.updateLabel(); }, updateTraitVisibility() { const attrs = this.getAttributes(); const type = attrs['data-placeholder-type'] || 'custom'; const isDb = type === 'database'; const tableTrait = getTraitByName(this, 'data-placeholder-table'); const columnTrait = getTraitByName(this, 'data-placeholder-column'); const keyTrait = getTraitByName(this, 'data-placeholder-key'); if (tableTrait && tableTrait.view && tableTrait.view.el) { tableTrait.view.el.style.display = isDb ? '' : 'none'; } if (columnTrait && columnTrait.view && columnTrait.view.el) { columnTrait.view.el.style.display = isDb ? '' : 'none'; } if (keyTrait && keyTrait.view && keyTrait.view.el) { keyTrait.view.el.style.display = isDb ? 'none' : ''; } }, updateSchemaTraits(tablesOverride) { const tables = Array.isArray(tablesOverride) ? tablesOverride : placeholderSchemaStore.tables; const tableTrait = getTraitByName(this, 'data-placeholder-table'); const columnTrait = getTraitByName(this, 'data-placeholder-column'); const loading = !tablesOverride && placeholderSchemaStore.promise && !tables.length; if (tableTrait) { let opts; if (tables.length) { opts = tables.map(function (tbl) { return { id: tbl.name, label: tbl.name }; }); } else if (loading) { opts = [{ id: '', label: 'Tabellen werden geladen…', disabled: true }]; } else { opts = [{ id: '', label: 'Keine Tabellen verfügbar', disabled: true }]; } setTraitOptions(tableTrait, opts); } if (columnTrait) { const attrs = this.getAttributes(); const tableName = (attrs['data-placeholder-table'] || '').toLowerCase(); const table = tables.find(function (tbl) { return tbl.name.toLowerCase() === tableName; }); const colOpts = table ? table.columns.map(function (col) { return { id: col.name, label: col.name + ' (' + col.type + ')' }; }) : [{ id: '', label: table ? 'Keine Felder' : 'Feld wählen', disabled: !table }]; setTraitOptions(columnTrait, colOpts); } }, updateLabel() { const attrs = this.getAttributes(); const type = attrs['data-placeholder-type'] || 'custom'; let label; if (type === 'database') { const table = attrs['data-placeholder-table'] || 'TABELLE'; const column = attrs['data-placeholder-column'] || 'FELD'; label = (table + '.' + column).toUpperCase(); } else { label = (attrs['data-placeholder-key'] || 'PLATZHALTER').toUpperCase(); } const text = '{{' + label + '}}'; const comps = this.components(); const onlyText = comps.length === 1 && comps.at(0).is('textnode'); if (onlyText) { comps.at(0).set('content', text); } else { comps.reset([{ type: 'textnode', content: text }]); } } }); PlaceholderModel.prototype.defaults = placeholderDefaults; PlaceholderModel.isComponent = function (el) { if (el && el.hasAttribute && el.hasAttribute('data-placeholder-type')) { return { type: PLACEHOLDER_COMPONENT }; } return false; }; const PlaceholderView = BaseView.extend({ render() { BaseView.prototype.render.apply(this, arguments); this.el.classList.add('placeholder-block'); this.el.style.display = 'inline-block'; this.el.style.padding = '2px 8px'; this.el.style.border = '1px dashed #94a3b8'; this.el.style.borderRadius = '6px'; this.el.style.background = '#f1f5f9'; this.el.style.fontFamily = 'monospace'; this.el.style.fontSize = '12px'; return this; }, }); domc.addType(PLACEHOLDER_COMPONENT, { model: PlaceholderModel, view: PlaceholderView, }); }; function setTraitOptions(trait, options) { if (!trait) return; trait.set('options', options); if (trait.view && typeof trait.view.render === 'function') { trait.view.render(); } } function register(editor) { log('EXECUTION', `Starte Block-Registrierung für ${TARGET_CAT_ID}.`, '#DAA520'); const bm = editor.BlockManager; ensurePlaceholderComponent(editor); // --- Custom-Blöcke DEFINIEREN --- // PLACEHOLDER (Custom) const customPlaceholderBlock = { id: 'cust-placeholder-custom', label: '🔖 Placeholder (Text)', content: `{{UEBERSCHRIFT}}` }; addOnce(bm, customPlaceholderBlock.id, customPlaceholderBlock); // Datenbank Placeholder – erst registrieren, wenn Tabellen verfügbar fetchPlaceholderSchema() .then(tables => { if (!tables || !tables.length) { log('PLACEHOLDER INFO', 'Keine Tabellen – DB Placeholder Block wird nicht angezeigt.', '#888'); return; } const firstTable = tables[0] || {}; const tableName = firstTable.name || 'tabelle'; const columns = Array.isArray(firstTable.columns) ? firstTable.columns : []; const firstColumn = columns.length ? columns[0].name : 'feld'; const placeholderLabel = (tableName + '.' + firstColumn).toUpperCase(); addOnce(bm, 'cust-placeholder-db', { id: 'cust-placeholder-db', label: '🗄️ Placeholder (DB)', content: `{{${placeholderLabel}}}` }); }) .catch(() => { log('PLACEHOLDER WARN', 'DB Placeholder Block ausgeblendet (Schemafehler).', '#b45309'); }); // TEXT addOnce(bm, 'cust-text', { id:'cust-text', label:'📝 Text', content:`

Dies ist ein Absatz. Doppelklick zum Bearbeiten.

` }); // IMAGE addOnce(bm, 'cust-image', { id:'cust-image', label:'🖼️ Bild', content:`
Bild
` }); // BUTTON addOnce(bm, 'cust-button', { id:'cust-button', label:'🔘 Button', content:`
Call To Action
` }); // DIVIDER addOnce(bm, 'cust-divider',{ id:'cust-divider',label:'⎯ Divider', content:`
` }); // SPACER addOnce(bm, 'cust-spacer', { id:'cust-spacer', label:'↕ Spacer', content:`
` }); // 2 COL addOnce(bm, 'cust-2col', { id:'cust-2col', label:'🧩 2 Spalten', content:`

Linke Spalte – Inhalt hier.

Rechte Spalte – Inhalt hier.

` }); // MEDIA LEFT addOnce(bm, 'cust-media-left', { id:'cust-media-left', label:'🖼️◀ Text', content:`
Bild

Überschrift

Beschreibungstext …

Mehr erfahren
` }); // HERO addOnce(bm, 'cust-hero', { id:'cust-hero', label:'🌄 Hero', content:`
Hero

Titel des Newsletters

Kurzer Untertitel oder Einleitung.

` }); // FOOTER addOnce(bm, 'cust-footer', { id:'cust-footer', label:'⚓ Footer', content:`

Dein Unternehmen GmbH • Musterstraße 1 • 12345 Berlin

Abmelden · Impressum · Datenschutz

` }); log('SUCCESS', `Registrierung abgeschlossen. ${ALL_CUSTOM_BLOCK_IDS.length} Blöcke erstellt.`, '#008000', 'info', true); } // 🛑 KRITISCHE EXPORT-KORREKTUR: Exportiere 'register', um den Fehler in bridge-core.js zu beheben window.BridgeBlocksCustom = { IDS: ALL_CUSTOM_BLOCK_IDS, register: register // <--- NEU: Exportiert die Register-Funktion }; // Registriere das Modul als GrapesJS Plugin if (B && B.registerGrapesJSPlugin && typeof register === 'function') { B.registerGrapesJSPlugin('bridge-blocks-custom', register); log('PLUGIN REGISTER', `'bridge-blocks-custom' erfolgreich zur Bridge Plugin Registry hinzugefügt.`, '#008000'); } else { log('CRITICAL ERROR', `BridgeParts oder registerGrapesJSPlugin fehlt! Plugin-Registrierung gescheitert.`, 'red', 'error'); } })();