diff --git a/app/Filament/Admin/Resources/HeaderTemplates/Schemas/HeaderTemplateForm.php b/app/Filament/Admin/Resources/HeaderTemplates/Schemas/HeaderTemplateForm.php index a34b981..9266bd3 100644 --- a/app/Filament/Admin/Resources/HeaderTemplates/Schemas/HeaderTemplateForm.php +++ b/app/Filament/Admin/Resources/HeaderTemplates/Schemas/HeaderTemplateForm.php @@ -8,6 +8,7 @@ use Filament\Forms\Components\Toggle; use Filament\Schemas\Components\Section; use Filament\Schemas\Components\View; use Filament\Schemas\Schema; +use Filament\Forms\Components\CodeEditor\Enums\Language; class HeaderTemplateForm { @@ -27,6 +28,7 @@ class HeaderTemplateForm ->label(__('header-templates.field_html_content')) ->required() ->live(onBlur: false) // Real-time updates + ->language(Language::Html) ->columnSpanFull() ->helperText(__('header-templates.field_html_content_help')) ->extraAttributes([ diff --git a/app/Filament/Admin/Resources/SectionTemplates/Schemas/SectionTemplateForm.php b/app/Filament/Admin/Resources/SectionTemplates/Schemas/SectionTemplateForm.php index 6c2ca56..0e68a02 100644 --- a/app/Filament/Admin/Resources/SectionTemplates/Schemas/SectionTemplateForm.php +++ b/app/Filament/Admin/Resources/SectionTemplates/Schemas/SectionTemplateForm.php @@ -8,6 +8,7 @@ use Filament\Forms\Components\Toggle; use Filament\Schemas\Components\Section; use Filament\Schemas\Components\View; use Filament\Schemas\Schema; +use Filament\Forms\Components\CodeEditor\Enums\Language; class SectionTemplateForm { @@ -27,6 +28,7 @@ class SectionTemplateForm ->label(__('section-templates.field_html_content')) ->required() ->live(onBlur: false) // Real-time updates + ->language(Language::Html) ->columnSpanFull() ->helperText(__('section-templates.field_html_content_help')) ->extraAttributes([ diff --git a/app/Services/TemplatePreviewService.php b/app/Services/TemplatePreviewService.php index bb57087..1f6b301 100644 --- a/app/Services/TemplatePreviewService.php +++ b/app/Services/TemplatePreviewService.php @@ -106,6 +106,90 @@ class TemplatePreviewService return $html; } + /** + * Convert relative asset paths to absolute URLs in HTML content + * + * @param string $html The HTML content + * @return string HTML with absolute URLs + */ + public static function convertRelativePathsToAbsolute(string $html): string + { + // Base URL for assets + $baseUrl = rtrim(url('/'), '/'); + + // Pattern 1: Match attributes with relative paths (src, href, data-src, etc.) + // Matches: src="assets/...", href="assets/...", data-src="assets/..." + // Also matches: src="/assets/...", href="./assets/...", etc. + $html = preg_replace_callback( + '/(\s+(?:src|href|data-src|data-href|srcset|action|poster|background)\s*=\s*["\'])((?:(?:\.\/|\.\.\/)*)?(?:assets|html|images?|img|css|js|fonts?|media|uploads?)[\/\w\-\.]+)(["\'])/i', + function ($matches) use ($baseUrl) { + $path = $matches[2]; + + // Skip if already absolute URL (http://, https://, //, data:, mailto:, etc.) + if (preg_match('/^(https?:|\/\/|data:|mailto:|#|javascript:)/i', $path)) { + return $matches[0]; + } + + // Remove leading slash if exists (we'll add it via asset helper) + $path = ltrim($path, '/'); + + // Convert to absolute URL using Laravel's asset helper + $absoluteUrl = asset($path); + + return $matches[1] . $absoluteUrl . $matches[3]; + }, + $html + ); + + // Pattern 2: Match CSS url() functions with relative paths + // Matches: url('assets/...'), url("assets/..."), url(assets/...) + $html = preg_replace_callback( + '/url\s*\(\s*(["\']?)((?:(?:\.\/|\.\.\/)*)?(?:assets|html|images?|img|css|js|fonts?|media|uploads?)[\/\w\-\.]+)\1\s*\)/i', + function ($matches) use ($baseUrl) { + $path = $matches[2]; + + // Skip if already absolute URL + if (preg_match('/^(https?:|\/\/|data:)/i', $path)) { + return $matches[0]; + } + + // Remove leading slash + $path = ltrim($path, '/'); + + // Convert to absolute URL + $absoluteUrl = asset($path); + + return 'url(' . $matches[1] . $absoluteUrl . $matches[1] . ')'; + }, + $html + ); + + // Pattern 3: Match inline style attributes with background-image or background + // This is already covered by Pattern 2 (url()), but we ensure it works + $html = preg_replace_callback( + '/(style\s*=\s*["\'])([^"\']*)(["\'])/i', + function ($matches) { + // Process url() in style attribute + return $matches[1] . preg_replace_callback( + '/url\s*\(\s*(["\']?)((?:(?:\.\/|\.\.\/)*)?(?:assets|html|images?|img|css|js|fonts?|media|uploads?)[\/\w\-\.]+)\1\s*\)/i', + function ($urlMatches) { + $path = $urlMatches[2]; + if (preg_match('/^(https?:|\/\/|data:)/i', $path)) { + return $urlMatches[0]; + } + $path = ltrim($path, '/'); + $absoluteUrl = asset($path); + return 'url(' . $urlMatches[1] . $absoluteUrl . $urlMatches[1] . ')'; + }, + $matches[2] + ) . $matches[3]; + }, + $html + ); + + return $html; + } + /** * Get full HTML structure for preview * @@ -121,6 +205,10 @@ class TemplatePreviewService $viewport = $meta['viewport'] ?? 'width=device-width, initial-scale=1'; $charset = $meta['charset'] ?? 'utf-8'; + $baseUrl = rtrim(url('/'), '/'); + + // Convert relative paths to absolute URLs in content + $content = self::convertRelativePathsToAbsolute($content); // Wrap content based on type $bodyContent = match($type) { @@ -137,6 +225,7 @@ class TemplatePreviewService