view('admin/dashboard', [ 'title' => 'Admin Dashboard', 'health' => $report ]); } // Settings public function settings() { $this->view('admin/settings', [ 'title' => 'Einstellungen' ]); } // User & Invite Management public function users() { $userModel = $this->model('User'); $invitationModel = $this->model('Invitation'); $users = $userModel->getAll(); $invitations = $invitationModel->getAll(); $allModules = ModuleService::getAll(); $activeTab = $_GET['tab'] ?? 'users'; $this->view('admin/users', [ 'title' => 'Benutzer- & Zugangsverwaltung', 'users' => $users, 'invitations' => $invitations, 'allModules' => $allModules, 'activeTab' => $activeTab ]); } // User Form public function user_form($id = null) { $user = null; if ($id) { $userModel = $this->model('User'); $user = $userModel->getById($id); if (!$user) { Flash::set('Nutzer nicht gefunden.', 'error'); header('Location: ' . Config::get('BASE_URL', '/') . 'admin/users'); exit; } } $this->view('admin/user_form', [ 'title' => $id ? 'Benutzer bearbeiten' : 'Neuen Benutzer anlegen', 'user' => $user, 'allModules' => ModuleService::getAll() ]); } // Save User public function user_save() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { header('Location: ' . Config::get('BASE_URL', '/') . 'admin/users'); exit; } try { Security::checkCsrf($_POST['csrf_token'] ?? ''); $id = !empty($_POST['id']) ? (int)$_POST['id'] : null; $userModel = $this->model('User'); if (empty($_POST['name']) || empty($_POST['email'])) { throw new Exception("Name und E-Mail sind Pflichtfelder."); } // Projects from checkboxes $projects = isset($_POST['projects']) && is_array($_POST['projects']) ? $_POST['projects'] : []; $postData = $_POST; $postData['projects'] = $projects; $userModel->save($postData, $id); Flash::set('Nutzer erfolgreich gespeichert!', 'success'); } catch (Exception $e) { Flash::set($e->getMessage(), 'error'); } header('Location: ' . Config::get('BASE_URL', '/') . 'admin/users'); exit; } // Quick toggle user status (Active / Inactive) public function user_toggle_status($id) { if ($_SERVER['REQUEST_METHOD'] === 'POST') { try { Security::checkCsrf($_POST['csrf_token'] ?? ''); $userModel = $this->model('User'); $user = $userModel->getById($id); if (!$user) { throw new Exception("Nutzer nicht gefunden."); } $newStatus = ($user['status'] === 'Active') ? 'Inactive' : 'Active'; $user['status'] = $newStatus; $userModel->save($user, $id); Flash::set('Status für ' . htmlspecialchars($user['name']) . ' geändert auf: ' . $newStatus, 'success'); } catch (Exception $e) { Flash::set('Fehler: ' . $e->getMessage(), 'error'); } } header('Location: ' . Config::get('BASE_URL', '/') . 'admin/users'); exit; } // Delete User public function user_delete($id) { if ($_SERVER['REQUEST_METHOD'] === 'POST') { try { Security::checkCsrf($_POST['csrf_token'] ?? ''); $userModel = $this->model('User'); $userModel->delete($id); Flash::set('Nutzer wurde gelöscht.', 'success'); } catch (Exception $e) { Flash::set('Fehler beim Löschen: ' . $e->getMessage(), 'error'); } } header('Location: ' . Config::get('BASE_URL', '/') . 'admin/users'); exit; } // Create Invitation Link public function invite_create() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { header('Location: ' . Config::get('BASE_URL', '/') . 'admin/users?tab=invites'); exit; } try { Security::checkCsrf($_POST['csrf_token'] ?? ''); $invitationModel = $this->model('Invitation'); $userModel = $this->model('User'); $assignedProjects = isset($_POST['projects']) && is_array($_POST['projects']) ? $_POST['projects'] : []; $note = trim($_POST['note'] ?? ''); $maxUses = !empty($_POST['max_uses']) ? max(1, (int)$_POST['max_uses']) : 1; $recipientEmail = trim(strtolower($_POST['recipient_email'] ?? '')); $sendEmailNow = !empty($_POST['send_email_now']); if (!empty($recipientEmail)) { if (!filter_var($recipientEmail, FILTER_VALIDATE_EMAIL)) { throw new Exception('Bitte gib eine gültige E-Mail-Adresse an.'); } $existingUser = $userModel->findByEmail($recipientEmail); if ($existingUser) { throw new Exception("Ein Benutzer mit der E-Mail '{$recipientEmail}' existiert bereits."); } } $expiresAt = null; if (!empty($_POST['days_valid']) && (int)$_POST['days_valid'] > 0) { $days = (int)$_POST['days_valid']; $expiresAt = (new DateTime("+{$days} days"))->format('Y-m-d H:i:s'); } $token = $invitationModel->create($assignedProjects, $note ?: null, $maxUses, $expiresAt, $recipientEmail ?: null); $baseUrl = rtrim(Config::get('BASE_URL', 'https://philippurbschat.de/'), '/'); $inviteUrl = $baseUrl . '/register?token=' . $token; $mailSentNotice = ''; if (!empty($recipientEmail) && $sendEmailNow) { require_once __DIR__ . '/../services/MailService.php'; MailService::sendInvitation($recipientEmail, $token, $assignedProjects, $note ?: null); $mailSentNotice = " und E-Mail an {$recipientEmail} gesendet"; } Flash::set('Einladungslink erfolgreich erstellt' . $mailSentNotice . ': ' . $inviteUrl, 'success'); } catch (Exception $e) { Flash::set('Fehler beim Erstellen des Einladungslinks: ' . $e->getMessage(), 'error'); } header('Location: ' . Config::get('BASE_URL', '/') . 'admin/users?tab=invites'); exit; } // Resend Invitation Email public function invite_resend($id) { if ($_SERVER['REQUEST_METHOD'] === 'POST') { try { Security::checkCsrf($_POST['csrf_token'] ?? ''); $invitationModel = $this->model('Invitation'); $inv = $invitationModel->getById((int)$id); if (!$inv) { throw new Exception('Einladung nicht gefunden.'); } if (empty($inv['recipient_email'])) { throw new Exception('Dieser Einladungslink ist keiner konkreten E-Mail-Adresse zugewiesen.'); } require_once __DIR__ . '/../services/MailService.php'; MailService::sendInvitation($inv['recipient_email'], $inv['token'], $inv['assigned_projects'], $inv['note'] ?? null); Flash::set("Einladungs-E-Mail erfolgreich erneut an {$inv['recipient_email']} gesendet.", 'success'); } catch (Exception $e) { Flash::set('Fehler beim Senden der E-Mail: ' . $e->getMessage(), 'error'); } } header('Location: ' . Config::get('BASE_URL', '/') . 'admin/users?tab=invites'); exit; } // Delete Invitation Link public function invite_delete($id) { if ($_SERVER['REQUEST_METHOD'] === 'POST') { try { Security::checkCsrf($_POST['csrf_token'] ?? ''); $invitationModel = $this->model('Invitation'); $invitationModel->delete((int)$id); Flash::set('Einladungslink wurde gelöscht.', 'success'); } catch (Exception $e) { Flash::set('Fehler beim Löschen: ' . $e->getMessage(), 'error'); } } header('Location: ' . Config::get('BASE_URL', '/') . 'admin/users?tab=invites'); exit; } }