This commit is contained in:
2025-12-10 22:37:59 +01:00
parent f9454605e1
commit e5fed53ac3
2 changed files with 132 additions and 43 deletions

View File

@@ -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

View File

@@ -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 = `<span ${PLACEHOLDER_MARKER_ATTR}="${markerId}"></span>`;
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', {