isSMTP();
$mail->Host = defined('SMTP_HOST') ? SMTP_HOST : 'smtp.world4you.com';
$mail->SMTPAuth = true;
$mail->Username = defined('SMTP_USER') ? SMTP_USER : 'support@geograsim.at';
$mail->Password = defined('SMTP_PASS') ? SMTP_PASS : 'geo8Zeichen!';
$mail->SMTPSecure = \PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = defined('SMTP_PORT') ? (int)SMTP_PORT : 587;
$mail->CharSet = 'UTF-8';
[$fromEmail, $fromName] = self::configuredFrom();
$mail->setFrom($fromEmail, $fromName);
return $mail;
}
/** Absender aus Admin-Config (platform_config_v2), Fallback auf .env-Konstanten.
* SMTP-Zugangsdaten bleiben bewusst in .env (geheim) — nur die sichtbare
* Absenderadresse/-name ist zentral in der Admin-Oberflaeche pflegbar. */
private static function configuredFrom(): array {
$email = defined('SMTP_FROM') ? SMTP_FROM : 'support@geograsim.at';
$name = defined('SMTP_FROM_NAME') ? SMTP_FROM_NAME : 'GeoGraSim';
try {
if (class_exists('Database')) {
$rows = Database::get()->fetchAll(
"SELECT skey, sval FROM app_settings WHERE skey IN ('mail_from_email','mail_from_name')");
foreach ($rows as $row) {
if ($row['skey'] === 'mail_from_email' && !empty($row['sval'])) $email = $row['sval'];
if ($row['skey'] === 'mail_from_name' && !empty($row['sval'])) $name = $row['sval'];
}
}
} catch (\Throwable $e) { /* Config nicht verfuegbar -> Defaults */ }
return [$email, $name];
}
public static function send(string $to, string $subject, string $htmlBody, string $textBody = ''): bool {
try {
$mail = self::create();
$mail->addAddress($to);
$mail->isHTML(true);
$mail->Subject = $subject;
$mail->Body = self::wrapHtml($htmlBody);
$mail->AltBody = $textBody ?: strip_tags(str_replace(['
','
','
',''], "\n", $htmlBody));
$mail->send();
return true;
} catch (\Exception $e) {
error_log('Mailer error: ' . $e->getMessage());
return false;
}
}
/**
* Versand mit optionalen Datei-Anhaengen (z. B. Lizenz-Codes als .txt/.csv
* bei grossen Chargen). $attachments: [ ['content'=>string,'name'=>string,'type'=>string], ... ]
* Wirft bei Fehler eine Exception (Aufrufer archiviert Status/Fehler selbst).
*/
public static function sendWithAttachments(string $to, string $subject, string $htmlBody, string $textBody = '', array $attachments = []): void {
$mail = self::create();
$mail->addAddress($to);
$mail->isHTML(true);
$mail->Subject = $subject;
$mail->Body = self::wrapHtml($htmlBody);
$mail->AltBody = $textBody ?: strip_tags(str_replace(['
','
','',''], "\n", $htmlBody));
foreach ($attachments as $a) {
if (empty($a['content']) || empty($a['name'])) continue;
$mail->addStringAttachment($a['content'], $a['name'], \PHPMailer\PHPMailer\PHPMailer::ENCODING_BASE64, $a['type'] ?? 'text/plain');
}
$mail->send();
}
/** Freitext-Body (aus Admin-Textbaustein) in die Card-Optik giessen. */
public static function renderPlainBody(string $text): string {
$safe = nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'));
return '' . $safe . '
';
}
// ─── Willkommen (Lehrperson registriert sich) ───
public static function sendWelcomeTeacher(string $email, string $name): bool {
$n = htmlspecialchars($name);
$html = self::greeting($n)
. self::text('Willkommen bei GeoGraSim — interaktive Simulationen für den Geografie-Unterricht!')
. self::text('Ihr Konto wurde erfolgreich erstellt. Sie können jetzt:')
. self::checklist([
'Klassen anlegen und Schüler*innen-Zugänge generieren',
'Module freischalten und den Unterricht steuern',
'Ergebnisse und Fortschritte im Dashboard verfolgen',
])
. self::button('Zum Dashboard', 'https://geograsim.at/teacher.html')
. self::hint('Im Didaktischen Handbuch finden Sie zu jedem Modul einen fertigen 45-Minuten-Unterrichtsentwurf.');
return self::send($email, 'Willkommen bei GeoGraSim', $html);
}
// ─── Zugangsdaten (Schüler*in wurde erstellt) ───
public static function sendStudentCredentials(string $email, string $studentName, string $username, string $password, string $classCode, string $className): bool {
$n = htmlspecialchars($studentName);
$html = self::greeting($n)
. self::text('Du hast einen Zugang zu GeoGraSim erhalten — interaktive Simulationen für den Geografie-Unterricht.')
. self::credentialBox($username, $password, $classCode, $className)
. self::button('Jetzt anmelden', 'https://geograsim.at/login.html')
. self::hint('Bewahre diese Zugangsdaten gut auf. Bei Problemen wende dich an deine Lehrperson.');
return self::send($email, 'Dein GeoGraSim-Zugang', $html);
}
// ─── Passwort-Reset ───
public static function sendPasswordReset(string $email, string $name, string $token): bool {
$link = 'https://geograsim.at/login.html?reset=' . urlencode($token);
$n = htmlspecialchars($name);
$html = self::greeting($n)
. self::text('Sie haben ein neues Passwort für Ihr GeoGraSim-Konto angefordert.')
. self::text('Klicken Sie auf den Button, um ein neues Passwort zu wählen:')
. self::button('Neues Passwort wählen', $link)
. self::hint('Dieser Link ist 1 Stunde gültig und kann nur einmal verwendet werden.')
. self::hint('Falls Sie kein neues Passwort angefordert haben, können Sie diese E-Mail ignorieren. Ihr bestehendes Passwort bleibt unverändert.')
. self::hint('Bei Fragen wenden Sie sich an support@geograsim.at.');
return self::send($email, 'GeoGraSim — Passwort zurücksetzen', $html);
}
// ─── Admin 2FA PIN ───
public static function sendAdminPin(string $email, string $pin): bool {
$html = '🔐 Admin-Anmeldung
'
. 'Bestätige deinen Login
'
. 'Gib den folgenden PIN im Admin-Login ein, um deine Anmeldung abzuschließen:
'
. self::pinBox($pin)
. ''
. 'Falls du keinen Login angefordert hast, kannst du diese E-Mail ignorieren — niemand kommt ohne PIN in dein Admin-Konto.'
. '
';
return self::send($email, 'GeoGraSim — Dein Admin-PIN', $html);
}
// ─── Batch-Zugangsdaten (Lehrperson bekommt Übersicht) ───
public static function sendBatchCredentials(string $teacherEmail, string $teacherName, string $className, array $students): bool {
$n = htmlspecialchars($teacherName);
$html = self::greeting($n)
. self::text('Die folgenden Zugangsdaten wurden für Klasse ' . htmlspecialchars($className) . ' erstellt:')
. self::studentTable($students, $className)
. self::button('Zum Dashboard', 'https://geograsim.at/teacher.html')
. self::hint('Sie können diese Zugangsdaten auch im Dashboard unter „Tickets drucken" als PDF herunterladen.');
return self::send($teacherEmail, 'GeoGraSim — Zugangsdaten für ' . $className, $html);
}
// ═══════════════════════════════════════════════════════
// Bausteine
// ═══════════════════════════════════════════════════════
private static function greeting(string $name): string {
return 'Hallo ' . $name . ',
';
}
private static function text(string $t): string {
return '' . $t . '
';
}
private static function button(string $label, string $url): string {
return ''
. ''
. $label . '
';
}
private static function hint(string $t): string {
return '' . $t . '
';
}
private static function checklist(array $items): string {
$html = '';
foreach ($items as $item) {
$html .= '| ✓ | '
. '' . $item . ' |
';
}
return $html . '
';
}
private static function credentialBox(string $username, string $password, string $classCode, string $className): string {
return ''
. '
'
. self::credRow('Klassencode', $classCode, '#e8833a')
. self::credRow('Klasse', htmlspecialchars($className), '#333')
. self::credRow('Benutzername', $username, '#1f4e5a')
. self::credRow('Passwort', $password, '#1f4e5a')
. '
'
. '
geograsim.at
'
. '
';
}
private static function credRow(string $label, string $value, string $color): string {
return '| ' . $label . ' | '
. '' . htmlspecialchars($value) . ' |
';
}
private static function pinBox(string $pin): string {
$pinSafe = htmlspecialchars($pin);
return '| '
. ' Dein Anmelde-PIN '
. ''
. '' . $pinSafe . ''
. ' '
. '10 Minuten gültig '
. ' |
';
}
private static function studentTable(array $students, string $className): string {
$html = '';
$html .= '| Benutzer | '
. 'Passwort |
';
foreach ($students as $s) {
$html .= '| ' . htmlspecialchars($s['username']) . ' | '
. '' . htmlspecialchars($s['password']) . ' |
';
}
return $html . '
';
}
// ═══════════════════════════════════════════════════════
// HTML-Wrapper (Briefpapier)
// ═══════════════════════════════════════════════════════
private static function wrapHtml(string $content): string {
return <<
GeoGraSim
GeoGraSim — Sandbox für Geographie-Unterricht.
|
GeoGraSim
Sandbox für Geographie
|
|
{$content}
|
|
Diese Nachricht wurde automatisch von geograsim.at versendet.
|
|
HTML;
}
}