Files
fotospiel-app/app/Http/Controllers/LegalPageController.php
Codex Agent fa33e7cbcf Fix Event & EventType resource issues and apply formatting
- Fix EventType deletion error handling (constraint violations)
- Fix Event update error (package_id column missing)
- Fix Event Type dropdown options (JSON display issue)
- Fix EventPackagesRelationManager query error
- Add missing translations for deletion errors
- Apply Pint formatting
2026-01-21 10:34:06 +01:00

180 lines
5.8 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\LegalPage;
use Illuminate\Database\QueryException;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Inertia\Inertia;
use Inertia\Response;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\Autolink\AutolinkExtension;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\Strikethrough\StrikethroughExtension;
use League\CommonMark\Extension\Table\TableExtension;
use League\CommonMark\Extension\TaskList\TaskListExtension;
use League\CommonMark\MarkdownConverter;
class LegalPageController extends Controller
{
public function show(Request $request, string $locale, ?string $slug = null): Response
{
$slugCandidates = $this->resolveSlugs($slug);
$page = null;
try {
$page = LegalPage::query()
->whereIn('slug', $slugCandidates)
->where('is_published', true)
->orderByDesc('version')
->first();
} catch (QueryException $exception) {
// Table does not exist or query failed; fallback to filesystem documents
$page = null;
}
$locale = $request->route('locale', app()->getLocale());
if (! $page) {
$fallback = $this->loadFallbackDocument($slugCandidates, $locale);
if (! $fallback) {
abort(404);
}
return Inertia::render('legal/Show', [
'seoTitle' => $fallback['title'].' - '.config('app.name', 'Fotospiel'),
'title' => $fallback['title'],
'content' => $this->convertMarkdownToHtml($fallback['markdown']),
'effectiveFrom' => null,
'effectiveFromLabel' => null,
'versionLabel' => null,
'slug' => $slugCandidates[0],
]);
}
$title = $page->title[$locale]
?? $page->title[$page->locale_fallback]
?? $page->title['de']
?? $page->title['en']
?? Str::title($slugCandidates[0]);
$title = Str::ucfirst($title);
$bodyMarkdown = $page->body_markdown[$locale]
?? $page->body_markdown[$page->locale_fallback]
?? reset($page->body_markdown)
?? '';
$effectiveFrom = optional($page->effective_from);
return Inertia::render('legal/Show', [
'seoTitle' => $title.' - '.config('app.name', 'Fotospiel'),
'title' => $title,
'content' => $this->convertMarkdownToHtml($bodyMarkdown),
'effectiveFrom' => $effectiveFrom ? $effectiveFrom->toDateString() : null,
'effectiveFromLabel' => $effectiveFrom
? __('legal.effective_from', ['date' => $effectiveFrom->translatedFormat('d. F Y')])
: null,
'versionLabel' => __('legal.version', ['version' => $page->version]),
'slug' => $slugCandidates[0],
]);
}
private function resolveSlugs(?string $slug): array
{
$slug = strtolower($slug ?? '');
$aliases = [
'imprint' => 'impressum',
'privacy' => 'datenschutz',
'terms' => 'agb',
'withdrawal' => 'widerrufsbelehrung',
'cancellation' => 'widerrufsbelehrung',
'cancellation-policy' => 'widerrufsbelehrung',
'widerruf' => 'widerrufsbelehrung',
];
$canonical = $aliases[$slug] ?? $slug ?: 'impressum';
// Support both slugs for withdrawal in case DB uses a shorter slug
$fallbacks = $canonical === 'widerrufsbelehrung'
? ['widerrufsbelehrung', 'widerruf']
: [$canonical];
return array_values(array_unique($fallbacks));
}
private function convertMarkdownToHtml(string $markdown): string
{
$environment = new Environment([
'html_input' => 'strip',
'allow_unsafe_links' => false,
]);
$environment->addExtension(new CommonMarkCoreExtension);
$environment->addExtension(new TableExtension);
$environment->addExtension(new AutolinkExtension);
$environment->addExtension(new StrikethroughExtension);
$environment->addExtension(new TaskListExtension);
$converter = new MarkdownConverter($environment);
return trim((string) $converter->convert($markdown));
}
private function loadFallbackDocument(array $slugCandidates, string $locale): ?array
{
$candidates = array_unique([
strtolower($locale),
strtolower(config('app.fallback_locale', 'de')),
'de',
'en',
]);
foreach ($slugCandidates as $slug) {
foreach ($candidates as $candidateLocale) {
$path = base_path("docs/legal/{$slug}-{$candidateLocale}.md");
if (! is_file($path)) {
continue;
}
$markdown = (string) file_get_contents($path);
$title = $this->extractTitleFromMarkdown($markdown) ?? Str::title($slug);
return [
'markdown' => $markdown,
'title' => $title,
];
}
}
return null;
}
private function extractTitleFromMarkdown(string $markdown): ?string
{
foreach (preg_split('/\r?\n/', $markdown) as $line) {
$trimmed = trim($line);
if ($trimmed === '') {
continue;
}
if (str_starts_with($trimmed, '# ')) {
return trim(substr($trimmed, 2));
}
if (str_starts_with($trimmed, '## ')) {
return trim(substr($trimmed, 3));
}
// First non-empty line can act as fallback title
return $trimmed;
}
return null;
}
}