adsad
This commit is contained in:
@@ -502,6 +502,12 @@
|
|||||||
// 🚨 KRITISCH: Server erwartet das Feld 'json'
|
// 🚨 KRITISCH: Server erwartet das Feld 'json'
|
||||||
json: jsonProjectDataRaw,
|
json: jsonProjectDataRaw,
|
||||||
};
|
};
|
||||||
|
const activateNext = B.NEXT_ACTIVATE_VERSION || window.NEXT_ACTIVATE_VERSION;
|
||||||
|
if (activateNext) {
|
||||||
|
dataToSend.activate_version = 1;
|
||||||
|
B.NEXT_ACTIVATE_VERSION = 0;
|
||||||
|
window.NEXT_ACTIVATE_VERSION = 0;
|
||||||
|
}
|
||||||
if (SECTION_ID) {
|
if (SECTION_ID) {
|
||||||
dataToSend.section_id = SECTION_ID;
|
dataToSend.section_id = SECTION_ID;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,7 +191,7 @@
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const promises = sections.map(section =>
|
const promises = sections.map(section =>
|
||||||
fetchData('content', 'list', { section_id: section.id })
|
fetchData('content', 'list', { section_id: section.id, active_only: 1 })
|
||||||
.then(items => (Array.isArray(items) ? items : []).map(i => ({
|
.then(items => (Array.isArray(items) ? items : []).map(i => ({
|
||||||
...i,
|
...i,
|
||||||
kind: String(section.slug || '').toLowerCase(),
|
kind: String(section.slug || '').toLowerCase(),
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ export function initEditor() {
|
|||||||
const btnUnsavedCancel = document.getElementById('btn-unsaved-cancel');
|
const btnUnsavedCancel = document.getElementById('btn-unsaved-cancel');
|
||||||
const btnUnsavedDiscard = document.getElementById('btn-unsaved-discard');
|
const btnUnsavedDiscard = document.getElementById('btn-unsaved-discard');
|
||||||
const btnUnsavedSave = document.getElementById('btn-unsaved-save');
|
const btnUnsavedSave = document.getElementById('btn-unsaved-save');
|
||||||
|
const btnDeactivateVersion = document.getElementById('btn-deactivate-version');
|
||||||
|
const activateDialog = document.getElementById('activateVersionDialog');
|
||||||
|
const btnActivateCancel = document.getElementById('btn-activate-cancel');
|
||||||
|
const btnActivateNo = document.getElementById('btn-activate-no');
|
||||||
|
const btnActivateYes = document.getElementById('btn-activate-yes');
|
||||||
|
|
||||||
let current = null; // { resource, id, name, section }
|
let current = null; // { resource, id, name, section }
|
||||||
let bridgeListener = null;
|
let bridgeListener = null;
|
||||||
@@ -49,6 +54,9 @@ export function initEditor() {
|
|||||||
let suppressDirty = false;
|
let suppressDirty = false;
|
||||||
let suppressTimer = null;
|
let suppressTimer = null;
|
||||||
let baselineReady = false;
|
let baselineReady = false;
|
||||||
|
let versionMap = new Map();
|
||||||
|
let currentVersionId = 0;
|
||||||
|
let currentVersionMeta = null;
|
||||||
|
|
||||||
const ok = (m) => toast(m, true);
|
const ok = (m) => toast(m, true);
|
||||||
const err = (m) => toast(m, false);
|
const err = (m) => toast(m, false);
|
||||||
@@ -94,6 +102,7 @@ export function initEditor() {
|
|||||||
function setVersionUiVisible(show) {
|
function setVersionUiVisible(show) {
|
||||||
if (versionSelect) versionSelect.classList.toggle('hidden', !show);
|
if (versionSelect) versionSelect.classList.toggle('hidden', !show);
|
||||||
// restore button removed
|
// restore button removed
|
||||||
|
if (btnDeactivateVersion) btnDeactivateVersion.classList.toggle('hidden', !show);
|
||||||
}
|
}
|
||||||
|
|
||||||
setVersionUiVisible(false);
|
setVersionUiVisible(false);
|
||||||
@@ -262,11 +271,49 @@ export function initEditor() {
|
|||||||
return choice;
|
return choice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showActivateDialog() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (!activateDialog || typeof activateDialog.showModal !== 'function') {
|
||||||
|
resolve('no');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const cleanup = () => {
|
||||||
|
btnActivateCancel && btnActivateCancel.removeEventListener('click', onCancel);
|
||||||
|
btnActivateNo && btnActivateNo.removeEventListener('click', onNo);
|
||||||
|
btnActivateYes && btnActivateYes.removeEventListener('click', onYes);
|
||||||
|
};
|
||||||
|
const closeWith = (choice) => {
|
||||||
|
cleanup();
|
||||||
|
activateDialog.close();
|
||||||
|
resolve(choice);
|
||||||
|
};
|
||||||
|
const onCancel = () => closeWith('cancel');
|
||||||
|
const onNo = () => closeWith('no');
|
||||||
|
const onYes = () => closeWith('yes');
|
||||||
|
btnActivateCancel && btnActivateCancel.addEventListener('click', onCancel);
|
||||||
|
btnActivateNo && btnActivateNo.addEventListener('click', onNo);
|
||||||
|
btnActivateYes && btnActivateYes.addEventListener('click', onYes);
|
||||||
|
activateDialog.showModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateVersionMeta(id) {
|
||||||
|
const key = id ? String(id) : '';
|
||||||
|
currentVersionId = id ? Number(id) : 0;
|
||||||
|
currentVersionMeta = key && versionMap.has(key) ? versionMap.get(key) : null;
|
||||||
|
if (btnDeactivateVersion) {
|
||||||
|
const isActive = !!(currentVersionMeta && Number(currentVersionMeta.is_active) === 1);
|
||||||
|
btnDeactivateVersion.classList.toggle('hidden', !isActive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderVersionOptions(items) {
|
function renderVersionOptions(items) {
|
||||||
versionItems = items || [];
|
versionItems = items || [];
|
||||||
if (!versionSelect) return;
|
versionMap = new Map();
|
||||||
|
if (!versionSelect) return '';
|
||||||
const rows = Array.isArray(versionItems) ? versionItems : [];
|
const rows = Array.isArray(versionItems) ? versionItems : [];
|
||||||
versionSelect.innerHTML = '';
|
versionSelect.innerHTML = '';
|
||||||
|
lastVersionSelection = '';
|
||||||
if (!rows.length) {
|
if (!rows.length) {
|
||||||
const opt = document.createElement('option');
|
const opt = document.createElement('option');
|
||||||
opt.value = '';
|
opt.value = '';
|
||||||
@@ -274,33 +321,41 @@ export function initEditor() {
|
|||||||
opt.disabled = true;
|
opt.disabled = true;
|
||||||
versionSelect.appendChild(opt);
|
versionSelect.appendChild(opt);
|
||||||
versionSelect.disabled = true;
|
versionSelect.disabled = true;
|
||||||
return;
|
updateVersionMeta(0);
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
versionSelect.disabled = false;
|
versionSelect.disabled = false;
|
||||||
rows.forEach((item, idx) => {
|
let activeId = '';
|
||||||
|
rows.forEach((item) => {
|
||||||
const opt = document.createElement('option');
|
const opt = document.createElement('option');
|
||||||
const label = `#${item.version_no} – ${formatVersionDate(item.created_at)}`;
|
const label = `#${item.version_no} – ${formatVersionDate(item.created_at)}` + (Number(item.is_active) === 1 ? ' (aktiv)' : '');
|
||||||
opt.value = String(item.id);
|
opt.value = String(item.id);
|
||||||
opt.textContent = label;
|
opt.textContent = label;
|
||||||
versionSelect.appendChild(opt);
|
versionSelect.appendChild(opt);
|
||||||
if (!lastVersionSelection && idx === 0) {
|
versionMap.set(String(item.id), item);
|
||||||
lastVersionSelection = String(item.id);
|
if (Number(item.is_active) === 1 && !activeId) activeId = String(item.id);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
if (lastVersionSelection) versionSelect.value = lastVersionSelection;
|
const fallbackId = activeId || (rows[0] ? String(rows[0].id) : '');
|
||||||
|
if (fallbackId) {
|
||||||
|
lastVersionSelection = fallbackId;
|
||||||
|
versionSelect.value = fallbackId;
|
||||||
|
updateVersionMeta(Number(fallbackId));
|
||||||
|
}
|
||||||
|
return lastVersionSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadVersionsForCurrent() {
|
async function loadVersionsForCurrent() {
|
||||||
if (!current?.id) {
|
if (!current?.id) {
|
||||||
renderVersionOptions([]);
|
renderVersionOptions([]);
|
||||||
return;
|
return '';
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const res = await apiAction('content_versions.list', { method: 'GET', data: { content_id: current.id, id: current.id } });
|
const res = await apiAction('content_versions.list', { method: 'GET', data: { content_id: current.id, id: current.id } });
|
||||||
if (!res?.ok) throw new Error(res?.error || 'Versionen konnten nicht geladen werden');
|
if (!res?.ok) throw new Error(res?.error || 'Versionen konnten nicht geladen werden');
|
||||||
renderVersionOptions(Array.isArray(res?.items) ? res.items : []);
|
return renderVersionOptions(Array.isArray(res?.items) ? res.items : []);
|
||||||
} catch {
|
} catch {
|
||||||
renderVersionOptions([]);
|
renderVersionOptions([]);
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,9 +649,11 @@ export function initEditor() {
|
|||||||
// Overlay zeigen
|
// Overlay zeigen
|
||||||
showVeil();
|
showVeil();
|
||||||
|
|
||||||
// Daten parallel laden (fresh HTML + kontextgefilterte Snippets + Referenzen)
|
const requestedVersionId = Number(item?.version_id || item?.versionId || 0);
|
||||||
let fresh = '';
|
|
||||||
let snippets = [];
|
// Daten parallel laden (fresh HTML + kontextgefilterte Snippets + Referenzen)
|
||||||
|
let fresh = '';
|
||||||
|
let snippets = [];
|
||||||
let refLib = { sections: [], blocks: [] };
|
let refLib = { sections: [], blocks: [] };
|
||||||
let hasJson = false;
|
let hasJson = false;
|
||||||
let jsonState = '';
|
let jsonState = '';
|
||||||
@@ -604,6 +661,7 @@ export function initEditor() {
|
|||||||
let editorType = 'grapesjs';
|
let editorType = 'grapesjs';
|
||||||
let craftJson = '';
|
let craftJson = '';
|
||||||
|
|
||||||
|
let defaultVersionId = '';
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
(async() => {
|
(async() => {
|
||||||
try {
|
try {
|
||||||
@@ -624,9 +682,25 @@ export function initEditor() {
|
|||||||
})(),
|
})(),
|
||||||
(async() => { snippets = await buildSnippetsForContext(current); })(),
|
(async() => { snippets = await buildSnippetsForContext(current); })(),
|
||||||
(async() => { refLib = await buildRefLibForContext(current); })(),
|
(async() => { refLib = await buildRefLibForContext(current); })(),
|
||||||
(async() => { await loadVersionsForCurrent(); })()
|
(async() => { defaultVersionId = await loadVersionsForCurrent(); })()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const effectiveVersionId = requestedVersionId ? String(requestedVersionId) : defaultVersionId;
|
||||||
|
if (effectiveVersionId) {
|
||||||
|
try {
|
||||||
|
const res = await apiAction('content_versions.get', { method: 'GET', data: { id: effectiveVersionId, content_id: current.id } });
|
||||||
|
if (res?.ok && res?.item) {
|
||||||
|
const fields = extractContentFields(res.item);
|
||||||
|
fresh = fields.html || '';
|
||||||
|
jsonState = fields.json || '';
|
||||||
|
editorType = fields.editorType || editorType;
|
||||||
|
craftJson = fields.craftJson || '';
|
||||||
|
updateVersionMeta(Number(effectiveVersionId));
|
||||||
|
lastVersionSelection = String(effectiveVersionId);
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
editorType = editorType === 'craftjs' ? 'craftjs' : 'grapesjs';
|
editorType = editorType === 'craftjs' ? 'craftjs' : 'grapesjs';
|
||||||
setSavedSnapshotFromData({ html: fresh, content: jsonState, editor_type: editorType, craft_json: craftJson });
|
setSavedSnapshotFromData({ html: fresh, content: jsonState, editor_type: editorType, craft_json: craftJson });
|
||||||
setEditorType(editorType);
|
setEditorType(editorType);
|
||||||
@@ -744,12 +818,20 @@ export function initEditor() {
|
|||||||
async function save() {
|
async function save() {
|
||||||
if (!current?.id) return err('Keine aktive ID');
|
if (!current?.id) return err('Keine aktive ID');
|
||||||
|
|
||||||
|
let activateNext = false;
|
||||||
|
if (currentVersionMeta && (Number(currentVersionMeta.is_active) === 1 || Number(currentVersionMeta.was_active) === 1)) {
|
||||||
|
const decision = await showActivateDialog();
|
||||||
|
if (decision === 'cancel') return false;
|
||||||
|
activateNext = decision === 'yes';
|
||||||
|
}
|
||||||
|
|
||||||
if (currentEditorType === 'craftjs') {
|
if (currentEditorType === 'craftjs') {
|
||||||
const html = craftEditor ? craftEditor.getContent() : '';
|
const html = craftEditor ? craftEditor.getContent() : '';
|
||||||
const craftJson = craftEditor && craftEditor.getCraftJson
|
const craftJson = craftEditor && craftEditor.getCraftJson
|
||||||
? craftEditor.getCraftJson()
|
? craftEditor.getCraftJson()
|
||||||
: JSON.stringify({ html });
|
: JSON.stringify({ html });
|
||||||
const payload = { html, craft_json: craftJson, editor_type: 'craftjs', section_id: current.section.id };
|
const payload = { html, craft_json: craftJson, editor_type: 'craftjs', section_id: current.section.id };
|
||||||
|
if (activateNext) payload.activate_version = 1;
|
||||||
const res = await apiUpdate('content', current.id, payload);
|
const res = await apiUpdate('content', current.id, payload);
|
||||||
if (res?.ok) ok('Gespeichert');
|
if (res?.ok) ok('Gespeichert');
|
||||||
else err(res?.error || 'Speichern fehlgeschlagen');
|
else err(res?.error || 'Speichern fehlgeschlagen');
|
||||||
@@ -757,6 +839,14 @@ export function initEditor() {
|
|||||||
return res?.ok;
|
return res?.ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (activateNext) {
|
||||||
|
const win = iframe?.contentWindow;
|
||||||
|
if (win && win.BridgeParts) {
|
||||||
|
win.BridgeParts.NEXT_ACTIVATE_VERSION = 1;
|
||||||
|
} else if (win) {
|
||||||
|
win.NEXT_ACTIVATE_VERSION = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
const okSave = await delegateCommand('save-data');
|
const okSave = await delegateCommand('save-data');
|
||||||
if (okSave) {
|
if (okSave) {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
@@ -921,6 +1011,18 @@ export function initEditor() {
|
|||||||
btnCancelSend&& (btnCancelSend.onclick= closeSend);
|
btnCancelSend&& (btnCancelSend.onclick= closeSend);
|
||||||
sendForm && (sendForm.onsubmit = doSend);
|
sendForm && (sendForm.onsubmit = doSend);
|
||||||
editorSelect && (editorSelect.onchange = () => switchEditor(editorSelect.value));
|
editorSelect && (editorSelect.onchange = () => switchEditor(editorSelect.value));
|
||||||
|
btnDeactivateVersion && (btnDeactivateVersion.onclick = async () => {
|
||||||
|
if (!current?.id) return;
|
||||||
|
if (!currentVersionMeta || Number(currentVersionMeta.is_active) !== 1) return;
|
||||||
|
try {
|
||||||
|
const res = await apiAction('content_versions.deactivate', { method: 'POST', data: { content_id: current.id } });
|
||||||
|
if (!res?.ok) throw new Error(res?.error || 'Deaktivieren fehlgeschlagen');
|
||||||
|
await loadVersionsForCurrent();
|
||||||
|
toast('Aktive Version deaktiviert', true);
|
||||||
|
} catch (e) {
|
||||||
|
err(e.message || 'Deaktivieren fehlgeschlagen');
|
||||||
|
}
|
||||||
|
});
|
||||||
versionSelect && (versionSelect.onchange = async () => {
|
versionSelect && (versionSelect.onchange = async () => {
|
||||||
if (!current?.id) return;
|
if (!current?.id) return;
|
||||||
const previousSelection = lastVersionSelection;
|
const previousSelection = lastVersionSelection;
|
||||||
@@ -939,6 +1041,7 @@ export function initEditor() {
|
|||||||
if (!res?.ok) throw new Error(res?.error || 'Version konnte nicht geladen werden');
|
if (!res?.ok) throw new Error(res?.error || 'Version konnte nicht geladen werden');
|
||||||
await applyVersionPayload(res?.item || res);
|
await applyVersionPayload(res?.item || res);
|
||||||
lastVersionSelection = String(versionId);
|
lastVersionSelection = String(versionId);
|
||||||
|
updateVersionMeta(versionId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err(e.message || 'Version konnte nicht geladen werden');
|
err(e.message || 'Version konnte nicht geladen werden');
|
||||||
versionSelect.value = previousSelection;
|
versionSelect.value = previousSelection;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ async function fetchContentItem(id, sectionId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function openContentEditor(item, section) {
|
async function openContentEditor(item, section) {
|
||||||
|
const versionId = Number(item?.version_id || 0);
|
||||||
const id = Number(item?.id || 0);
|
const id = Number(item?.id || 0);
|
||||||
const name = item?.name || '';
|
const name = item?.name || '';
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
@@ -39,9 +40,9 @@ async function openContentEditor(item, section) {
|
|||||||
window.__currentEditorCtx = { id, mode: section.slug, section };
|
window.__currentEditorCtx = { id, mode: section.slug, section };
|
||||||
|
|
||||||
if (window.EditorUI && typeof window.EditorUI.open === 'function') {
|
if (window.EditorUI && typeof window.EditorUI.open === 'function') {
|
||||||
window.EditorUI.open({ id, name, html, section }, 'content');
|
window.EditorUI.open({ id, name, html, section, version_id: versionId }, 'content');
|
||||||
} else if (window.__openEditor) {
|
} else if (window.__openEditor) {
|
||||||
window.__openEditor({ resource: 'content', id, name, html, section });
|
window.__openEditor({ resource: 'content', id, name, html, section, version_id: versionId });
|
||||||
} else {
|
} else {
|
||||||
toast('Editor ist nicht initialisiert.', false);
|
toast('Editor ist nicht initialisiert.', false);
|
||||||
}
|
}
|
||||||
@@ -189,11 +190,56 @@ export async function loadList(section) {
|
|||||||
return name.includes(q) || (api && api.includes(q));
|
return name.includes(q) || (api && api.includes(q));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const versionCache = new Map();
|
||||||
|
|
||||||
|
async function loadVersionOptions(selectEl, itemId) {
|
||||||
|
if (!selectEl || !itemId) return;
|
||||||
|
if (versionCache.has(itemId)) {
|
||||||
|
const cached = versionCache.get(itemId);
|
||||||
|
renderVersionSelect(selectEl, cached.items, cached.activeId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await apiAction('content_versions.list', { method: 'GET', data: { content_id: itemId } });
|
||||||
|
const items = Array.isArray(res?.items) ? res.items : [];
|
||||||
|
const active = items.find(v => Number(v.is_active) === 1);
|
||||||
|
const activeId = active ? String(active.id) : (items[0] ? String(items[0].id) : '');
|
||||||
|
versionCache.set(itemId, { items, activeId });
|
||||||
|
renderVersionSelect(selectEl, items, activeId);
|
||||||
|
} catch {
|
||||||
|
renderVersionSelect(selectEl, [], '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderVersionSelect(selectEl, items, activeId) {
|
||||||
|
selectEl.innerHTML = '';
|
||||||
|
if (!items.length) {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = '';
|
||||||
|
opt.textContent = 'Keine Versionen';
|
||||||
|
opt.disabled = true;
|
||||||
|
selectEl.appendChild(opt);
|
||||||
|
selectEl.disabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectEl.disabled = false;
|
||||||
|
items.forEach(item => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = String(item.id);
|
||||||
|
opt.textContent = `#${item.version_no}` + (Number(item.is_active) === 1 ? ' (aktiv)' : '');
|
||||||
|
selectEl.appendChild(opt);
|
||||||
|
});
|
||||||
|
if (activeId) selectEl.value = activeId;
|
||||||
|
}
|
||||||
|
|
||||||
function render(items) {
|
function render(items) {
|
||||||
list.innerHTML = items.map(item => {
|
list.innerHTML = items.map(item => {
|
||||||
const name = esc(item.name || '');
|
const name = esc(item.name || '');
|
||||||
const apiName = isTemplate ? esc(item.api_name || '') : '';
|
const apiName = isTemplate ? esc(item.api_name || '') : '';
|
||||||
const apiLine = (isTemplate && apiName) ? `<div class='text-xs text-slate-500'>API: ${apiName}</div>` : '';
|
const apiLine = (isTemplate && apiName) ? `<div class='text-xs text-slate-500'>API: ${apiName}</div>` : '';
|
||||||
|
const versionSelect = `<select class="input h-8 py-0 text-xs min-w-[160px]" data-version-select="${item.id}" disabled>
|
||||||
|
<option value="">Versionen laden…</option>
|
||||||
|
</select>`;
|
||||||
const nameCell = `<div class='min-w-48'>
|
const nameCell = `<div class='min-w-48'>
|
||||||
<div class='font-medium truncate' title="${name}">${name || '(ohne Name)'}</div>
|
<div class='font-medium truncate' title="${name}">${name || '(ohne Name)'}</div>
|
||||||
${apiLine}
|
${apiLine}
|
||||||
@@ -207,6 +253,7 @@ export async function loadList(section) {
|
|||||||
return `<div class='p-3 flex items-center gap-3'>
|
return `<div class='p-3 flex items-center gap-3'>
|
||||||
${nameCell}
|
${nameCell}
|
||||||
<div class='text-xs text-gray-500'>#${item.id}</div>
|
<div class='text-xs text-gray-500'>#${item.id}</div>
|
||||||
|
${versionSelect}
|
||||||
<div class='ms-auto flex gap-2'>${[openBtn, editTplBtn, testBtn, prevBtn, delBtn].filter(Boolean).join('')}</div>
|
<div class='ms-auto flex gap-2'>${[openBtn, editTplBtn, testBtn, prevBtn, delBtn].filter(Boolean).join('')}</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
@@ -250,6 +297,18 @@ export async function loadList(section) {
|
|||||||
applyFilter();
|
applyFilter();
|
||||||
|
|
||||||
function bindListHandlers(scope) {
|
function bindListHandlers(scope) {
|
||||||
|
scope.querySelectorAll('[data-version-select]').forEach(sel => {
|
||||||
|
const id = Number(sel.getAttribute('data-version-select') || 0);
|
||||||
|
sel.addEventListener('focus', () => loadVersionOptions(sel, id));
|
||||||
|
sel.addEventListener('click', () => loadVersionOptions(sel, id));
|
||||||
|
sel.addEventListener('change', () => {
|
||||||
|
const versionId = Number(sel.value || 0);
|
||||||
|
if (!versionId) return;
|
||||||
|
const item = data.find(it => Number(it.id) === id);
|
||||||
|
if (!item) return;
|
||||||
|
openContentEditor({ ...item, version_id: versionId }, section);
|
||||||
|
});
|
||||||
|
});
|
||||||
scope.querySelectorAll('[data-open]').forEach(btn => btn.addEventListener('click', () => {
|
scope.querySelectorAll('[data-open]').forEach(btn => btn.addEventListener('click', () => {
|
||||||
const id = Number(btn.dataset.open || 0);
|
const id = Number(btn.dataset.open || 0);
|
||||||
const item = data.find(it => Number(it.id) === id);
|
const item = data.find(it => Number(it.id) === id);
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ require __DIR__ . '/../partials/structure/layout_start.php';
|
|||||||
<select id="versionSelect" class="input h-8 py-0 text-sm min-w-[200px]">
|
<select id="versionSelect" class="input h-8 py-0 text-sm min-w-[200px]">
|
||||||
<option value="">Letzte Versionen</option>
|
<option value="">Letzte Versionen</option>
|
||||||
</select>
|
</select>
|
||||||
|
<button id="btn-deactivate-version" type="button" class="btn">Aktiv deaktivieren</button>
|
||||||
<button id="btn-clear-main" type="button" class="btn" title="Leeren">🧹</button>
|
<button id="btn-clear-main" type="button" class="btn" title="Leeren">🧹</button>
|
||||||
<button id="btn-preview" type="button" class="btn">Vorschau</button>
|
<button id="btn-preview" type="button" class="btn">Vorschau</button>
|
||||||
<button id="btn-test" type="button" class="btn">Testversand</button>
|
<button id="btn-test" type="button" class="btn">Testversand</button>
|
||||||
@@ -125,6 +126,18 @@ require __DIR__ . '/../partials/structure/layout_start.php';
|
|||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="activateVersionDialog" class="rounded-2xl p-0 w-[440px]">
|
||||||
|
<div class="p-4 bg-white rounded-2xl space-y-4">
|
||||||
|
<h3 class="text-lg font-semibold">Neue Version aktivieren</h3>
|
||||||
|
<p class="text-sm text-slate-600">Soll die neu gespeicherte Version als aktiv gesetzt werden?</p>
|
||||||
|
<div class="flex justify-end gap-2">
|
||||||
|
<button type="button" id="btn-activate-cancel" class="btn">Abbrechen</button>
|
||||||
|
<button type="button" id="btn-activate-no" class="btn">Nein</button>
|
||||||
|
<button type="button" id="btn-activate-yes" class="btn">Ja</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
<!-- Edit Snippet Dialog -->
|
<!-- Edit Snippet Dialog -->
|
||||||
<dialog id="editSnippetDialog" class="rounded-2xl p-0 w-[700px]">
|
<dialog id="editSnippetDialog" class="rounded-2xl p-0 w-[700px]">
|
||||||
<form id="editSnippetForm" method="dialog" class="p-4 bg-white rounded-2xl">
|
<form id="editSnippetForm" method="dialog" class="p-4 bg-white rounded-2xl">
|
||||||
|
|||||||
@@ -321,6 +321,8 @@ CREATE TABLE IF NOT EXISTS `emailtemplate_content_versions` (
|
|||||||
`content_id` int(10) unsigned NOT NULL,
|
`content_id` int(10) unsigned NOT NULL,
|
||||||
`section_id` int(10) unsigned NOT NULL,
|
`section_id` int(10) unsigned NOT NULL,
|
||||||
`version_no` int(10) unsigned NOT NULL,
|
`version_no` int(10) unsigned NOT NULL,
|
||||||
|
`is_active` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
`was_active` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
`editor_type` varchar(32) DEFAULT NULL,
|
`editor_type` varchar(32) DEFAULT NULL,
|
||||||
`json_content` mediumtext DEFAULT NULL,
|
`json_content` mediumtext DEFAULT NULL,
|
||||||
`html` mediumtext DEFAULT NULL,
|
`html` mediumtext DEFAULT NULL,
|
||||||
|
|||||||
@@ -372,6 +372,8 @@ class ApiKernel
|
|||||||
'editor' => $this->firstExisting($cols, ['editor_type', 'editor']),
|
'editor' => $this->firstExisting($cols, ['editor_type', 'editor']),
|
||||||
'craft' => $this->firstExisting($cols, ['craft_json', 'craft_content', 'craft_data']),
|
'craft' => $this->firstExisting($cols, ['craft_json', 'craft_content', 'craft_data']),
|
||||||
'settings' => $this->firstExisting($cols, ['settings_json', 'settings']),
|
'settings' => $this->firstExisting($cols, ['settings_json', 'settings']),
|
||||||
|
'is_active' => $this->firstExisting($cols, ['is_active']),
|
||||||
|
'was_active' => $this->firstExisting($cols, ['was_active']),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,12 +382,12 @@ class ApiKernel
|
|||||||
return $this->tableExists($this->contentItemsTable()) && $this->tableExists($this->contentSectionsTable());
|
return $this->tableExists($this->contentItemsTable()) && $this->tableExists($this->contentSectionsTable());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createContentVersion(array $current, array $itemCols, int $customerId, int $sectionId): void
|
private function createContentVersion(array $current, array $itemCols, int $customerId, int $sectionId): ?int
|
||||||
{
|
{
|
||||||
$table = $this->contentVersionsTable();
|
$table = $this->contentVersionsTable();
|
||||||
if (!$this->tableExists($table)) return;
|
if (!$this->tableExists($table)) return null;
|
||||||
$contentId = (int)($current['id'] ?? 0);
|
$contentId = (int)($current['id'] ?? 0);
|
||||||
if ($contentId <= 0) return;
|
if ($contentId <= 0) return null;
|
||||||
|
|
||||||
$jsonCol = $itemCols['json'] ?? null;
|
$jsonCol = $itemCols['json'] ?? null;
|
||||||
$htmlCol = $itemCols['html'] ?? null;
|
$htmlCol = $itemCols['html'] ?? null;
|
||||||
@@ -426,6 +428,7 @@ class ApiKernel
|
|||||||
$stmt = $this->pdo->prepare("INSERT INTO `$table` ($insertCols) VALUES ($placeholders)");
|
$stmt = $this->pdo->prepare("INSERT INTO `$table` ($insertCols) VALUES ($placeholders)");
|
||||||
foreach ($data as $k => $v) $stmt->bindValue(":$k", $v);
|
foreach ($data as $k => $v) $stmt->bindValue(":$k", $v);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
|
$newId = (int)$this->pdo->lastInsertId();
|
||||||
|
|
||||||
$cleanup = $this->pdo->prepare(
|
$cleanup = $this->pdo->prepare(
|
||||||
"DELETE FROM `$table` WHERE `id` IN (
|
"DELETE FROM `$table` WHERE `id` IN (
|
||||||
@@ -435,11 +438,32 @@ class ApiKernel
|
|||||||
)"
|
)"
|
||||||
);
|
);
|
||||||
$cleanup->execute([':cid' => $contentId]);
|
$cleanup->execute([':cid' => $contentId]);
|
||||||
|
return $newId;
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
// Versioning darf nicht das Speichern blockieren.
|
// Versioning darf nicht das Speichern blockieren.
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function activateContentVersion(int $customerId, int $contentId, int $versionId): void
|
||||||
|
{
|
||||||
|
$table = $this->contentVersionsTable();
|
||||||
|
if (!$this->tableExists($table)) return;
|
||||||
|
$this->pdo->prepare("UPDATE `$table` SET `is_active` = 0 WHERE `customer_id` = :cid AND `content_id` = :content")
|
||||||
|
->execute([':cid' => $customerId, ':content' => $contentId]);
|
||||||
|
$this->pdo->prepare(
|
||||||
|
"UPDATE `$table` SET `is_active` = 1, `was_active` = 1 WHERE `customer_id` = :cid AND `content_id` = :content AND `id` = :id LIMIT 1"
|
||||||
|
)->execute([':cid' => $customerId, ':content' => $contentId, ':id' => $versionId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function deactivateContentVersion(int $customerId, int $contentId): void
|
||||||
|
{
|
||||||
|
$table = $this->contentVersionsTable();
|
||||||
|
if (!$this->tableExists($table)) return;
|
||||||
|
$this->pdo->prepare("UPDATE `$table` SET `is_active` = 0 WHERE `customer_id` = :cid AND `content_id` = :content")
|
||||||
|
->execute([':cid' => $customerId, ':content' => $contentId]);
|
||||||
|
}
|
||||||
|
|
||||||
private function isLegacyContentKind(string $kind): bool
|
private function isLegacyContentKind(string $kind): bool
|
||||||
{
|
{
|
||||||
return in_array($kind, ['templates', 'sections', 'blocks', 'snippets'], true);
|
return in_array($kind, ['templates', 'sections', 'blocks', 'snippets'], true);
|
||||||
@@ -635,6 +659,7 @@ class ApiKernel
|
|||||||
|
|
||||||
$itemsTable = $this->contentItemsTable();
|
$itemsTable = $this->contentItemsTable();
|
||||||
$sectionsTable = $this->contentSectionsTable();
|
$sectionsTable = $this->contentSectionsTable();
|
||||||
|
$versionsTable = $this->contentVersionsTable();
|
||||||
if (!$this->tableExists($itemsTable) || !$this->tableExists($sectionsTable)) {
|
if (!$this->tableExists($itemsTable) || !$this->tableExists($sectionsTable)) {
|
||||||
$this->fail('Content tables not available', null, 500);
|
$this->fail('Content tables not available', null, 500);
|
||||||
}
|
}
|
||||||
@@ -642,6 +667,10 @@ class ApiKernel
|
|||||||
$catCol = $itemCols['category'];
|
$catCol = $itemCols['category'];
|
||||||
$htmlCol = $itemCols['html'];
|
$htmlCol = $itemCols['html'];
|
||||||
$jsonCol = $itemCols['json'];
|
$jsonCol = $itemCols['json'];
|
||||||
|
$onlyActive = (int)$this->val($this->in, ['active_only', 'only_active', 'active'], 0) === 1;
|
||||||
|
$versionCols = $onlyActive && $this->tableExists($versionsTable)
|
||||||
|
? $this->resolveContentVersionColumns($versionsTable)
|
||||||
|
: null;
|
||||||
|
|
||||||
$section = $fixedSection ?: $this->resolveSectionFromInput($customerId);
|
$section = $fixedSection ?: $this->resolveSectionFromInput($customerId);
|
||||||
$q = trim((string)$this->val($this->in, 'q', ''));
|
$q = trim((string)$this->val($this->in, 'q', ''));
|
||||||
@@ -663,9 +692,20 @@ class ApiKernel
|
|||||||
$params[':q'] = '%' . $q . '%';
|
$params[':q'] = '%' . $q . '%';
|
||||||
}
|
}
|
||||||
|
|
||||||
$sql = "SELECT i.*, s.`name` AS section_name, s.`slug` AS section_slug, s.`position` AS section_position, s.`is_template` AS section_is_template
|
$join = '';
|
||||||
|
$select = "i.*, s.`name` AS section_name, s.`slug` AS section_slug, s.`position` AS section_position, s.`is_template` AS section_is_template";
|
||||||
|
if ($onlyActive && $versionCols) {
|
||||||
|
$join = " JOIN `$versionsTable` v ON v.`content_id` = i.`id` AND v.`is_active` = 1";
|
||||||
|
$select .= ", v.`id` AS active_version_id, v.`version_no` AS active_version_no, v.`is_active` AS version_is_active, v.`was_active` AS version_was_active";
|
||||||
|
if (!empty($versionCols['html'])) $select .= ", v.`{$versionCols['html']}` AS version_html";
|
||||||
|
if (!empty($versionCols['json'])) $select .= ", v.`{$versionCols['json']}` AS version_json";
|
||||||
|
if (!empty($versionCols['craft'])) $select .= ", v.`{$versionCols['craft']}` AS version_craft";
|
||||||
|
if (!empty($versionCols['editor'])) $select .= ", v.`{$versionCols['editor']}` AS version_editor";
|
||||||
|
}
|
||||||
|
$sql = "SELECT $select
|
||||||
FROM `$itemsTable` i
|
FROM `$itemsTable` i
|
||||||
JOIN `$sectionsTable` s ON s.`id` = i.`section_id`
|
JOIN `$sectionsTable` s ON s.`id` = i.`section_id`
|
||||||
|
$join
|
||||||
$where
|
$where
|
||||||
ORDER BY i.`updated_at` DESC, i.`id` DESC
|
ORDER BY i.`updated_at` DESC, i.`id` DESC
|
||||||
LIMIT :off,:lim";
|
LIMIT :off,:lim";
|
||||||
@@ -691,8 +731,16 @@ class ApiKernel
|
|||||||
'updated_at' => $r['updated_at'] ?? null,
|
'updated_at' => $r['updated_at'] ?? null,
|
||||||
'created_at' => $r['created_at'] ?? null,
|
'created_at' => $r['created_at'] ?? null,
|
||||||
];
|
];
|
||||||
|
if ($onlyActive && $versionCols) {
|
||||||
|
if (!empty($r['active_version_id'])) $item['active_version_id'] = (int)$r['active_version_id'];
|
||||||
|
if (array_key_exists('version_html', $r)) $item['html'] = (string)($r['version_html'] ?? '');
|
||||||
|
if (array_key_exists('version_json', $r)) $item['content'] = $r['version_json'];
|
||||||
|
if (array_key_exists('version_craft', $r)) $item['craft_json'] = $r['version_craft'];
|
||||||
|
if (array_key_exists('version_editor', $r)) $item['editor_type'] = $r['version_editor'];
|
||||||
|
} else {
|
||||||
if ($htmlCol && array_key_exists($htmlCol, $r)) $item['html'] = (string)($r[$htmlCol] ?? '');
|
if ($htmlCol && array_key_exists($htmlCol, $r)) $item['html'] = (string)($r[$htmlCol] ?? '');
|
||||||
if ($jsonCol && array_key_exists($jsonCol, $r)) $item['content'] = $r[$jsonCol];
|
if ($jsonCol && array_key_exists($jsonCol, $r)) $item['content'] = $r[$jsonCol];
|
||||||
|
}
|
||||||
$out[] = $item;
|
$out[] = $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -857,12 +905,16 @@ class ApiKernel
|
|||||||
foreach ($data as $k => $v) $stmt->bindValue(":$k", $v);
|
foreach ($data as $k => $v) $stmt->bindValue(":$k", $v);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$newId = (int)$this->pdo->lastInsertId();
|
$newId = (int)$this->pdo->lastInsertId();
|
||||||
|
$activateVersion = (int)$this->val($this->in, ['activate_version', 'activate', 'set_active'], 0) === 1;
|
||||||
try {
|
try {
|
||||||
$stmt = $this->pdo->prepare("SELECT * FROM `$itemsTable` WHERE `id` = :id AND `customer_id` = :cid LIMIT 1");
|
$stmt = $this->pdo->prepare("SELECT * FROM `$itemsTable` WHERE `id` = :id AND `customer_id` = :cid LIMIT 1");
|
||||||
$stmt->execute([':id' => $newId, ':cid' => $customerId]);
|
$stmt->execute([':id' => $newId, ':cid' => $customerId]);
|
||||||
$row = $stmt->fetch();
|
$row = $stmt->fetch();
|
||||||
if ($row) {
|
if ($row) {
|
||||||
$this->createContentVersion($row, $itemCols, $customerId, (int)$section['id']);
|
$vid = $this->createContentVersion($row, $itemCols, $customerId, (int)$section['id']);
|
||||||
|
if ($activateVersion && $vid) {
|
||||||
|
$this->activateContentVersion($customerId, (int)$row['id'], $vid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
// ignore versioning failures on create
|
// ignore versioning failures on create
|
||||||
@@ -964,6 +1016,7 @@ class ApiKernel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$activateVersion = (int)$this->val($this->in, ['activate_version', 'activate', 'set_active'], 0) === 1;
|
||||||
$versionCols = array_filter([$jsonCol, $htmlCol, $craftCol, $settingsCol, $editorCol]);
|
$versionCols = array_filter([$jsonCol, $htmlCol, $craftCol, $settingsCol, $editorCol]);
|
||||||
$shouldSnapshot = false;
|
$shouldSnapshot = false;
|
||||||
foreach ($versionCols as $col) {
|
foreach ($versionCols as $col) {
|
||||||
@@ -972,9 +1025,6 @@ class ApiKernel
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($shouldSnapshot) {
|
|
||||||
$this->createContentVersion($current, $itemCols, $customerId, (int)($section['id'] ?? 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
$set = implode(',', array_map(static fn($c) => "`$c` = :$c", array_keys($data)));
|
$set = implode(',', array_map(static fn($c) => "`$c` = :$c", array_keys($data)));
|
||||||
$data['id'] = $id;
|
$data['id'] = $id;
|
||||||
@@ -983,6 +1033,21 @@ class ApiKernel
|
|||||||
$stmt = $this->pdo->prepare($sql);
|
$stmt = $this->pdo->prepare($sql);
|
||||||
foreach ($data as $k => $v) $stmt->bindValue(":$k", $v);
|
foreach ($data as $k => $v) $stmt->bindValue(":$k", $v);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
|
if ($shouldSnapshot) {
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->prepare("SELECT * FROM `$itemsTable` WHERE `customer_id` = :cid AND `id` = :id LIMIT 1");
|
||||||
|
$stmt->execute([':cid' => $customerId, ':id' => $id]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
if ($row) {
|
||||||
|
$vid = $this->createContentVersion($row, $itemCols, $customerId, (int)($section['id'] ?? 0));
|
||||||
|
if ($activateVersion && $vid) {
|
||||||
|
$this->activateContentVersion($customerId, (int)$row['id'], $vid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
// ignore versioning failures
|
||||||
|
}
|
||||||
|
}
|
||||||
$this->respond(['ok' => true, 'kind' => 'content', 'id' => $id, 'updated' => true]);
|
$this->respond(['ok' => true, 'kind' => 'content', 'id' => $id, 'updated' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1049,6 +1114,8 @@ class ApiKernel
|
|||||||
'version_no' => (int)($row['version_no'] ?? 0),
|
'version_no' => (int)($row['version_no'] ?? 0),
|
||||||
'editor_type' => $row['editor_type'] ?? null,
|
'editor_type' => $row['editor_type'] ?? null,
|
||||||
'created_at' => $row['created_at'] ?? null,
|
'created_at' => $row['created_at'] ?? null,
|
||||||
|
'is_active' => (int)($row['is_active'] ?? 0),
|
||||||
|
'was_active' => (int)($row['was_active'] ?? 0),
|
||||||
];
|
];
|
||||||
}, $rows);
|
}, $rows);
|
||||||
$this->respond(['ok' => true, 'items' => $items, 'data' => $items]);
|
$this->respond(['ok' => true, 'items' => $items, 'data' => $items]);
|
||||||
@@ -1130,6 +1197,38 @@ class ApiKernel
|
|||||||
$this->respond(['ok' => true, 'restored' => true, 'content_id' => (int)($version['content_id'] ?? 0)]);
|
$this->respond(['ok' => true, 'restored' => true, 'content_id' => (int)($version['content_id'] ?? 0)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function handleContentVersionsActivate(): void
|
||||||
|
{
|
||||||
|
$auth = $this->requireAuth();
|
||||||
|
$customerId = (int)($auth['customer_id'] ?? 0);
|
||||||
|
if ($customerId <= 0) $this->fail('Customer context missing', null, 500);
|
||||||
|
$versionId = (int)$this->val($this->in, ['id', 'version_id', 'version'], 0);
|
||||||
|
if ($versionId <= 0) $this->fail('version id required', null, 422);
|
||||||
|
|
||||||
|
$table = $this->contentVersionsTable();
|
||||||
|
if (!$this->tableExists($table)) $this->fail('Versions table not available', null, 500);
|
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare("SELECT `id`,`content_id` FROM `$table` WHERE `id` = :id AND `customer_id` = :cid LIMIT 1");
|
||||||
|
$stmt->execute([':id' => $versionId, ':cid' => $customerId]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
if (!$row) $this->fail('Not found', ['id' => $versionId], 404);
|
||||||
|
|
||||||
|
$this->activateContentVersion($customerId, (int)$row['content_id'], $versionId);
|
||||||
|
$this->respond(['ok' => true, 'activated' => true, 'id' => $versionId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleContentVersionsDeactivate(): void
|
||||||
|
{
|
||||||
|
$auth = $this->requireAuth();
|
||||||
|
$customerId = (int)($auth['customer_id'] ?? 0);
|
||||||
|
if ($customerId <= 0) $this->fail('Customer context missing', null, 500);
|
||||||
|
$contentId = (int)$this->val($this->in, ['content_id', 'content'], 0);
|
||||||
|
if ($contentId <= 0) $this->fail('content_id required', null, 422);
|
||||||
|
|
||||||
|
$this->deactivateContentVersion($customerId, $contentId);
|
||||||
|
$this->respond(['ok' => true, 'deactivated' => true, 'content_id' => $contentId]);
|
||||||
|
}
|
||||||
|
|
||||||
private function handleSectionsConfigList(): void
|
private function handleSectionsConfigList(): void
|
||||||
{
|
{
|
||||||
$auth = $this->requireAuth();
|
$auth = $this->requireAuth();
|
||||||
@@ -2325,6 +2424,12 @@ class ApiKernel
|
|||||||
case 'content_versions.restore':
|
case 'content_versions.restore':
|
||||||
$this->handleContentVersionsRestore();
|
$this->handleContentVersionsRestore();
|
||||||
break;
|
break;
|
||||||
|
case 'content_versions.activate':
|
||||||
|
$this->handleContentVersionsActivate();
|
||||||
|
break;
|
||||||
|
case 'content_versions.deactivate':
|
||||||
|
$this->handleContentVersionsDeactivate();
|
||||||
|
break;
|
||||||
|
|
||||||
/* ---------- CRUD HANDLER ---------- */
|
/* ---------- CRUD HANDLER ---------- */
|
||||||
default:
|
default:
|
||||||
@@ -2645,16 +2750,20 @@ class ApiKernel
|
|||||||
}
|
}
|
||||||
$itemsTable = $this->contentItemsTable();
|
$itemsTable = $this->contentItemsTable();
|
||||||
$itemCols = $this->resolveContentItemColumns($itemsTable);
|
$itemCols = $this->resolveContentItemColumns($itemsTable);
|
||||||
|
$versionsTable = $this->contentVersionsTable();
|
||||||
|
$versionCols = $this->tableExists($versionsTable) ? $this->resolveContentVersionColumns($versionsTable) : null;
|
||||||
$htmlCol = $itemCols['html'];
|
$htmlCol = $itemCols['html'];
|
||||||
$jsonCol = $itemCols['json'];
|
$jsonCol = $itemCols['json'];
|
||||||
if (!$htmlCol && !$jsonCol) {
|
if (!$htmlCol && !$jsonCol && (!$versionCols || !$versionCols['html'])) {
|
||||||
$cache[$cacheKey] = null;
|
$cache[$cacheKey] = null;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$selectCols = [];
|
$selectCols = [];
|
||||||
if ($htmlCol) $selectCols[] = "`$htmlCol`";
|
if ($htmlCol) $selectCols[] = "i.`$htmlCol` AS item_html";
|
||||||
if ($jsonCol) $selectCols[] = "`$jsonCol`";
|
if ($jsonCol) $selectCols[] = "i.`$jsonCol` AS item_json";
|
||||||
$sql = "SELECT " . implode(',', $selectCols) . " FROM `$itemsTable` WHERE `customer_id` = :cid AND `section_id` = :sid AND `id` = :id LIMIT 1";
|
if ($versionCols && $versionCols['html']) $selectCols[] = "v.`{$versionCols['html']}` AS version_html";
|
||||||
|
$join = $versionCols ? "LEFT JOIN `$versionsTable` v ON v.`content_id` = i.`id` AND v.`is_active` = 1" : "";
|
||||||
|
$sql = "SELECT " . implode(',', $selectCols) . " FROM `$itemsTable` i $join WHERE i.`customer_id` = :cid AND i.`section_id` = :sid AND i.`id` = :id LIMIT 1";
|
||||||
$stmt = $this->pdo->prepare($sql);
|
$stmt = $this->pdo->prepare($sql);
|
||||||
$stmt->execute([':cid' => $customerId, ':sid' => (int)$section['id'], ':id' => $id]);
|
$stmt->execute([':cid' => $customerId, ':sid' => (int)$section['id'], ':id' => $id]);
|
||||||
$row = $stmt->fetch();
|
$row = $stmt->fetch();
|
||||||
@@ -2662,7 +2771,7 @@ class ApiKernel
|
|||||||
$cache[$cacheKey] = null;
|
$cache[$cacheKey] = null;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$html = $htmlCol ? (string)($row[$htmlCol] ?? '') : '';
|
$html = (string)($row['version_html'] ?? $row['item_html'] ?? '');
|
||||||
} else {
|
} else {
|
||||||
if (!$kindKey) return null;
|
if (!$kindKey) return null;
|
||||||
$table = $this->tableMap[$kindKey] ?? null;
|
$table = $this->tableMap[$kindKey] ?? null;
|
||||||
|
|||||||
Reference in New Issue
Block a user