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 '
' . nl2br(htmlspecialchars($content)) . '
';
if (count($sources) > 0) {
echo '
';
echo '
Quellen:';
foreach ($sources as $src) {
$score = round(($src['score'] ?? 0) * 100);
echo '- ' . htmlspecialchars($src['source'] ?? '') . ' (' . $score . '%)
';
}
echo '
';
}
echo '
';
}
/**
* Render critique partial
*/
private function renderCritiquePartial(array $result): void
{
$critiques = $result['critiques'] ?? [];
$allPassed = $result['all_passed'] ?? false;
echo '';
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 '
';
}
}