ensureBelongsToEvent($event, $joinToken); $layouts = JoinTokenLayoutRegistry::toResponse(function (string $layoutId, string $format) use ($event, $joinToken) { return route('tenant.events.join-tokens.layouts.download', [ 'event' => $event, 'joinToken' => $joinToken, 'layout' => $layoutId, 'format' => $format, ]); }); return response()->json([ 'data' => $layouts, ]); } public function download(Request $request, Event $event, EventJoinToken $joinToken, string $layout, string $format) { $this->ensureBelongsToEvent($event, $joinToken); $layoutConfig = JoinTokenLayoutRegistry::find($layout); if (! $layoutConfig) { abort(404, 'Layout nicht gefunden.'); } if (! in_array($format, ['pdf', 'svg'], true)) { abort(404, 'Unbekanntes Exportformat.'); } $layoutConfig = $this->applyCustomization($layoutConfig, $joinToken); $tokenUrl = url('/e/'.$joinToken->token); $qrPngDataUri = 'data:image/png;base64,'.base64_encode( QrCode::format('png') ->margin(0) ->size($layoutConfig['qr']['size_px']) ->generate($tokenUrl) ); $backgroundStyle = $this->buildBackgroundStyle($layoutConfig); $eventName = $this->resolveEventName($event); $viewData = [ 'layout' => $layoutConfig, 'event' => $event, 'eventName' => $eventName, 'token' => $joinToken, 'tokenUrl' => $tokenUrl, 'qrPngDataUri' => $qrPngDataUri, 'backgroundStyle' => $backgroundStyle, 'customization' => $joinToken->metadata['layout_customization'] ?? null, ]; $filename = sprintf('%s-%s.%s', Str::slug($eventName ?: 'event'), $layoutConfig['id'], $format); if ($format === 'svg') { $svg = view('layouts.join-token.svg', $viewData)->render(); return response($svg) ->header('Content-Type', 'image/svg+xml') ->header('Content-Disposition', 'attachment; filename="'.$filename.'"'); } $html = view('layouts.join-token.pdf', $viewData)->render(); $options = new Options; $options->set('isHtml5ParserEnabled', true); $options->set('isRemoteEnabled', true); $options->set('defaultFont', 'Helvetica'); $dompdf = new Dompdf($options); $dompdf->setPaper(strtoupper($layoutConfig['paper']), $layoutConfig['orientation'] === 'landscape' ? 'landscape' : 'portrait'); $dompdf->loadHtml($html, 'UTF-8'); $dompdf->render(); return response($dompdf->output()) ->header('Content-Type', 'application/pdf') ->header('Content-Disposition', 'attachment; filename="'.$filename.'"'); } private function ensureBelongsToEvent(Event $event, EventJoinToken $joinToken): void { if ($joinToken->event_id !== $event->id) { abort(404); } } private function resolveEventName(Event $event): string { $name = $event->name; if (is_array($name)) { $locale = $event->default_locale ?? 'de'; return $name[$locale] ?? $name['de'] ?? reset($name) ?: 'Event'; } return is_string($name) && $name !== '' ? $name : 'Event'; } private function applyCustomization(array $layout, EventJoinToken $joinToken): array { $customization = data_get($joinToken->metadata, 'layout_customization'); if (! is_array($customization)) { return $layout; } $layoutId = $customization['layout_id'] ?? null; if (is_string($layoutId) && isset($layout['id']) && $layoutId !== $layout['id']) { // Allow customization to target a specific layout; if mismatch, skip style overrides. // General text overrides are still applied below. } $colorKeys = [ 'accent' => 'accent_color', 'text' => 'text_color', 'background' => 'background_color', 'secondary' => 'secondary_color', 'badge' => 'badge_color', ]; foreach ($colorKeys as $layoutKey => $customKey) { if (isset($customization[$customKey]) && is_string($customization[$customKey])) { $layout[$layoutKey] = $customization[$customKey]; } } if (isset($customization['background_gradient']) && is_array($customization['background_gradient'])) { $layout['background_gradient'] = $customization['background_gradient']; } foreach (['headline' => 'name', 'subtitle', 'description', 'badge_label', 'instructions_heading', 'link_heading', 'cta_label', 'cta_caption', 'link_label'] as $customKey => $layoutKey) { if (isset($customization[$customKey]) && is_string($customization[$customKey])) { $layout[$layoutKey] = $customization[$customKey]; } } if (array_key_exists('instructions', $customization) && is_array($customization['instructions'])) { $layout['instructions'] = array_values(array_filter($customization['instructions'], fn ($value) => is_string($value) && trim($value) !== '')); } if (! empty($customization['logo_data_url']) && is_string($customization['logo_data_url'])) { $layout['logo_url'] = $customization['logo_data_url']; } elseif (! empty($customization['logo_url']) && is_string($customization['logo_url'])) { $layout['logo_url'] = $customization['logo_url']; } return $layout; } private function buildBackgroundStyle(array $layout): string { $gradient = $layout['background_gradient'] ?? null; if (is_array($gradient) && ! empty($gradient['stops'])) { $angle = $gradient['angle'] ?? 180; $stops = implode(',', $gradient['stops']); return sprintf('linear-gradient(%ddeg,%s)', $angle, $stops); } return $layout['background'] ?? '#FFFFFF'; } }