*/ function legal_page(string $key): array { $fromCms = app(CmsContentService::class)->get('legal.'.$key); if (is_array($fromCms) && isset($fromCms['content'])) { return $fromCms; } return trans('b2in_legal.'.$key); } } if (! function_exists('cms')) { /** * Resolve a CMS content value by dot-notation key. * * First segment is the group, rest is the key: "home.hero.title" * Falls back to __() if no DB entry exists. * * @param array $replace */ function cms(string $key, array $replace = [], ?string $locale = null): mixed { return app(CmsContentService::class)->get($key, $replace, $locale); } } if (! function_exists('cms_theme_section')) { /** * Theme-Sektion: zuerst CMS (gemäß cms_section_map), sonst Lang b2in.themes.{theme}.{section}. * * @return array|string|mixed */ function cms_theme_section(string $sectionKey, ?string $theme = null): mixed { $theme = $theme ?? config('app.theme', 'b2in'); /** @var array $sections */ $sections = config('cms_section_map.sections', []); /** @var array $langKeys */ $langKeys = config('cms_section_map.lang_keys', []); if (isset($sections[$sectionKey])) { [$group, $key] = $sections[$sectionKey]; $fromCms = app(CmsContentService::class)->getIfExists("{$group}.{$key}"); if ($fromCms !== null) { return $fromCms; } } $langKey = $langKeys[$sectionKey] ?? $sectionKey; return trans("b2in.themes.{$theme}.{$langKey}"); } } if (! function_exists('theme_image_url')) { /** * Bild-URL für Theme-/CMS-Inhalte: zuerst Medienbibliothek (CmsMedia, oft nur Dateiname z. B. *.webp), * sonst statische Assets unter public/img/assets/ (Legacy-Pfade wie b2in/…). */ function theme_image_url(?string $value): string { if ($value === null) { return ''; } $trimmed = ltrim(trim($value), '/'); if ($trimmed === '') { return ''; } if (str_starts_with($trimmed, 'http://') || str_starts_with($trimmed, 'https://')) { return $trimmed; } $candidates = array_values(array_unique(array_filter([$trimmed, basename($trimmed)]))); foreach ($candidates as $candidate) { if (CmsMedia::query()->where('filename', $candidate)->exists()) { return media_url($candidate); } } if (str_contains($trimmed, '/')) { return asset('img/assets/'.$trimmed); } return media_url($trimmed); } } if (! function_exists('tcms')) { /** * Typed CMS – always returns a string. * * @param array $replace */ function tcms(string $key, array $replace = [], ?string $locale = null): string { $text = cms($key, $replace, $locale); return is_string($text) ? $text : (string) $text; } } if (! function_exists('cms_media_url')) { /** * Resolve a CMS content key (type=image) to a full media URL. * Falls back to asset('assets/images/...') if not found in media library. */ function cms_media_url(string $key, string $profile = ''): string { $filename = cms($key); if (! $filename || ! is_string($filename) || $filename === $key) { $fallback = str_replace('.', '/', $key); return asset('assets/images/'.basename($fallback)); } return media_url($filename, $profile); } } if (! function_exists('media_url')) { /** * Resolve a CmsMedia filename to its full storage URL. * Uses in-memory cache to avoid repeated DB queries. */ function media_url(?string $filename, string $profile = ''): string { if (! $filename || $filename === '') { return ''; } static $resolved = []; $cacheKey = $filename.'|'.$profile; if (isset($resolved[$cacheKey])) { return $resolved[$cacheKey]; } $media = CmsMedia::where('filename', $filename)->first(); if (! $media) { $resolved[$cacheKey] = asset('assets/images/'.$filename); return $resolved[$cacheKey]; } if ($profile && $media->hasConversion($profile)) { $resolved[$cacheKey] = $media->getConversionUrl($profile); } else { $resolved[$cacheKey] = $media->getUrl(); } return $resolved[$cacheKey]; } }