admin widget zu dokploy geswitched, viele übersetzungen im Frontend vervollständigt und Anlässe-Seiten mit ChatGPT ausgebaut
This commit is contained in:
@@ -4,8 +4,8 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Mail\ContactConfirmation;
|
||||
use App\Models\BlogPost;
|
||||
use App\Models\Event;
|
||||
use App\Models\CheckoutSession;
|
||||
use App\Models\Event;
|
||||
use App\Models\Package;
|
||||
use App\Models\PackagePurchase;
|
||||
use App\Models\TenantPackage;
|
||||
@@ -33,6 +33,8 @@ class MarketingController extends Controller
|
||||
{
|
||||
use PresentsPackages;
|
||||
|
||||
private ?MarkdownConverter $markdownConverter = null;
|
||||
|
||||
public function __construct(
|
||||
private readonly CheckoutSessionService $checkoutSessions,
|
||||
private readonly PaddleCheckoutService $paddleCheckout,
|
||||
@@ -292,11 +294,14 @@ class MarketingController extends Controller
|
||||
$posts = $query->orderBy('published_at', 'desc')
|
||||
->paginate(4)
|
||||
->through(function (BlogPost $post) use ($locale) {
|
||||
$excerpt = $post->getTranslation('excerpt', $locale) ?? $post->getTranslation('excerpt', 'de') ?? '';
|
||||
|
||||
return [
|
||||
'id' => $post->id,
|
||||
'slug' => $post->slug,
|
||||
'title' => $post->getTranslation('title', $locale) ?? $post->getTranslation('title', 'de') ?? '',
|
||||
'excerpt' => $post->getTranslation('excerpt', $locale) ?? $post->getTranslation('excerpt', 'de') ?? '',
|
||||
'excerpt' => $excerpt,
|
||||
'excerpt_html' => $this->convertMarkdownToHtml($excerpt),
|
||||
'featured_image' => $post->featured_image ?? $post->banner_url ?? null,
|
||||
'published_at' => optional($post->published_at)->toDateString(),
|
||||
'author' => $post->author ? ['name' => $post->author->name] : null,
|
||||
@@ -310,7 +315,18 @@ class MarketingController extends Controller
|
||||
'first_post_title' => $posts->count() > 0 ? ($posts->first()['title'] ?? 'No title') : 'No posts',
|
||||
]);
|
||||
|
||||
return Inertia::render('marketing/Blog', compact('posts'));
|
||||
$postsArray = $posts->toArray();
|
||||
$postsArray['links'] = array_map(function (array $link) use ($locale) {
|
||||
return [
|
||||
'url' => $link['url'],
|
||||
'label' => $this->localizePaginationLabel($link['label'] ?? '', $locale),
|
||||
'active' => (bool) ($link['active'] ?? false),
|
||||
];
|
||||
}, $postsArray['links'] ?? []);
|
||||
|
||||
return Inertia::render('marketing/Blog', [
|
||||
'posts' => $postsArray,
|
||||
]);
|
||||
}
|
||||
|
||||
public function blogShow(string $locale, string $slug)
|
||||
@@ -330,29 +346,27 @@ class MarketingController extends Controller
|
||||
|
||||
// Transform to array with translated strings for the current locale
|
||||
$markdown = $postModel->getTranslation('content', $locale) ?? $postModel->getTranslation('content', 'de') ?? '';
|
||||
|
||||
$environment = new Environment;
|
||||
$environment->addExtension(new CommonMarkCoreExtension);
|
||||
$environment->addExtension(new TableExtension);
|
||||
$environment->addExtension(new AutolinkExtension);
|
||||
$environment->addExtension(new StrikethroughExtension);
|
||||
$environment->addExtension(new TaskListExtension);
|
||||
|
||||
$converter = new MarkdownConverter($environment);
|
||||
$contentHtml = (string) $converter->convert($markdown);
|
||||
$excerpt = $postModel->getTranslation('excerpt', $locale) ?? $postModel->getTranslation('excerpt', 'de') ?? '';
|
||||
$contentHtml = $this->convertMarkdownToHtml($markdown);
|
||||
[$contentHtmlWithIds, $headings] = $this->decorateHeadings($contentHtml);
|
||||
|
||||
$post = [
|
||||
'id' => $postModel->id,
|
||||
'title' => $postModel->getTranslation('title', $locale) ?? $postModel->getTranslation('title', 'de') ?? '',
|
||||
'excerpt' => $postModel->getTranslation('excerpt', $locale) ?? $postModel->getTranslation('excerpt', 'de') ?? '',
|
||||
'excerpt' => $excerpt,
|
||||
'excerpt_html' => $this->convertMarkdownToHtml($excerpt),
|
||||
'content' => $markdown,
|
||||
'content_html' => $contentHtml,
|
||||
'content_html' => $contentHtmlWithIds,
|
||||
'headings' => $headings,
|
||||
'featured_image' => $postModel->featured_image ?? $postModel->banner_url ?? null,
|
||||
'published_at' => $postModel->published_at->toDateString(),
|
||||
'slug' => $postModel->slug,
|
||||
'url' => route('blog.show', ['locale' => $locale, 'slug' => $postModel->slug], absolute: true),
|
||||
'author' => $postModel->author ? [
|
||||
'name' => $postModel->author->name,
|
||||
] : null,
|
||||
'previous_post' => $this->presentAdjacentPost($postModel, $locale, direction: 'previous'),
|
||||
'next_post' => $this->presentAdjacentPost($postModel, $locale, direction: 'next'),
|
||||
];
|
||||
|
||||
return Inertia::render('marketing/BlogShow', compact('post'));
|
||||
@@ -481,4 +495,119 @@ class MarketingController extends Controller
|
||||
'requestedType' => $normalized,
|
||||
]);
|
||||
}
|
||||
|
||||
private function localizePaginationLabel(?string $label, string $locale): string
|
||||
{
|
||||
$decoded = trim(html_entity_decode(strip_tags($label ?? '')));
|
||||
|
||||
if ($decoded === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($decoded === '...') {
|
||||
return '…';
|
||||
}
|
||||
|
||||
$normalized = Str::lower($decoded);
|
||||
|
||||
if (Str::contains($normalized, ['previous', 'vorherige', 'zurück'])) {
|
||||
return __('marketing.blog.pagination.previous', [], $locale);
|
||||
}
|
||||
|
||||
if (Str::contains($normalized, ['next', 'weiter', 'nächste'])) {
|
||||
return __('marketing.blog.pagination.next', [], $locale);
|
||||
}
|
||||
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
private function markdownConverter(): MarkdownConverter
|
||||
{
|
||||
if (! $this->markdownConverter instanceof MarkdownConverter) {
|
||||
$environment = new Environment;
|
||||
$environment->addExtension(new CommonMarkCoreExtension);
|
||||
$environment->addExtension(new TableExtension);
|
||||
$environment->addExtension(new AutolinkExtension);
|
||||
$environment->addExtension(new StrikethroughExtension);
|
||||
$environment->addExtension(new TaskListExtension);
|
||||
|
||||
$this->markdownConverter = new MarkdownConverter($environment);
|
||||
}
|
||||
|
||||
return $this->markdownConverter;
|
||||
}
|
||||
|
||||
private function convertMarkdownToHtml(?string $markdown): string
|
||||
{
|
||||
if ($markdown === null || trim((string) $markdown) === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (string) $this->markdownConverter()->convert($markdown);
|
||||
}
|
||||
|
||||
private function decorateHeadings(string $html): array
|
||||
{
|
||||
$headings = [];
|
||||
$usedSlugs = [];
|
||||
|
||||
$updatedHtml = preg_replace_callback('/<h([2-3])>(.*?)<\/h\1>/', function ($matches) use (&$headings, &$usedSlugs) {
|
||||
$level = (int) $matches[1];
|
||||
$text = trim(strip_tags($matches[2]));
|
||||
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5);
|
||||
|
||||
if ($text === '') {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
$baseSlug = Str::slug($text) ?: 'section';
|
||||
$slug = $baseSlug;
|
||||
$iterator = 1;
|
||||
|
||||
while (in_array($slug, $usedSlugs, true)) {
|
||||
$slug = $baseSlug.'-'.(++$iterator);
|
||||
}
|
||||
|
||||
$usedSlugs[] = $slug;
|
||||
$headings[] = [
|
||||
'text' => $text,
|
||||
'slug' => $slug,
|
||||
'level' => $level,
|
||||
];
|
||||
|
||||
return sprintf('<h%s id="%s">%s</h%s>', $level, $slug, $matches[2], $level);
|
||||
}, $html) ?: $html;
|
||||
|
||||
return [$updatedHtml, $headings];
|
||||
}
|
||||
|
||||
private function presentAdjacentPost(BlogPost $current, string $locale, string $direction): ?array
|
||||
{
|
||||
$operator = $direction === 'previous' ? '<' : '>';
|
||||
$orderDirection = $direction === 'previous' ? 'desc' : 'asc';
|
||||
|
||||
$neighbor = BlogPost::query()
|
||||
->with('author')
|
||||
->whereHas('category', function ($query) {
|
||||
$query->where('slug', 'blog');
|
||||
})
|
||||
->where('is_published', true)
|
||||
->whereNotNull('published_at')
|
||||
->where('published_at', $operator, $current->published_at)
|
||||
->orderBy('published_at', $orderDirection)
|
||||
->first();
|
||||
|
||||
if (! $neighbor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$excerpt = $neighbor->getTranslation('excerpt', $locale) ?? $neighbor->getTranslation('excerpt', 'de') ?? '';
|
||||
|
||||
return [
|
||||
'slug' => $neighbor->slug,
|
||||
'title' => $neighbor->getTranslation('title', $locale) ?? $neighbor->getTranslation('title', 'de') ?? '',
|
||||
'excerpt' => $excerpt,
|
||||
'excerpt_html' => $this->convertMarkdownToHtml($excerpt),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user