using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Hosting; using Oqtane.Shared; using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Controllers; using SZUAbsolventenverein.Module.HallOfFame.Services; using System.Linq; using System.Threading.Tasks; using QuestPDF.Fluent; using QuestPDF.Helpers; using QuestPDF.Infrastructure; namespace SZUAbsolventenverein.Module.HallOfFame.Controllers { [Route(ControllerRoutes.ApiRoute)] public class HallOfFamePdfController : ModuleControllerBase { private readonly IHallOfFameService _hallOfFameService; private readonly IWebHostEnvironment _environment; public HallOfFamePdfController(IHallOfFameService hallOfFameService, ILogManager logger, IHttpContextAccessor accessor, IWebHostEnvironment environment) : base(logger, accessor) { _hallOfFameService = hallOfFameService; _environment = environment; } // GET: api/?moduleid=x&download=true/false [HttpGet] [Authorize(Policy = PolicyNames.ViewModule)] public async Task Get(string moduleid, bool download = false) { int ModuleId; if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId)) { var entries = await _hallOfFameService.GetHallOfFamesAsync(ModuleId); var publishedEntries = entries.Where(e => e.Status == "Published").ToList(); var document = Document.Create(container => { foreach (var entry in publishedEntries) { // Bild laden falls vorhanden byte[] imageBytes = null; if (!string.IsNullOrEmpty(entry.Image)) { try { var fullImagePath = System.IO.Path.Combine( _environment.WebRootPath, entry.Image.TrimStart('/')); if (System.IO.File.Exists(fullImagePath)) imageBytes = System.IO.File.ReadAllBytes(fullImagePath); } catch { } } container.Page(page => { page.Size(PageSizes.A4); page.Margin(0); page.Content().Layers(layers => { // Hintergrund-Layer: Bild als Vollbild if (imageBytes != null) { layers.Layer().Image(imageBytes).FitUnproportionally(); } else { layers.Layer().Background(Colors.Grey.Darken3); } // Inhalt-Layer (PrimaryLayer bestimmt die Größe) layers.PrimaryLayer() .Column(column => { // === OBEN: Name und Jahr in halbtransparenter grauer Box === column.Item() .Background("#AA9E9E9E") // halbtransparentes Grau .Padding(20) .Row(row => { row.RelativeItem().Column(inner => { inner.Item().Text(entry.Name) .FontSize(28).Bold().FontColor(Colors.White); inner.Item().Text($"Jahrgang {entry.Year}") .FontSize(14).FontColor(Colors.White); }); }); // === UNTEN: Beschreibung am Seitenende === var description = entry.Description ?? ""; var sections = description.Split('\n') .Select(s => s.Trim()) .Where(s => !string.IsNullOrEmpty(s)) .ToArray(); column.Item().ExtendVertical().AlignBottom() .Background("#CC333333") // dunkles halbtransparentes Overlay .Padding(25) .Column(descColumn => { if (sections.Length > 0) { descColumn.Item().PaddingBottom(10) .Text("Beschreibung:") .FontSize(16).Bold().FontColor(Colors.White); foreach (var line in sections) { descColumn.Item().PaddingBottom(6) .Text(line) .FontSize(11).FontColor(Colors.White) .LineHeight(1.4f); } } }); }); }); }); } }); byte[] pdfBytes = document.GeneratePdf(); if (download) { return File(pdfBytes, "application/pdf", "HallOfFame.pdf"); } // Inline: PDF wird im Browser angezeigt (Vorschau) return File(pdfBytes, "application/pdf"); } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame PDF Get Attempt {ModuleId}", moduleid); return Forbid(); } } } }