166 lines
5.2 KiB
PHP
166 lines
5.2 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
|
|
{
|
|
$resolvedSlug = $this->resolveSlug($slug);
|
|
$page = null;
|
|
|
|
try {
|
|
$page = LegalPage::query()
|
|
->where('slug', $resolvedSlug)
|
|
->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($resolvedSlug, $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' => $resolvedSlug,
|
|
]);
|
|
}
|
|
|
|
$title = $page->title[$locale]
|
|
?? $page->title[$page->locale_fallback]
|
|
?? $page->title['de']
|
|
?? $page->title['en']
|
|
?? Str::title($resolvedSlug);
|
|
|
|
$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' => $resolvedSlug,
|
|
]);
|
|
}
|
|
|
|
private function resolveSlug(?string $slug): string
|
|
{
|
|
$slug = strtolower($slug ?? '');
|
|
|
|
$aliases = [
|
|
'imprint' => 'impressum',
|
|
'privacy' => 'datenschutz',
|
|
'terms' => 'agb',
|
|
];
|
|
|
|
return $aliases[$slug] ?? $slug ?: 'impressum';
|
|
}
|
|
|
|
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(string $slug, string $locale): ?array
|
|
{
|
|
$candidates = array_unique([
|
|
strtolower($locale),
|
|
strtolower(config('app.fallback_locale', 'de')),
|
|
'de',
|
|
'en',
|
|
]);
|
|
|
|
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;
|
|
}
|
|
}
|