diff --git a/config/current.ver b/config/current.ver
index d9e75ec..280fc9d 100644
--- a/config/current.ver
+++ b/config/current.ver
@@ -1 +1 @@
-1.2.28
\ No newline at end of file
+1.2.29
\ No newline at end of file
diff --git a/public/assets/js/ui-editor.js b/public/assets/js/ui-editor.js
index d76e79e..c58053c 100644
--- a/public/assets/js/ui-editor.js
+++ b/public/assets/js/ui-editor.js
@@ -107,10 +107,16 @@ export function initEditor() {
});
};
- return async ({ title, text, confirmLabel = 'Bestätigen', cancelLabel = 'Abbrechen' }) => {
+ return async ({ title, text, html = false, confirmLabel = 'Bestätigen', cancelLabel = 'Abbrechen' }) => {
ensure();
if (titleEl) titleEl.textContent = title || 'Bestätigung';
- if (textEl) textEl.textContent = text || '';
+ if (textEl) {
+ if (html) {
+ textEl.innerHTML = text || '';
+ } else {
+ textEl.textContent = text || '';
+ }
+ }
if (btnOk) btnOk.textContent = confirmLabel;
if (btnCancel) btnCancel.textContent = cancelLabel;
if (!dialog.open) dialog.showModal();
@@ -120,6 +126,24 @@ export function initEditor() {
};
})();
+ function formatReferencesHtml(refs = []) {
+ if (!refs.length) return '';
+ const lines = refs.map(ref => {
+ const name = String(ref.name || 'Template')
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ const id = Number(ref.id || 0);
+ const versions = Array.isArray(ref.versions) && ref.versions.length
+ ? ` – Versionen: ${ref.versions.join(', ')}`
+ : '';
+ return `• ${name} #${id}${versions}`;
+ });
+ return lines.join('
');
+ }
+
// ---------- Hilfen ----------
function activeMode() {
const activeSection = window.__activeSection || current?.section || null;
@@ -1174,9 +1198,22 @@ export function initEditor() {
if (!refs.length) return true;
const preview = refs.slice(0, 6).map(r => `${r.name || 'Template'} #${r.id}`).join(', ');
const more = refs.length > 6 ? ` und ${refs.length - 6} weitere` : '';
+ const escPreview = preview
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ const escMore = more
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
return await showConfirmDialog({
title: 'Template wird verwendet',
- text: `Dieses Template wird in ${refs.length} anderen Template(s) verwendet (${preview}${more}). ${actionLabel} trotzdem?`,
+ html: true,
+ text: `Dieses Template wird in ${refs.length} anderen Template(s) verwendet (${escPreview}${escMore}).
${formatReferencesHtml(refs)}
${actionLabel} trotzdem?`,
confirmLabel: actionLabel,
});
}
@@ -1211,9 +1248,11 @@ export function initEditor() {
if (!okRefs) return;
let res = await apiAction('content_versions.deactivate', { method: 'POST', data: { content_id: current.id } });
if (res && res.ok === false && Array.isArray(res.references) && res.references.length) {
+ const refs = res.references || [];
const ok = await showConfirmDialog({
title: 'Template wird verwendet',
- text: 'Dieses Template wird in anderen Templates verwendet. Trotzdem deaktivieren?',
+ html: true,
+ text: `Dieses Template wird in anderen Templates verwendet.
${formatReferencesHtml(refs)}
Deaktivieren trotzdem?`,
confirmLabel: 'Deaktivieren',
});
if (!ok) return;
diff --git a/public/assets/js/ui-list.js b/public/assets/js/ui-list.js
index e4045d7..3047c29 100644
--- a/public/assets/js/ui-list.js
+++ b/public/assets/js/ui-list.js
@@ -53,10 +53,16 @@ const showConfirmDialog = (() => {
});
};
- return async ({ title, text, confirmLabel = 'Bestätigen', cancelLabel = 'Abbrechen' }) => {
+ return async ({ title, text, html = false, confirmLabel = 'Bestätigen', cancelLabel = 'Abbrechen' }) => {
ensure();
if (titleEl) titleEl.textContent = title || 'Bestätigung';
- if (textEl) textEl.textContent = text || '';
+ if (textEl) {
+ if (html) {
+ textEl.innerHTML = text || '';
+ } else {
+ textEl.textContent = text || '';
+ }
+ }
if (btnOk) btnOk.textContent = confirmLabel;
if (btnCancel) btnCancel.textContent = cancelLabel;
if (!dialog.open) dialog.showModal();
@@ -66,6 +72,19 @@ const showConfirmDialog = (() => {
};
})();
+function formatReferencesHtml(refs = []) {
+ if (!refs.length) return '';
+ const lines = refs.map(ref => {
+ const name = esc(ref.name || 'Template');
+ const id = Number(ref.id || 0);
+ const versions = Array.isArray(ref.versions) && ref.versions.length
+ ? ` – Versionen: ${ref.versions.join(', ')}`
+ : '';
+ return `• ${name} #${id}${versions}`;
+ });
+ return lines.join('
');
+}
+
function formatVersionDate(value) {
if (!value) return '';
try {
@@ -173,7 +192,8 @@ async function openTemplateManager(item, section) {
const more = refs.length > 6 ? ` und ${refs.length - 6} weitere` : '';
return await showConfirmDialog({
title: 'Template wird verwendet',
- text: `Dieses Template wird in ${refs.length} anderen Template(s) verwendet (${preview}${more}). ${actionLabel} trotzdem?`,
+ html: true,
+ text: `Dieses Template wird in ${refs.length} anderen Template(s) verwendet (${esc(preview)}${esc(more)}).
${formatReferencesHtml(refs)}
${esc(actionLabel)} trotzdem?`,
confirmLabel: actionLabel,
});
};
@@ -327,9 +347,11 @@ async function openTemplateManager(item, section) {
}
let res = await apiAction('content_versions.deactivate', { method: 'POST', data: { content_id: item.id } });
if (res && res.ok === false && Array.isArray(res.references) && res.references.length) {
+ const refs = res.references || [];
const ok = await showConfirmDialog({
title: 'Template wird verwendet',
- text: 'Dieses Template wird in anderen Templates verwendet. Trotzdem deaktivieren?',
+ html: true,
+ text: `Dieses Template wird in anderen Templates verwendet.
${formatReferencesHtml(refs)}
Deaktivieren trotzdem?`,
confirmLabel: 'Deaktivieren',
});
if (!ok) return;
@@ -346,9 +368,11 @@ async function openTemplateManager(item, section) {
if (!confirm('Version wirklich löschen?')) return;
let res = await apiAction('content_versions.delete', { method: 'POST', data: { id: vid, content_id: item.id } });
if (res && res.ok === false && Array.isArray(res.references) && res.references.length) {
+ const refs = res.references || [];
const ok = await showConfirmDialog({
title: 'Template wird verwendet',
- text: 'Dieses Template wird in anderen Templates verwendet. Trotzdem löschen?',
+ html: true,
+ text: `Dieses Template wird in anderen Templates verwendet.
${formatReferencesHtml(refs)}
Löschen trotzdem?`,
confirmLabel: 'Löschen',
});
if (!ok) return;
diff --git a/src/ApiKernel.php b/src/ApiKernel.php
index c594077..5bd6d78 100644
--- a/src/ApiKernel.php
+++ b/src/ApiKernel.php
@@ -2337,7 +2337,7 @@ class ApiKernel
private function findTemplateReferences(int $customerId, int $templateId, array &$debug = []): array
{
$out = [];
- $seen = [];
+ $byId = [];
$debug = [
'time' => date(DATE_ATOM),
'customer_id' => $customerId,
@@ -2347,6 +2347,31 @@ class ApiKernel
'matched_rows' => [],
'template_items_matches' => [],
];
+ $addRef = function (int $id, string $name) use (&$out, &$byId) {
+ if ($id <= 0) return;
+ if (!isset($byId[$id])) {
+ $entry = [
+ 'id' => $id,
+ 'name' => $name,
+ 'versions' => [],
+ ];
+ $byId[$id] = count($out);
+ $out[] = $entry;
+ } elseif ($name !== '') {
+ $out[$byId[$id]]['name'] = $name;
+ }
+ };
+ $addVersion = function (int $id, ?int $ver) use (&$out, &$byId) {
+ if ($id <= 0 || $ver === null || $ver <= 0) return;
+ if (!isset($byId[$id])) {
+ return;
+ }
+ $idx = $byId[$id];
+ if (!in_array($ver, $out[$idx]['versions'], true)) {
+ $out[$idx]['versions'][] = $ver;
+ sort($out[$idx]['versions']);
+ }
+ };
$matches = function (?string $html, array $libKinds = []) use ($templateId): bool {
if (!$html) return false;
$id = preg_quote((string)$templateId, '/');
@@ -2463,12 +2488,8 @@ class ApiKernel
}
if ($found) {
$id = (int)($row['id'] ?? 0);
- if ($id <= 0 || isset($seen[$id])) continue;
- $seen[$id] = true;
- $out[] = [
- 'id' => $id,
- 'name' => (string)($row['name'] ?? ''),
- ];
+ if ($id <= 0) continue;
+ $addRef($id, (string)($row['name'] ?? ''));
if (count($debug['matched_rows']) < 50) {
$debug['matched_rows'][] = [
'id' => $id,
@@ -2478,6 +2499,62 @@ class ApiKernel
}
}
}
+
+ if ($this->tableExists($versionsTable)) {
+ $vCols = $this->tableColumns($versionsTable);
+ $vHtml = $versionHtmlCol;
+ $vJson = $versionJsonCol;
+ $vCraft = $versionCraftCol;
+ $vSettings = $versionSettingsCol;
+ $vNo = $this->firstExisting($vCols, ['version_no', 'version', 'ver', 'version_nr']);
+ $vContentId = $this->firstExisting($vCols, ['content_id', 'content']);
+ $vCustomerId = $this->firstExisting($vCols, ['customer_id', 'customer']);
+ $vSectionId = $this->firstExisting($vCols, ['section_id', 'section']);
+ if ($vContentId && $vNo) {
+ $select = "v.`$vContentId` AS content_id, v.`$vNo` AS version_no, i.`name` AS name";
+ if ($vHtml) $select .= ", v.`$vHtml` AS version_html";
+ if ($vJson) $select .= ", v.`$vJson` AS version_json";
+ if ($vCraft) $select .= ", v.`$vCraft` AS version_craft";
+ if ($vSettings) $select .= ", v.`$vSettings` AS version_settings";
+ $join = "LEFT JOIN `$itemsTable` i ON i.`id` = v.`$vContentId`";
+ $where = [];
+ $params = [];
+ if ($vCustomerId) {
+ $where[] = "v.`$vCustomerId` = :cid";
+ $params[':cid'] = $customerId;
+ }
+ if ($vSectionId) {
+ $where[] = "v.`$vSectionId` = :sid";
+ $params[':sid'] = (int)$section['id'];
+ }
+ $where[] = "v.`$vContentId` <> :id";
+ $params[':id'] = $templateId;
+ $sql = "SELECT $select FROM `$versionsTable` v $join WHERE " . implode(' AND ', $where);
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->execute($params);
+ $rows = $stmt->fetchAll() ?: [];
+ foreach ($rows as $row) {
+ $blobs = [
+ (string)($row['version_html'] ?? ''),
+ (string)($row['version_json'] ?? ''),
+ (string)($row['version_craft'] ?? ''),
+ (string)($row['version_settings'] ?? ''),
+ ];
+ $found = false;
+ foreach ($blobs as $blob) {
+ if ($matches($blob, $libKinds)) {
+ $found = true;
+ break;
+ }
+ }
+ if ($found) {
+ $cid = (int)($row['content_id'] ?? 0);
+ $addRef($cid, (string)($row['name'] ?? ''));
+ $addVersion($cid, (int)($row['version_no'] ?? 0));
+ }
+ }
+ }
+ }
}
if (!$this->useUnifiedContent()) {
$table = $this->tableMap['templates'] ?? null;
@@ -2520,12 +2597,8 @@ class ApiKernel
}
if ($found) {
$id = (int)($row['id'] ?? 0);
- if ($id <= 0 || isset($seen[$id])) continue;
- $seen[$id] = true;
- $out[] = [
- 'id' => $id,
- 'name' => (string)($row['name'] ?? ''),
- ];
+ if ($id <= 0) continue;
+ $addRef($id, (string)($row['name'] ?? ''));
if (count($debug['matched_rows']) < 50) {
$debug['matched_rows'][] = [
'id' => $id,
@@ -2559,12 +2632,8 @@ class ApiKernel
$rows = $stmt->fetchAll() ?: [];
foreach ($rows as $row) {
$id = (int)($row['id'] ?? 0);
- if ($id <= 0 || isset($seen[$id])) continue;
- $seen[$id] = true;
- $out[] = [
- 'id' => $id,
- 'name' => (string)($row['name'] ?? ''),
- ];
+ if ($id <= 0) continue;
+ $addRef($id, (string)($row['name'] ?? ''));
}
}
} else {
@@ -2588,12 +2657,8 @@ class ApiKernel
$rows = $stmt->fetchAll() ?: [];
foreach ($rows as $row) {
$id = (int)($row['id'] ?? 0);
- if ($id <= 0 || isset($seen[$id])) continue;
- $seen[$id] = true;
- $out[] = [
- 'id' => $id,
- 'name' => (string)($row['name'] ?? ''),
- ];
+ if ($id <= 0) continue;
+ $addRef($id, (string)($row['name'] ?? ''));
}
}
}