From e5fed53ac32b6d9b1585111465fe99f42a94d917 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Wed, 10 Dec 2025 22:37:59 +0100 Subject: [PATCH] asd --- .gitlab-ci.yml | 42 ++++++ public/assets/js/bridge/blocks-placeholder.js | 133 ++++++++++++------ 2 files changed, 132 insertions(+), 43 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 492e0a4..4d82eba 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -96,6 +96,27 @@ deploy:staging: PATCH=$(grep '\$patchversion' "${VERSION_WORK_FILE}" 2>/dev/null | tr -cd '0-9') fi + REMOTE_PATCH_TMP=".ci_remote_patch_${CI_ENVIRONMENT_NAME}.txt" + REMOTE_PATCH_TARGET="${TARGET_PATH}${CONFIG_BASE_DIR}/versionsnummer.txt" + echo "📥 Prüfe entfernte Versionsnummer (${REMOTE_PATCH_TARGET})..." + lftp -u "${FTP_USER}","${FTP_PASSWORD}" "${FTP_HOST}" -e " + set ftp:ssl-force true; + set ftp:passive-mode true; + set ftp:ssl-protect-data true; + set ssl:verify-certificate no; + set cmd:fail-exit false; + get ${REMOTE_PATCH_TARGET} ${REMOTE_PATCH_TMP}; + bye + " >/dev/null 2>&1 || true + if [ -f "${REMOTE_PATCH_TMP}" ]; then + REMOTE_PATCH=$(tr -cd '0-9' < "${REMOTE_PATCH_TMP}") + if [ -n "${REMOTE_PATCH}" ] && [ "${REMOTE_PATCH}" -gt "${PATCH:-0}" ]; then + echo "🔄 Entfernte Versionsnummer (${REMOTE_PATCH}) ist höher als lokal (${PATCH:-0})." + PATCH=${REMOTE_PATCH} + fi + rm -f "${REMOTE_PATCH_TMP}" + fi + [ -z "$MAIN" ] && MAIN=1 [ -z "$SUB" ] && SUB=0 [ -z "$PATCH" ] && PATCH=0 @@ -303,6 +324,27 @@ deploy:production: PATCH=$(grep '\$patchversion' "${VERSION_WORK_FILE}" 2>/dev/null | tr -cd '0-9') fi + REMOTE_PATCH_TMP=".ci_remote_patch_${CI_ENVIRONMENT_NAME}.txt" + REMOTE_PATCH_TARGET="${TARGET_PATH}${CONFIG_BASE_DIR}/versionsnummer.txt" + echo "📥 Prüfe entfernte Versionsnummer (${REMOTE_PATCH_TARGET})..." + lftp -u "${FTP_USER}","${FTP_PASSWORD}" "${FTP_HOST}" -e " + set ftp:ssl-force true; + set ftp:passive-mode true; + set ftp:ssl-protect-data true; + set ssl:verify-certificate no; + set cmd:fail-exit false; + get ${REMOTE_PATCH_TARGET} ${REMOTE_PATCH_TMP}; + bye + " >/dev/null 2>&1 || true + if [ -f "${REMOTE_PATCH_TMP}" ]; then + REMOTE_PATCH=$(tr -cd '0-9' < "${REMOTE_PATCH_TMP}") + if [ -n "${REMOTE_PATCH}" ] && [ "${REMOTE_PATCH}" -gt "${PATCH:-0}" ]; then + echo "🔄 Entfernte Versionsnummer (${REMOTE_PATCH}) ist höher als lokal (${PATCH:-0})." + PATCH=${REMOTE_PATCH} + fi + rm -f "${REMOTE_PATCH_TMP}" + fi + [ -z "$MAIN" ] && MAIN=1 [ -z "$SUB" ] && SUB=0 [ -z "$PATCH" ] && PATCH=0 diff --git a/public/assets/js/bridge/blocks-placeholder.js b/public/assets/js/bridge/blocks-placeholder.js index 181f970..f32756f 100644 --- a/public/assets/js/bridge/blocks-placeholder.js +++ b/public/assets/js/bridge/blocks-placeholder.js @@ -24,6 +24,7 @@ const TARGET_CAT_ID = 'placeholders'; const PLACEHOLDER_COMPONENT = 'placeholder-block'; const ALL_PLACEHOLDER_BLOCK_IDS = []; const INLINE_PLACEHOLDER_CLASS = 'bridge-placeholder-inline'; +const PLACEHOLDER_MARKER_ATTR = 'data-placeholder-marker'; const placeholderSchemaStore = { promise: null, @@ -220,31 +221,56 @@ const buildPlaceholderHTML = (payload) => { return true; }; - const captureRteSelection = (rteInstance) => { - const doc = rteInstance && rteInstance.doc - ? rteInstance.doc - : (rteInstance && rteInstance.el && rteInstance.el.ownerDocument) || document; - if (!doc || !doc.getSelection) return null; - const sel = doc.getSelection(); - if (!sel || !sel.rangeCount) return null; - return { - doc, - range: sel.getRangeAt(0).cloneRange() - }; + const insertCaretMarker = (rteInstance, markerId) => { + if (!markerId) return false; + const markerHtml = ``; + if (insertPlaceholderIntoSelection(rteInstance, markerHtml)) { + return true; + } + if (rteInstance && typeof rteInstance.insertHTML === 'function') { + rteInstance.insertHTML(markerHtml); + return true; + } + if (typeof document !== 'undefined' && document.execCommand) { + document.execCommand('insertHTML', false, markerHtml); + return true; + } + return false; }; - const restoreRteSelection = (snapshot) => { - if (!snapshot || !snapshot.doc || !snapshot.range) return false; - const sel = snapshot.doc.getSelection && snapshot.doc.getSelection(); - if (!sel) return false; - try { - sel.removeAllRanges(); - sel.addRange(snapshot.range); - return true; - } catch (err) { - log('PLACEHOLDER WARN', `Auswahl konnte nicht wiederhergestellt werden: ${err && err.message ? err.message : err}`, '#b45309'); + const removeMarkerFromComponent = (component, markerId, editor) => { + if (!component || !markerId) return false; + const viewEl = component.view && component.view.el; + if (!viewEl) return false; + const selector = `[${PLACEHOLDER_MARKER_ATTR}="${markerId}"]`; + const marker = viewEl.querySelector(selector); + if (!marker || !marker.parentNode) return false; + marker.parentNode.removeChild(marker); + reparseTextComponent(component, editor); + return true; + }; + + const replaceMarkerWithPlaceholder = (component, markerId, payload, editor) => { + if (!component || !markerId) return false; + const viewEl = component.view && component.view.el; + if (!viewEl) return false; + const marker = viewEl.querySelector(`[${PLACEHOLDER_MARKER_ATTR}="${markerId}"]`); + if (!marker) return false; + if (typeof document === 'undefined') return false; + const temp = document.createElement('div'); + temp.innerHTML = buildPlaceholderHTML(payload); + const placeholderNode = temp.firstElementChild || temp.firstChild; + if (!placeholderNode) return false; + if (typeof marker.replaceWith === 'function') { + marker.replaceWith(placeholderNode); + } else if (marker.parentNode) { + marker.parentNode.insertBefore(placeholderNode, marker); + marker.parentNode.removeChild(marker); + } else { return false; } + reparseTextComponent(component, editor); + return true; }; const openPlaceholderModal = (editor, component, opts = {}) => { @@ -361,6 +387,15 @@ const buildPlaceholderHTML = (payload) => { container.appendChild(form); let tablesCache = placeholderSchemaStore.tables || []; + if (tablesCache.length) { + populateTables(tablesCache); + } else if (placeholderSchemaStore.promise) { + placeholderSchemaStore.promise.then(populateTables).catch(() => populateTables([])); + } else if (placeholderSchemaStore.status !== false) { + fetchPlaceholderSchema() + .then(populateTables) + .catch(() => populateTables([])); + } const toggleSections = () => { const type = typeSelect.value || 'custom'; @@ -416,6 +451,16 @@ const buildPlaceholderHTML = (payload) => { }); }; + if (tablesCache.length) { + populateTables(tablesCache); + } else if (placeholderSchemaStore.promise) { + placeholderSchemaStore.promise.then(populateTables).catch(() => populateTables([])); + } else if (placeholderSchemaStore.status !== false) { + fetchPlaceholderSchema() + .then(populateTables) + .catch(() => populateTables([])); + } + tableSelect.addEventListener('change', () => { populateColumns(tableSelect.value); }); @@ -550,38 +595,37 @@ const buildPlaceholderHTML = (payload) => { log('PLACEHOLDER INFO', 'Bitte zuerst ein Text-Element auswählen.', '#888'); return; } - const selectionSnapshot = captureRteSelection(rteInstance); + const markerId = `bridge-placeholder-marker-${Date.now()}-${Math.random().toString(36).slice(2)}`; + const markerInserted = insertCaretMarker(rteInstance, markerId); + if (!markerInserted) { + log('PLACEHOLDER ERROR', 'Marker konnte nicht gesetzt werden. Bitte erneut versuchen.', 'red', 'error'); + return; + } + setTimeout(() => reparseTextComponent(target, editor), 0); + const cleanupMarker = () => removeMarkerFromComponent(target, markerId, editor); openPlaceholderModal(editor, null, { onCancel: () => { - if (selectionSnapshot) { - restoreRteSelection(selectionSnapshot); - } + cleanupMarker(); if (rteInstance && typeof rteInstance.focus === 'function') { setTimeout(() => rteInstance.focus(), 0); } }, onSubmit: (payload) => { - if (selectionSnapshot) { - restoreRteSelection(selectionSnapshot); - } - const html = buildPlaceholderHTML(payload); - let inserted = false; - if (insertPlaceholderIntoSelection(rteInstance, html)) { - inserted = true; - } else if (rteInstance && typeof rteInstance.insertHTML === 'function') { - rteInstance.insertHTML(html); - inserted = true; - } else if (typeof document !== 'undefined' && document.execCommand) { - document.execCommand('insertHTML', false, html); - inserted = true; - } else { - log('PLACEHOLDER ERROR', 'Placeholder konnte nicht eingefügt werden (kein RTE).', 'red', 'error'); - return false; + const replaced = replaceMarkerWithPlaceholder(target, markerId, payload, editor); + if (!replaced) { + cleanupMarker(); + const viewEl = target.view && target.view.el; + if (viewEl) { + viewEl.insertAdjacentHTML('beforeend', buildPlaceholderHTML(payload)); + reparseTextComponent(target, editor); + } else { + log('PLACEHOLDER ERROR', 'Placeholder konnte nicht eingefügt werden (kein Ziel).', 'red', 'error'); + return false; + } } if (rteInstance && typeof rteInstance.focus === 'function') { - rteInstance.focus(); + setTimeout(() => rteInstance.focus(), 0); } - setTimeout(() => reparseTextComponent(target, editor), 0); return true; } }); @@ -940,6 +984,9 @@ const buildPlaceholderHTML = (payload) => { const bm = editor.BlockManager; ensurePlaceholderComponent(editor); ensureTextSupportsPlaceholders(editor); + if (!placeholderSchemaStore.promise && placeholderSchemaStore.status !== false) { + fetchPlaceholderSchema().catch(() => {}); + } if (!editor.Commands.get('bridge-placeholder:edit')) { editor.Commands.add('bridge-placeholder:edit', {