web - refactor core modules, harden authorization, simplify endpoints
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
const NEWS_PNG_DIRECTORY = __DIR__ . '/news/png';
|
||||
|
||||
function isValidNewsFileBase(string $fileBase): bool
|
||||
{
|
||||
return preg_match('/^\d{4}-\d{2}-\d{2}-[A-Za-z0-9_-]+$/D', $fileBase) === 1;
|
||||
}
|
||||
|
||||
function deleteNewsFiles(string $fileBase): bool
|
||||
{
|
||||
if (!isValidNewsFileBase($fileBase)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$success = true;
|
||||
|
||||
foreach (glob(NEWS_PNG_DIRECTORY . '/' . $fileBase . '_page_*.png') ?: [] as $file) {
|
||||
if (!unlink($file)) {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
function cleanupExpiredNews(): void
|
||||
{
|
||||
$today = new DateTimeImmutable('today');
|
||||
|
||||
foreach (glob(NEWS_PNG_DIRECTORY . '/*_page_1.png') ?: [] as $file) {
|
||||
$fileBase = basename($file, '_page_1.png');
|
||||
|
||||
if (!isValidNewsFileBase($fileBase)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$dateString = substr($fileBase, 0, 10);
|
||||
$expiryDate = DateTimeImmutable::createFromFormat('!Y-m-d', $dateString);
|
||||
|
||||
if (
|
||||
$expiryDate !== false &&
|
||||
$expiryDate->format('Y-m-d') === $dateString &&
|
||||
$today > $expiryDate
|
||||
) {
|
||||
deleteNewsFiles($fileBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,574 @@
|
||||
<?php
|
||||
|
||||
const CONTENTPANEL_NEWS_PNG_DIRECTORY = __DIR__ . '/../news/png';
|
||||
|
||||
function contentpanel_html($value): string
|
||||
{
|
||||
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function contentpanel_csrf_token(): string
|
||||
{
|
||||
if (empty($_SESSION['contentpanel_csrf_token'])) {
|
||||
$_SESSION['contentpanel_csrf_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
return (string) $_SESSION['contentpanel_csrf_token'];
|
||||
}
|
||||
|
||||
function contentpanel_verify_csrf($postedToken): void
|
||||
{
|
||||
$sessionToken = $_SESSION['contentpanel_csrf_token'] ?? '';
|
||||
|
||||
if (
|
||||
!is_string($postedToken)
|
||||
|| !is_string($sessionToken)
|
||||
|| $sessionToken === ''
|
||||
|| !hash_equals($sessionToken, $postedToken)
|
||||
) {
|
||||
throw new InvalidArgumentException('Ungültige Anfrage. Bitte laden Sie die Seite neu.');
|
||||
}
|
||||
}
|
||||
|
||||
function contentpanel_action_tab(string $action): string
|
||||
{
|
||||
return match ($action) {
|
||||
'create_announcement', 'delete_announcement' => 'ansagen',
|
||||
'upload_news', 'delete_news' => 'dateien',
|
||||
default => 'gottesdienst',
|
||||
};
|
||||
}
|
||||
|
||||
function contentpanel_required_text(array $input, string $key, string $message): string
|
||||
{
|
||||
$value = trim((string) ($input[$key] ?? ''));
|
||||
if ($value === '') {
|
||||
throw new InvalidArgumentException($message);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
function contentpanel_date($value, string $message): DateTimeImmutable
|
||||
{
|
||||
$raw = trim((string) $value);
|
||||
$date = DateTimeImmutable::createFromFormat('!Y-m-d', $raw);
|
||||
$errors = DateTimeImmutable::getLastErrors();
|
||||
|
||||
if (
|
||||
$date === false
|
||||
|| ($errors !== false && ($errors['warning_count'] > 0 || $errors['error_count'] > 0))
|
||||
|| $date->format('Y-m-d') !== $raw
|
||||
) {
|
||||
throw new InvalidArgumentException($message);
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
function contentpanel_optional_date($value, string $message): ?DateTimeImmutable
|
||||
{
|
||||
$raw = trim((string) $value);
|
||||
return $raw === '' ? null : contentpanel_date($raw, $message);
|
||||
}
|
||||
|
||||
function contentpanel_optional_time($value, string $message): ?string
|
||||
{
|
||||
$raw = trim((string) $value);
|
||||
if ($raw === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (preg_match('/^(?:[01]\d|2[0-3]):[0-5]\d$/D', $raw) !== 1) {
|
||||
throw new InvalidArgumentException($message);
|
||||
}
|
||||
|
||||
return $raw;
|
||||
}
|
||||
|
||||
function contentpanel_positive_id($value): int
|
||||
{
|
||||
$id = filter_var($value, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
|
||||
if ($id === false) {
|
||||
throw new InvalidArgumentException('Ungültiger Eintrag.');
|
||||
}
|
||||
|
||||
return (int) $id;
|
||||
}
|
||||
|
||||
function contentpanel_format_message_date(DateTimeInterface $date): string
|
||||
{
|
||||
static $months = [
|
||||
1 => 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
|
||||
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember',
|
||||
];
|
||||
|
||||
return sprintf('%02d. %s', (int) $date->format('d'), $months[(int) $date->format('n')]);
|
||||
}
|
||||
|
||||
function contentpanel_format_display_date($value): string
|
||||
{
|
||||
$raw = trim((string) $value);
|
||||
if ($raw === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$date = DateTimeImmutable::createFromFormat('!Y-m-d', substr($raw, 0, 10));
|
||||
return $date ? $date->format('d.m.Y') : $raw;
|
||||
}
|
||||
|
||||
function contentpanel_find_binary(string $name): ?string
|
||||
{
|
||||
$output = [];
|
||||
$status = 0;
|
||||
exec('command -v ' . escapeshellarg($name) . ' 2>/dev/null', $output, $status);
|
||||
|
||||
if ($status !== 0 || empty($output[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return trim((string) $output[0]);
|
||||
}
|
||||
|
||||
function contentpanel_validate_pdf_upload(array $file, string $uploadError, string $typeError): string
|
||||
{
|
||||
if ((int) ($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
|
||||
throw new InvalidArgumentException($uploadError);
|
||||
}
|
||||
|
||||
$name = (string) ($file['name'] ?? '');
|
||||
$tmpPath = (string) ($file['tmp_name'] ?? '');
|
||||
if (strtolower(pathinfo($name, PATHINFO_EXTENSION)) !== 'pdf' || !is_file($tmpPath)) {
|
||||
throw new InvalidArgumentException($typeError);
|
||||
}
|
||||
|
||||
$prefix = file_get_contents($tmpPath, false, null, 0, 1024);
|
||||
if ($prefix === false || strpos($prefix, '%PDF-') === false) {
|
||||
throw new InvalidArgumentException($typeError);
|
||||
}
|
||||
|
||||
return $tmpPath;
|
||||
}
|
||||
|
||||
function contentpanel_pdf_to_text(string $pdfPath): string
|
||||
{
|
||||
$binary = contentpanel_find_binary('pdftotext');
|
||||
if ($binary === null) {
|
||||
throw new RuntimeException("'pdftotext' wurde nicht gefunden.");
|
||||
}
|
||||
|
||||
$tempPath = tempnam(sys_get_temp_dir(), 'contentpanel_lg_');
|
||||
if ($tempPath === false) {
|
||||
throw new RuntimeException('Temporäre Datei konnte nicht angelegt werden.');
|
||||
}
|
||||
|
||||
try {
|
||||
$command = escapeshellarg($binary)
|
||||
. ' -layout -enc UTF-8 '
|
||||
. escapeshellarg($pdfPath) . ' '
|
||||
. escapeshellarg($tempPath)
|
||||
. ' 2>&1';
|
||||
|
||||
$output = [];
|
||||
$status = 0;
|
||||
exec($command, $output, $status);
|
||||
if ($status !== 0) {
|
||||
throw new RuntimeException('pdftotext fehlgeschlagen: ' . implode("\n", $output));
|
||||
}
|
||||
|
||||
$text = file_get_contents($tempPath);
|
||||
if ($text === false) {
|
||||
throw new RuntimeException('Der extrahierte PDF-Text konnte nicht gelesen werden.');
|
||||
}
|
||||
|
||||
return $text;
|
||||
} finally {
|
||||
@unlink($tempPath);
|
||||
}
|
||||
}
|
||||
|
||||
function contentpanel_parse_leitgedanken(string $rawText): array
|
||||
{
|
||||
$text = preg_replace("/\r\n?/", "\n", $rawText);
|
||||
$text = preg_replace("/(?<=\p{L})-\n(?=\p{L})/u", '', (string) $text);
|
||||
$lines = explode("\n", (string) $text);
|
||||
|
||||
$dateHeaderRegex = '/(?P<prefix>.*?)(?P<d1>\d{1,2})(?:\.\s*\/\s*(?P<d2>\d{1,2}))?\.(?P<m>\d{1,2})\.(?P<y>\d{4})\s*$/u';
|
||||
$books = '(?:Mo|Sam|Kön|Chr|Dan|Ps|Spr|Pred|Hld|Hi|Jes|Jer|Klgl|Hes|Ez|Hos|Joel|Am|Ob|Mi|Nah|Hab|Zef|Hag|Sach|Mal|Rut|Ri|Jos|Esra|Neh|Est|'
|
||||
. 'Mt|Mk|Lk|Joh|Apg|Röm|Kor|Gal|Eph|Phil|Kol|Thess|Tim|Tit|Phlm|Hebr|Jak|Petr|Joh|Jud|Offb)';
|
||||
$referenceRegex = '/(?P<ref>(?:[1-3]\.?\s*)?' . $books . '\s*\d{1,3}\s*,\s*\d{1,3}(?:[–\-]\d{1,3}(?:[a-z])?|(?:\.\d{1,3})+(?:[a-z])?|[a-z])?)\s*$/u';
|
||||
|
||||
$sections = [];
|
||||
foreach ($lines as $index => $line) {
|
||||
if (!preg_match($dateHeaderRegex, $line, $match)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$day = str_pad((string) (int) $match['d1'], 2, '0', STR_PAD_LEFT);
|
||||
$month = str_pad((string) (int) $match['m'], 2, '0', STR_PAD_LEFT);
|
||||
$sections[] = [
|
||||
'start' => $index,
|
||||
'date' => sprintf('%04d-%s-%s', (int) $match['y'], $month, $day),
|
||||
];
|
||||
}
|
||||
|
||||
if ($sections === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($sections as $index => &$section) {
|
||||
$section['end'] = $sections[$index + 1]['start'] ?? count($lines);
|
||||
}
|
||||
unset($section);
|
||||
|
||||
$entries = [];
|
||||
foreach ($sections as $section) {
|
||||
for ($i = $section['start']; $i < $section['end']; $i++) {
|
||||
$line = rtrim($lines[$i]);
|
||||
if ($line === '' || !preg_match($referenceRegex, $line, $referenceMatch)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$beforeReference = trim((string) preg_replace($referenceRegex, '', $line));
|
||||
$previousLines = [];
|
||||
for ($k = $i - 1; $k >= $section['start'] && $k >= $i - 15; $k--) {
|
||||
$previous = rtrim($lines[$k]);
|
||||
if ($previous === '' || preg_match($referenceRegex, $previous)) {
|
||||
break;
|
||||
}
|
||||
$previousLines[] = $previous;
|
||||
}
|
||||
|
||||
$verseLines = array_reverse($previousLines);
|
||||
if ($beforeReference !== '') {
|
||||
$verseLines[] = $beforeReference;
|
||||
}
|
||||
|
||||
$verse = trim((string) preg_replace('/\s+/', ' ', implode(' ', $verseLines)));
|
||||
if ($verse !== '') {
|
||||
$entries[] = [
|
||||
'bibeldatum' => $section['date'],
|
||||
'bibelwort' => trim($referenceMatch['ref']),
|
||||
'bibelvers' => $verse,
|
||||
];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$unique = [];
|
||||
$result = [];
|
||||
foreach ($entries as $entry) {
|
||||
$key = $entry['bibeldatum'] . '|' . $entry['bibelwort'];
|
||||
if (!isset($unique[$key])) {
|
||||
$unique[$key] = true;
|
||||
$result[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function contentpanel_import_leitgedanken(PDO $pdo, array $file): string
|
||||
{
|
||||
$pdfPath = contentpanel_validate_pdf_upload(
|
||||
$file,
|
||||
'Fehler beim Upload der Leitgedanken-PDF.',
|
||||
'Nur PDF-Dateien sind für den Import erlaubt.'
|
||||
);
|
||||
$entries = contentpanel_parse_leitgedanken(contentpanel_pdf_to_text($pdfPath));
|
||||
|
||||
if ($entries === []) {
|
||||
return 'Keine neuen Einträge erkannt.';
|
||||
}
|
||||
|
||||
$exists = $pdo->prepare('SELECT 1 FROM gottesdienst WHERE bibeldatum = ? AND bibelwort = ? LIMIT 1');
|
||||
$insert = $pdo->prepare('INSERT INTO gottesdienst (bibelwort, bibelvers, bibeldatum) VALUES (?, ?, ?)');
|
||||
$created = 0;
|
||||
$duplicates = 0;
|
||||
$ownsTransaction = !$pdo->inTransaction();
|
||||
|
||||
if ($ownsTransaction) {
|
||||
$pdo->beginTransaction();
|
||||
}
|
||||
|
||||
try {
|
||||
foreach ($entries as $entry) {
|
||||
$exists->execute([$entry['bibeldatum'], $entry['bibelwort']]);
|
||||
if ($exists->fetchColumn() !== false) {
|
||||
$duplicates++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$insert->execute([$entry['bibelwort'], $entry['bibelvers'], $entry['bibeldatum']]);
|
||||
$created++;
|
||||
}
|
||||
|
||||
if ($ownsTransaction) {
|
||||
$pdo->commit();
|
||||
}
|
||||
} catch (Throwable $exception) {
|
||||
if ($ownsTransaction && $pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
if ($created === 0) {
|
||||
return 'Keine neuen Einträge erkannt.' . ($duplicates > 0 ? " ($duplicates Duplikate)" : '');
|
||||
}
|
||||
|
||||
return "Leitgedanken importiert: $created Einträge angelegt"
|
||||
. ($duplicates > 0 ? ", $duplicates dupliziert übersprungen" : '')
|
||||
. '.';
|
||||
}
|
||||
|
||||
function contentpanel_create_service(PDO $pdo, array $input): string
|
||||
{
|
||||
$date = contentpanel_date($input['bibeldatum'] ?? '', 'Bitte ein gültiges Gottesdienst-Datum angeben.');
|
||||
$reference = contentpanel_required_text($input, 'bibelwort', 'Bitte eine Bibelstelle angeben.');
|
||||
$verse = contentpanel_required_text($input, 'bibelvers', 'Bitte einen Bibelvers angeben.');
|
||||
|
||||
$statement = $pdo->prepare('INSERT INTO gottesdienst (bibelwort, bibelvers, bibeldatum) VALUES (?, ?, ?)');
|
||||
$statement->execute([$reference, $verse, $date->format('Y-m-d')]);
|
||||
|
||||
return 'Gottesdienst am ' . contentpanel_format_message_date($date) . ' erfolgreich angelegt!';
|
||||
}
|
||||
|
||||
function contentpanel_delete_service(PDO $pdo, $id): string
|
||||
{
|
||||
$statement = $pdo->prepare('DELETE FROM gottesdienst WHERE id = ?');
|
||||
$statement->execute([contentpanel_positive_id($id)]);
|
||||
return 'Eintrag erfolgreich gelöscht!';
|
||||
}
|
||||
|
||||
function contentpanel_create_announcement(PDO $pdo, array $input): string
|
||||
{
|
||||
$startDate = contentpanel_date($input['ansage_datum_von'] ?? '', 'Bitte ein gültiges Startdatum angeben.');
|
||||
$endDate = contentpanel_optional_date($input['ansage_datum_bis'] ?? '', 'Bitte ein gültiges Enddatum angeben.');
|
||||
if ($endDate !== null && $endDate < $startDate) {
|
||||
throw new InvalidArgumentException('Das Enddatum darf nicht vor dem Startdatum liegen.');
|
||||
}
|
||||
|
||||
$time = contentpanel_optional_time($input['ansage_uhrzeit'] ?? '', 'Bitte eine gültige Uhrzeit angeben.');
|
||||
$title = contentpanel_required_text($input, 'ansage_titel', 'Bitte einen Titel angeben.');
|
||||
$location = trim((string) ($input['ansage_ort'] ?? ''));
|
||||
$subtitle = trim((string) ($input['ansage_untertitel'] ?? ''));
|
||||
|
||||
$statement = $pdo->prepare(
|
||||
'INSERT INTO ansagen (ansage_datum_von, ansage_datum_bis, ansage_uhrzeit, ansage_ort, ansage_titel, ansage_untertitel)
|
||||
VALUES (?, ?, ?, ?, ?, ?)'
|
||||
);
|
||||
$statement->execute([
|
||||
$startDate->format('Y-m-d'),
|
||||
$endDate?->format('Y-m-d'),
|
||||
$time,
|
||||
$location,
|
||||
$title,
|
||||
$subtitle,
|
||||
]);
|
||||
|
||||
return $title . ' am ' . contentpanel_format_message_date($startDate) . ' erfolgreich angelegt!';
|
||||
}
|
||||
|
||||
function contentpanel_delete_announcement(PDO $pdo, $id): string
|
||||
{
|
||||
$statement = $pdo->prepare('DELETE FROM ansagen WHERE id = ?');
|
||||
$statement->execute([contentpanel_positive_id($id)]);
|
||||
return 'Ansage erfolgreich gelöscht!';
|
||||
}
|
||||
|
||||
function contentpanel_sanitize_news_title(string $name): string
|
||||
{
|
||||
$safe = preg_replace('/[^A-Za-z0-9_\-]/', '_', str_replace(' ', '_', $name));
|
||||
return trim((string) $safe, '_-');
|
||||
}
|
||||
|
||||
function contentpanel_is_valid_news_file_base(string $fileBase): bool
|
||||
{
|
||||
return preg_match('/^\d{4}-\d{2}-\d{2}-[A-Za-z0-9_-]+$/D', $fileBase) === 1;
|
||||
}
|
||||
|
||||
function contentpanel_delete_news_files(string $fileBase): bool
|
||||
{
|
||||
if (!contentpanel_is_valid_news_file_base($fileBase)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$success = true;
|
||||
foreach (glob(CONTENTPANEL_NEWS_PNG_DIRECTORY . '/' . $fileBase . '_page_*.png') ?: [] as $file) {
|
||||
if (!@unlink($file)) {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
function contentpanel_cleanup_expired_news(): void
|
||||
{
|
||||
$today = new DateTimeImmutable('today');
|
||||
|
||||
foreach (glob(CONTENTPANEL_NEWS_PNG_DIRECTORY . '/*_page_1.png') ?: [] as $file) {
|
||||
$fileBase = basename($file, '_page_1.png');
|
||||
if (!contentpanel_is_valid_news_file_base($fileBase)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$dateString = substr($fileBase, 0, 10);
|
||||
$expiryDate = DateTimeImmutable::createFromFormat('!Y-m-d', $dateString);
|
||||
if ($expiryDate !== false && $expiryDate->format('Y-m-d') === $dateString && $today > $expiryDate) {
|
||||
contentpanel_delete_news_files($fileBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function contentpanel_convert_pdf_to_png(string $pdfPath, string $outputDirectory, string $baseFileName): void
|
||||
{
|
||||
$binary = contentpanel_find_binary('gs');
|
||||
if ($binary === null) {
|
||||
throw new RuntimeException("'gs' (Ghostscript) wurde nicht gefunden.");
|
||||
}
|
||||
|
||||
$outputPattern = $outputDirectory . '/' . $baseFileName . '_page_%d.png';
|
||||
$command = escapeshellarg($binary)
|
||||
. ' -dSAFER -dBATCH -dNOPAUSE -sDEVICE=png16m -r300'
|
||||
. ' -sOutputFile=' . escapeshellarg($outputPattern)
|
||||
. ' ' . escapeshellarg($pdfPath)
|
||||
. ' 2>&1';
|
||||
|
||||
$output = [];
|
||||
$status = 0;
|
||||
exec($command, $output, $status);
|
||||
if ($status !== 0 || !is_file($outputDirectory . '/' . $baseFileName . '_page_1.png')) {
|
||||
throw new RuntimeException('Ghostscript-Konvertierung fehlgeschlagen: ' . implode("\n", $output));
|
||||
}
|
||||
}
|
||||
|
||||
function contentpanel_upload_news(array $input, array $file): string
|
||||
{
|
||||
$expiryDate = contentpanel_date($input['date'] ?? '', 'Ungültiges Verfallsdatum.');
|
||||
$title = contentpanel_sanitize_news_title(trim((string) ($input['name'] ?? '')));
|
||||
if ($title === '') {
|
||||
throw new InvalidArgumentException('Bitte einen gültigen Titel eingeben.');
|
||||
}
|
||||
|
||||
$pdfPath = contentpanel_validate_pdf_upload(
|
||||
$file,
|
||||
'Bitte eine gültige PDF-Datei hochladen.',
|
||||
'Bitte eine gültige PDF-Datei hochladen.'
|
||||
);
|
||||
|
||||
if (!is_dir(CONTENTPANEL_NEWS_PNG_DIRECTORY) && !mkdir(CONTENTPANEL_NEWS_PNG_DIRECTORY, 0755, true)) {
|
||||
throw new RuntimeException('Das Aushang-Verzeichnis konnte nicht angelegt werden.');
|
||||
}
|
||||
|
||||
$fileBase = $expiryDate->format('Y-m-d') . '-' . $title;
|
||||
$tempDirectory = sys_get_temp_dir() . '/contentpanel_news_' . bin2hex(random_bytes(8));
|
||||
if (!mkdir($tempDirectory, 0700, true)) {
|
||||
throw new RuntimeException('Temporäres Aushang-Verzeichnis konnte nicht angelegt werden.');
|
||||
}
|
||||
|
||||
$stagedFiles = [];
|
||||
|
||||
try {
|
||||
contentpanel_convert_pdf_to_png($pdfPath, $tempDirectory, $fileBase);
|
||||
$generatedFiles = glob($tempDirectory . '/' . $fileBase . '_page_*.png') ?: [];
|
||||
natsort($generatedFiles);
|
||||
if ($generatedFiles === []) {
|
||||
throw new RuntimeException('Die PDF-Konvertierung hat keine Seiten erzeugt.');
|
||||
}
|
||||
|
||||
$token = bin2hex(random_bytes(6));
|
||||
foreach ($generatedFiles as $source) {
|
||||
$target = CONTENTPANEL_NEWS_PNG_DIRECTORY . '/' . basename($source);
|
||||
$staged = $target . '.tmp-' . $token;
|
||||
if (!copy($source, $staged)) {
|
||||
throw new RuntimeException('Konvertierte Aushangseite konnte nicht bereitgestellt werden.');
|
||||
}
|
||||
$stagedFiles[$staged] = $target;
|
||||
}
|
||||
|
||||
contentpanel_delete_news_files($fileBase);
|
||||
foreach ($stagedFiles as $staged => $target) {
|
||||
if (!rename($staged, $target)) {
|
||||
throw new RuntimeException('Konvertierte Aushangseite konnte nicht veröffentlicht werden.');
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
foreach (glob($tempDirectory . '/*') ?: [] as $filePath) {
|
||||
@unlink($filePath);
|
||||
}
|
||||
@rmdir($tempDirectory);
|
||||
foreach (array_keys($stagedFiles) as $temporaryFile) {
|
||||
@unlink($temporaryFile);
|
||||
}
|
||||
}
|
||||
|
||||
return 'Aushang wurde erfolgreich hochgeladen.';
|
||||
}
|
||||
|
||||
function contentpanel_list_news(): array
|
||||
{
|
||||
$items = [];
|
||||
$files = glob(CONTENTPANEL_NEWS_PNG_DIRECTORY . '/*_page_1.png') ?: [];
|
||||
sort($files, SORT_STRING);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$fileBase = basename($file, '_page_1.png');
|
||||
if (!contentpanel_is_valid_news_file_base($fileBase)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$expiry = substr($fileBase, 0, 10);
|
||||
$items[] = [
|
||||
'base' => $fileBase,
|
||||
'title' => substr($fileBase, 11),
|
||||
'expiry' => contentpanel_format_display_date($expiry),
|
||||
'pages' => count(glob(CONTENTPANEL_NEWS_PNG_DIRECTORY . '/' . $fileBase . '_page_*.png') ?: []),
|
||||
'image_url' => '/intranet_repo/projects/contentpanel/insertion/news/png/' . rawurlencode($fileBase) . '_page_1.png',
|
||||
];
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
function contentpanel_fetch_services(PDO $pdo, string $today): array
|
||||
{
|
||||
$statement = $pdo->prepare(
|
||||
'SELECT id, bibelwort, bibelvers, bibeldatum
|
||||
FROM gottesdienst
|
||||
WHERE bibeldatum >= ?
|
||||
ORDER BY bibeldatum ASC'
|
||||
);
|
||||
$statement->execute([$today]);
|
||||
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($rows as &$row) {
|
||||
$row['datum'] = contentpanel_format_display_date($row['bibeldatum'] ?? '');
|
||||
}
|
||||
unset($row);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
function contentpanel_fetch_announcements(PDO $pdo, string $today): array
|
||||
{
|
||||
$statement = $pdo->prepare(
|
||||
'SELECT id, ansage_untertitel, ansage_titel, ansage_ort, ansage_uhrzeit, ansage_datum_von, ansage_datum_bis
|
||||
FROM ansagen
|
||||
WHERE ansage_datum_von >= ? OR ansage_datum_bis >= ?
|
||||
ORDER BY ansage_datum_von ASC'
|
||||
);
|
||||
$statement->execute([$today, $today]);
|
||||
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($rows as &$row) {
|
||||
$row['datum_von'] = contentpanel_format_display_date($row['ansage_datum_von'] ?? '');
|
||||
$row['datum_bis'] = contentpanel_format_display_date($row['ansage_datum_bis'] ?? '');
|
||||
$row['uhrzeit'] = empty($row['ansage_uhrzeit']) ? '' : substr((string) $row['ansage_uhrzeit'], 0, 5);
|
||||
}
|
||||
unset($row);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
Reference in New Issue
Block a user