prepare intranet for first Dokploy deployment
This commit is contained in:
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
/* Name: send_mail.php
|
||||
Beschreibung: Zentrale Mail-Funktion für das Intranet
|
||||
Sendet direkt über SMTP (smtp.strato.de:587 mit STARTTLS)
|
||||
HTML-Template für moderne Darstellung
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/runtime_env.php';
|
||||
|
||||
define('SMTP_HOST', intranet_env('SMTP_HOST', 'smtp.strato.de'));
|
||||
define('SMTP_PORT', (int) intranet_env('SMTP_PORT', '587'));
|
||||
define('SMTP_USER', intranet_env('SMTP_USER'));
|
||||
define('SMTP_PASS', intranet_env('SMTP_PASSWORD'));
|
||||
define('ADMIN_EMAIL', intranet_env('ADMIN_EMAIL', 'technik@nak-reinickendorf.de'));
|
||||
|
||||
/**
|
||||
* Erzeugt das HTML-Template für E-Mails im NAK-Design.
|
||||
*/
|
||||
function build_html_mail(string $betreff, string $inhalt, ?string $buttonText = null, ?string $buttonUrl = null): string {
|
||||
$buttonHtml = '';
|
||||
if ($buttonText && $buttonUrl) {
|
||||
$buttonHtml = '
|
||||
<tr><td style="padding:24px 0 8px 0;" align="center">
|
||||
<a href="' . htmlspecialchars($buttonUrl) . '" style="display:inline-block;padding:14px 32px;background:#2786cf;color:#ffffff;text-decoration:none;font-weight:700;font-size:15px;letter-spacing:0.02em;">
|
||||
' . htmlspecialchars($buttonText) . '
|
||||
</a>
|
||||
</td></tr>';
|
||||
}
|
||||
|
||||
// Inhalt: Zeilenumbrüche in <br> umwandeln, aber vorhandenes HTML beibehalten
|
||||
$inhaltHtml = nl2br(htmlspecialchars($inhalt));
|
||||
|
||||
$datum = date('d.m.Y, H:i') . ' Uhr';
|
||||
|
||||
// Logo-URL dynamisch ermitteln
|
||||
$host = $_SERVER['HTTP_HOST'] ?? intranet_env('INTRANET_FQDN');
|
||||
$logoUrl = 'https://' . $host . '/intranet_repo/projects/_central/img/nak-logo.svg';
|
||||
|
||||
return '<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"></head>
|
||||
<body style="margin:0;padding:0;background-color:#f0f2f5;font-family:-apple-system,BlinkMacSystemFont,\'Segoe UI\',Roboto,Helvetica,Arial,sans-serif;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#f0f2f5;padding:32px 16px;">
|
||||
<tr><td align="center">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="max-width:560px;overflow:hidden;">
|
||||
<!-- Header with Logo + Wordmark -->
|
||||
<tr><td style="background:#ffffff;padding:28px 32px;border-bottom:3px solid #2786cf;" align="left">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0"><tr>
|
||||
<td style="vertical-align:bottom;padding-right:14px;">
|
||||
<img src="' . htmlspecialchars($logoUrl) . '" alt="NAK" width="48" height="48" style="display:block;width:48px;height:48px;">
|
||||
</td>
|
||||
<td style="vertical-align:bottom;line-height:1.2;">
|
||||
<div style="font-size:15px;font-weight:500;color:#1a1a1a;white-space:nowrap;">Neuapostolische Kirche</div>
|
||||
<div style="font-size:12px;font-weight:300;color:#6b7280;white-space:nowrap;">Gemeinde Reinickendorf</div>
|
||||
</td>
|
||||
</tr></table>
|
||||
</td></tr>
|
||||
<!-- Blue title bar -->
|
||||
<tr><td style="background:linear-gradient(135deg,#2786cf,#1e6fad);padding:20px 32px;">
|
||||
<div style="font-size:20px;font-weight:700;color:#ffffff;letter-spacing:-0.01em;">' . htmlspecialchars($betreff) . '</div>
|
||||
</td></tr>
|
||||
<!-- Content -->
|
||||
<tr><td style="background:#ffffff;padding:28px 32px 12px 32px;">
|
||||
<div style="font-size:15px;line-height:1.7;color:#374151;">' . $inhaltHtml . '</div>
|
||||
</td></tr>
|
||||
' . $buttonHtml . '
|
||||
<!-- Footer -->
|
||||
<tr><td style="background:#ffffff;padding:20px 32px 24px 32px;border-top:1px solid #e5e7eb;">
|
||||
<div style="font-size:12px;color:#9ca3af;text-align:center;line-height:1.5;">
|
||||
Gesendet am ' . $datum . '<br>
|
||||
Intranet NAK Reinickendorf · Automatische Benachrichtigung
|
||||
</div>
|
||||
</td></tr>
|
||||
</table>
|
||||
</td></tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sendet eine HTML-E-Mail über SMTP.
|
||||
*
|
||||
* @param string $empfaenger Empfänger
|
||||
* @param string $betreff Betreff
|
||||
* @param string $nachricht Klartext-Nachricht (wird ins HTML-Template eingebettet)
|
||||
* @param string|null $cc CC-Adresse
|
||||
* @param string $absender Absender
|
||||
* @param string|null $buttonText Optionaler Button-Text
|
||||
* @param string|null $buttonUrl Optionale Button-URL
|
||||
* @return bool
|
||||
*/
|
||||
function send_intranet_mail(
|
||||
string $empfaenger,
|
||||
string $betreff,
|
||||
string $nachricht,
|
||||
?string $cc = null,
|
||||
string $absender = "intranet@nak-reinickendorf.de",
|
||||
?string $buttonText = null,
|
||||
?string $buttonUrl = null
|
||||
): bool {
|
||||
$htmlBody = build_html_mail($betreff, $nachricht, $buttonText, $buttonUrl);
|
||||
|
||||
// MIME-Header
|
||||
$boundary = 'boundary_' . bin2hex(random_bytes(16));
|
||||
$msgId = '<' . bin2hex(random_bytes(16)) . '@nak-reinickendorf.de>';
|
||||
$headers = "From: Intranet NAK Reinickendorf <{$absender}>\r\n";
|
||||
$headers .= "Reply-To: {$absender}\r\n";
|
||||
$headers .= "To: {$empfaenger}\r\n";
|
||||
if (!empty($cc)) {
|
||||
$headers .= "Cc: {$cc}\r\n";
|
||||
}
|
||||
$headers .= "Subject: =?UTF-8?B?" . base64_encode($betreff) . "?=\r\n";
|
||||
$headers .= "Message-ID: {$msgId}\r\n";
|
||||
$headers .= "MIME-Version: 1.0\r\n";
|
||||
$headers .= "Content-Type: multipart/alternative; boundary=\"{$boundary}\"\r\n";
|
||||
$headers .= "X-Mailer: Intranet-NAK-Reinickendorf/1.0\r\n";
|
||||
$headers .= "Date: " . date('r') . "\r\n";
|
||||
|
||||
// Plaintext-Fallback
|
||||
$plainBody = strip_tags($nachricht);
|
||||
|
||||
// Multipart Body
|
||||
$body = "--{$boundary}\r\n";
|
||||
$body .= "Content-Type: text/plain; charset=utf-8\r\n";
|
||||
$body .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
|
||||
$body .= $plainBody . "\r\n\r\n";
|
||||
$body .= "--{$boundary}\r\n";
|
||||
$body .= "Content-Type: text/html; charset=utf-8\r\n";
|
||||
$body .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
|
||||
$body .= $htmlBody . "\r\n\r\n";
|
||||
$body .= "--{$boundary}--\r\n";
|
||||
|
||||
$message = $headers . "\r\n" . $body;
|
||||
|
||||
$recipients = [$empfaenger];
|
||||
if (!empty($cc)) {
|
||||
$recipients[] = $cc;
|
||||
}
|
||||
|
||||
return smtp_send(SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, $absender, $recipients, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Direkter SMTP-Versand mit STARTTLS und AUTH LOGIN.
|
||||
*/
|
||||
function smtp_send(string $host, int $port, string $user, string $pass, string $from, array $to, string $data): bool {
|
||||
$errno = 0;
|
||||
$errstr = '';
|
||||
$socket = @stream_socket_client("tcp://{$host}:{$port}", $errno, $errstr, 30);
|
||||
if (!$socket) {
|
||||
error_log("SMTP connect failed: {$errstr} ({$errno})");
|
||||
return false;
|
||||
}
|
||||
stream_set_timeout($socket, 30);
|
||||
|
||||
$response = smtp_read($socket);
|
||||
if (substr($response, 0, 3) !== '220') { fclose($socket); return false; }
|
||||
|
||||
smtp_write($socket, "EHLO " . gethostname());
|
||||
$response = smtp_read($socket);
|
||||
if (substr($response, 0, 3) !== '250') { fclose($socket); return false; }
|
||||
|
||||
smtp_write($socket, "STARTTLS");
|
||||
$response = smtp_read($socket);
|
||||
if (substr($response, 0, 3) !== '220') { fclose($socket); return false; }
|
||||
|
||||
$crypto = stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT);
|
||||
if (!$crypto) {
|
||||
error_log("SMTP STARTTLS failed");
|
||||
fclose($socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
smtp_write($socket, "EHLO " . gethostname());
|
||||
$response = smtp_read($socket);
|
||||
if (substr($response, 0, 3) !== '250') { fclose($socket); return false; }
|
||||
|
||||
smtp_write($socket, "AUTH LOGIN");
|
||||
$response = smtp_read($socket);
|
||||
if (substr($response, 0, 3) !== '334') { fclose($socket); return false; }
|
||||
|
||||
smtp_write($socket, base64_encode($user));
|
||||
$response = smtp_read($socket);
|
||||
if (substr($response, 0, 3) !== '334') { fclose($socket); return false; }
|
||||
|
||||
smtp_write($socket, base64_encode($pass));
|
||||
$response = smtp_read($socket);
|
||||
if (substr($response, 0, 3) !== '235') {
|
||||
error_log("SMTP auth failed: {$response}");
|
||||
fclose($socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
smtp_write($socket, "MAIL FROM:<{$from}>");
|
||||
$response = smtp_read($socket);
|
||||
if (substr($response, 0, 3) !== '250') { fclose($socket); return false; }
|
||||
|
||||
foreach ($to as $recipient) {
|
||||
smtp_write($socket, "RCPT TO:<{$recipient}>");
|
||||
$response = smtp_read($socket);
|
||||
if (substr($response, 0, 3) !== '250') { fclose($socket); return false; }
|
||||
}
|
||||
|
||||
smtp_write($socket, "DATA");
|
||||
$response = smtp_read($socket);
|
||||
if (substr($response, 0, 3) !== '354') { fclose($socket); return false; }
|
||||
|
||||
$data = str_replace("\r\n.", "\r\n..", $data);
|
||||
fwrite($socket, $data . "\r\n.\r\n");
|
||||
$response = smtp_read($socket);
|
||||
if (substr($response, 0, 3) !== '250') { fclose($socket); return false; }
|
||||
|
||||
smtp_write($socket, "QUIT");
|
||||
smtp_read($socket);
|
||||
fclose($socket);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function smtp_write($socket, string $cmd): void {
|
||||
fwrite($socket, $cmd . "\r\n");
|
||||
}
|
||||
|
||||
function smtp_read($socket): string {
|
||||
$response = '';
|
||||
while ($line = fgets($socket, 512)) {
|
||||
$response .= $line;
|
||||
if (isset($line[3]) && $line[3] === ' ') break;
|
||||
if (strlen($line) < 4) break;
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ermittelt alle E-Mail-Adressen von Nutzern, die BEIDE angegebenen Rollen besitzen.
|
||||
* Benötigt eine bestehende $pdo-Verbindung.
|
||||
*
|
||||
* @param PDO $pdo Datenbankverbindung
|
||||
* @param array $roles Array von Rollennamen, die alle erfüllt sein müssen
|
||||
* @return array Liste der E-Mail-Adressen
|
||||
*/
|
||||
function get_emails_by_roles(PDO $pdo, array $roles): array {
|
||||
if (empty($roles)) return [];
|
||||
|
||||
$conditions = [];
|
||||
foreach ($roles as $role) {
|
||||
$conditions[] = '"' . str_replace('"', '""', $role) . '" = 1';
|
||||
}
|
||||
$where = implode(' AND ', $conditions);
|
||||
|
||||
$sql = "SELECT ugi.mail FROM user_permissions up
|
||||
INNER JOIN user_general_information ugi ON up.user_id = ugi.user_id
|
||||
WHERE {$where} AND ugi.mail IS NOT NULL AND ugi.mail != ''";
|
||||
$result = $pdo->query($sql);
|
||||
$emails = [];
|
||||
if ($result) {
|
||||
while ($row = $result->fetch()) {
|
||||
$emails[] = $row['mail'];
|
||||
}
|
||||
}
|
||||
return $emails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sendet eine Mail an alle Nutzer mit den Rollen Admin UND Technik (Sammelmail).
|
||||
*
|
||||
* @param PDO $pdo Datenbankverbindung
|
||||
* @param string $betreff Betreff
|
||||
* @param string $nachricht Nachricht
|
||||
* @param string|null $buttonText Optionaler Button-Text
|
||||
* @param string|null $buttonUrl Optionale Button-URL
|
||||
* @return bool True wenn mindestens eine Mail gesendet wurde
|
||||
*/
|
||||
function send_admin_team_mail(
|
||||
PDO $pdo,
|
||||
string $betreff,
|
||||
string $nachricht,
|
||||
?string $buttonText = null,
|
||||
?string $buttonUrl = null
|
||||
): bool {
|
||||
$emails = get_emails_by_roles($pdo, ['Admin', 'Technik']);
|
||||
if (empty($emails)) {
|
||||
// Fallback auf ADMIN_EMAIL
|
||||
return send_intranet_mail(ADMIN_EMAIL, $betreff, $nachricht, null, "intranet@nak-reinickendorf.de", $buttonText, $buttonUrl);
|
||||
}
|
||||
$success = false;
|
||||
foreach ($emails as $email) {
|
||||
if (send_intranet_mail($email, $betreff, $nachricht, null, "intranet@nak-reinickendorf.de", $buttonText, $buttonUrl)) {
|
||||
$success = true;
|
||||
}
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sendet eine Mail an alle Nutzer mit der Rolle Brandschutzbeauftragter.
|
||||
*
|
||||
* @param PDO $pdo Datenbankverbindung
|
||||
* @param string $betreff Betreff
|
||||
* @param string $nachricht Nachricht
|
||||
* @param string|null $buttonText Optionaler Button-Text
|
||||
* @param string|null $buttonUrl Optionale Button-URL
|
||||
* @return bool True wenn mindestens eine Mail gesendet wurde
|
||||
*/
|
||||
function send_brandschutzbeauftragter_mail(
|
||||
PDO $pdo,
|
||||
string $betreff,
|
||||
string $nachricht,
|
||||
?string $buttonText = null,
|
||||
?string $buttonUrl = null
|
||||
): bool {
|
||||
$emails = get_emails_by_roles($pdo, ['Brandschutzbeauftragter']);
|
||||
if (empty($emails)) return false;
|
||||
$success = false;
|
||||
foreach ($emails as $email) {
|
||||
if (send_intranet_mail($email, $betreff, $nachricht, null, "intranet@nak-reinickendorf.de", $buttonText, $buttonUrl)) {
|
||||
$success = true;
|
||||
}
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user