Compare commits

...

21 Commits

Author SHA1 Message Date
smueller
a441c4b8af Update newsletter HTML in index.php to improve language and tone. Changed greeting to a more casual "Servus" and adjusted article summary text for clarity. Enhanced closing remarks to foster a friendly connection with recipients. 2026-07-07 12:48:02 +02:00
smueller
dc44e38e32 Refactor saveJsonFile function in index.php and wiki.php to include JSON_INVALID_UTF8_SUBSTITUTE flag for better handling of invalid UTF-8 characters. Enhanced error messages for file saving failures to include the file name, improving debugging and user guidance. 2026-07-07 12:38:56 +02:00
smueller
24f16e454e Enhance error handling in saveJsonFile function in index.php. Added checks for JSON encoding and file writing, throwing exceptions with descriptive messages for better debugging and user guidance. 2026-07-07 12:35:36 +02:00
smueller
251575c8b4 Fix German language typos in newsletter HTML within index.php, including corrections to "für," "Übersicht," "Beiträge," and "Grüsse." Update hint text for highlight URLs to clarify behavior when fields are empty, enhancing user understanding. 2026-07-07 12:31:07 +02:00
smueller
ede523b02c Add editor tip and highlight URLs to newsletter HTML in index.php. Implemented logic to display manual highlights and an optional editor tip section, enhancing content customization and visibility for recipients. 2026-07-07 12:27:14 +02:00
smueller
80bc0bb728 Add top highlights section to newsletter HTML in index.php, enhancing content visibility. Updated hero headline and adjusted article layout for improved readability and aesthetics. 2026-07-07 12:22:08 +02:00
smueller
3df01289a5 Update newsletter HTML styles in index.php by modifying color schemes for text and backgrounds, enhancing visual appeal and consistency. Adjusted padding and font colors to improve readability and overall aesthetics. 2026-07-07 11:28:37 +02:00
smueller
f840b68c9c Update newsletter HTML styles in index.php by refining background colors, font colors, and padding for improved visual consistency and readability. Adjusted table and text styles to enhance overall aesthetics. 2026-07-07 11:26:11 +02:00
smueller
9950aa25fd Refine newsletter HTML layout in index.php by updating padding, font sizes, and background colors for improved aesthetics and readability. Adjusted article count message for grammatical accuracy in German. 2026-07-07 11:21:51 +02:00
smueller
b536712ea7 Refactor test mode article fetching logic in index.php and wiki.php. Unified article filtering for both test and live modes, enhancing code clarity and reducing redundancy. 2026-07-07 11:11:10 +02:00
smueller
ec13c9680d Remove redundant date range calculation logic from buildNewsletterHtml function in index.php and wiki.php. This cleanup enhances code maintainability by eliminating unnecessary complexity. 2026-07-07 11:06:10 +02:00
smueller
d1e3ccc00c Update date range handling in newsletter HTML for index.php and wiki.php. Adjusted end date calculation for current month and refined date range display logic to improve clarity and accuracy in article summaries. 2026-07-07 11:03:56 +02:00
smueller
91b59f7bc0 Refactor date range calculation in newsletter HTML for index.php and wiki.php. Replaced manual timestamp handling with a streamlined approach using newsletterPeriod function, enhancing code clarity and maintainability. 2026-07-07 10:59:07 +02:00
smueller
69ba3f1595 Enhance newsletter HTML in index.php and wiki.php by updating article count messages to include date ranges. Added logic to calculate and display the date range of articles, improving clarity for recipients. 2026-07-07 10:56:59 +02:00
smueller
3dc07b9fb2 Add logo image to newsletter HTML in index.php for enhanced branding. Adjusted layout for improved presentation. 2026-07-07 10:52:56 +02:00
smueller
65ea9f6e30 Improve article fetching logic in index.php by adding a check for DOMElement instances. Clean up newsletter HTML by removing outdated text and logo image for a more streamlined presentation. 2026-07-07 10:47:11 +02:00
smueller
ecd7d2b341 Add flash messaging for newsletter actions in index.php and wiki.php. Implemented session handling for success and error messages, and added redirection after form submissions to enhance user experience. 2026-07-07 10:44:07 +02:00
smueller
7e3c186419 Remove logo SVG file and clean up newsletter HTML in wiki.php by eliminating outdated text. This update streamlines the email layout for better presentation. 2026-07-07 10:39:58 +02:00
smueller
1c4baed125 Update newsletter HTML layout in index.php and wiki.php. Replaced text with logo image, adjusted padding, and modified font sizes for improved readability and aesthetics. 2026-07-07 10:38:28 +02:00
smueller
b62ef7a1d7 Merge branch 'master' of ssh://git.hexahost.dev:8006/smueller/TK-Wiki-Newsletter-azillner 2026-07-07 10:35:11 +02:00
smueller
b71cf73f1e Remove outdated newsletter message file and update HTML layout in index.php and wiki.php. Adjusted padding, colors, and font sizes for improved readability, and added test badge visibility. Updated background color for better contrast. 2026-07-07 10:35:09 +02:00
3 changed files with 335 additions and 149 deletions

1
.gitignore vendored
View File

@@ -1 +0,0 @@
WG Neue Server spannendes Know-how Der Thomas-Krenn-Newsletter im Mai.msg

313
index.php
View File

@@ -44,6 +44,10 @@ $defaultConfig = [
'subject' => 'Wiki Newsletter - {MONAT}', 'subject' => 'Wiki Newsletter - {MONAT}',
'firstname' => 'zusammen', 'firstname' => 'zusammen',
'editor_tip' => '',
'highlight_url_1' => '',
'highlight_url_2' => '',
'highlight_url_3' => '',
'reply_to' => '', 'reply_to' => '',
'unsubscribe_url' => '#', 'unsubscribe_url' => '#',
'cron_token' => bin2hex(random_bytes(24)), 'cron_token' => bin2hex(random_bytes(24)),
@@ -72,11 +76,18 @@ function loadJsonFile(string $file, array $default): array
function saveJsonFile(string $file, array $data): void function saveJsonFile(string $file, array $data): void
{ {
file_put_contents( $json = json_encode(
$file, $data,
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE
LOCK_EX
); );
if ($json === false) {
throw new RuntimeException('Konfiguration konnte nicht kodiert werden.');
}
$written = @file_put_contents($file, $json, LOCK_EX);
if ($written === false) {
throw new RuntimeException('Konfiguration konnte nicht gespeichert werden: ' . $file . ' (Dateirechte prüfen).');
}
} }
function isAdminLoggedIn(): bool function isAdminLoggedIn(): bool
@@ -170,6 +181,10 @@ function fetchLatestArticles(string $wikiUrl, int $limit = 150): array
$nodes = $xpath->query('//li'); $nodes = $xpath->query('//li');
foreach ($nodes as $li) { foreach ($nodes as $li) {
if (!$li instanceof DOMElement) {
continue;
}
$text = trim(preg_replace('/\s+/', ' ', $li->textContent ?? '')); $text = trim(preg_replace('/\s+/', ' ', $li->textContent ?? ''));
if (!preg_match('/\((\d{2}\.\d{2}\.\d{4})\)/', $text, $dateMatch)) { if (!preg_match('/\((\d{2}\.\d{2}\.\d{4})\)/', $text, $dateMatch)) {
@@ -281,7 +296,7 @@ function newsletterPeriod(string $mode): array
if ($mode === 'current_month') { if ($mode === 'current_month') {
$start = $now->modify('first day of this month')->setTime(0, 0, 0); $start = $now->modify('first day of this month')->setTime(0, 0, 0);
$end = $now->modify('first day of next month')->setTime(0, 0, 0); $end = $now->modify('tomorrow')->setTime(0, 0, 0);
} else { } else {
$start = $now->modify('first day of previous month')->setTime(0, 0, 0); $start = $now->modify('first day of previous month')->setTime(0, 0, 0);
$end = $now->modify('first day of this month')->setTime(0, 0, 0); $end = $now->modify('first day of this month')->setTime(0, 0, 0);
@@ -339,33 +354,82 @@ function buildNewsletterHtml(array $config, array $articles, string $periodLabel
$replyEmail = trim((string)($config['reply_to'] ?: $config['from_email'])); $replyEmail = trim((string)($config['reply_to'] ?: $config['from_email']));
$replyHref = 'mailto:' . $replyEmail; $replyHref = 'mailto:' . $replyEmail;
$summaryText = $count === 1 $summaryText = $count === 1
? 'Im ' . h($periodLabel) . ' wurde 1 neuer Wiki-Artikel veröffentlicht.' ? 'Es wurde ein neuer Wiki-Artikel veröffentlicht.'
: 'Im ' . h($periodLabel) . ' wurden ' . $count . ' neue Wiki-Artikel veröffentlicht.'; : 'Es wurden ' . $count . ' neue Wiki-Artikel veröffentlicht.';
$heroHeadline = 'Die neuesten Wiki-Artikel';
$editorTip = trim((string)($config['editor_tip'] ?? ''));
$testBadge = $isTest $manualHighlightUrls = [];
? '<tr> foreach (['highlight_url_1', 'highlight_url_2', 'highlight_url_3'] as $highlightField) {
<td style="padding:0 24px 12px 24px;" class="mobile-padding"> $url = trim((string)($config[$highlightField] ?? ''));
<table role="presentation" border="0" cellspacing="0" cellpadding="0"> if ($url !== '') {
<tr> $manualHighlightUrls[] = $url;
<td bgcolor="#fff3cd" style="background-color:#fff3cd;border:1px solid #f4d58a;padding:6px 10px;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:15px;font-weight:700;color:#7a5200;"> }
TESTVERSAND }
</td>
</tr> $highlightArticles = [];
</table> if (!empty($manualHighlightUrls)) {
</td> foreach ($manualHighlightUrls as $manualUrl) {
</tr>' foreach ($articles as $article) {
: ''; if (($article['url'] ?? '') === $manualUrl) {
$highlightArticles[] = $article;
break;
}
}
}
}
[$periodStart, $periodEnd] = newsletterPeriod((string)$config['period_mode']);
$rangeEnd = $periodEnd;
if (($config['period_mode'] ?? '') === 'current_month') {
$rangeEnd = new DateTimeImmutable('today');
}
$dateRangeText = 'vom ' . $periodStart->format('d.m') . ' bis ' . $rangeEnd->format('d.m');
$highlightsRows = '';
if (!empty($highlightArticles)) {
$maxHighlights = min(3, count($highlightArticles));
for ($i = 0; $i < $maxHighlights; $i++) {
$highlightsRows .= '
<tr>
<td style="padding:0 0 8px 0;font-family:Arial,Helvetica,sans-serif;font-size:15px;line-height:22px;color:#2f3237;">
<span style="display:inline-block;min-width:22px;font-weight:700;color:#ff7d00;">' . ($i + 1) . '.</span>
<a href="' . h($highlightArticles[$i]['url']) . '" target="_blank" style="color:#2f3237;text-decoration:none;">' . h($highlightArticles[$i]['title']) . '</a>
</td>
</tr>';
}
}
$highlightsSection = '';
if ($highlightsRows !== '') {
$highlightsSection = '
<tr>
<td style="padding:0 32px 20px 32px;" class="mobile-padding">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;background:#fff7f0;border:1px solid #ffd6b0;border-radius:12px;">
<tr>
<td style="padding:16px 18px 8px 18px;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:15px;color:#8e4e12;font-weight:700;letter-spacing:.8px;text-transform:uppercase;">
Top Highlights
</td>
</tr>
<tr>
<td style="padding:0 18px 12px 18px;">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0">' . $highlightsRows . '</table>
</td>
</tr>
</table>
</td>
</tr>';
}
$articleRows = ''; $articleRows = '';
if ($count === 0) { if ($count === 0) {
$articleRows = ' $articleRows = '
<tr> <tr>
<td style="padding:0 24px 18px 24px;" class="mobile-padding"> <td style="padding:0 32px 24px 32px;" class="mobile-padding">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;border:1px solid #d7d7d7;background:#ffffff;"> <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;border:1px solid #d8e0ea;background:#ffffff;border-radius:14px;">
<tr> <tr>
<td style="padding:18px;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;color:#333333;"> <td style="padding:22px;font-family:Arial,Helvetica,sans-serif;font-size:15px;line-height:23px;color:#2e3b4d;">
Für diesen Zeitraum wurden keine neuen Artikel gefunden. Für diesen Zeitraum wurden keine neuen Artikel gefunden.
</td> </td>
</tr> </tr>
@@ -376,29 +440,34 @@ function buildNewsletterHtml(array $config, array $articles, string $periodLabel
foreach ($articles as $article) { foreach ($articles as $article) {
$articleRows .= ' $articleRows .= '
<tr> <tr>
<td style="padding:0 24px 18px 24px;" class="mobile-padding"> <td style="padding:0 32px 16px 32px;" class="mobile-padding">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;background:#ffffff;"> <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;background:#ffffff;border:1px solid #d7dee8;border-radius:14px;">
<tr> <tr>
<td style="padding:0 0 5px 0;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:17px;color:#595959;"> <td style="padding:18px 22px 4px 22px;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;color:#5f6d82;font-weight:700;letter-spacing:.7px;text-transform:uppercase;">
Thomas-Krenn-Wiki Wiki-Beitrag
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding:0 0 5px 0;font-family:Arial,Helvetica,sans-serif;font-size:22px;line-height:28px;font-weight:700;color:#ef7d00;"> <td class="mobile-card-title" style="padding:0 22px 6px 22px;font-family:Arial,Helvetica,sans-serif;font-size:24px;line-height:32px;font-weight:700;color:#2f3237;">
<a href="' . h($article['url']) . '" target="_blank" style="color:#ef7d00;text-decoration:none;">' . h($article['title']) . '</a> <a href="' . h($article['url']) . '" target="_blank" style="color:#2f3237;text-decoration:none;">' . h($article['title']) . '</a>
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding:0 0 10px 0;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:22px;color:#2e2e2e;"> <td style="padding:0 22px 8px 22px;font-family:Arial,Helvetica,sans-serif;font-size:15px;line-height:24px;color:#40444b;">
Kompakte technische Einordnung und direkte Handlungsempfehlungen aus dem Thomas-Krenn-Wiki.
</td>
</tr>
<tr>
<td style="padding:0 22px 16px 22px;font-family:Arial,Helvetica,sans-serif;font-size:13px;line-height:20px;color:#667086;">
Veröffentlicht am ' . h($article['date_de']) . ' von ' . h($article['author']) . '. Veröffentlicht am ' . h($article['date_de']) . ' von ' . h($article['author']) . '.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding:0;"> <td style="padding:0 22px 20px 22px;">
<table role="presentation" border="0" cellspacing="0" cellpadding="0"> <table role="presentation" border="0" cellspacing="0" cellpadding="0">
<tr> <tr>
<td bgcolor="#ef7d00" style="background:#ef7d00;"> <td bgcolor="#ff7d00" style="background:#ff7d00;">
<a href="' . h($article['url']) . '" target="_blank" style="display:inline-block;padding:8px 14px;font-family:Arial,Helvetica,sans-serif;font-size:13px;line-height:16px;color:#ffffff;text-decoration:none;">Jetzt Artikel lesen</a> <a href="' . h($article['url']) . '" target="_blank" style="display:inline-block;padding:11px 18px;font-family:Arial,Helvetica,sans-serif;font-size:13px;line-height:16px;font-weight:700;color:#ffffff;text-decoration:none;">Zum Beitrag</a>
</td> </td>
</tr> </tr>
</table> </table>
@@ -410,10 +479,45 @@ function buildNewsletterHtml(array $config, array $articles, string $periodLabel
} }
} }
$testBadge = $isTest
? '<tr>
<td style="padding:0 32px 14px 32px;" class="mobile-padding">
<table role="presentation" border="0" cellspacing="0" cellpadding="0">
<tr>
<td bgcolor="#fff4e6" style="background:#fff4e6;border:1px solid #ffd2a3;padding:8px 12px;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:14px;font-weight:700;color:#a34f00;">
TESTVERSAND
</td>
</tr>
</table>
</td>
</tr>'
: '';
$unsubscribeFooter = ''; $unsubscribeFooter = '';
$unsubscribeUrl = trim((string)($config['unsubscribe_url'] ?? '')); $unsubscribeUrl = trim((string)($config['unsubscribe_url'] ?? ''));
if ($unsubscribeUrl !== '' && $unsubscribeUrl !== '#' && filter_var($unsubscribeUrl, FILTER_VALIDATE_URL)) { if ($unsubscribeUrl !== '' && $unsubscribeUrl !== '#' && filter_var($unsubscribeUrl, FILTER_VALIDATE_URL)) {
$unsubscribeFooter = '<br><a href="' . h($unsubscribeUrl) . '" target="_blank" style="color:#ef7d00;text-decoration:underline;">Abmelden</a>'; $unsubscribeFooter = '<br><a href="' . h($unsubscribeUrl) . '" target="_blank" style="color:#ff7d00;text-decoration:underline;">Abmelden</a>';
}
$editorTipSection = '';
if ($editorTip !== '') {
$editorTipSection = '
<tr>
<td style="padding:8px 32px 24px 32px;" class="mobile-padding">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;background:#f7f8fa;border:1px solid #e1e5eb;border-radius:12px;">
<tr>
<td style="padding:16px 18px 8px 18px;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:15px;color:#5f6064;font-weight:700;letter-spacing:.8px;text-transform:uppercase;">
Redaktionstipp
</td>
</tr>
<tr>
<td style="padding:0 18px 16px 18px;font-family:Arial,Helvetica,sans-serif;font-size:15px;line-height:24px;color:#32363d;">
' . nl2br(h($editorTip)) . '
</td>
</tr>
</table>
</td>
</tr>';
} }
return '<!doctype html> return '<!doctype html>
@@ -424,83 +528,84 @@ function buildNewsletterHtml(array $config, array $articles, string $periodLabel
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Thomas-Krenn Wiki Newsletter</title> <title>Thomas-Krenn Wiki Newsletter</title>
<style type="text/css"> <style type="text/css">
html, body { margin:0 !important; padding:0 !important; width:100% !important; background:#e6e6e6; } html, body { margin:0 !important; padding:0 !important; width:100% !important; background:#eceef1; }
table, td { border-collapse:collapse; mso-table-lspace:0pt; mso-table-rspace:0pt; } table, td { border-collapse:collapse; mso-table-lspace:0pt; mso-table-rspace:0pt; }
img { border:0; outline:none; text-decoration:none; -ms-interpolation-mode:bicubic; display:block; } img { border:0; outline:none; text-decoration:none; -ms-interpolation-mode:bicubic; display:block; }
a { text-decoration:none; } a { text-decoration:none; }
* { -ms-text-size-adjust:100%; -webkit-text-size-adjust:100%; } * { -ms-text-size-adjust:100%; -webkit-text-size-adjust:100%; }
.preheader { display:none !important; visibility:hidden; opacity:0; color:transparent; height:0; width:0; max-height:0; max-width:0; overflow:hidden; mso-hide:all; } .preheader { display:none !important; visibility:hidden; opacity:0; color:transparent; height:0; width:0; max-height:0; max-width:0; overflow:hidden; mso-hide:all; }
@media only screen and (max-width: 640px) { @media only screen and (max-width: 680px) {
.email-container { width:100% !important; } .email-container { width:100% !important; }
.mobile-padding { padding-left:14px !important; padding-right:14px !important; } .mobile-padding { padding-left:16px !important; padding-right:16px !important; }
.mobile-body { font-size:15px !important; line-height:23px !important; } .mobile-intro { font-size:17px !important; line-height:27px !important; }
.mobile-title { font-size:19px !important; line-height:25px !important; } .mobile-headline { font-size:30px !important; line-height:37px !important; }
.mobile-card-title { font-size:22px !important; line-height:29px !important; }
.mobile-hero-copy { font-size:17px !important; line-height:26px !important; }
} }
</style> </style>
</head> </head>
<body style="margin:0;padding:0;background:#e6e6e6;"> <body style="margin:0;padding:0;background:#eceef1;">
<div class="preheader">' . h($preheader) . '&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;</div> <div class="preheader">' . h($preheader) . '&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;</div>
<center role="article" aria-roledescription="email" lang="de" style="width:100%;background:#e6e6e6;"> <center role="article" aria-roledescription="email" lang="de" style="width:100%;background:#eceef1;">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#e6e6e6" style="width:100%;background:#e6e6e6;"> <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#eceef1" style="width:100%;background:#eceef1;">
<tr> <tr>
<td align="center" style="padding:6px 8px;"> <td align="center" style="padding:22px 10px;">
<table role="presentation" width="620" border="0" cellspacing="0" cellpadding="0" class="email-container" style="width:100%;max-width:620px;background:#ffffff;"> <table role="presentation" width="700" border="0" cellspacing="0" cellpadding="0" class="email-container" style="width:100%;max-width:700px;background:#ffffff;border-top:4px solid #ff7d00;">
<tr> <tr>
<td align="center" style="padding:8px 20px 6px 20px;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:15px;color:#4e4e4e;"> <td align="center" style="padding:24px 32px 18px 32px;background:#5f6064;" class="mobile-padding">
Sollte diese E-Mail nicht einwandfrei dargestellt werden, klicken Sie bitte hier. <img src="https://www.thomas-krenn.com/res/pics/tk_logo_340px.png" width="190" alt="Thomas-Krenn Logo" style="display:block;margin:0 auto;border:0;outline:none;text-decoration:none;height:auto;">
</td> </td>
</tr> </tr>
<tr> <tr>
<td align="center" style="padding:4px 20px 10px 20px;font-family:Arial,Helvetica,sans-serif;font-size:30px;line-height:32px;font-weight:700;color:#555555;"> <td style="padding:0 32px;" class="mobile-padding">
THOMAS<br>KRENN <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;background:#5f6064;">
</td>
</tr>
<tr>
<td style="padding:0 24px;" class="mobile-padding">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;background:#142232;">
<tr> <tr>
<td style="padding:16px 14px;font-family:Arial,Helvetica,sans-serif;color:#ffffff;"> <td style="padding:30px 26px;font-family:Arial,Helvetica,sans-serif;">
<div style="font-size:12px;line-height:16px;font-weight:700;letter-spacing:0.6px;color:#ef7d00;">NEWSLETTER</div> <div style="font-size:12px;line-height:16px;font-weight:700;color:#ff7d00;letter-spacing:1px;text-transform:uppercase;">Thomas-Krenn Wiki Update</div>
<div class="mobile-title" style="font-size:24px;line-height:30px;font-weight:700;color:#ffffff;">' . h($periodLabel) . '</div> <div class="mobile-headline" style="font-size:34px;line-height:40px;font-weight:700;color:#ffffff;margin-top:10px;">' . h($heroHeadline) . '</div>
<div class="mobile-hero-copy" style="font-size:18px;line-height:27px;color:#ffffff;margin-top:10px;">' . h($periodLabel) . ' | ' . h($summaryText) . '</div>
</td> </td>
</tr> </tr>
</table> </table>
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding:10px 24px 8px 24px;background:#f3f3f3;" class="mobile-padding"> <td style="padding:22px 32px 22px 32px;" class="mobile-padding">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0"> <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0">
<tr> <tr>
<td class="mobile-body" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;color:#1f1f1f;"> <td class="mobile-intro" style="font-family:Arial,Helvetica,sans-serif;font-size:18px;line-height:29px;color:#22324a;">
Guten Tag,<br><br> Servus,<br><br>
„oh mei“ sagen wir in Bayern, wenn mal was schiefgeht. In diesem Newsletter finden Sie aktuelles und kostenfreies IT-Know-how aus dem Thomas-Krenn-Wiki. hier ist deine Übersicht mit den neuesten Beiträgen aus dem Thomas-Krenn-Wiki ' . $dateRangeText . '.<br>
<br><br>' . $summaryText . ' Wir hoffen, dass dir diese Beiträge gefallen.
<br><br>Viel Spaß beim Lesen wünscht<br>Ihr Team von Thomas-Krenn
</td> </td>
</tr> </tr>
</table> </table>
</td> </td>
</tr> </tr>
' . $highlightsSection . '
' . $testBadge . ' ' . $testBadge . '
' . $articleRows . ' ' . $articleRows . '
' . $editorTipSection . '
<tr> <tr>
<td style="padding:0 24px 18px 24px;" class="mobile-padding"> <td style="padding:0 32px 28px 32px;" class="mobile-padding">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;"> <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0">
<tr> <tr>
<td style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:22px;color:#2e2e2e;"> <td style="font-family:Arial,Helvetica,sans-serif;font-size:15px;line-height:24px;color:#2a3950;">
Haben Sie Fragen oder einen Themenwunsch? Hast du Fragen oder einen Themenwunsch?
<a href="' . h($replyHref) . '" style="color:#ef7d00;text-decoration:underline;">Kontaktieren Sie uns gerne direkt.</a> <a href="' . h($replyHref) . '" style="color:#ff7d00;text-decoration:underline;">Schreib uns gerne direkt.</a><br><br>
Viele Grüße<br>
Euer Thomas-Krenn-Wiki Team
</td> </td>
</tr> </tr>
</table> </table>
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="background:#ececec;padding:18px 24px;text-align:center;font-family:Arial,Helvetica,sans-serif;"> <td style="background:#5f6064;padding:22px 20px;text-align:center;font-family:Arial,Helvetica,sans-serif;">
<div style="font-size:12px;line-height:18px;color:#666666;"> <div style="font-size:12px;line-height:19px;color:#eef0f2;">
Tel.: +49 8551 9150 0 | Fax: +49 8551 9150 55<br> Thomas-Krenn.AG | Speltenbach-Steinäcker 1 | D-94078 Freyung<br>
<a href="https://www.thomas-krenn.com/de/wiki/Hauptseite" target="_blank" style="color:#ef7d00;text-decoration:underline;">thomas-krenn.com</a><br><br> Tel.: +49 8551 9150 0 | Fax: +49 8551 9150 55 |
Thomas-Krenn.AG | Speltenbach-Steinäcker 1 | D-94078 Freyung <a href="https://www.thomas-krenn.com/de/wiki/Hauptseite" target="_blank" style="color:#ff7d00;text-decoration:underline;">thomas-krenn.com</a>
' . $unsubscribeFooter . ' ' . $unsubscribeFooter . '
</div> </div>
</td> </td>
@@ -629,12 +734,10 @@ function newsletterData(array $config, bool $testMode = false): array
[$start, $end, $periodLabel, $periodKey] = newsletterPeriod($config['period_mode']); [$start, $end, $periodLabel, $periodKey] = newsletterPeriod($config['period_mode']);
$allArticles = fetchLatestArticles($config['wiki_url'], (int)$config['article_fetch_limit']); $allArticles = fetchLatestArticles($config['wiki_url'], (int)$config['article_fetch_limit']);
// Testversand nutzt denselben Zeitraum wie der Liveversand.
$articles = filterArticlesForPeriod($allArticles, $start, $end, (int)$config['article_limit']);
if ($testMode) { if ($testMode) {
$articles = array_slice($allArticles, 0, (int)$config['article_limit']);
$periodLabel = 'aktuelle Übersicht';
$periodKey = 'test-' . date('Y-m-d-H-i-s'); $periodKey = 'test-' . date('Y-m-d-H-i-s');
} else {
$articles = filterArticlesForPeriod($allArticles, $start, $end, (int)$config['article_limit']);
} }
return [$articles, $periodLabel, $periodKey]; return [$articles, $periodLabel, $periodKey];
@@ -690,7 +793,8 @@ function configFromPost(array $current): array
'wiki_url', 'article_limit', 'article_fetch_limit', 'period_mode', 'wiki_url', 'article_limit', 'article_fetch_limit', 'period_mode',
'smtp_host', 'smtp_port', 'smtp_encryption', 'smtp_user', 'smtp_host', 'smtp_port', 'smtp_encryption', 'smtp_user',
'from_email', 'from_name', 'recipient_email', 'test_recipient_email', 'from_email', 'from_name', 'recipient_email', 'test_recipient_email',
'subject', 'reply_to', 'unsubscribe_url', 'cron_token' 'subject', 'editor_tip', 'highlight_url_1', 'highlight_url_2', 'highlight_url_3',
'reply_to', 'unsubscribe_url', 'cron_token'
]; ];
foreach ($fields as $field) { foreach ($fields as $field) {
@@ -728,6 +832,15 @@ $error = '';
$previewHtml = ''; $previewHtml = '';
$previewClipboardHtml = ''; $previewClipboardHtml = '';
$flash = $_SESSION['wiki_newsletter_flash'] ?? null;
if (is_array($flash)) {
$message = (string)($flash['message'] ?? '');
$error = (string)($flash['error'] ?? '');
$previewHtml = (string)($flash['preview_html'] ?? '');
$previewClipboardHtml = (string)($flash['preview_clipboard_html'] ?? '');
unset($_SESSION['wiki_newsletter_flash']);
}
$isCron = (PHP_SAPI === 'cli' && cliArg('cron') !== null) || isset($_GET['cron']); $isCron = (PHP_SAPI === 'cli' && cliArg('cron') !== null) || isset($_GET['cron']);
if ($isCron) { if ($isCron) {
@@ -757,6 +870,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'login
} }
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') !== 'login') { if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') !== 'login') {
$redirectUrl = strtok($_SERVER['REQUEST_URI'], '?');
try { try {
requireAdmin(); requireAdmin();
$config = configFromPost($config); $config = configFromPost($config);
@@ -776,9 +891,24 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') !== 'login
} elseif ($action === 'monthly_send') { } elseif ($action === 'monthly_send') {
$message = sendMonthlyIfDue($config); $message = sendMonthlyIfDue($config);
} }
$_SESSION['wiki_newsletter_flash'] = [
'message' => $message,
'error' => '',
'preview_html' => $previewHtml,
'preview_clipboard_html' => $previewClipboardHtml,
];
} catch (Throwable $e) { } catch (Throwable $e) {
$error = $e->getMessage(); $_SESSION['wiki_newsletter_flash'] = [
'message' => '',
'error' => $e->getMessage(),
'preview_html' => '',
'preview_clipboard_html' => '',
];
} }
header('Location: ' . $redirectUrl);
exit;
} }
$isLoggedIn = isAdminLoggedIn(); $isLoggedIn = isAdminLoggedIn();
@@ -834,7 +964,7 @@ $isLoggedIn = isAdminLoggedIn();
.card.full { grid-column: 1 / -1; } .card.full { grid-column: 1 / -1; }
h2 { margin: 0 0 18px; font-size: 21px; letter-spacing: -.02em; } h2 { margin: 0 0 18px; font-size: 21px; letter-spacing: -.02em; }
label { display: block; margin: 14px 0 7px; color: #374151; font-size: 14px; font-weight: 700; } label { display: block; margin: 14px 0 7px; color: #374151; font-size: 14px; font-weight: 700; }
input, select { input, select, textarea {
width: 100%; width: 100%;
height: 42px; height: 42px;
padding: 0 12px; padding: 0 12px;
@@ -845,7 +975,14 @@ $isLoggedIn = isAdminLoggedIn();
font-size: 14px; font-size: 14px;
outline: none; outline: none;
} }
input:focus, select:focus { border-color: rgba(37,99,235,.55); box-shadow: 0 0 0 4px rgba(37,99,235,.08); } textarea {
min-height: 110px;
height: auto;
padding: 10px 12px;
resize: vertical;
line-height: 1.5;
}
input:focus, select:focus, textarea:focus { border-color: rgba(37,99,235,.55); box-shadow: 0 0 0 4px rgba(37,99,235,.08); }
.hint { margin: 7px 0 0; color: var(--muted); font-size: 13px; line-height: 1.5; } .hint { margin: 7px 0 0; color: var(--muted); font-size: 13px; line-height: 1.5; }
.actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; } .actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }
button { button {
@@ -951,6 +1088,20 @@ $isLoggedIn = isAdminLoggedIn();
<input name="subject" value="<?= h($config['subject']) ?>"> <input name="subject" value="<?= h($config['subject']) ?>">
<p class="hint">Platzhalter: <code>{MONAT}</code>, <code>{ANZAHL}</code></p> <p class="hint">Platzhalter: <code>{MONAT}</code>, <code>{ANZAHL}</code></p>
<label>Redaktionstipp (optional)</label>
<textarea name="editor_tip" placeholder="Optionaler Hinweis für den Newsletter..."><?= h((string)($config['editor_tip'] ?? '')) ?></textarea>
<p class="hint">Wird nur im Newsletter angezeigt, wenn ein Text hinterlegt ist.</p>
<label>Top Highlight 1 URL (optional)</label>
<input name="highlight_url_1" value="<?= h((string)($config['highlight_url_1'] ?? '')) ?>" placeholder="https://www.thomas-krenn.com/de/wiki/...">
<label>Top Highlight 2 URL (optional)</label>
<input name="highlight_url_2" value="<?= h((string)($config['highlight_url_2'] ?? '')) ?>" placeholder="https://www.thomas-krenn.com/de/wiki/...">
<label>Top Highlight 3 URL (optional)</label>
<input name="highlight_url_3" value="<?= h((string)($config['highlight_url_3'] ?? '')) ?>" placeholder="https://www.thomas-krenn.com/de/wiki/...">
<p class="hint">Wenn gesetzt, werden diese 3 Artikel in genau dieser Reihenfolge als Highlights verwendet. Sind alle Felder leer, wird kein Highlights-Block angezeigt.</p>
</section> </section>
<section class="card"> <section class="card">

170
wiki.php
View File

@@ -72,11 +72,18 @@ function loadJsonFile(string $file, array $default): array
function saveJsonFile(string $file, array $data): void function saveJsonFile(string $file, array $data): void
{ {
file_put_contents( $json = json_encode(
$file, $data,
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE
LOCK_EX
); );
if ($json === false) {
throw new RuntimeException('Konfiguration konnte nicht kodiert werden.');
}
$written = @file_put_contents($file, $json, LOCK_EX);
if ($written === false) {
throw new RuntimeException('Konfiguration konnte nicht gespeichert werden: ' . $file . ' (Dateirechte prüfen).');
}
} }
function isAdminLoggedIn(): bool function isAdminLoggedIn(): bool
@@ -170,6 +177,10 @@ function fetchLatestArticles(string $wikiUrl, int $limit = 150): array
$nodes = $xpath->query('//li'); $nodes = $xpath->query('//li');
foreach ($nodes as $li) { foreach ($nodes as $li) {
if (!$li instanceof DOMElement) {
continue;
}
$text = trim(preg_replace('/\s+/', ' ', $li->textContent ?? '')); $text = trim(preg_replace('/\s+/', ' ', $li->textContent ?? ''));
if (!preg_match('/\((\d{2}\.\d{2}\.\d{4})\)/', $text, $dateMatch)) { if (!preg_match('/\((\d{2}\.\d{2}\.\d{4})\)/', $text, $dateMatch)) {
@@ -281,7 +292,7 @@ function newsletterPeriod(string $mode): array
if ($mode === 'current_month') { if ($mode === 'current_month') {
$start = $now->modify('first day of this month')->setTime(0, 0, 0); $start = $now->modify('first day of this month')->setTime(0, 0, 0);
$end = $now->modify('first day of next month')->setTime(0, 0, 0); $end = $now->modify('tomorrow')->setTime(0, 0, 0);
} else { } else {
$start = $now->modify('first day of previous month')->setTime(0, 0, 0); $start = $now->modify('first day of previous month')->setTime(0, 0, 0);
$end = $now->modify('first day of this month')->setTime(0, 0, 0); $end = $now->modify('first day of this month')->setTime(0, 0, 0);
@@ -339,33 +350,25 @@ function buildNewsletterHtml(array $config, array $articles, string $periodLabel
$replyEmail = trim((string)($config['reply_to'] ?: $config['from_email'])); $replyEmail = trim((string)($config['reply_to'] ?: $config['from_email']));
$replyHref = 'mailto:' . $replyEmail; $replyHref = 'mailto:' . $replyEmail;
$summaryText = $count === 1 $summaryText = $count === 1
? 'Im ' . h($periodLabel) . ' wurde 1 neuer Wiki-Artikel veröffentlicht.' ? 'In diesem Zeitraum wurde 1 neuer Wiki-Artikel veröffentlicht.'
: 'Im ' . h($periodLabel) . ' wurden ' . $count . ' neue Wiki-Artikel veröffentlicht.'; : 'In diesem Zeitraum wurden ' . $count . ' neue Wiki-Artikel veröffentlicht.';
$testBadge = $isTest [$periodStart, $periodEnd] = newsletterPeriod((string)$config['period_mode']);
? '<tr> $rangeEnd = $periodEnd;
<td style="padding:0 24px 12px 24px;" class="mobile-padding"> if (($config['period_mode'] ?? '') === 'current_month') {
<table role="presentation" border="0" cellspacing="0" cellpadding="0"> $rangeEnd = new DateTimeImmutable('today');
<tr> }
<td bgcolor="#fff3cd" style="background-color:#fff3cd;border:1px solid #f4d58a;padding:6px 10px;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:15px;font-weight:700;color:#7a5200;"> $dateRangeText = 'vom ' . $periodStart->format('d.m') . ' bis ' . $rangeEnd->format('d.m');
TESTVERSAND
</td>
</tr>
</table>
</td>
</tr>'
: '';
$articleRows = ''; $articleRows = '';
if ($count === 0) { if ($count === 0) {
$articleRows = ' $articleRows = '
<tr> <tr>
<td style="padding:0 24px 18px 24px;" class="mobile-padding"> <td style="padding:0 26px 18px 26px;" class="mobile-padding">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;border:1px solid #d7d7d7;background:#ffffff;"> <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;border:1px solid #d9dfe7;background:#ffffff;">
<tr> <tr>
<td style="padding:18px;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;color:#333333;"> <td style="padding:18px;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:22px;color:#2b3340;">
Für diesen Zeitraum wurden keine neuen Artikel gefunden. Für diesen Zeitraum wurden keine neuen Artikel gefunden.
</td> </td>
</tr> </tr>
@@ -376,29 +379,29 @@ function buildNewsletterHtml(array $config, array $articles, string $periodLabel
foreach ($articles as $article) { foreach ($articles as $article) {
$articleRows .= ' $articleRows .= '
<tr> <tr>
<td style="padding:0 24px 18px 24px;" class="mobile-padding"> <td style="padding:0 26px 16px 26px;" class="mobile-padding">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;background:#ffffff;"> <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;background:#ffffff;border:1px solid #e2e7ee;border-left:4px solid #ef7d00;">
<tr> <tr>
<td style="padding:0 0 5px 0;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:17px;color:#595959;"> <td style="padding:14px 16px 0 16px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:17px;color:#5c6675;">
Thomas-Krenn-Wiki Thomas-Krenn-Wiki
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding:0 0 5px 0;font-family:Arial,Helvetica,sans-serif;font-size:22px;line-height:28px;font-weight:700;color:#ef7d00;"> <td style="padding:4px 16px 6px 16px;font-family:Arial,Helvetica,sans-serif;font-size:21px;line-height:28px;font-weight:700;color:#ef7d00;">
<a href="' . h($article['url']) . '" target="_blank" style="color:#ef7d00;text-decoration:none;">' . h($article['title']) . '</a> <a href="' . h($article['url']) . '" target="_blank" style="color:#ef7d00;text-decoration:none;">' . h($article['title']) . '</a>
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding:0 0 10px 0;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:22px;color:#2e2e2e;"> <td style="padding:0 16px 12px 16px;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;color:#313844;">
Veröffentlicht am ' . h($article['date_de']) . ' von ' . h($article['author']) . '. Veröffentlicht am ' . h($article['date_de']) . ' von ' . h($article['author']) . '.
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding:0;"> <td style="padding:0 16px 15px 16px;">
<table role="presentation" border="0" cellspacing="0" cellpadding="0"> <table role="presentation" border="0" cellspacing="0" cellpadding="0">
<tr> <tr>
<td bgcolor="#ef7d00" style="background:#ef7d00;"> <td bgcolor="#ef7d00" style="background:#ef7d00;">
<a href="' . h($article['url']) . '" target="_blank" style="display:inline-block;padding:8px 14px;font-family:Arial,Helvetica,sans-serif;font-size:13px;line-height:16px;color:#ffffff;text-decoration:none;">Jetzt Artikel lesen</a> <a href="' . h($article['url']) . '" target="_blank" style="display:inline-block;padding:8px 14px;font-family:Arial,Helvetica,sans-serif;font-size:13px;line-height:16px;font-weight:700;color:#ffffff;text-decoration:none;">Jetzt Artikel lesen</a>
</td> </td>
</tr> </tr>
</table> </table>
@@ -410,6 +413,20 @@ function buildNewsletterHtml(array $config, array $articles, string $periodLabel
} }
} }
$testBadge = $isTest
? '<tr>
<td style="padding:0 26px 12px 26px;" class="mobile-padding">
<table role="presentation" border="0" cellspacing="0" cellpadding="0">
<tr>
<td bgcolor="#fff3cd" style="background:#fff3cd;border:1px solid #f3d38a;padding:6px 10px;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:14px;font-weight:700;color:#7a5200;">
TESTVERSAND
</td>
</tr>
</table>
</td>
</tr>'
: '';
$unsubscribeFooter = ''; $unsubscribeFooter = '';
$unsubscribeUrl = trim((string)($config['unsubscribe_url'] ?? '')); $unsubscribeUrl = trim((string)($config['unsubscribe_url'] ?? ''));
if ($unsubscribeUrl !== '' && $unsubscribeUrl !== '#' && filter_var($unsubscribeUrl, FILTER_VALIDATE_URL)) { if ($unsubscribeUrl !== '' && $unsubscribeUrl !== '#' && filter_var($unsubscribeUrl, FILTER_VALIDATE_URL)) {
@@ -424,59 +441,54 @@ function buildNewsletterHtml(array $config, array $articles, string $periodLabel
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Thomas-Krenn Wiki Newsletter</title> <title>Thomas-Krenn Wiki Newsletter</title>
<style type="text/css"> <style type="text/css">
html, body { margin:0 !important; padding:0 !important; width:100% !important; background:#e6e6e6; } html, body { margin:0 !important; padding:0 !important; width:100% !important; background:#dadada; }
table, td { border-collapse:collapse; mso-table-lspace:0pt; mso-table-rspace:0pt; } table, td { border-collapse:collapse; mso-table-lspace:0pt; mso-table-rspace:0pt; }
img { border:0; outline:none; text-decoration:none; -ms-interpolation-mode:bicubic; display:block; } img { border:0; outline:none; text-decoration:none; -ms-interpolation-mode:bicubic; display:block; }
a { text-decoration:none; } a { text-decoration:none; }
* { -ms-text-size-adjust:100%; -webkit-text-size-adjust:100%; } * { -ms-text-size-adjust:100%; -webkit-text-size-adjust:100%; }
.preheader { display:none !important; visibility:hidden; opacity:0; color:transparent; height:0; width:0; max-height:0; max-width:0; overflow:hidden; mso-hide:all; } .preheader { display:none !important; visibility:hidden; opacity:0; color:transparent; height:0; width:0; max-height:0; max-width:0; overflow:hidden; mso-hide:all; }
@media only screen and (max-width: 640px) { @media only screen and (max-width: 680px) {
.email-container { width:100% !important; } .email-container { width:100% !important; }
.mobile-padding { padding-left:14px !important; padding-right:14px !important; } .mobile-padding { padding-left:14px !important; padding-right:14px !important; }
.mobile-body { font-size:15px !important; line-height:23px !important; } .mobile-intro { font-size:16px !important; line-height:24px !important; }
.mobile-title { font-size:19px !important; line-height:25px !important; }
} }
</style> </style>
</head> </head>
<body style="margin:0;padding:0;background:#e6e6e6;"> <body style="margin:0;padding:0;background:#dadada;">
<div class="preheader">' . h($preheader) . '&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;</div> <div class="preheader">' . h($preheader) . '&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;</div>
<center role="article" aria-roledescription="email" lang="de" style="width:100%;background:#e6e6e6;"> <center role="article" aria-roledescription="email" lang="de" style="width:100%;background:#dadada;">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#e6e6e6" style="width:100%;background:#e6e6e6;"> <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#dadada" style="width:100%;background:#dadada;">
<tr> <tr>
<td align="center" style="padding:6px 8px;"> <td align="center" style="padding:12px 8px;">
<table role="presentation" width="620" border="0" cellspacing="0" cellpadding="0" class="email-container" style="width:100%;max-width:620px;background:#ffffff;"> <table role="presentation" width="690" border="0" cellspacing="0" cellpadding="0" class="email-container" style="width:100%;max-width:690px;background:#ffffff;">
<tr> <tr>
<td align="center" style="padding:8px 20px 6px 20px;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:15px;color:#4e4e4e;"> <td align="center" style="padding:8px 26px 14px 26px;">
Sollte diese E-Mail nicht einwandfrei dargestellt werden, klicken Sie bitte hier. <img src="https://www.thomas-krenn.com/res/pics/tk_logo_340px.png" width="170" alt="Thomas-Krenn Logo" style="display:block;margin:0 auto;border:0;outline:none;text-decoration:none;height:auto;">
</td> </td>
</tr> </tr>
<tr> <tr>
<td align="center" style="padding:4px 20px 10px 20px;font-family:Arial,Helvetica,sans-serif;font-size:30px;line-height:32px;font-weight:700;color:#555555;"> <td style="padding:0 26px;" class="mobile-padding">
THOMAS<br>KRENN <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;background:#10233a;">
</td>
</tr>
<tr>
<td style="padding:0 24px;" class="mobile-padding">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;background:#142232;">
<tr> <tr>
<td style="padding:16px 14px;font-family:Arial,Helvetica,sans-serif;color:#ffffff;"> <td style="padding:18px 18px 20px 18px;font-family:Arial,Helvetica,sans-serif;">
<div style="font-size:12px;line-height:16px;font-weight:700;letter-spacing:0.6px;color:#ef7d00;">NEWSLETTER</div> <div style="font-size:13px;line-height:18px;font-weight:700;color:#ef7d00;letter-spacing:0.7px;">NEWSLETTER</div>
<div class="mobile-title" style="font-size:24px;line-height:30px;font-weight:700;color:#ffffff;">' . h($periodLabel) . '</div> <div style="font-size:20px;line-height:26px;font-weight:700;color:#ffffff;margin-top:2px;">' . h($periodLabel) . '</div>
</td> </td>
</tr> </tr>
</table> </table>
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding:10px 24px 8px 24px;background:#f3f3f3;" class="mobile-padding"> <td style="padding:14px 26px 12px 26px;background:#eef0f3;" class="mobile-padding">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0"> <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0">
<tr> <tr>
<td class="mobile-body" style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:21px;color:#1f1f1f;"> <td class="mobile-intro" style="font-family:Arial,Helvetica,sans-serif;font-size:17px;line-height:27px;color:#1e2530;">
Guten Tag,<br><br> Guten Tag,<br><br>
„oh mei“ sagen wir in Bayern, wenn mal was schiefgeht. In diesem Newsletter finden Sie aktuelles und kostenfreies IT-Know-how aus dem Thomas-Krenn-Wiki. hier erhalten Sie Ihre aktuelle Übersicht aus dem Thomas-Krenn-Wiki, ' . $dateRangeText . '.<br>
<br><br>' . $summaryText . ' ' . $summaryText . '<br><br>
<br><br>Viel Spaß beim Lesen wünscht<br>Ihr Team von Thomas-Krenn Viel Spaß beim Lesen wünscht<br>Ihr Team von Thomas-Krenn
</td> </td>
</tr> </tr>
</table> </table>
</td> </td>
@@ -484,10 +496,10 @@ function buildNewsletterHtml(array $config, array $articles, string $periodLabel
' . $testBadge . ' ' . $testBadge . '
' . $articleRows . ' ' . $articleRows . '
<tr> <tr>
<td style="padding:0 24px 18px 24px;" class="mobile-padding"> <td style="padding:4px 26px 20px 26px;" class="mobile-padding">
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="width:100%;"> <table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0">
<tr> <tr>
<td style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:22px;color:#2e2e2e;"> <td style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:22px;color:#2a303b;">
Haben Sie Fragen oder einen Themenwunsch? Haben Sie Fragen oder einen Themenwunsch?
<a href="' . h($replyHref) . '" style="color:#ef7d00;text-decoration:underline;">Kontaktieren Sie uns gerne direkt.</a> <a href="' . h($replyHref) . '" style="color:#ef7d00;text-decoration:underline;">Kontaktieren Sie uns gerne direkt.</a>
</td> </td>
@@ -496,8 +508,8 @@ function buildNewsletterHtml(array $config, array $articles, string $periodLabel
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="background:#ececec;padding:18px 24px;text-align:center;font-family:Arial,Helvetica,sans-serif;"> <td style="background:#eceef1;padding:20px 26px;text-align:center;font-family:Arial,Helvetica,sans-serif;">
<div style="font-size:12px;line-height:18px;color:#666666;"> <div style="font-size:12px;line-height:20px;color:#646d7b;">
Tel.: +49 8551 9150 0 | Fax: +49 8551 9150 55<br> Tel.: +49 8551 9150 0 | Fax: +49 8551 9150 55<br>
<a href="https://www.thomas-krenn.com/de/wiki/Hauptseite" target="_blank" style="color:#ef7d00;text-decoration:underline;">thomas-krenn.com</a><br><br> <a href="https://www.thomas-krenn.com/de/wiki/Hauptseite" target="_blank" style="color:#ef7d00;text-decoration:underline;">thomas-krenn.com</a><br><br>
Thomas-Krenn.AG | Speltenbach-Steinäcker 1 | D-94078 Freyung Thomas-Krenn.AG | Speltenbach-Steinäcker 1 | D-94078 Freyung
@@ -630,12 +642,10 @@ function newsletterData(array $config, bool $testMode = false): array
[$start, $end, $periodLabel, $periodKey] = newsletterPeriod($config['period_mode']); [$start, $end, $periodLabel, $periodKey] = newsletterPeriod($config['period_mode']);
$allArticles = fetchLatestArticles($config['wiki_url'], (int)$config['article_fetch_limit']); $allArticles = fetchLatestArticles($config['wiki_url'], (int)$config['article_fetch_limit']);
// Testversand nutzt denselben Zeitraum wie der Liveversand.
$articles = filterArticlesForPeriod($allArticles, $start, $end, (int)$config['article_limit']);
if ($testMode) { if ($testMode) {
$articles = array_slice($allArticles, 0, (int)$config['article_limit']);
$periodLabel = 'aktuelle Übersicht';
$periodKey = 'test-' . date('Y-m-d-H-i-s'); $periodKey = 'test-' . date('Y-m-d-H-i-s');
} else {
$articles = filterArticlesForPeriod($allArticles, $start, $end, (int)$config['article_limit']);
} }
return [$articles, $periodLabel, $periodKey]; return [$articles, $periodLabel, $periodKey];
@@ -729,6 +739,15 @@ $error = '';
$previewHtml = ''; $previewHtml = '';
$previewClipboardHtml = ''; $previewClipboardHtml = '';
$flash = $_SESSION['wiki_newsletter_flash'] ?? null;
if (is_array($flash)) {
$message = (string)($flash['message'] ?? '');
$error = (string)($flash['error'] ?? '');
$previewHtml = (string)($flash['preview_html'] ?? '');
$previewClipboardHtml = (string)($flash['preview_clipboard_html'] ?? '');
unset($_SESSION['wiki_newsletter_flash']);
}
$isCron = (PHP_SAPI === 'cli' && cliArg('cron') !== null) || isset($_GET['cron']); $isCron = (PHP_SAPI === 'cli' && cliArg('cron') !== null) || isset($_GET['cron']);
if ($isCron) { if ($isCron) {
@@ -758,6 +777,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'login
} }
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') !== 'login') { if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') !== 'login') {
$redirectUrl = strtok($_SERVER['REQUEST_URI'], '?');
try { try {
requireAdmin(); requireAdmin();
$config = configFromPost($config); $config = configFromPost($config);
@@ -777,9 +798,24 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') !== 'login
} elseif ($action === 'monthly_send') { } elseif ($action === 'monthly_send') {
$message = sendMonthlyIfDue($config); $message = sendMonthlyIfDue($config);
} }
$_SESSION['wiki_newsletter_flash'] = [
'message' => $message,
'error' => '',
'preview_html' => $previewHtml,
'preview_clipboard_html' => $previewClipboardHtml,
];
} catch (Throwable $e) { } catch (Throwable $e) {
$error = $e->getMessage(); $_SESSION['wiki_newsletter_flash'] = [
'message' => '',
'error' => $e->getMessage(),
'preview_html' => '',
'preview_clipboard_html' => '',
];
} }
header('Location: ' . $redirectUrl);
exit;
} }
$isLoggedIn = isAdminLoggedIn(); $isLoggedIn = isAdminLoggedIn();