From 24b7f3383a740822792d40c400891efc498f14b4 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Wed, 4 Feb 2026 01:48:15 +0100 Subject: [PATCH] asd --- config/current.ver | 2 +- partials/landingpage/accountsetup/system.php | 3 ++ public/assets/js/ui-user.js | 11 ++++ src/ApiKernel.php | 53 ++++++++++++++++---- 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/config/current.ver b/config/current.ver index 84da421..8bd7d68 100644 --- a/config/current.ver +++ b/config/current.ver @@ -1 +1 @@ -1.2.39 \ No newline at end of file +1.2.40 \ No newline at end of file diff --git a/partials/landingpage/accountsetup/system.php b/partials/landingpage/accountsetup/system.php index 9b5b033..5a6a43c 100644 --- a/partials/landingpage/accountsetup/system.php +++ b/partials/landingpage/accountsetup/system.php @@ -15,6 +15,9 @@ require dirname(__DIR__) . '/../structure/layout_start.php'; +
diff --git a/public/assets/js/ui-user.js b/public/assets/js/ui-user.js index 354b942..9d790b1 100644 --- a/public/assets/js/ui-user.js +++ b/public/assets/js/ui-user.js @@ -442,6 +442,12 @@ function fillSettingsForm(settings) { if (settingsForm.sender_token) settingsForm.sender_token.value = settings.sender_token || ''; if (settingsForm.external_api_token) settingsForm.external_api_token.value = settings.external_api_token || ''; if (settingsForm.editor_default) settingsForm.editor_default.value = settings.editor_default || 'grapesjs'; + if (settingsForm.versions_retention) { + const retention = Number.isFinite(Number(settings.versions_retention)) + ? Number(settings.versions_retention) + : 0; + settingsForm.versions_retention.value = String(Math.max(0, retention)); + } refreshAdminTables(settings.bridge_setup?.tables || [], settings.bridge_tables || []); } @@ -490,6 +496,11 @@ async function submitSettingsForm(ev) { if (settingsForm.sender_token) data.sender_token = settingsForm.sender_token.value.trim(); if (settingsForm.external_api_token) data.external_api_token = settingsForm.external_api_token.value.trim(); if (settingsForm.editor_default) data.editor_default = settingsForm.editor_default.value; + if (settingsForm.versions_retention) { + const raw = settingsForm.versions_retention.value.trim(); + const parsed = raw === '' ? 0 : Number(raw); + data.versions_retention = Number.isFinite(parsed) ? Math.max(0, Math.floor(parsed)) : 0; + } if (adminTablesAllSelect && adminTablesSelectedSelect) { const bridgeTables = normalizeTableList(state.settings.bridge_tables || []); data.bridge_tables = bridgeTables; diff --git a/src/ApiKernel.php b/src/ApiKernel.php index 6b652a4..a40c51f 100644 --- a/src/ApiKernel.php +++ b/src/ApiKernel.php @@ -431,14 +431,7 @@ class ApiKernel $stmt->execute(); $newId = (int)$this->pdo->lastInsertId(); - $cleanup = $this->pdo->prepare( - "DELETE FROM `$table` WHERE `id` IN ( - SELECT `id` FROM ( - SELECT `id` FROM `$table` WHERE `content_id` = :cid ORDER BY `id` DESC LIMIT 10, 1000000 - ) t - )" - ); - $cleanup->execute([':cid' => $contentId]); + $this->applyContentVersionRetention($customerId, $contentId); return $newId; } catch (Throwable $e) { // Versioning darf nicht das Speichern blockieren. @@ -446,6 +439,35 @@ class ApiKernel } } + private function applyContentVersionRetention(int $customerId, int $contentId): void + { + $limit = $this->getContentVersionRetentionLimit($customerId); + if ($limit <= 0) return; + $table = $this->contentVersionsTable(); + if (!$this->tableExists($table)) return; + try { + $versionCols = $this->resolveContentVersionColumns($table); + $isActiveCol = $versionCols['is_active']; + $activeFilter = $isActiveCol ? " AND (`$isActiveCol` IS NULL OR `$isActiveCol` = 0)" : ''; + $keepSql = "SELECT `id` FROM `$table` WHERE `content_id` = :cid ORDER BY `version_no` DESC, `id` DESC LIMIT :lim"; + $deleteSql = "DELETE FROM `$table` WHERE `content_id` = :cid$activeFilter AND `id` NOT IN (SELECT `id` FROM ($keepSql) AS keep_ids)"; + $stmt = $this->pdo->prepare($deleteSql); + $stmt->bindValue(':cid', $contentId, PDO::PARAM_INT); + $stmt->bindValue(':lim', $limit, PDO::PARAM_INT); + $stmt->execute(); + } catch (Throwable $e) { + // Retention darf nicht das Speichern blockieren. + } + } + + private function getContentVersionRetentionLimit(int $customerId): int + { + if ($customerId <= 0) return 0; + $settings = $this->getCustomerSettings($customerId); + $limit = (int)($settings['versions_retention'] ?? 0); + return max(0, $limit); + } + private function activateContentVersion(int $customerId, int $contentId, int $versionId): bool { $table = $this->contentVersionsTable(); @@ -3711,13 +3733,14 @@ class ApiKernel $hasSenderToken = array_key_exists('sender_token', $this->in); $hasExternalToken = array_key_exists('external_api_token', $this->in); $hasEditorDefault = array_key_exists('editor_default', $this->in); + $hasVersionsRetention = array_key_exists('versions_retention', $this->in); $hasListSort = array_key_exists('list_sort', $this->in); $hasBridgeTables = array_key_exists('bridge_tables', $this->in); $rotateBridge = !empty($this->in['rotate_bridge_token']); $rotateSender = !empty($this->in['rotate_sender_token']); $rotateExternal = !empty($this->in['rotate_external_token']); $onlyListSort = $hasListSort && !$hasBridgeUrl && !$hasBridgeToken && !$hasSenderToken && !$hasExternalToken - && !$hasEditorDefault && !$hasBridgeTables && !$rotateBridge && !$rotateSender && !$rotateExternal; + && !$hasEditorDefault && !$hasBridgeTables && !$hasVersionsRetention && !$rotateBridge && !$rotateSender && !$rotateExternal; if (!$onlyListSort) { $this->ensureRole($user, ['owner', 'admin']); @@ -3730,6 +3753,7 @@ class ApiKernel $senderToken = $hasSenderToken ? trim((string)($this->in['sender_token'] ?? '')) : (string)($settings['sender_token'] ?? ''); $externalToken = $hasExternalToken ? trim((string)($this->in['external_api_token'] ?? '')) : (string)($settings['external_api_token'] ?? ''); $editorDefault = $hasEditorDefault ? strtolower(trim((string)($this->in['editor_default'] ?? ''))) : strtolower((string)($settings['editor_default'] ?? '')); + $versionsRetention = $hasVersionsRetention ? (int)($this->in['versions_retention'] ?? 0) : (int)($settings['versions_retention'] ?? 0); $listSort = $hasListSort ? strtolower(trim((string)($this->in['list_sort'] ?? ''))) : ''; $bridgeTables = $hasBridgeTables ? $this->normalizeBridgeTables($this->in['bridge_tables'] ?? []) : ($settings['bridge_tables'] ?? []); @@ -3749,6 +3773,9 @@ class ApiKernel if ($editorDefault !== '' && !in_array($editorDefault, ['grapesjs', 'craftjs'], true)) { $this->fail('Ungültiger Editor-Typ', null, 422); } + if ($versionsRetention < 0) { + $this->fail('Ungültiger Aufbewahrungswert', null, 422); + } $settings = $this->saveCustomerSettings($customerId, [ 'bridge_url' => $bridgeUrl, @@ -3757,6 +3784,7 @@ class ApiKernel 'external_api_token' => $externalToken, 'editor_default' => $editorDefault ?: null, 'bridge_tables' => $bridgeTables, + 'versions_retention' => $versionsRetention, ]); } else { $settings = $customerId ? $this->ensureSettingsTokens($customerId, $settings) : $settings; @@ -4318,7 +4346,7 @@ class ApiKernel { if ($customerId <= 0) return []; $this->ensureCustomerSettingsTableExists(); - $allowed = ['bridge_url', 'bridge_token', 'sender_token', 'external_api_token', 'editor_default', 'bridge_tables', 'bridge_setup']; + $allowed = ['bridge_url', 'bridge_token', 'sender_token', 'external_api_token', 'editor_default', 'bridge_tables', 'bridge_setup', 'versions_retention']; $fields = array_intersect_key($data, array_flip($allowed)); if (!$fields) return $this->getCustomerSettings($customerId); if (array_key_exists('bridge_tables', $fields)) { @@ -4384,6 +4412,11 @@ class ApiKernel if (empty($row['editor_default'])) { $row['editor_default'] = 'grapesjs'; } + if (!isset($row['versions_retention']) || $row['versions_retention'] === '') { + $row['versions_retention'] = 0; + } else { + $row['versions_retention'] = max(0, (int)$row['versions_retention']); + } return $row; }