This commit is contained in:
2026-02-04 01:48:15 +01:00
parent 58074631de
commit 24b7f3383a
4 changed files with 58 additions and 11 deletions

View File

@@ -1 +1 @@
1.2.39
1.2.40

View File

@@ -15,6 +15,9 @@ require dirname(__DIR__) . '/../structure/layout_start.php';
<option value="grapesjs">GrapesJS</option>
<option value="craftjs">Craft.js</option>
</select>
<label class="block text-sm text-slate-600">Versionen aufbewahren (0 = unbegrenzt)
<input type="number" name="versions_retention" class="input mt-1" min="0" step="1" placeholder="0">
</label>
<div class="flex justify-end">
<button type="submit" class="btn">Einstellungen speichern</button>
</div>

View File

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

View File

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