Files
LehrerApp/LehrerApp.TemplateDesigner/StarterTemplateLibrary.cs
T
admin 2f2761caf8
CI / build-and-test (push) Canceled after 0s
QuestPDF-Vorlagensystem und Designer hinzufügen
2026-08-30 15:37:22 +02:00

123 lines
5.1 KiB
C#

using System.IO.Compression;
using LehrerApp.Templating;
namespace LehrerApp.TemplateDesigner;
public sealed record StarterTemplateItem(
string Id, string Name, string Description, string PackagePath, DateTime UpdatedAtUtc)
{
public string Display => string.IsNullOrWhiteSpace(Description) ? Name : $"{Name} · {Description}";
}
public sealed class StarterTemplateLibrary
{
private readonly string _directory;
private readonly ITemplateLoader _loader;
public StarterTemplateLibrary(string? directory = null, ITemplateLoader? loader = null)
{
_directory = directory ?? Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"LehrerApp", "TemplateDesigner", "starter-templates");
Directory.CreateDirectory(_directory);
_loader = loader ?? new TemplateLoader();
}
public IReadOnlyList<StarterTemplateItem> GetAll()
{
var result = new List<StarterTemplateItem>();
foreach (var path in Directory.EnumerateFiles(_directory, $"*{TemplatePackage.Extension}"))
{
try
{
var template = _loader.LoadFromPackage(path);
result.Add(ToItem(template.Manifest, path));
}
catch (Exception ex) when (ex is IOException or InvalidDataException or TemplateValidationException) { }
}
return result.OrderBy(x => x.Name, StringComparer.CurrentCultureIgnoreCase).ToList();
}
public StarterTemplateItem Save(TemplateManifest manifest, string layoutSource,
IReadOnlyDictionary<string, byte[]> assets)
{
var path = Path.Combine(_directory, SafeId(manifest.Id) + TemplatePackage.Extension);
TemplatePackage.Create(path, manifest, layoutSource, assets);
return ToItem(manifest, path);
}
public StarterTemplateItem Import(string sourcePath)
{
var (template, layout) = LoadPackage(sourcePath);
return Save(template.Manifest, layout, template.Assets);
}
public void Export(StarterTemplateItem item, string destinationPath)
{
if (!File.Exists(item.PackagePath)) throw new FileNotFoundException("Ausgangsvorlage nicht gefunden.", item.PackagePath);
if (!string.Equals(Path.GetExtension(destinationPath), TemplatePackage.Extension, StringComparison.OrdinalIgnoreCase))
destinationPath += TemplatePackage.Extension;
var directory = Path.GetDirectoryName(destinationPath);
if (!string.IsNullOrEmpty(directory)) Directory.CreateDirectory(directory);
File.Copy(item.PackagePath, destinationPath, overwrite: true);
}
public StarterTemplateItem Duplicate(StarterTemplateItem item)
{
var (template, layout) = Load(item);
var id = CreateUniqueId(template.Manifest.Id + "-kopie");
var manifest = CopyManifest(template.Manifest, id, template.Manifest.Name + " - Kopie");
return Save(manifest, layout, template.Assets);
}
public (LoadedTemplate Template, string LayoutSource) Load(StarterTemplateItem item) =>
LoadPackage(item.PackagePath);
public void Delete(StarterTemplateItem item)
{
if (File.Exists(item.PackagePath)) File.Delete(item.PackagePath);
}
private (LoadedTemplate Template, string LayoutSource) LoadPackage(string path)
{
var template = _loader.LoadFromPackage(path);
using var archive = ZipFile.OpenRead(path);
var layoutEntry = archive.Entries.FirstOrDefault(x => x.FullName.Equals(
template.Manifest.LayoutFile.Replace('\\', '/'), StringComparison.OrdinalIgnoreCase))
?? throw new InvalidDataException($"Layoutdatei „{template.Manifest.LayoutFile}“ fehlt.");
using var reader = new StreamReader(layoutEntry.Open());
return (template, reader.ReadToEnd());
}
public string CreateUniqueId(string baseId)
{
var existing = GetAll().Select(x => x.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
if (!existing.Contains(baseId)) return baseId;
for (var index = 2; ; index++)
{
var candidate = $"{baseId}-{index}";
if (!existing.Contains(candidate)) return candidate;
}
}
private static TemplateManifest CopyManifest(TemplateManifest source, string id, string name) => new()
{
SchemaVersion = source.SchemaVersion,
Id = id,
Name = name,
Description = source.Description,
PageSize = new(source.PageSize.Width, source.PageSize.Height, source.PageSize.Unit),
LayoutFile = source.LayoutFile,
Placeholders = source.Placeholders.Select(x => new PlaceholderDefinition(x.Name, x.Type, x.Required)).ToList(),
};
private static StarterTemplateItem ToItem(TemplateManifest manifest, string path) =>
new(manifest.Id, manifest.Name, manifest.Description, path, File.GetLastWriteTimeUtc(path));
private static string SafeId(string id)
{
var safe = string.Concat(id.Select(c => char.IsAsciiLetterOrDigit(c) || c == '-' ? c : '-')).Trim('-');
return safe.Length > 0 ? safe : throw new InvalidDataException("Die Vorlagen-ID ist ungültig.");
}
}