Setup Git Repository Parsing

This commit is contained in:
Simon Martens
2023-09-10 01:09:20 +02:00
parent 4e3c65dc6f
commit 8fd0050cf3
69 changed files with 1228 additions and 1461 deletions

View File

@@ -0,0 +1,85 @@
using System.Timers;
namespace HaWeb.FileHelpers;
public class ConfigurationMonitor {
private System.Timers.Timer? _timer;
private (string, byte[])[]? _h;
private IServiceProvider _serviceProvider;
public ConfigurationMonitor(string[] paths, IServiceProvider services) {
_h = _getHash(paths);
_serviceProvider = services;
}
private static (string, byte[])[]? _getHash(string[] paths) {
if (paths == null || !paths.Any()) return null;
var ret = new List<(string, byte[])>();
foreach(var c in paths)
ret.Add((c, _computeHash(c)));
return ret.ToArray();
}
private bool isEqual((string, byte[])[]? _h1, (string, byte[])[]? _h2) {
if (_h1 == null && _h2 == null) return true;
if (_h1 == null && _h2 != null) return false;
if (_h2 == null && _h1 != null) return false;
if (_h1!.Count() != _h2!.Count()) return false;
foreach (var h1 in _h1!) {
foreach (var h2 in _h2!) {
if (h1.Item1 == h2.Item1 && !Enumerable.SequenceEqual(h1.Item2, h2.Item2)) return false;
}
}
return true;
}
public void InvokeChanged(string[] paths) {
var h = _getHash(paths);
if (_timer == null && !isEqual(h, _h)) {
_h = h;
_timer = new(5000) { AutoReset = false };
_timer.Enabled = true;
_timer.Elapsed += Action;
}
}
private void Action(Object source, System.Timers.ElapsedEventArgs e) {
Console.WriteLine("Configuration changed (ConfigurationMonitor Class)");
using IServiceScope serviceScope = _serviceProvider.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;
var cP = provider.GetRequiredService<IConfiguration>();
var hP = provider.GetRequiredService<IHaDocumentWrappper>();
hP.ParseConfiguration(cP);
var fP = provider.GetRequiredService<IXMLFileProvider>();
fP.Reload(cP);
// _lifetime.StopApplication();
_timer = null;
}
private static byte[] _computeHash(string filePath) {
var runCount = 1;
while(runCount < 4) {
try {
if (File.Exists(filePath))
using (var fs = File.OpenRead(filePath)) {
return System.Security.Cryptography.SHA1
.Create().ComputeHash(fs);
}
else {
throw new FileNotFoundException();
}
}
catch (IOException ex) {
if (runCount == 3)
throw;
Thread.Sleep(TimeSpan.FromSeconds(Math.Pow(2, runCount)));
runCount++;
}
}
return new byte[20];
}
}

View File

@@ -9,12 +9,13 @@ using HaXMLReader.Interfaces;
using HaWeb.SearchHelpers;
using HaWeb.XMLParser;
using System.Text;
using System.Xml.Linq;
using System.Diagnostics;
public class HaDocumentWrapper : IHaDocumentWrappper {
private ILibrary Library;
private IXMLProvider _xmlProvider;
private IXMLService _xmlService;
private string _filepath;
private IFileInfo _ActiveFile;
private ILibrary? Library;
private IXMLInteractionService _xmlService;
private int _startYear;
private int _endYear;
private List<Person>? _availablePersons;
@@ -22,23 +23,14 @@ public class HaDocumentWrapper : IHaDocumentWrappper {
// public List<SearchHelpers.CollectedItem>? SearchableLetters { get; private set; }
public HaDocumentWrapper(IXMLProvider xmlProvider, IXMLService service, IConfiguration configuration) {
_xmlProvider = xmlProvider;
public HaDocumentWrapper(IXMLInteractionService service, IConfiguration configuration) {
_xmlService = service;
ParseConfiguration(configuration);
}
public void ParseConfiguration(IConfiguration configuration) {
_startYear = configuration.GetValue<int>("AvailableStartYear");
_endYear = configuration.GetValue<int>("AvailableEndYear");
var filelist = xmlProvider.GetHamannFiles();
if (filelist != null && filelist.Any()) {
_AutoLoad(filelist);
}
// Use Fallback library
if (Library == null) {
var options = new HaWeb.Settings.HaDocumentOptions();
if (SetLibrary(options.HamannXMLFilePath) == null) {
throw new Exception("Die Fallback Hamann.xml unter " + options.HamannXMLFilePath + " kann nicht geparst werden.");
}
}
}
public List<Person>? GetAvailablePersons() => _availablePersons;
@@ -49,26 +41,29 @@ public class HaDocumentWrapper : IHaDocumentWrappper {
public int GetEndYear() => _endYear;
public void SetEndYear(int end) {
this._endYear = end;
SetLibrary(_filepath);
}
public IFileInfo GetActiveFile() => _ActiveFile;
public ILibrary? SetLibrary(string filepath, ModelStateDictionary? ModelState = null) {
// 1. Set ILibrary
public ILibrary? SetLibrary(IFileInfo? file, XDocument? doc, ModelStateDictionary? ModelState = null) {
// Handle null on file & doc
var path = file == null ? new HaWeb.Settings.HaDocumentOptions().HamannXMLFilePath : file.PhysicalPath;
if (doc == null) doc = XDocument.Load(path, LoadOptions.PreserveWhitespace);
// 1. Parse the Document, create search Index
if (_xmlService != null)
_xmlService.CreateSearchables(doc);
// 2. Set ILibrary
try {
Library = HaDocument.Document.Create(new HaWeb.Settings.HaDocumentOptions() { HamannXMLFilePath = filepath, AvailableYearRange = (_startYear, _endYear) });
Library = HaDocument.Document.Create(new HaWeb.Settings.HaDocumentOptions() { HamannXMLFilePath = path, AvailableYearRange = (_startYear, _endYear) }, doc.Root);
} catch (Exception ex) {
if (ModelState != null) ModelState.AddModelError("Error", "Das Dokument konnte nicht geparst werden: " + ex.Message);
return null;
}
// 1a. Set Available Persons
// 3a. Set Available Persons
var persons = Library.Metas.SelectMany(x => x.Value.Senders.Union(x.Value.Receivers)).Distinct();
_availablePersons = persons.Select(x => Library.Persons[x]).OrderBy(x => x.Surname).ThenBy(x => x.Prename).ToList();
// 1b. Setup a Dictionary with available Person ovierview Pages
// 3b. Setup a Dictionary with available Person ovierview Pages
_personsWithLetters = new Dictionary<string, Person>();
var availablePersonPages = Library.Persons.Where(x => !String.IsNullOrWhiteSpace(x.Value.Komm));
foreach (var p in availablePersonPages) {
@@ -77,30 +72,12 @@ public class HaDocumentWrapper : IHaDocumentWrappper {
}
}
// 2. Set Library in Production, collect some Objects
if (_xmlService != null)
_xmlService.SetInProduction(System.Xml.Linq.XDocument.Load(filepath, System.Xml.Linq.LoadOptions.PreserveWhitespace));
// 3. Set Filepath
_filepath = filepath;
// 4. Set info on loaded file
_ActiveFile = file;
return Library;
}
public ILibrary GetLibrary() {
public ILibrary? GetLibrary() {
return Library;
}
private void _AutoLoad(List<IFileInfo> files) {
var orderdlist = files.OrderByDescending(x => x.LastModified);
foreach (var item in orderdlist) {
if (SetLibrary(item.PhysicalPath) != null) {
_xmlProvider.SetInProduction(item);
return;
}
}
}
private string _prepareSearch(HaDocument.Interfaces.ISearchable objecttoseach) {
return SearchHelpers.StringHelpers.NormalizeWhiteSpace(objecttoseach.Element, ' ', false);
}
}

View File

@@ -0,0 +1,6 @@
namespace HaWeb.FileHelpers;
public interface IConfigurationMonitor
{
}

View File

@@ -3,14 +3,16 @@ using HaDocument.Interfaces;
using HaDocument.Models;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using HaXMLReader.Interfaces;
using Microsoft.Extensions.FileProviders;
using System.Xml.Linq;
public interface IHaDocumentWrappper {
public ILibrary? SetLibrary(string filepath, ModelStateDictionary ModelState);
public ILibrary GetLibrary();
public ILibrary? SetLibrary(IFileInfo? file, XDocument? doc, ModelStateDictionary? ModelState);
public ILibrary? GetLibrary();
public void ParseConfiguration(IConfiguration configuration);
public int GetStartYear();
public int GetEndYear();
public IFileInfo GetActiveFile();
public List<Person>? GetAvailablePersons();
public Dictionary<string, Person>? GetPersonsWithLetters();
public void SetEndYear(int end);
}

View File

@@ -0,0 +1,17 @@
namespace HaWeb.FileHelpers;
using Microsoft.Extensions.FileProviders;
using System.Xml.Linq;
using HaWeb.Models;
using Microsoft.AspNetCore.Mvc.ModelBinding;
public interface IXMLFileProvider {
public List<IFileInfo>? GetWorkingTreeFiles();
public IFileInfo? SaveHamannFile(XElement element, string basefilepath, ModelStateDictionary ModelState);
public List<IFileInfo>? GetHamannFiles();
public (DateTime PullTime, string Hash)? GetGitData();
public void Reload(IConfiguration config);
public bool HasChanged();
public void DeleteHamannFile(string filename);
public void Scan();
}

View File

@@ -1,16 +0,0 @@
namespace HaWeb.FileHelpers;
using Microsoft.Extensions.FileProviders;
using System.Xml.Linq;
using HaWeb.Models;
using Microsoft.AspNetCore.Mvc.ModelBinding;
public interface IXMLProvider {
public FileList? GetFiles(string prefix);
public Task Save(XMLRootDocument doc, string basefilepath, ModelStateDictionary ModelState);
public Task<IFileInfo?> SaveHamannFile(XElement element, string basefilepath, ModelStateDictionary ModelState);
public List<IFileInfo>? GetHamannFiles();
public IFileInfo? GetInProduction();
public void SetInProduction(IFileInfo info);
public void DeleteHamannFile(string filename);
}

View File

@@ -1,32 +0,0 @@
namespace HaWeb.FileHelpers;
using System.Xml.Linq;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.Text;
using System.Xml;
public static class XDocumentFileHelper {
private readonly static XmlReaderSettings _Settings = new XmlReaderSettings() {
CloseInput = true,
CheckCharacters = false,
ConformanceLevel = ConformanceLevel.Fragment,
IgnoreComments = true,
IgnoreProcessingInstructions = true,
IgnoreWhitespace = false
};
public static async Task<XDocument?> ProcessStreamedFile(byte[] bytes, ModelStateDictionary modelState) {
try {
using (var stream = new MemoryStream(bytes)) {
using (var xmlreader = XmlReader.Create(stream, _Settings)) {
return XDocument.Load(xmlreader, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
}
}
} catch (Exception ex) {
modelState.AddModelError("Error", $"Kein gültiges XML-Dokument geladen. Error: {ex.Message}");
}
return null;
}
}

View File

@@ -54,152 +54,54 @@ public static class XMLFileHelpers {
}
};
// Unused as of rn, used to take a file and do the same sanity checks as below
// public static async Task<byte[]> ProcessFormFile<T>(IFormFile formFile, ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit)
// {
// var fieldDisplayName = string.Empty;
// // Use reflection to obtain the display name for the model
// // property associated with this IFormFile. If a display
// // name isn't found, error messages simply won't show
// // a display name.
// MemberInfo property =
// typeof(T).GetProperty(
// formFile.Name.Substring(formFile.Name.IndexOf(".",
// StringComparison.Ordinal) + 1));
// if (property != null)
// {
// if (property.GetCustomAttribute(typeof(DisplayAttribute)) is
// DisplayAttribute displayAttribute)
// {
// fieldDisplayName = $"{displayAttribute.Name} ";
// }
// }
// // Don't trust the file name sent by the client. To display
// // the file name, HTML-encode the value.
// var trustedFileNameForDisplay = WebUtility.HtmlEncode(
// formFile.FileName);
// // Check the file length. This check doesn't catch files that only have
// // a BOM as their content.
// if (formFile.Length == 0)
// {
// modelState.AddModelError(formFile.Name,
// $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty.");
// return Array.Empty<byte>();
// }
// if (formFile.Length > sizeLimit)
// {
// var megabyteSizeLimit = sizeLimit / 1048576;
// modelState.AddModelError(formFile.Name,
// $"{fieldDisplayName}({trustedFileNameForDisplay}) exceeds " +
// $"{megabyteSizeLimit:N1} MB.");
// return Array.Empty<byte>();
// }
// try
// {
// using (var memoryStream = new MemoryStream())
// {
// await formFile.CopyToAsync(memoryStream);
// // Check the content length in case the file's only
// // content was a BOM and the content is actually
// // empty after removing the BOM.
// if (memoryStream.Length == 0)
// {
// modelState.AddModelError(formFile.Name,
// $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty.");
// }
// if (!IsValidFileExtensionAndSignature(
// formFile.FileName, memoryStream, permittedExtensions))
// {
// modelState.AddModelError(formFile.Name,
// $"{fieldDisplayName}({trustedFileNameForDisplay}) file " +
// "type isn't permitted or the file's signature " +
// "doesn't match the file's extension.");
// }
// else
// {
// return memoryStream.ToArray();
// }
// }
// }
// catch (Exception ex)
// {
// modelState.AddModelError(formFile.Name,
// $"{fieldDisplayName}({trustedFileNameForDisplay}) upload failed. " +
// $"Please contact the Help Desk for support. Error: {ex.HResult}");
// }
// return Array.Empty<byte>();
// public static List<FileModel>? ToFileModel(FileList? fileList) {
// if (fileList == null) return null;
// var fL = fileList.GetFileList();
// if (fL == null) return null;
// var ret = new List<FileModel>();
// foreach (var f in fL) {
// if (f.File == null) continue;
// ret.Add(ToFileModel(f));
// };
// return ret.OrderBy(x => x.LastModified).ToList();
// }
public static List<FileModel>? ToFileModel(FileList? fileList, Dictionary<string, FileList?>? productionFiles = null, Dictionary<string, FileList?>? usedFiles = null) {
if (fileList == null) return null;
var fL = fileList.GetFileList();
if (fL == null) return null;
var ret = new List<FileModel>();
foreach (var f in fL) {
if (f.File == null) continue;
ret.Add(ToFileModel(f, productionFiles, usedFiles));
};
return ret.OrderBy(x => x.LastModified).ToList();
}
// // TODO: File State IsValid
// public static FileModel ToFileModel(XMLRootDocument document) {
// string id = document.Prefix;
// var model = new FileModel(document.FileName, document.File.LastModified.LocalDateTime, true) {
// Fields = document.Fields,
// Messages = document.GetLog(),
// Prefix = id
// };
// return model;
// }
public static FileModel ToFileModel(XMLRootDocument document, Dictionary<string, FileList?>? productionFiles = null, Dictionary<string, FileList?>? usedFiles = null) {
string id = document.Prefix;
bool inProduction = false;
if (productionFiles != null && productionFiles.ContainsKey(id)) {
inProduction = productionFiles[id]!.Contains(document);
}
bool isUsed = false;
if (usedFiles != null && usedFiles.ContainsKey(id)) {
isUsed = usedFiles[id]!.Contains(document);
}
var model = new FileModel(document.FileName, document.Prefix, document.File.LastModified.LocalDateTime, isUsed, inProduction) { Fields = document.Fields };
model.Messages = document.GetLog();
return model;
}
public static async Task<byte[]?> ProcessStreamedFile(
MultipartSection section, ContentDispositionHeaderValue contentDisposition,
ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit) {
public static bool ProcessFile(
Stream file,
string fileName,
StringBuilder errorMessages,
string[] permittedExtensions,
long sizeLimit) {
try {
using (var memoryStream = new MemoryStream()) {
await section.Body.CopyToAsync(memoryStream);
// Check if the file is empty or exceeds the size limit.
if (memoryStream.Length == 0)
modelState.AddModelError("Error", "The file is empty.");
else if (memoryStream.Length > sizeLimit) {
var megabyteSizeLimit = sizeLimit / 1048576;
modelState.AddModelError("Error", $"The file exceeds {megabyteSizeLimit:N1} MB.");
}
// Check file extension and first bytes
else if (!IsValidFileExtensionAndSignature(contentDisposition.FileName.Value, memoryStream, permittedExtensions))
modelState.AddModelError("Error", "The file must be of the following specs:<br>" +
"1. The file must hava a .xml File-Extension<br>" +
"2. To make sure the file isn't executable the file must start with: <?xml version=\"1.0\" encoding=\"utf-8\"?> or <?xml version=\"1.0\"?>");
// Return the File as a byte array
else return memoryStream.ToArray();
// Check if the file is empty or exceeds the size limit.
if (file.Length == 0) {
errorMessages.AppendLine("Die Datei ist leer.");
return false;
}
else if (file.Length > sizeLimit) {
var megabyteSizeLimit = sizeLimit / 1048576;
errorMessages.AppendLine($"Die Datei überschreitet das Größenlimit {megabyteSizeLimit:N1} MB.");
return false;
}
} catch (Exception ex) {
modelState.AddModelError("Error", $"The upload failed. Error: {ex.Message}");
}
return null;
// Return orderly, if signature & extension okay
else return IsValidFileExtensionAndSignature(fileName, file, errorMessages, permittedExtensions);
} catch (Exception ex) {
errorMessages.AppendLine($"The upload failed. Error: {ex.Message}");
return false;
}
}
public static string? StreamToString(System.IO.Stream stream, ModelStateDictionary modelState) {
@@ -216,22 +118,26 @@ public static class XMLFileHelpers {
}
}
private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, string[] permittedExtensions) {
private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, StringBuilder errorMessages, string[] permittedExtensions) {
if (string.IsNullOrEmpty(fileName) || data == null || data.Length == 0)
return false;
var ext = Path.GetExtension(fileName).ToLowerInvariant();
if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext))
if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext)) {
errorMessages.AppendLine("Dateiname endet nicht auf .xml");
return false;
}
data.Position = 0;
using (var reader = new BinaryReader(data)) {
var signatures = _fileSignature[ext];
var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length));
return signatures.Any(signature =>
headerBytes.Take(signature.Length).SequenceEqual(signature));
if (!signatures.Any(signature =>
headerBytes.Take(signature.Length).SequenceEqual(signature))) {
errorMessages.AppendLine("Datei muss mit <?xml version=\"1.0\" encoding=\"utf-8\"?> oder <?xml version=\"1.0\"?> beginnen.");
return false;
};
}
return true;
}
}

View File

@@ -0,0 +1,228 @@
namespace HaWeb.FileHelpers;
using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using HaWeb.Models;
using HaWeb.XMLParser;
using HaWeb.XMLTests;
using System.Xml.Linq;
using System.Runtime.InteropServices;
using System.Diagnostics;
// XMLProvider provides a wrapper around the available XML data on a FILE basis
public class XMLFileProvider : IXMLFileProvider {
private readonly IHaDocumentWrappper _Lib;
private readonly IXMLInteractionService _XMLService;
private IFileProvider _hamannFileProvider;
private IFileProvider _bareRepositoryFileProvider;
private IFileProvider _workingTreeFileProvider;
private string _Branch;
private List<IFileInfo>? _WorkingTreeFiles;
private List<IFileInfo>? _HamannFiles;
private static (DateTime PullTime, string Hash)? _GitData;
// Startup (LAST)
public XMLFileProvider(IXMLInteractionService xmlservice, IHaDocumentWrappper _lib, IConfiguration config) {
// TODO: Test Read / Write Access
_Lib = _lib;
_XMLService = xmlservice;
_Branch = config.GetValue<string>("RepositoryBranch");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
_hamannFileProvider = new PhysicalFileProvider(config.GetValue<string>("HamannFileStoreWindows"));
_bareRepositoryFileProvider = new PhysicalFileProvider(config.GetValue<string>("BareRepositoryPathWindows"));
_workingTreeFileProvider = new PhysicalFileProvider(config.GetValue<string>("WorkingTreePathWindows"));
}
else {
_hamannFileProvider = new PhysicalFileProvider(config.GetValue<string>("HamannFileStoreLinux"));
_bareRepositoryFileProvider = new PhysicalFileProvider(config.GetValue<string>("BareRepositoryPathLinux"));
_workingTreeFileProvider = new PhysicalFileProvider(config.GetValue<string>("WorkingTreePathLinux"));
}
// Create File Lists; Here and in xmlservice, which does preliminary checking
Scan();
if (_WorkingTreeFiles != null && _WorkingTreeFiles.Any()) {
xmlservice.Collect(_WorkingTreeFiles);
}
_HamannFiles = _ScanHamannFiles();
// Check if hamann file already is current working tree status
// -> YES: Load up the file via _lib.SetLibrary();
if (_IsAlreadyParsed()) {
_Lib.SetLibrary(_HamannFiles.First(), null, null);
if (_Lib.GetLibrary() != null) return;
}
// -> NO: Try to create a new file
var created = xmlservice.TryCreate();
if (created != null) {
var file = SaveHamannFile(created, _hamannFileProvider.GetFileInfo("./").PhysicalPath, null);
if (file != null) {
_lib.SetLibrary(file, created.Document, null);
if (_Lib.GetLibrary() != null) return;
}
}
// It failed, so use the last best File:
else if (_HamannFiles != null && _HamannFiles.Any()) {
_Lib.SetLibrary(_HamannFiles.First(), null, null);
if (_Lib.GetLibrary() != null) return;
}
// -> There is none? Use Fallback:
else {
var options = new HaWeb.Settings.HaDocumentOptions();
if (_lib.SetLibrary(null, null, null) == null) {
throw new Exception("Die Fallback Hamann.xml unter " + options.HamannXMLFilePath + " kann nicht geparst werden.");
}
}
}
public void Reload(IConfiguration config) {
_Branch = config.GetValue<string>("RepositoryBranch");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
_hamannFileProvider = new PhysicalFileProvider(config.GetValue<string>("HamannFileStoreWindows"));
_bareRepositoryFileProvider = new PhysicalFileProvider(config.GetValue<string>("BareRepositoryPathWindows"));
_workingTreeFileProvider = new PhysicalFileProvider(config.GetValue<string>("WorkingTreePathWindows"));
}
else {
_hamannFileProvider = new PhysicalFileProvider(config.GetValue<string>("HamannFileStoreLinux"));
_bareRepositoryFileProvider = new PhysicalFileProvider(config.GetValue<string>("BareRepositoryPathLinux"));
_workingTreeFileProvider = new PhysicalFileProvider(config.GetValue<string>("WorkingTreePathLinux"));
}
// Create File Lists; Here and in xmlservice, which does preliminary checking
Scan();
if (_WorkingTreeFiles != null && _WorkingTreeFiles.Any()) {
_XMLService.Collect(_WorkingTreeFiles);
}
_HamannFiles = _ScanHamannFiles();
// Check if hamann file already is current working tree status
// -> YES: Load up the file via _lib.SetLibrary();
if (_IsAlreadyParsed()) {
_Lib.SetLibrary(_HamannFiles.First(), null, null);
if (_Lib.GetLibrary() != null) return;
}
// -> NO: Try to create a new file
var created = _XMLService.TryCreate();
if (created != null) {
var file = SaveHamannFile(created, _hamannFileProvider.GetFileInfo("./").PhysicalPath, null);
if (file != null) {
_Lib.SetLibrary(file, created.Document, null);
if (_Lib.GetLibrary() != null) return;
}
}
// It failed, so use the last best File:
else if (_HamannFiles != null && _HamannFiles.Any()) {
_Lib.SetLibrary(_HamannFiles.First(), null, null);
if (_Lib.GetLibrary() != null) return;
}
// -> There is none? Use Fallback:
else {
var options = new HaWeb.Settings.HaDocumentOptions();
if (_Lib.SetLibrary(null, null, null) == null) {
throw new Exception("Die Fallback Hamann.xml unter " + options.HamannXMLFilePath + " kann nicht geparst werden.");
}
}
}
// Getters and Setters
public List<IFileInfo>? GetWorkingTreeFiles() => _WorkingTreeFiles;
public (DateTime PullTime, string Hash)? GetGitData() => _GitData;
public List<IFileInfo>? GetHamannFiles() => this._HamannFiles;
// Functions
public void DeleteHamannFile(string filename) {
if (_HamannFiles == null) return;
var files = _HamannFiles.Where(x => x.Name == filename);
foreach (var file in files) {
File.Delete(file.PhysicalPath);
}
_HamannFiles.RemoveAll(x => x.Name == filename);
}
public void Scan() {
_WorkingTreeFiles = _ScanWorkingTreeFiles();
_GitData = _ScanGitData();
}
public IFileInfo? SaveHamannFile(XElement element, string basefilepath, ModelStateDictionary? ModelState) {
if (!_GitData.HasValue) return null;
var filename = "hamann_" + _GitData.Value.PullTime.Year + "-" + _GitData.Value.PullTime.Month + "-" + _GitData.Value.PullTime.Day + "_" + _GitData.Value.PullTime.Hour + "-" + _GitData.Value.PullTime.Minute + "." + _GitData.Value.Hash.Substring(0,7) + ".xml";
var path = Path.Combine(basefilepath, filename);
try {
if (!Directory.Exists(basefilepath))
Directory.CreateDirectory(basefilepath);
using (var targetStream = System.IO.File.Create(path))
element.Save(targetStream, SaveOptions.DisableFormatting);
} catch (Exception ex) {
if (ModelState != null) ModelState.AddModelError("Error", "Die Datei konnte nicht gespeichert werden: " + ex.Message);
return null;
}
var info = _hamannFileProvider.GetFileInfo(filename);
if (info == null) {
if (ModelState != null) ModelState.AddModelError("Error", "Auf die neu erstellte Datei konnte nicht zugegriffen werden.");
return null;
}
if (_HamannFiles == null) _HamannFiles = new List<IFileInfo>();
_HamannFiles.RemoveAll(x => x.Name == info.Name);
_HamannFiles.Add(info);
return info;
}
public bool HasChanged() {
if (!_GitData.HasValue) return true;
var current = _ScanGitData();
if (current.Item2 != _GitData.Value.Hash) {
_GitData = current;
return true;
}
return false;
}
private (DateTime, string) _ScanGitData() {
var head = _bareRepositoryFileProvider.GetFileInfo("refs/heads/" + _Branch);
return (head.LastModified.DateTime, File.ReadAllText(head.PhysicalPath));
}
private void _RegisterChangeCallbacks() {
var cT = _bareRepositoryFileProvider.Watch("refs/heads/" + _Branch);
}
// Gets all XML Files
private List<IFileInfo>? _ScanWorkingTreeFiles() {
var files = _workingTreeFileProvider.GetDirectoryContents(string.Empty)!.Where(x => !x.IsDirectory && x.Name.EndsWith(".xml"))!.ToList();
return files;
}
private List<IFileInfo>? _ScanHamannFiles() {
var files = _hamannFileProvider.GetDirectoryContents(string.Empty).Where(x => !x.IsDirectory && x.Name.StartsWith("hamann") && x.Name.EndsWith(".xml"));
if (files == null || !files.Any()) return null;
return files.OrderByDescending(x => x.LastModified).ToList();
}
private string? _GetHashFromHamannFilename(string filename) {
var s = filename.Split('.', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (s.Length != 3 || s.Last() != "xml" || !s.First().StartsWith("hamann")) return null;
return s[1];
}
private bool _IsAlreadyParsed() {
if (_HamannFiles == null || !_HamannFiles.Any() || !_GitData.HasValue) return false;
var fhash = _GetHashFromHamannFilename(_HamannFiles.First().Name);
var ghash = _GitData.Value.Hash.Substring(0,7);
return fhash == ghash;
}
}

View File

@@ -1,146 +0,0 @@
namespace HaWeb.FileHelpers;
using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using HaWeb.Models;
using HaWeb.XMLParser;
using HaWeb.XMLTests;
using System.Xml.Linq;
// XMLProvider provides a wrapper around the available XML data on a FILE basis
public class XMLProvider : IXMLProvider {
private IFileProvider _fileProvider;
private Dictionary<string, FileList?>? _Files;
private Dictionary<string, IXMLRoot>? _Roots;
private List<IFileInfo>? _HamannFiles;
private Stack<IFileInfo>? _InProduction;
public XMLProvider(IFileProvider provider, IXMLService xmlservice, IXMLTestService testService) {
_fileProvider = provider;
_Roots = xmlservice.GetRootsDictionary();
_Files = _ScanFiles();
_HamannFiles = _ScanHamannFiles();
if (_Files != null)
foreach (var category in _Files)
if (category.Value != null)
xmlservice.AutoUse(category.Value);
testService.Test();
}
public List<IFileInfo>? GetHamannFiles() => this._HamannFiles;
public IFileInfo? GetInProduction() {
if (_InProduction == null || !_InProduction.Any()) return null;
return this._InProduction.Peek();
}
public void DeleteHamannFile(string filename) {
if (_HamannFiles == null) return;
var files = _HamannFiles.Where(x => x.Name == filename);
foreach (var file in files) {
File.Delete(file.PhysicalPath);
}
_HamannFiles.RemoveAll(x => x.Name == filename);
}
public void SetInProduction(IFileInfo info) {
if (_InProduction == null) _InProduction = new Stack<IFileInfo>();
_InProduction.Push(info);
}
public FileList? GetFiles(string prefix)
=> _Files != null && _Files.ContainsKey(prefix) ? _Files[prefix] : null;
// Saves a Document as file and adds it to the collection
public async Task Save(XMLRootDocument doc, string basefilepath, ModelStateDictionary ModelState) {
var type = doc.Prefix;
var directory = Path.Combine(basefilepath, type);
var path = Path.Combine(directory, doc.FileName);
try {
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
using (var targetStream = System.IO.File.Create(path))
await doc.Save(targetStream, ModelState);
} catch (Exception ex) {
ModelState.AddModelError("Error", "Speichern der Datei fehlgeschlagen: " + ex.Message);
return;
}
var info = _fileProvider.GetFileInfo(Path.Combine(doc.Prefix, doc.FileName));
if (info == null) {
ModelState.AddModelError("Error", "Auf die neu erstellte Datei konnte nicht zugegriffen werden.");
return;
}
doc.File = info;
if (_Files == null) _Files = new Dictionary<string, FileList?>();
if (!_Files.ContainsKey(doc.Prefix)) _Files.Add(doc.Prefix, new FileList(doc.XMLRoot));
_Files[doc.Prefix]!.Add(doc);
}
public async Task<IFileInfo?> SaveHamannFile(XElement element, string basefilepath, ModelStateDictionary ModelState) {
var date = DateTime.Now;
var filename = "hamann_" + date.Year + "-" + date.Month + "-" + date.Day + "." + Path.GetRandomFileName() + ".xml";
var directory = Path.Combine(basefilepath, "hamann");
var path = Path.Combine(directory, filename);
try {
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
using (var targetStream = System.IO.File.Create(path))
await element.SaveAsync(targetStream, SaveOptions.DisableFormatting, new CancellationToken());
} catch (Exception ex) {
ModelState.AddModelError("Error", "Die Datei konnte nicht gespeichert werden: " + ex.Message);
return null;
}
var info = _fileProvider.GetFileInfo(Path.Combine("hamann", filename));
if (info == null) {
ModelState.AddModelError("Error", "Auf die neu erstellte Dtaei konnte nicht zugegriffen werden.");
return null;
}
if (_HamannFiles == null) _HamannFiles = new List<IFileInfo>();
_HamannFiles.RemoveAll(x => x.Name == info.Name);
_HamannFiles.Add(info);
return info;
}
private Dictionary<string, FileList?>? _ScanFiles() {
if (_Roots == null) return null;
Dictionary<string, FileList?>? res = null;
var dirs = _fileProvider.GetDirectoryContents(string.Empty).Where(x => x.IsDirectory);
foreach (var dir in dirs) {
if (_Roots.ContainsKey(dir.Name)) {
if (_Files == null) _Files = new Dictionary<string, FileList?>();
if (res == null) res = new Dictionary<string, FileList?>();
res.Add(dir.Name, _ScanFiles(dir.Name));
}
}
return res;
}
private FileList? _ScanFiles(string prefix) {
if (_Roots == null) return null;
FileList? res = null;
var files = _fileProvider.GetDirectoryContents(prefix).Where(x => !x.IsDirectory && x.Name.StartsWith(prefix) && x.Name.EndsWith(".xml"));
foreach (var file in files) {
if (_Roots == null || !_Roots.ContainsKey(prefix))
throw new Exception("Attempting to read a File from an unrecognized Prefix: " + prefix);
if (res == null) res = new FileList(_Roots[prefix]);
res.Add(new XMLRootDocument(_Roots[prefix], file));
}
return res;
}
private List<IFileInfo>? _ScanHamannFiles() {
var dir = _fileProvider.GetDirectoryContents(string.Empty).Where(x => x.IsDirectory && x.Name == "hamann");
if (dir == null || !dir.Any()) return null;
var files = _fileProvider.GetDirectoryContents(dir.First().Name).Where(x => !x.IsDirectory && x.Name.StartsWith("hamann") && x.Name.EndsWith(".xml"));
if (files == null || !files.Any()) return null;
return files.ToList();
}
}