M6: Mailversand als nachvollziehbaren Versandjob mit Dry-Run/Log
mailversenden.php hatte keine Zugriffskontrolle, versendete bei jedem GET-Request sofort echte Mails und nutzte PHPMailer, dessen Quelldateien im Repo gar nicht vorhanden waren (nur composer.json/Lizenz) - der Aufruf waere also ohnehin mit Fatal Error abgebrochen. Zusaetzlich waren SMTP- Host, Absender, PayPal-Link und FAQ-URL fest auf einen Alt-Kunden (AOK) codiert. - Ersetzt PHPMailer durch die bestehende saas_send_mail()-Abstraktion aus M3 (Transport log/mail je nach APP_MAIL_TRANSPORT) statt eine fehlende Abhaengigkeit nachzuvendoren. - Neue Tabelle outbound_emails protokolliert jeden Versandversuch: Mandant, Mitglied, Vorlage, Betreff, Status, Fehler - das Versandlog. - Formular hat eine standardmaessig aktive Dry-Run-Checkbox; im Dry-Run wird nur geloggt, saas_send_mail() nicht aufgerufen. - Mailtext ist jetzt tenant-generisch (Saldo, optionaler PayPal-Link nur wenn der Mandant PayPal aktiviert hat, eigener Dashboard-Link) statt hartcodierter Alt-Kunden-Inhalte. - Zugriffskontrolle ergaenzt (owner/admin/treasurer + Legacy-Fallback). - http-smoke.php: mailversenden.php ist jetzt reguel</EOF>
This commit is contained in:
@@ -131,6 +131,85 @@ function saas_send_password_reset_mail(string $to, string $token): array
|
||||
return saas_send_mail($to, 'Kaffeeliste Passwort zurücksetzen', $body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the tenant-generic balance notification body. Replaces the old
|
||||
* mailversenden.php text, which hardcoded one customer's SMTP host,
|
||||
* PayPal link and FAQ URL and therefore only ever worked for that one
|
||||
* tenant.
|
||||
*/
|
||||
function saas_render_balance_mail_body(string $displayName, int $balanceCents, array $tenantSettings, string $dashboardUrl): string
|
||||
{
|
||||
$amount = saas_format_money_cents(abs($balanceCents)) . ' €';
|
||||
$lines = ["Hallo {$displayName},", ''];
|
||||
|
||||
if ($balanceCents < 0) {
|
||||
$lines[] = "dein aktueller Kaffeelisten-Stand ist im Minus: {$amount}.";
|
||||
$lines[] = "Bitte gleiche den Betrag bei Gelegenheit aus.";
|
||||
if ((int)($tenantSettings['paypal_enabled'] ?? 0) === 1 && trim((string)($tenantSettings['paypal_url_template'] ?? '')) !== '') {
|
||||
$paypalLink = trim((string)$tenantSettings['paypal_url_template']) . number_format(abs($balanceCents) / 100, 2, '.', '');
|
||||
$lines[] = '';
|
||||
$lines[] = "Direkt bezahlen: {$paypalLink}";
|
||||
}
|
||||
} else {
|
||||
$lines[] = "dein aktuelles Guthaben in der Kaffeeliste beträgt {$amount}.";
|
||||
}
|
||||
|
||||
$lines[] = '';
|
||||
$lines[] = "Deinen aktuellen Stand findest du hier: {$dashboardUrl}";
|
||||
$lines[] = '';
|
||||
$lines[] = 'Deine Kaffeeliste';
|
||||
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
function saas_log_outbound_email(
|
||||
PDO $pdo,
|
||||
int $tenantId,
|
||||
?int $participantId,
|
||||
string $template,
|
||||
string $subject,
|
||||
string $status,
|
||||
?string $error,
|
||||
?int $createdByUserId
|
||||
): int {
|
||||
$stmt = $pdo->prepare(
|
||||
'INSERT INTO outbound_emails
|
||||
(tenant_id, participant_id, template, subject, status, sent_at, error, created_by_user_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)'
|
||||
);
|
||||
$stmt->execute([
|
||||
$tenantId,
|
||||
$participantId,
|
||||
$template,
|
||||
$subject,
|
||||
$status,
|
||||
in_array($status, ['sent', 'failed'], true) ? date('Y-m-d H:i:s') : null,
|
||||
$error,
|
||||
$createdByUserId,
|
||||
]);
|
||||
|
||||
return (int)$pdo->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{id: int, participant_id: ?int, display_name: ?string, template: string, subject: string, status: string, sent_at: ?string, error: ?string, created_at: string}>
|
||||
*/
|
||||
function saas_fetch_outbound_email_log(PDO $pdo, int $tenantId, int $limit = 50): array
|
||||
{
|
||||
$limit = max(1, min($limit, 200));
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT o.id, o.participant_id, p.display_name, o.template, o.subject, o.status, o.sent_at, o.error, o.created_at
|
||||
FROM outbound_emails o
|
||||
LEFT JOIN participants p ON p.id = o.participant_id
|
||||
WHERE o.tenant_id = ?
|
||||
ORDER BY o.created_at DESC, o.id DESC
|
||||
LIMIT {$limit}"
|
||||
);
|
||||
$stmt->execute([$tenantId]);
|
||||
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
function saas_send_invite_mail(string $to, string $tenantName, string $role, string $token): array
|
||||
{
|
||||
$link = saas_app_url('passwort-zuruecksetzen.php?token=' . urlencode($token));
|
||||
|
||||
Reference in New Issue
Block a user