Warnung deaktivierung Template
This commit is contained in:
@@ -1 +1 @@
|
|||||||
1.2.1
|
1.2.2
|
||||||
@@ -1060,6 +1060,16 @@ export function initEditor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function confirmTemplateReferences(actionLabel) {
|
||||||
|
if (!current?.section?.is_template || !current?.id) return true;
|
||||||
|
const res = await apiAction('templates.references', { method: 'GET', data: { template_id: current.id } }).catch(() => ({}));
|
||||||
|
const refs = Array.isArray(res?.references) ? res.references : [];
|
||||||
|
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` : '';
|
||||||
|
return confirm(`Dieses Template wird in ${refs.length} anderen Template(s) verwendet (${preview}${more}). ${actionLabel} trotzdem?`);
|
||||||
|
}
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
btnSave && (btnSave.onclick = save);
|
btnSave && (btnSave.onclick = save);
|
||||||
btnClear && (btnClear.onclick = clearEditor);
|
btnClear && (btnClear.onclick = clearEditor);
|
||||||
@@ -1086,6 +1096,8 @@ export function initEditor() {
|
|||||||
if (!current?.id) return;
|
if (!current?.id) return;
|
||||||
if (!currentVersionMeta || Number(currentVersionMeta.is_active) !== 1) return;
|
if (!currentVersionMeta || Number(currentVersionMeta.is_active) !== 1) return;
|
||||||
try {
|
try {
|
||||||
|
const okRefs = await confirmTemplateReferences('Deaktivieren');
|
||||||
|
if (!okRefs) return;
|
||||||
const res = await apiAction('content_versions.deactivate', { method: 'POST', data: { content_id: current.id } });
|
const res = await apiAction('content_versions.deactivate', { method: 'POST', data: { content_id: current.id } });
|
||||||
if (!res?.ok) throw new Error(res?.error || 'Deaktivieren fehlgeschlagen');
|
if (!res?.ok) throw new Error(res?.error || 'Deaktivieren fehlgeschlagen');
|
||||||
await loadVersionsForCurrent({ keepSelection: true, preferredId: currentVersionId });
|
await loadVersionsForCurrent({ keepSelection: true, preferredId: currentVersionId });
|
||||||
|
|||||||
@@ -88,6 +88,21 @@ async function openTemplateManager(item, section) {
|
|||||||
let versions = [];
|
let versions = [];
|
||||||
let activeId = 0;
|
let activeId = 0;
|
||||||
|
|
||||||
|
const fetchTemplateReferences = async () => {
|
||||||
|
if (!section?.is_template) return [];
|
||||||
|
const res = await apiAction('templates.references', { method: 'GET', data: { template_id: item.id } }).catch(() => ({}));
|
||||||
|
return Array.isArray(res?.references) ? res.references : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmTemplateReferences = async (actionLabel) => {
|
||||||
|
if (!section?.is_template) return true;
|
||||||
|
const refs = await fetchTemplateReferences();
|
||||||
|
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` : '';
|
||||||
|
return confirm(`Dieses Template wird in ${refs.length} anderen Template(s) verwendet (${preview}${more}). ${actionLabel} trotzdem?`);
|
||||||
|
};
|
||||||
|
|
||||||
const updateDeleteState = () => {
|
const updateDeleteState = () => {
|
||||||
const hasActive = !!activeId;
|
const hasActive = !!activeId;
|
||||||
if (btnDelete) btnDelete.disabled = hasActive;
|
if (btnDelete) btnDelete.disabled = hasActive;
|
||||||
@@ -176,6 +191,10 @@ async function openTemplateManager(item, section) {
|
|||||||
|
|
||||||
const onDeleteItem = async () => {
|
const onDeleteItem = async () => {
|
||||||
if (activeId) return;
|
if (activeId) return;
|
||||||
|
if (section?.is_template) {
|
||||||
|
const ok = await confirmTemplateReferences('Löschen');
|
||||||
|
if (!ok) return;
|
||||||
|
}
|
||||||
if (!delDlg || !delForm) {
|
if (!delDlg || !delForm) {
|
||||||
const res = await apiAction('content.delete', { method: 'POST', data: { id: item.id, section_id: section.id } });
|
const res = await apiAction('content.delete', { method: 'POST', data: { id: item.id, section_id: section.id } });
|
||||||
toast(res && res.ok ? 'Gelöscht' : 'Löschen fehlgeschlagen', !!(res && res.ok), { duration: 3000 });
|
toast(res && res.ok ? 'Gelöscht' : 'Löschen fehlgeschlagen', !!(res && res.ok), { duration: 3000 });
|
||||||
@@ -193,6 +212,10 @@ async function openTemplateManager(item, section) {
|
|||||||
if (delCancel) delCancel.onclick = () => { delDlg.close(); cleanupDelete(); };
|
if (delCancel) delCancel.onclick = () => { delDlg.close(); cleanupDelete(); };
|
||||||
delForm.onsubmit = async (ev) => {
|
delForm.onsubmit = async (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
if (section?.is_template) {
|
||||||
|
const ok = await confirmTemplateReferences('Löschen');
|
||||||
|
if (!ok) return;
|
||||||
|
}
|
||||||
const res = await apiAction('content.delete', { method: 'POST', data: { id: item.id, section_id: section.id } });
|
const res = await apiAction('content.delete', { method: 'POST', data: { id: item.id, section_id: section.id } });
|
||||||
delDlg.close();
|
delDlg.close();
|
||||||
cleanupDelete();
|
cleanupDelete();
|
||||||
@@ -223,6 +246,10 @@ async function openTemplateManager(item, section) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (target.dataset.versionDeactivate !== undefined) {
|
if (target.dataset.versionDeactivate !== undefined) {
|
||||||
|
if (section?.is_template) {
|
||||||
|
const ok = await confirmTemplateReferences('Deaktivieren');
|
||||||
|
if (!ok) return;
|
||||||
|
}
|
||||||
const res = await apiAction('content_versions.deactivate', { method: 'POST', data: { content_id: item.id } });
|
const res = await apiAction('content_versions.deactivate', { method: 'POST', data: { content_id: item.id } });
|
||||||
toast(res && res.ok ? 'Aktive Version deaktiviert' : 'Deaktivieren fehlgeschlagen', !!(res && res.ok));
|
toast(res && res.ok ? 'Aktive Version deaktiviert' : 'Deaktivieren fehlgeschlagen', !!(res && res.ok));
|
||||||
await loadVersions();
|
await loadVersions();
|
||||||
|
|||||||
@@ -2204,6 +2204,104 @@ class ApiKernel
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function handleTemplateReferences(): void
|
||||||
|
{
|
||||||
|
$auth = $this->requireAuth();
|
||||||
|
$customerId = (int)($auth['customer_id'] ?? 0);
|
||||||
|
if ($customerId <= 0) {
|
||||||
|
$this->fail('Customer context missing', null, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
$templateId = (int)$this->val($this->in, ['template_id', 'id', 'template'], 0);
|
||||||
|
if ($templateId <= 0) {
|
||||||
|
$this->fail('template_id required', null, 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$references = $this->findTemplateReferences($customerId, $templateId);
|
||||||
|
|
||||||
|
$this->respond([
|
||||||
|
'ok' => true,
|
||||||
|
'template_id' => $templateId,
|
||||||
|
'count' => count($references),
|
||||||
|
'references' => $references,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findTemplateReferences(int $customerId, int $templateId): array
|
||||||
|
{
|
||||||
|
$out = [];
|
||||||
|
$matches = function (?string $html) use ($templateId): bool {
|
||||||
|
if (!$html) return false;
|
||||||
|
$id = preg_quote((string)$templateId, '/');
|
||||||
|
$typePattern = '/data-ref-type\s*=\s*(["\"])template\1/i';
|
||||||
|
$idPattern = '/data-ref-id\s*=\s*(["\"])' . $id . '\1/i';
|
||||||
|
return (bool)(preg_match($typePattern, $html) && preg_match($idPattern, $html));
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($this->useUnifiedContent()) {
|
||||||
|
$section = $this->ensureEmailtemplateSection($customerId);
|
||||||
|
if (!$section) return [];
|
||||||
|
|
||||||
|
$itemsTable = $this->contentItemsTable();
|
||||||
|
if (!$this->tableExists($itemsTable)) return [];
|
||||||
|
$itemCols = $this->resolveContentItemColumns($itemsTable);
|
||||||
|
$htmlCol = $itemCols['html'];
|
||||||
|
|
||||||
|
$versionsTable = $this->contentVersionsTable();
|
||||||
|
$versionCols = ($this->tableExists($versionsTable)) ? $this->resolveContentVersionColumns($versionsTable) : null;
|
||||||
|
$versionHtmlCol = $versionCols['html'] ?? null;
|
||||||
|
$versionActiveCol = $versionCols['is_active'] ?? null;
|
||||||
|
|
||||||
|
$select = "i.`id` AS id, i.`name` AS name";
|
||||||
|
if ($htmlCol) $select .= ", i.`$htmlCol` AS item_html";
|
||||||
|
$join = '';
|
||||||
|
if ($versionHtmlCol && $versionActiveCol) {
|
||||||
|
$select .= ", v.`$versionHtmlCol` AS version_html";
|
||||||
|
$join = "LEFT JOIN `$versionsTable` v ON v.`content_id` = i.`id` AND v.`$versionActiveCol` = 1";
|
||||||
|
}
|
||||||
|
$sql = "SELECT $select FROM `$itemsTable` i $join WHERE i.`customer_id` = :cid AND i.`section_id` = :sid AND i.`id` <> :id";
|
||||||
|
$stmt = $this->pdo->prepare($sql);
|
||||||
|
$stmt->execute([':cid' => $customerId, ':sid' => (int)$section['id'], ':id' => $templateId]);
|
||||||
|
$rows = $stmt->fetchAll() ?: [];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$html = (string)($row['version_html'] ?? $row['item_html'] ?? '');
|
||||||
|
if ($matches($html)) {
|
||||||
|
$out[] = [
|
||||||
|
'id' => (int)($row['id'] ?? 0),
|
||||||
|
'name' => (string)($row['name'] ?? ''),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = $this->tableMap['templates'] ?? null;
|
||||||
|
if (!$table || !$this->tableExists($table)) return [];
|
||||||
|
|
||||||
|
[$idCol, $allCols] = $this->resolveIdCol('templates');
|
||||||
|
$htmlCol = $this->firstExisting($allCols, ['html', 'body', 'markup', 'content']);
|
||||||
|
if (!$htmlCol) return [];
|
||||||
|
$nameCol = $this->conf['columns']['templates']['name'] ?? ($this->firstExisting($allCols, ['name']) ?: $idCol);
|
||||||
|
|
||||||
|
[$tw, $tp] = $this->tenantWhere(['customer_id' => $customerId]);
|
||||||
|
$sql = "SELECT `$idCol` AS id, `$nameCol` AS name, `$htmlCol` AS html FROM `$table` WHERE `$idCol` <> :id" . $tw;
|
||||||
|
$stmt = $this->pdo->prepare($sql);
|
||||||
|
$stmt->bindValue(':id', $templateId, PDO::PARAM_INT);
|
||||||
|
foreach ($tp as $k => $v) $stmt->bindValue($k, $v);
|
||||||
|
$stmt->execute();
|
||||||
|
$rows = $stmt->fetchAll() ?: [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
if ($matches((string)($row['html'] ?? ''))) {
|
||||||
|
$out[] = [
|
||||||
|
'id' => (int)($row['id'] ?? 0),
|
||||||
|
'name' => (string)($row['name'] ?? ''),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
private function handleExternalRender(): void
|
private function handleExternalRender(): void
|
||||||
{
|
{
|
||||||
$token = trim((string)($this->in['token'] ?? ''));
|
$token = trim((string)($this->in['token'] ?? ''));
|
||||||
@@ -2536,6 +2634,9 @@ class ApiKernel
|
|||||||
case 'templates.test_send':
|
case 'templates.test_send':
|
||||||
$this->handleTemplateTestSend();
|
$this->handleTemplateTestSend();
|
||||||
break;
|
break;
|
||||||
|
case 'templates.references':
|
||||||
|
$this->handleTemplateReferences();
|
||||||
|
break;
|
||||||
case 'sections_config.reorder':
|
case 'sections_config.reorder':
|
||||||
$this->handleSectionsConfigReorder();
|
$this->handleSectionsConfigReorder();
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user