This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace LehrerApp.Templating;
|
||||
|
||||
public static class TemplatePackage
|
||||
{
|
||||
public const string Extension = ".lavorlage";
|
||||
|
||||
public static void Create(string outputPath, TemplateManifest manifest, string layoutSource,
|
||||
IReadOnlyDictionary<string, byte[]> assets)
|
||||
{
|
||||
if (!string.Equals(Path.GetExtension(outputPath), Extension, StringComparison.OrdinalIgnoreCase))
|
||||
outputPath += Extension;
|
||||
var directory = Path.GetDirectoryName(outputPath);
|
||||
if (!string.IsNullOrEmpty(directory)) Directory.CreateDirectory(directory);
|
||||
var temporary = outputPath + ".tmp";
|
||||
try
|
||||
{
|
||||
using (var archive = ZipFile.Open(temporary, ZipArchiveMode.Create))
|
||||
{
|
||||
WriteText(archive, "manifest.json", JsonSerializer.Serialize(manifest, TemplateLoader.JsonOptions));
|
||||
WriteText(archive, manifest.LayoutFile, layoutSource);
|
||||
foreach (var asset in assets)
|
||||
{
|
||||
if (!TemplateLoader.IsSafeRelativePath(asset.Key))
|
||||
throw new InvalidDataException($"Unsicherer Assetpfad „{asset.Key}“.");
|
||||
var entry = archive.CreateEntry(TemplateLoader.Normalize(asset.Key), CompressionLevel.Optimal);
|
||||
using var stream = entry.Open(); stream.Write(asset.Value);
|
||||
}
|
||||
}
|
||||
using (var verify = File.OpenRead(temporary)) new TemplateLoader().LoadFromPackage(verify, Path.GetFileName(outputPath));
|
||||
File.Move(temporary, outputPath, overwrite: true);
|
||||
}
|
||||
finally { if (File.Exists(temporary)) File.Delete(temporary); }
|
||||
}
|
||||
|
||||
private static void WriteText(ZipArchive archive, string path, string content)
|
||||
{
|
||||
var entry = archive.CreateEntry(TemplateLoader.Normalize(path), CompressionLevel.Optimal);
|
||||
using var writer = new StreamWriter(entry.Open()); writer.Write(content);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record InstalledTemplate(string Id, string Name, string Description, string PackagePath,
|
||||
DateTime ImportedAtUtc);
|
||||
|
||||
public sealed class TemplateStore
|
||||
{
|
||||
private readonly string _directory;
|
||||
private readonly ITemplateLoader _loader;
|
||||
|
||||
public TemplateStore(string appDataPath, ITemplateLoader? loader = null)
|
||||
{
|
||||
_directory = Path.Combine(appDataPath, "letter-template-packages");
|
||||
Directory.CreateDirectory(_directory);
|
||||
_loader = loader ?? new TemplateLoader();
|
||||
}
|
||||
|
||||
public IReadOnlyList<InstalledTemplate> GetTemplates()
|
||||
{
|
||||
var result = new List<InstalledTemplate>();
|
||||
foreach (var path in Directory.EnumerateFiles(_directory, $"*{TemplatePackage.Extension}"))
|
||||
{
|
||||
try
|
||||
{
|
||||
var loaded = _loader.LoadFromPackage(path);
|
||||
result.Add(new(loaded.Manifest.Id, loaded.Manifest.Name, loaded.Manifest.Description, path,
|
||||
File.GetLastWriteTimeUtc(path)));
|
||||
}
|
||||
catch (Exception ex) when (ex is InvalidDataException or TemplateValidationException) { }
|
||||
}
|
||||
return result.OrderBy(x => x.Name, StringComparer.CurrentCultureIgnoreCase).ToList();
|
||||
}
|
||||
|
||||
public InstalledTemplate Import(string sourcePath)
|
||||
{
|
||||
var loaded = _loader.LoadFromPackage(sourcePath);
|
||||
var safeId = string.Concat(loaded.Manifest.Id.Select(c => char.IsAsciiLetterOrDigit(c) || c == '-' ? c : '-')).Trim('-');
|
||||
if (safeId.Length == 0) throw new InvalidDataException("Die Vorlagen-ID ist ungültig.");
|
||||
var destination = Path.Combine(_directory, safeId + TemplatePackage.Extension);
|
||||
var temporary = destination + ".tmp";
|
||||
File.Copy(sourcePath, temporary, overwrite: true);
|
||||
File.Move(temporary, destination, overwrite: true);
|
||||
return new(loaded.Manifest.Id, loaded.Manifest.Name, loaded.Manifest.Description, destination, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
public LoadedTemplate Load(InstalledTemplate template) => _loader.LoadFromPackage(template.PackagePath);
|
||||
public void Delete(string id)
|
||||
{
|
||||
var match = GetTemplates().FirstOrDefault(x => x.Id == id);
|
||||
if (match is not null && File.Exists(match.PackagePath)) File.Delete(match.PackagePath);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user