Protokoll #2385
| ID | 2385 |
|---|---|
| Zeitstempel | 2025-12-21 14:42:09.541608 |
| Client | root |
| IP | Warning: Undefined array key "request_ip" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13 Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13 |
| Modell | claude-sonnet-4-20250514 |
| Status | completed |
| Tokens | 2,681 (Input: 0, Output: 0) |
| Dauer | 80 ms |
| Request-Zeit | Warning: Undefined array key "request_timestamp" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 18 |
| Response-Zeit | - |
Warning: Undefined array key "error_message" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 22
Request
{
"event": "PreToolUse",
"tool_name": "Read",
"tool_input": {
"file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Controller\/ConfigController.php"
}
}
Response
{
"tool_response": {
"type": "text",
"file": {
"filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Controller\/ConfigController.php",
"content": "<?php\n\nnamespace Controller;\n\nuse Framework\\Controller;\nuse Infrastructure\\Config\\DatabaseFactory;\n\nclass ConfigController extends Controller\n{\n private \\PDO $db;\n\n private const TYPES = ['author_profile', 'structure', 'organization', 'contract', 'rule', 'system_prompt'];\n private const TYPE_LABELS = [\n 'author_profile' => 'Autorenprofil',\n 'structure' => 'Struktur',\n 'organization' => 'Organisation',\n 'contract' => 'Contract',\n 'rule' => 'Regel',\n 'system_prompt' => 'System-Prompt',\n ];\n\n public function __construct()\n {\n $this->db = DatabaseFactory::content();\n }\n\n \/**\n * GET \/config\n *\/\n public function index(): void\n {\n $typeFilter = $_GET['type'] ?? '';\n $statusFilter = $_GET['status'] ?? '';\n\n $sql = 'SELECT c.*, p.name as parent_name\n FROM content_config c\n LEFT JOIN content_config p ON c.parent_id = p.id\n WHERE 1=1';\n $params = [];\n\n if ($typeFilter !== '' && in_array($typeFilter, self::TYPES, true)) {\n $sql .= ' AND c.type = ?';\n $params[] = $typeFilter;\n }\n\n if ($statusFilter !== '' && in_array($statusFilter, ['draft', 'active', 'deprecated'], true)) {\n $sql .= ' AND c.status = ?';\n $params[] = $statusFilter;\n }\n\n $sql .= ' ORDER BY c.type, c.name';\n\n $stmt = $this->db->prepare($sql);\n $stmt->execute($params);\n $configs = $stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\n $stats = $this->getStatistics();\n\n $this->view('config.index', [\n 'title' => 'Content-Konfiguration',\n 'configs' => $configs,\n 'stats' => $stats,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'currentType' => $typeFilter,\n 'currentStatus' => $statusFilter,\n ]);\n }\n\n \/**\n * GET \/config\/new\n *\/\n public function configNew(): void\n {\n $parents = $this->getParentOptions();\n\n $this->view('config.form', [\n 'title' => 'Neue Konfiguration',\n 'config' => null,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'parents' => $parents,\n 'isEdit' => false,\n ]);\n }\n\n \/**\n * POST \/config\n *\/\n public function store(): void\n {\n $this->requireCsrf();\n\n $type = $_POST['type'] ?? '';\n $name = trim($_POST['name'] ?? '');\n $slug = trim($_POST['slug'] ?? '');\n $description = trim($_POST['description'] ?? '');\n $content = $_POST['content'] ?? '{}';\n $version = trim($_POST['version'] ?? '1.0');\n $status = $_POST['status'] ?? 'draft';\n $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;\n\n if (!in_array($type, self::TYPES, true) || $name === '' || $slug === '') {\n $_SESSION['error'] = 'Typ, Name und Slug sind erforderlich.';\n header('Location: \/config\/new');\n exit;\n }\n\n \/\/ JSON validieren\n $decoded = json_decode($content, true);\n if ($decoded === null && $content !== 'null') {\n $_SESSION['error'] = 'Ungültiges JSON-Format.';\n header('Location: \/config\/new');\n exit;\n }\n\n $stmt = $this->db->prepare(\n 'INSERT INTO content_config (type, name, slug, description, content, version, status, parent_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)'\n );\n $stmt->execute([$type, $name, $slug, $description ?: null, $content, $version, $status, $parentId]);\n $id = $this->db->lastInsertId();\n\n $_SESSION['success'] = 'Konfiguration erfolgreich erstellt.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n \/**\n * GET \/config\/{id}\n *\/\n public function show(string $id): void\n {\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n http_response_code(404);\n echo 'Konfiguration nicht gefunden';\n\n return;\n }\n\n $children = $this->getChildren((int) $id);\n $history = $this->getHistory((int) $id);\n\n $this->view('config.show', [\n 'title' => $config['name'],\n 'config' => $config,\n 'children' => $children,\n 'history' => $history,\n 'typeLabels' => self::TYPE_LABELS,\n ]);\n }\n\n \/**\n * GET \/config\/{id}\/edit\n *\/\n public function edit(string $id): void\n {\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n http_response_code(404);\n echo 'Konfiguration nicht gefunden';\n\n return;\n }\n\n $parents = $this->getParentOptions((int) $id);\n\n $this->view('config.form', [\n 'title' => 'Bearbeiten: ' . $config['name'],\n 'config' => $config,\n 'types' => self::TYPES,\n 'typeLabels' => self::TYPE_LABELS,\n 'parents' => $parents,\n 'isEdit' => true,\n ]);\n }\n\n \/**\n * POST \/config\/{id}\n *\/\n public function update(string $id): void\n {\n $this->requireCsrf();\n\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n http_response_code(404);\n echo 'Konfiguration nicht gefunden';\n\n return;\n }\n\n $name = trim($_POST['name'] ?? '');\n $slug = trim($_POST['slug'] ?? '');\n $description = trim($_POST['description'] ?? '');\n $content = $_POST['content'] ?? '{}';\n $newVersion = trim($_POST['new_version'] ?? '');\n $changeDescription = trim($_POST['change_description'] ?? '');\n $status = $_POST['status'] ?? $config['status'];\n $parentId = !empty($_POST['parent_id']) ? (int) $_POST['parent_id'] : null;\n\n if ($name === '' || $slug === '' || $newVersion === '') {\n $_SESSION['error'] = 'Name, Slug und neue Version sind erforderlich.';\n header('Location: \/config\/' . $id . '\/edit');\n exit;\n }\n\n \/\/ JSON validieren\n $decoded = json_decode($content, true);\n if ($decoded === null && $content !== 'null') {\n $_SESSION['error'] = 'Ungültiges JSON-Format.';\n header('Location: \/config\/' . $id . '\/edit');\n exit;\n }\n\n \/\/ Alte Version in History speichern\n $stmt = $this->db->prepare(\n 'INSERT INTO content_config_history (config_id, content, version, changed_by, change_description)\n VALUES (?, ?, ?, ?, ?)'\n );\n $stmt->execute([\n $id,\n $config['content'],\n $config['version'],\n 'web-ui',\n $changeDescription ?: 'Update auf Version ' . $newVersion,\n ]);\n\n \/\/ Config aktualisieren\n $stmt = $this->db->prepare(\n 'UPDATE content_config\n SET name = ?, slug = ?, description = ?, content = ?, version = ?, status = ?, parent_id = ?\n WHERE id = ?'\n );\n $stmt->execute([$name, $slug, $description ?: null, $content, $newVersion, $status, $parentId, $id]);\n\n $_SESSION['success'] = 'Konfiguration auf Version ' . $newVersion . ' aktualisiert.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n \/**\n * POST \/config\/{id}\/delete\n *\/\n public function delete(string $id): void\n {\n $this->requireCsrf();\n\n $config = $this->findById((int) $id);\n\n if ($config === null) {\n http_response_code(404);\n echo 'Konfiguration nicht gefunden';\n\n return;\n }\n\n \/\/ Prüfen ob Children existieren\n $stmt = $this->db->prepare('SELECT COUNT(*) FROM content_config WHERE parent_id = ?');\n $stmt->execute([$id]);\n if ($stmt->fetchColumn() > 0) {\n $_SESSION['error'] = 'Kann nicht gelöscht werden: Es existieren verknüpfte Einträge.';\n header('Location: \/config\/' . $id);\n exit;\n }\n\n \/\/ History löschen\n $stmt = $this->db->prepare('DELETE FROM content_config_history WHERE config_id = ?');\n $stmt->execute([$id]);\n\n \/\/ Config löschen\n $stmt = $this->db->prepare('DELETE FROM content_config WHERE id = ?');\n $stmt->execute([$id]);\n\n $_SESSION['success'] = 'Konfiguration gelöscht.';\n header('Location: \/config');\n exit;\n }\n\n \/\/ ========== Private Helpers ==========\n\n private function findById(int $id): ?array\n {\n $stmt = $this->db->prepare(\n 'SELECT c.*, p.name as parent_name\n FROM content_config c\n LEFT JOIN content_config p ON c.parent_id = p.id\n WHERE c.id = ?'\n );\n $stmt->execute([$id]);\n $result = $stmt->fetch(\\PDO::FETCH_ASSOC);\n\n return $result !== false ? $result : null;\n }\n\n private function getStatistics(): array\n {\n $stats = ['total' => 0, 'by_type' => [], 'by_status' => []];\n\n $stmt = $this->db->query('SELECT type, COUNT(*) as cnt FROM content_config GROUP BY type');\n foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {\n $stats['by_type'][$row['type']] = (int) $row['cnt'];\n $stats['total'] += (int) $row['cnt'];\n }\n\n $stmt = $this->db->query('SELECT status, COUNT(*) as cnt FROM content_config GROUP BY status');\n foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {\n $stats['by_status'][$row['status']] = (int) $row['cnt'];\n }\n\n return $stats;\n }\n\n private function getParentOptions(?int $excludeId = null): array\n {\n $sql = 'SELECT id, type, name FROM content_config WHERE type IN (\"contract\", \"organization\") ORDER BY type, name';\n $stmt = $this->db->query($sql);\n $parents = [];\n foreach ($stmt->fetchAll(\\PDO::FETCH_ASSOC) as $row) {\n if ($excludeI... [TRUNCATED-b27339b9cb9e8c4b]",
"numLines": 351,
"startLine": 1,
"totalLines": 351
}
}
}