repository = new ContentRepository(); $this->pythonPath = $this->pipelinePath . '/venv/bin/python'; } /** * GET /content * List all content orders */ public function index(): void { $filters = []; if (isset($_GET['status']) && $_GET['status'] !== '') { $filters['status'] = $_GET['status']; } $orders = $this->repository->findAllOrders($filters); $stats = $this->repository->getStatistics(); $this->view('content.index', [ 'title' => 'Content Studio', 'orders' => $orders, 'stats' => $stats, 'currentStatus' => $_GET['status'] ?? '', ]); } /** * GET /content/new * Show create form */ public function contentNew(): void { $this->view('content.new', [ 'title' => 'Neuer Content-Auftrag', 'profiles' => $this->repository->findAllProfiles(), 'contracts' => $this->repository->findAllContracts(), 'structures' => $this->repository->findAllStructures(), ]); } /** * POST /content * Store new order */ public function store(): void { $this->requireCsrf(); $title = trim($_POST['title'] ?? ''); $briefing = trim($_POST['briefing'] ?? ''); if ($title === '' || $briefing === '') { $_SESSION['error'] = 'Titel und Briefing sind erforderlich.'; header('Location: /content/new'); exit; } // Auto-apply first active contract if none selected $contractId = $_POST['contract_id'] ?? null; if ($contractId === null || $contractId === '') { $contracts = $this->repository->findAllContracts(); if ($contracts !== []) { $contractId = $contracts[0]['id']; } } $orderId = $this->repository->createOrder([ 'title' => $title, 'briefing' => $briefing, 'author_profile_id' => $_POST['author_profile_id'] ?? null, 'contract_id' => $contractId, 'structure_id' => $_POST['structure_id'] ?? null, ]); header('Location: /content/' . $orderId); exit; } /** * GET /content/{id} * Show order details */ public function show(int $id): void { $order = $this->repository->findOrder($id); if ($order === null) { http_response_code(404); echo '404 - Auftrag nicht gefunden'; return; } $versions = $this->repository->findVersionsByOrder($id); $latestVersion = $versions[0] ?? null; $critiques = $latestVersion ? $this->repository->findCritiquesByVersion($latestVersion['id']) : []; $sources = $this->repository->findSourcesByOrder($id); $this->view('content.show', [ 'title' => $order['title'], 'order' => $order, 'versions' => $versions, 'latestVersion' => $latestVersion, 'critiques' => $critiques, 'sources' => $sources, ]); } /** * GET /content/{id}/edit * Show edit form */ public function edit(int $id): void { $order = $this->repository->findOrder($id); if ($order === null) { http_response_code(404); echo '404 - Auftrag nicht gefunden'; return; } $this->view('content.edit', [ 'title' => 'Auftrag bearbeiten', 'order' => $order, 'profiles' => $this->repository->findAllProfiles(), 'contracts' => $this->repository->findAllContracts(), 'structures' => $this->repository->findAllStructures(), ]); } /** * POST /content/{id}/generate * Generate content (HTMX) */ public function generate(int $id): void { $this->requireCsrf(); $model = $_POST['model'] ?? 'claude-opus-4-5-20251101'; $collection = $_POST['collection'] ?? 'documents'; $limit = (int) ($_POST['context_limit'] ?? 5); $result = $this->callPython('generate', $id, [$model, $collection, $limit]); if (isset($result['error'])) { echo '
Fehler: ' . htmlspecialchars($result['error']) . '
'; return; } // Return updated content section $this->renderVersionPartial($result); } /** * POST /content/{id}/critique * Run critique round (HTMX) */ public function critique(int $id): void { $this->requireCsrf(); // Get latest version $version = $this->repository->findLatestVersion($id); if ($version === null) { echo '
Keine Version vorhanden.
'; return; } $model = $_POST['model'] ?? 'claude-opus-4-5-20251101'; $result = $this->callPython('critique', $version['id'], [$model]); if (isset($result['error'])) { echo '
Fehler: ' . htmlspecialchars($result['error']) . '
'; return; } // Return critique results $this->renderCritiquePartial($result); } /** * POST /content/{id}/revise * Create revision (HTMX) */ public function revise(int $id): void { $this->requireCsrf(); $version = $this->repository->findLatestVersion($id); if ($version === null) { echo '
Keine Version vorhanden.
'; return; } $model = $_POST['model'] ?? 'claude-opus-4-5-20251101'; $result = $this->callPython('revise', $version['id'], [$model]); if (isset($result['error'])) { echo '
Fehler: ' . htmlspecialchars($result['error']) . '
'; return; } $this->renderVersionPartial($result); } /** * POST /content/{id}/approve * Approve content */ public function approve(int $id): void { $this->repository->updateOrderStatus($id, 'approve'); echo '
Content genehmigt!
'; echo ''; } /** * POST /content/{id}/decline * Decline content */ public function decline(int $id): void { $this->repository->updateOrderStatus($id, 'draft'); echo '
Content abgelehnt. Zurück zu Entwurf.
'; echo ''; } /** * Call Python script */ private function callPython(string $command, int $entityId, array $args = []): array { $scriptPath = $this->pipelinePath . '/web_generate.py'; $cmdArgs = array_map('escapeshellarg', $args); $cmd = "{$this->pythonPath} {$scriptPath} {$command} {$entityId} " . implode(' ', $cmdArgs) . ' 2>&1'; $output = shell_exec($cmd); if ($output === null) { return ['error' => 'Script konnte nicht ausgeführt werden']; } if (preg_match('/\{[\s\S]*\}/', $output, $matches)) { $result = json_decode($matches[0], true); if (json_last_error() === JSON_ERROR_NONE) { return $result; } } return ['error' => 'Ungültige Antwort: ' . substr($output, 0, 500)]; } /** * Render version partial */ private function renderVersionPartial(array $result): void { $content = $result['content'] ?? ''; $sources = $result['sources'] ?? []; echo '
'; echo '
'; echo 'Version ' . ($result['version_number'] ?? '?') . ''; echo 'Generiert'; echo '
'; echo '
' . nl2br(htmlspecialchars($content)) . '
'; if (count($sources) > 0) { echo '
'; echo 'Quellen:
'; } echo '
'; } /** * Render critique partial */ private function renderCritiquePartial(array $result): void { $critiques = $result['critiques'] ?? []; $allPassed = $result['all_passed'] ?? false; echo '
'; echo '
'; echo 'Kritik-Runde ' . ($result['round'] ?? '?') . ''; echo $allPassed ? 'Bestanden' : 'Revision nötig'; echo '
'; foreach ($critiques as $critique) { $passed = $critique['passed'] ?? false; $icon = $passed ? '✓' : '⚠'; $class = $passed ? 'passed' : 'failed'; echo '
'; echo '
' . $icon . ' ' . htmlspecialchars($critique['critic_name'] ?? '') . '
'; echo '
Bewertung: ' . ($critique['rating'] ?? 'N/A') . '/10
'; if (isset($critique['issues']) && $critique['issues'] !== []) { echo '
Probleme:
    '; foreach ($critique['issues'] as $issue) { echo '
  • ' . htmlspecialchars($issue) . '
  • '; } echo '
'; } if (isset($critique['summary']) && $critique['summary'] !== '') { echo '
' . htmlspecialchars($critique['summary']) . '
'; } echo '
'; } echo '
'; } }