prepare('SELECT COUNT(*) FROM staff WHERE LOWER(email) = LOWER(?) AND id != ?'); $dup->execute([$email, $staff['id']]); if ((int)$dup->fetchColumn()) $errors[] = 'Diese E-Mail-Adresse wird bereits von einem anderen Mitarbeiter verwendet.'; } if (!$errors) { $pdo->prepare('UPDATE staff SET name=?, email=?, phone=? WHERE id=?') ->execute([$name, $email, $phone ?: null, $staff['id']]); audit_log($staff['id'], 'Profil aktualisiert: Name/E-Mail/Telefon'); flash('success', 'Kontodaten gespeichert.'); redirect('/staff/profile.php?tab=account'); } $tab = 'account'; } // ── Passwort ───────────────────────────────────────────────── if ($action === 'save_password') { $current = $_POST['current_password'] ?? ''; $new = $_POST['new_password'] ?? ''; $confirm = $_POST['confirm_password'] ?? ''; if (!password_verify($current, $staff['password_hash'])) { $errors[] = 'Das aktuelle Passwort ist falsch.'; } $pwError = validate_password_strength($new); if ($pwError) { $errors[] = $pwError; } if ($new !== $confirm) { $errors[] = 'Die Passwörter stimmen nicht überein.'; } if (!$errors) { $pdo->prepare('UPDATE staff SET password_hash=? WHERE id=?') ->execute([password_hash($new, PASSWORD_DEFAULT), $staff['id']]); audit_log($staff['id'], 'Passwort geändert'); flash('success', 'Passwort geändert.'); redirect('/staff/profile.php?tab=account'); } $tab = 'account'; } // ── Avatar ──────────────────────────────────────────────────── if ($action === 'save_avatar') { if (!empty($_FILES['avatar']['name'])) { $allowed = ['jpg','jpeg','png','gif','webp']; $ext = strtolower(pathinfo($_FILES['avatar']['name'], PATHINFO_EXTENSION)); if (!in_array($ext, $allowed)) { $errors[] = 'Erlaubte Bildformate: JPG, PNG, GIF, WEBP.'; } elseif ($_FILES['avatar']['size'] > 2 * 1024 * 1024) { $errors[] = 'Maximale Dateigröße: 2 MB.'; } else { $dir = __DIR__ . '/../assets/avatars/'; if (!is_dir($dir)) mkdir($dir, 0755, true); // Alten Avatar löschen if ($staff['avatar_path']) { $old = __DIR__ . '/..' . $staff['avatar_path']; if (file_exists($old)) unlink($old); } $filename = 'avatar_' . $staff['id'] . '_' . time() . '.' . $ext; move_uploaded_file($_FILES['avatar']['tmp_name'], $dir . $filename); $path = '/assets/avatars/' . $filename; $pdo->prepare('UPDATE staff SET avatar_path=? WHERE id=?')->execute([$path, $staff['id']]); flash('success', 'Avatar aktualisiert.'); redirect('/staff/profile.php?tab=account'); } } $tab = 'account'; } if ($action === 'delete_avatar') { if ($staff['avatar_path']) { $old = __DIR__ . '/..' . $staff['avatar_path']; if (file_exists($old)) unlink($old); } $pdo->prepare('UPDATE staff SET avatar_path=NULL WHERE id=?')->execute([$staff['id']]); flash('success', 'Avatar entfernt.'); redirect('/staff/profile.php?tab=account'); } // ── Urlaub/Abwesenheit ───────────────────────────────────── if ($action === 'save_settings') { $onVacation = !empty($_POST['is_on_vacation']) ? 1 : 0; $substituteId = ($_POST['vacation_substitute_id'] ?? '') !== '' ? (int)$_POST['vacation_substitute_id'] : null; $pdo->prepare('UPDATE staff SET is_on_vacation=?, vacation_substitute_id=? WHERE id=?') ->execute([$onVacation, $substituteId, $staff['id']]); audit_log($staff['id'], $onVacation ? 'Abwesenheit aktiviert' : 'Abwesenheit deaktiviert'); flash('success', 'Einstellungen gespeichert.'); redirect('/staff/profile.php?tab=settings'); } // ── 2FA aktivieren ───────────────────────────────────────────── if ($action === 'enable_2fa') { $code = trim($_POST['totp_code'] ?? ''); $secret = $_SESSION['totp_setup_secret'] ?? ''; if (!$secret) { flash('error', 'Sitzungsfehler. Bitte nochmals versuchen.'); } elseif (!totp_verify($secret, $code)) { flash('error', 'Code ungültig. Bitte erneut versuchen.'); } else { $pdo->prepare('UPDATE staff SET totp_secret=?, totp_enabled=1 WHERE id=?')->execute([$secret, $staff['id']]); unset($_SESSION['totp_setup_secret']); audit_log($staff['id'], 'Zwei-Faktor-Authentifizierung aktiviert'); flash('success', '2FA wurde erfolgreich aktiviert.'); } redirect('/staff/profile.php?tab=security'); } // ── 2FA deaktivieren ─────────────────────────────────────────── if ($action === 'disable_2fa') { $pass = $_POST['confirm_password'] ?? ''; $me = $pdo->prepare('SELECT password_hash FROM staff WHERE id=?'); $me->execute([$staff['id']]); $me = $me->fetch(); if (!password_verify($pass, $me['password_hash'])) { flash('error', 'Falsches Passwort.'); } else { $pdo->prepare('UPDATE staff SET totp_secret=NULL, totp_enabled=0 WHERE id=?')->execute([$staff['id']]); audit_log($staff['id'], 'Zwei-Faktor-Authentifizierung deaktiviert'); flash('success', '2FA wurde deaktiviert.'); } redirect('/staff/profile.php?tab=security'); } // ── Signatur ────────────────────────────────────────────────── if ($action === 'save_signature') { $sig = sanitize_staff_html(trim($_POST['signature'] ?? '')); $pdo->prepare('UPDATE staff SET signature=? WHERE id=?')->execute([$sig ?: null, $staff['id']]); flash('success', 'Signatur gespeichert.'); redirect('/staff/profile.php?tab=signature'); } } // Aktuellen Stand neu laden (nach Redirect relevant wenn $_SESSION cached) $staffRow = $pdo->prepare('SELECT * FROM staff WHERE id = ?'); $staffRow->execute([$staff['id']]); $me = $staffRow->fetch(); $pageTitle = 'Mein Profil'; $activePage = 'profile'; require __DIR__ . '/_header.php'; ?>
Erhalte Desktop-Benachrichtigungen bei neuen Tickets und Antworten — auch wenn der Browser-Tab nicht aktiv ist. Funktioniert in Chrome, Firefox und Edge. Erfordert HTTPS.
Push-Benachrichtigungen sind auf diesem Server nicht verfügbar (OpenSSL-Erweiterung fehlt oder PHP zu alt).
Wird automatisch an Ihre ausgehenden Ticket-Antworten angehängt. Ist keine Mitarbeiter-Signatur gesetzt, greift die Abteilungs-Signatur.
Vorschau:
Um 2FA zu deaktivieren, bestätigen Sie mit Ihrem Passwort:
Schützen Sie Ihr Konto mit einem zeitbasierten Einmalcode (TOTP – kompatibel mit Google Authenticator, Authy, etc.).
= e(chunk_split($setupSecret, 4, ' ')) ?>