diff --git a/HaWeb/Controllers/APIController.cs b/HaWeb/Controllers/APIController.cs new file mode 100644 index 0000000..fd1fddf --- /dev/null +++ b/HaWeb/Controllers/APIController.cs @@ -0,0 +1,145 @@ +namespace HaWeb.Controllers; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Configuration; +using Microsoft.Net.Http.Headers; +using HaWeb.Filters; +using HaWeb.FileHelpers; +using HaWeb.XMLParser; +using System.Text.Json.Serialization; +using System.Text.Json; +using HaDocument.Interfaces; +using HaXMLReader.Interfaces; +using Microsoft.FeatureManagement.Mvc; +using System.Runtime.InteropServices; +using Microsoft.AspNetCore.Http.Features; + +// Controlling all the API-Endpoints +public class APIController : Controller { + + // DI + private ILibrary _lib; + private IReaderService _readerService; + private readonly long _fileSizeLimit; + private readonly string _targetFilePath; + private readonly IXMLService _xmlService; + + // Options + private static readonly string[] _permittedExtensions = { ".xml" }; + private static readonly FormOptions _defaultFormOptions = new FormOptions(); + + + public APIController(ILibrary lib, IReaderService readerService, IXMLService xmlService, IConfiguration config) { + _lib = lib; + _readerService = readerService; + _xmlService = xmlService; + _fileSizeLimit = config.GetValue("FileSizeLimit"); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + _targetFilePath = config.GetValue("StoredFilePathWindows"); + } else { + _targetFilePath = config.GetValue("StoredFilePathLinux"); + } + } + + [HttpGet] + [Route("API/Syntaxcheck/{id}")] + [DisableFormValueModelBinding] + [ValidateAntiForgeryToken] + [FeatureGate(Features.AdminService)] + public async Task SyntaxCheck(string id) { + return Ok(); + } + + //// UPLOAD //// + [HttpPost] + [Route("API/Upload")] + [DisableFormValueModelBinding] + [ValidateAntiForgeryToken] + [FeatureGate(Features.UploadService)] + public async Task Upload(string? id) { + List? docs = null; + //// 1. Stage: Check Request format and request spec + // Checks the Content-Type Field (must be multipart + Boundary) + if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { + ModelState.AddModelError("Error", $"Wrong / No Content Type on the Request"); + return BadRequest(ModelState); + } + + // Divides the multipart document into it's sections and sets up a reader + var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit); + var reader = new MultipartReader(boundary, HttpContext.Request.Body); + MultipartSection? section = null; + try { + section = await reader.ReadNextSectionAsync(); + } catch (Exception ex) { + ModelState.AddModelError("Error", "The Request is bad: " + ex.Message); + } + + while (section != null) { + // Multipart document content disposition header read for a section: + // Starts with boundary, contains field name, content-dispo, filename, content-type + var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); + + if (contentDisposition != null && contentDisposition.Name == "__RequestVerificationToken") { + try { + section = await reader.ReadNextSectionAsync(); + } catch (Exception ex) { + ModelState.AddModelError("Error", "The Request is bad: " + ex.Message); + } + continue; + } + + if (hasContentDispositionHeader && contentDisposition != null) { + // Checks if it is a section with content-disposition, name, filename + if (!MultipartRequestHelper.HasFileContentDisposition(contentDisposition)) { + ModelState.AddModelError("Error", $"Wrong Content-Dispostion Headers in Multipart Document"); + return BadRequest(ModelState); + } + + //// 2. Stage: Check File. Sanity checks on the file on a byte level, extension checking, is it empty etc. + var streamedFileContent = await XMLFileHelpers.ProcessStreamedFile( + section, contentDisposition, ModelState, + _permittedExtensions, _fileSizeLimit); + if (!ModelState.IsValid || streamedFileContent == null) + return BadRequest(ModelState); + + //// 3. Stage: Valid XML checking using a simple XDocument.Load() + var xdocument = await XDocumentFileHelper.ProcessStreamedFile(streamedFileContent, ModelState); + if (!ModelState.IsValid || xdocument == null) + return UnprocessableEntity(ModelState); + + //// 4. Stage: Is it a Hamann-Document? What kind? + var retdocs = await _xmlService.ProbeHamannFile(xdocument, ModelState); + if (!ModelState.IsValid || retdocs == null || !retdocs.Any()) + return UnprocessableEntity(ModelState); + + //// 5. Stage: Saving the File(s) + foreach (var doc in retdocs) { + await _xmlService.UpdateAvailableFiles(doc, _targetFilePath, ModelState); + if (!ModelState.IsValid) return StatusCode(500, ModelState); + if (docs == null) docs = new List(); + docs.Add(doc); + } + } + + try { + section = await reader.ReadNextSectionAsync(); + } catch (Exception ex) { + ModelState.AddModelError("Error", "The Request is bad: " + ex.Message); + } + } + + // 6. Stage: Success! Returning Ok, and redirecting + JsonSerializerOptions options = new() { + ReferenceHandler = ReferenceHandler.Preserve, + Converters = { + new IdentificationStringJSONConverter() + } + }; + + string json = JsonSerializer.Serialize(docs); + return Created(nameof(UploadController), json); + } +} \ No newline at end of file diff --git a/HaWeb/Controllers/AdminController.cs b/HaWeb/Controllers/AdminController.cs index cf27a25..ce3f1d2 100644 --- a/HaWeb/Controllers/AdminController.cs +++ b/HaWeb/Controllers/AdminController.cs @@ -3,13 +3,13 @@ using Microsoft.AspNetCore.Mvc; using HaDocument.Interfaces; using HaXMLReader.Interfaces; using Microsoft.FeatureManagement.Mvc; - +using HaWeb.FileHelpers; public class AdminController : Controller { // DI - private ILibrary _lib; + private IHaDocumentWrappper _lib; private IReaderService _readerService; - public AdminController(ILibrary lib, IReaderService readerService) { + public AdminController(IHaDocumentWrappper lib, IReaderService readerService) { _lib = lib; _readerService = readerService; } diff --git a/HaWeb/Controllers/BriefeContoller.cs b/HaWeb/Controllers/BriefeContoller.cs index c9542b1..1dc9a38 100644 --- a/HaWeb/Controllers/BriefeContoller.cs +++ b/HaWeb/Controllers/BriefeContoller.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using Microsoft.AspNetCore.Mvc; using HaWeb.Models; +using HaWeb.FileHelpers; using HaDocument.Interfaces; using HaXMLReader.Interfaces; using HaDocument.Models; @@ -12,10 +13,10 @@ public class Briefecontroller : Controller { public string? id { get; set; } // DI - private ILibrary _lib; + private IHaDocumentWrappper _lib; private IReaderService _readerService; - public Briefecontroller(ILibrary lib, IReaderService readerService) { + public Briefecontroller(IHaDocumentWrappper lib, IReaderService readerService) { _lib = lib; _readerService = readerService; } @@ -24,25 +25,26 @@ public class Briefecontroller : Controller { [Route("Briefe/{id?}")] public IActionResult Index(string? id) { // Setup settings and variables + var lib = _lib.GetLibrary(); var url = "/Briefe/"; var defaultID = "1"; // Normalisation and Validation, (some) data aquisition if (id == null) return Redirect(url + defaultID); this.id = id.ToLower(); - var preliminarymeta = _lib.Metas.Where(x => x.Value.Autopsic == this.id); + var preliminarymeta = lib.Metas.Where(x => x.Value.Autopsic == this.id); if (preliminarymeta == null || !preliminarymeta.Any()) return error404(); // Get all neccessary data var index = preliminarymeta.First().Key; var meta = preliminarymeta.First().Value; - var text = _lib.Letters.ContainsKey(index) ? _lib.Letters[index] : null; - var marginals = _lib.MarginalsByLetter.Contains(index) ? _lib.MarginalsByLetter[index] : null; - var tradition = _lib.Traditions.ContainsKey(index) ? _lib.Traditions[index] : null; - var editreasons = _lib.Editreasons.ContainsKey(index) ? _lib.EditreasonsByLetter[index] : null; // TODO: Order - var hands = _lib.Hands.ContainsKey(index) ? _lib.Hands[index] : null; - var nextmeta = meta != _lib.MetasByDate.Last() ? _lib.MetasByDate.ItemRef(_lib.MetasByDate.IndexOf(meta) + 1) : null; - var prevmeta = meta != _lib.MetasByDate.First() ? _lib.MetasByDate.ItemRef(_lib.MetasByDate.IndexOf(meta) - 1) : null; + var text = lib.Letters.ContainsKey(index) ? lib.Letters[index] : null; + var marginals = lib.MarginalsByLetter.Contains(index) ? lib.MarginalsByLetter[index] : null; + var tradition = lib.Traditions.ContainsKey(index) ? lib.Traditions[index] : null; + var editreasons = lib.Editreasons.ContainsKey(index) ? lib.EditreasonsByLetter[index] : null; // TODO: Order + var hands = lib.Hands.ContainsKey(index) ? lib.Hands[index] : null; + var nextmeta = meta != lib.MetasByDate.Last() ? lib.MetasByDate.ItemRef(lib.MetasByDate.IndexOf(meta) + 1) : null; + var prevmeta = meta != lib.MetasByDate.First() ? lib.MetasByDate.ItemRef(lib.MetasByDate.IndexOf(meta) - 1) : null; // More Settings and variables ViewData["Title"] = "Brief " + id.ToLower(); @@ -52,17 +54,17 @@ public class Briefecontroller : Controller { // Model creation var hasMarginals = false; if (marginals != null && marginals.Any()) hasMarginals = true; - var model = new BriefeViewModel(this.id, index, generateMetaViewModel(meta, hasMarginals)); - if (nextmeta != null) model.MetaData.Next = (generateMetaViewModel(nextmeta, false), url + nextmeta.Autopsic); - if (prevmeta != null) model.MetaData.Prev = (generateMetaViewModel(prevmeta, false), url + prevmeta.Autopsic); - if (hands != null && hands.Any()) model.ParsedHands = HaWeb.HTMLHelpers.LetterHelpers.CreateHands(_lib, hands); - if (editreasons != null && editreasons.Any()) model.ParsedEdits = HaWeb.HTMLHelpers.LetterHelpers.CreateEdits(_lib, _readerService, editreasons); + var model = new BriefeViewModel(this.id, index, generateMetaViewModel(lib, meta, hasMarginals)); + if (nextmeta != null) model.MetaData.Next = (generateMetaViewModel(lib, nextmeta, false), url + nextmeta.Autopsic); + if (prevmeta != null) model.MetaData.Prev = (generateMetaViewModel(lib, prevmeta, false), url + prevmeta.Autopsic); + if (hands != null && hands.Any()) model.ParsedHands = HaWeb.HTMLHelpers.LetterHelpers.CreateHands(lib, hands); + if (editreasons != null && editreasons.Any()) model.ParsedEdits = HaWeb.HTMLHelpers.LetterHelpers.CreateEdits(lib, _readerService, editreasons); if (tradition != null && !String.IsNullOrWhiteSpace(tradition.Element)) { - var parsedTraditions = HaWeb.HTMLHelpers.LetterHelpers.CreateTraditions(_lib, _readerService, marginals, tradition, hands, editreasons); + var parsedTraditions = HaWeb.HTMLHelpers.LetterHelpers.CreateTraditions(lib, _readerService, marginals, tradition, hands, editreasons); (model.ParsedTradition, model.ParsedMarginals, model.MinWidthTrad) = (parsedTraditions.sb_tradition.ToString(), parsedTraditions.ParsedMarginals, parsedTraditions.minwidth); } if (text != null && !String.IsNullOrWhiteSpace(text.Element)) { - var parsedLetter = HaWeb.HTMLHelpers.LetterHelpers.CreateLetter(_lib, _readerService, meta, text, marginals, hands, editreasons); + var parsedLetter = HaWeb.HTMLHelpers.LetterHelpers.CreateLetter(lib, _readerService, meta, text, marginals, hands, editreasons); (model.ParsedText, model.MinWidth) = (parsedLetter.sb_lettertext.ToString(), parsedLetter.minwidth); if (model.ParsedMarginals != null && parsedLetter.ParsedMarginals != null) model.ParsedMarginals.AddRange(parsedLetter.ParsedMarginals); else model.ParsedMarginals = parsedLetter.ParsedMarginals; @@ -81,9 +83,9 @@ public class Briefecontroller : Controller { return Redirect("/Error404"); } - private BriefeMetaViewModel generateMetaViewModel(Meta meta, bool hasMarginals) { - var senders = meta.Senders.Select(x => _lib.Persons[x].Name) ?? new List(); - var recivers = meta.Receivers.Select(x => _lib.Persons[x].Name) ?? new List(); + private BriefeMetaViewModel generateMetaViewModel(ILibrary lib, Meta meta, bool hasMarginals) { + var senders = meta.Senders.Select(x => lib.Persons[x].Name) ?? new List(); + var recivers = meta.Receivers.Select(x => lib.Persons[x].Name) ?? new List(); var zhstring = meta.ZH != null ? HaWeb.HTMLHelpers.LetterHelpers.CreateZHString(meta) : null; return new BriefeMetaViewModel(meta, hasMarginals, false) { ParsedZHString = zhstring, diff --git a/HaWeb/Controllers/RegisterController.cs b/HaWeb/Controllers/RegisterController.cs index a21ccb6..9b77639 100644 --- a/HaWeb/Controllers/RegisterController.cs +++ b/HaWeb/Controllers/RegisterController.cs @@ -8,6 +8,7 @@ using HaXMLReader; using HaXMLReader.EvArgs; using HaDocument.Models; using System.Collections.Concurrent; +using HaWeb.FileHelpers; namespace HaWeb.Controllers; @@ -16,20 +17,18 @@ public class RegisterController : Controller { [BindProperty(SupportsGet = true)] public string? search { get; set; } - [BindProperty(SupportsGet = true)] - public string? id { get; set; } - // DI - private ILibrary _lib; + private IHaDocumentWrappper _lib; private IReaderService _readerService; - public RegisterController(ILibrary lib, IReaderService readerService) { + public RegisterController(IHaDocumentWrappper lib, IReaderService readerService) { _lib = lib; _readerService = readerService; } public IActionResult Register(string? id) { // Setup settings and variables + var lib = _lib.GetLibrary(); var url = "/Register/Register/"; var category = "neuzeit"; var defaultLetter = "A"; @@ -39,30 +38,30 @@ public class RegisterController : Controller { // Normalisation and validation if (id == null) return Redirect(url + defaultLetter); - normalizeID(id, defaultLetter); - if (!_lib.CommentsByCategoryLetter[category].Contains(this.id)) return error404(); + id = normalizeID(id, defaultLetter); + if (!lib.CommentsByCategoryLetter[category].Contains(id)) return error404(); // Data aquisition and validation - var comments = _lib.CommentsByCategoryLetter[category][this.id].OrderBy(x => x.Index); - var availableCategories = _lib.CommentsByCategoryLetter[category].Select(x => (x.Key.ToUpper(), url + x.Key.ToUpper())).OrderBy(x => x.Item1).ToList(); + var comments = lib.CommentsByCategoryLetter[category][id].OrderBy(x => x.Index); + var availableCategories = lib.CommentsByCategoryLetter[category].Select(x => (x.Key.ToUpper(), url + x.Key.ToUpper())).OrderBy(x => x.Item1).ToList(); if (comments == null) return error404(); // Parsing var res = new List(); foreach (var comm in comments) { - var parsedComment = HTMLHelpers.CommentHelpers.CreateHTML(_lib, _readerService, comm, category, Settings.ParsingState.CommentType.Comment); + var parsedComment = HTMLHelpers.CommentHelpers.CreateHTML(lib, _readerService, comm, category, Settings.ParsingState.CommentType.Comment); List? parsedSubComments = null; if (comm.Kommentare != null) { parsedSubComments = new List(); foreach (var subcomm in comm.Kommentare.OrderBy(x => x.Value.Order)) { - parsedSubComments.Add(HTMLHelpers.CommentHelpers.CreateHTML(_lib, _readerService, subcomm.Value, category, Settings.ParsingState.CommentType.Subcomment)); + parsedSubComments.Add(HTMLHelpers.CommentHelpers.CreateHTML(lib, _readerService, subcomm.Value, category, Settings.ParsingState.CommentType.Subcomment)); } } res.Add(new CommentModel(parsedComment, parsedSubComments)); } // Model instantiation - var model = new RegisterViewModel(category, this.id, res, title) { + var model = new RegisterViewModel(category, id, res, title) { AvailableCategories = availableCategories, }; @@ -72,6 +71,7 @@ public class RegisterController : Controller { public IActionResult Bibelstellen(string? id) { // Setup settings and variables + var lib = _lib.GetLibrary(); var url = "/Register/Bibelstellen/"; var category = "bibel"; var defaultLetter = "AT"; @@ -81,30 +81,30 @@ public class RegisterController : Controller { // Normalisation and Validation if (id == null) return Redirect(url + defaultLetter); - normalizeID(id, defaultLetter); - if (this.id != "AT" && this.id != "AP" && this.id != "NT") return error404(); + id = normalizeID(id, defaultLetter); + if (id != "AT" && id != "AP" && id != "NT") return error404(); // Data aquisition and validation - var comments = _lib.CommentsByCategory[category].ToLookup(x => x.Index.Substring(0, 2).ToUpper())[this.id].OrderBy(x => x.Order); + var comments = lib.CommentsByCategory[category].ToLookup(x => x.Index.Substring(0, 2).ToUpper())[id].OrderBy(x => x.Order); var availableCategories = new List<(string, string)>() { ("Altes Testament", url + "AT"), ("Apogryphen", url + "AP"), ("Neues Testament", url + "NT") }; if (comments == null) return error404(); // Parsing var res = new List(); foreach (var comm in comments) { - var parsedComment = HTMLHelpers.CommentHelpers.CreateHTML(_lib, _readerService, comm, category, Settings.ParsingState.CommentType.Comment); + var parsedComment = HTMLHelpers.CommentHelpers.CreateHTML(lib, _readerService, comm, category, Settings.ParsingState.CommentType.Comment); List? parsedSubComments = null; if (comm.Kommentare != null) { parsedSubComments = new List(); foreach (var subcomm in comm.Kommentare.OrderBy(x => x.Value.Lemma.Length).ThenBy(x => x.Value.Lemma)) { - parsedSubComments.Add(HTMLHelpers.CommentHelpers.CreateHTML(_lib, _readerService, subcomm.Value, category, Settings.ParsingState.CommentType.Subcomment)); + parsedSubComments.Add(HTMLHelpers.CommentHelpers.CreateHTML(lib, _readerService, subcomm.Value, category, Settings.ParsingState.CommentType.Subcomment)); } } res.Add(new CommentModel(parsedComment, parsedSubComments)); } // Model instantiation - var model = new RegisterViewModel(category, this.id, res, title) { + var model = new RegisterViewModel(category, id, res, title) { AvailableCategories = availableCategories, }; @@ -114,6 +114,7 @@ public class RegisterController : Controller { public IActionResult Forschung(string? id) { // Setup settings and variables + var lib = _lib.GetLibrary(); var url = "/Register/Forschung/"; var category = "forschung"; var defaultLetter = "A"; @@ -123,37 +124,37 @@ public class RegisterController : Controller { // Normalisation and Validation if (id == null) return Redirect(url + defaultLetter); - normalizeID(id, defaultLetter); - if (this.id != "EDITIONEN" && !_lib.CommentsByCategoryLetter[category].Contains(this.id)) return error404(); - if (this.id == "EDITIONEN" && !_lib.CommentsByCategoryLetter.Keys.Contains(this.id.ToLower())) return error404(); + id = normalizeID(id, defaultLetter); + if (id != "EDITIONEN" && !lib.CommentsByCategoryLetter[category].Contains(id)) return error404(); + if (id == "EDITIONEN" && !lib.CommentsByCategoryLetter.Keys.Contains(id.ToLower())) return error404(); // Data aquisition and validation IOrderedEnumerable? comments = null; - if (this.id == "EDITIONEN") { - comments = _lib.CommentsByCategory[this.id.ToLower()].OrderBy(x => x.Index); + if (id == "EDITIONEN") { + comments = lib.CommentsByCategory[id.ToLower()].OrderBy(x => x.Index); } else { - comments = _lib.CommentsByCategoryLetter[category][this.id].OrderBy(x => x.Index); + comments = lib.CommentsByCategoryLetter[category][id].OrderBy(x => x.Index); } - var availableCategories = _lib.CommentsByCategoryLetter[category].Select(x => (x.Key.ToUpper(), url + x.Key.ToUpper())).OrderBy(x => x.Item1).ToList(); + var availableCategories = lib.CommentsByCategoryLetter[category].Select(x => (x.Key.ToUpper(), url + x.Key.ToUpper())).OrderBy(x => x.Item1).ToList(); var AvailableSideCategories = new List<(string, string)>() { ("Editionen", "Editionen") }; if (comments == null) return error404(); // Parsing var res = new List(); foreach (var comm in comments) { - var parsedComment = HTMLHelpers.CommentHelpers.CreateHTML(_lib, _readerService, comm, category, Settings.ParsingState.CommentType.Comment); + var parsedComment = HTMLHelpers.CommentHelpers.CreateHTML(lib, _readerService, comm, category, Settings.ParsingState.CommentType.Comment); List? parsedSubComments = null; if (comm.Kommentare != null) { parsedSubComments = new List(); foreach (var subcomm in comm.Kommentare.OrderBy(x => x.Value.Order)) { - parsedSubComments.Add(HTMLHelpers.CommentHelpers.CreateHTML(_lib, _readerService, subcomm.Value, category, Settings.ParsingState.CommentType.Subcomment)); + parsedSubComments.Add(HTMLHelpers.CommentHelpers.CreateHTML(lib, _readerService, subcomm.Value, category, Settings.ParsingState.CommentType.Subcomment)); } } res.Add(new CommentModel(parsedComment, parsedSubComments)); } // Model instantiation - var model = new RegisterViewModel(category, this.id, res, title) { + var model = new RegisterViewModel(category, id, res, title) { AvailableCategories = availableCategories, AvailableSideCategories = AvailableSideCategories }; @@ -162,11 +163,13 @@ public class RegisterController : Controller { return View("Index", model); } - private void normalizeID(string? id, string defaultid) { - this.id = this.id.ToUpper(); + private string? normalizeID(string? id, string defaultid) { + if (id == null) return null; + return id.ToUpper(); } - private bool validationCheck(HashSet permitted) { + private bool validationCheck(string? id, HashSet permitted) { + if (id == null) return false; if (!permitted.Contains(id)) { return false; } diff --git a/HaWeb/Controllers/UpdateController.cs b/HaWeb/Controllers/UpdateController.cs deleted file mode 100644 index 2fc3a9a..0000000 --- a/HaWeb/Controllers/UpdateController.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace HaWeb.Controllers; -using Microsoft.AspNetCore.Mvc; -using HaDocument.Interfaces; -using HaXMLReader.Interfaces; -using Microsoft.FeatureManagement.Mvc; - -public class UpdateController : Controller { - // DI - private ILibrary _lib; - private IReaderService _readerService; - - public UpdateController(ILibrary lib, IReaderService readerService) { - _lib = lib; - _readerService = readerService; - } - - - [Route("Admin/Update")] - [FeatureGate(Features.UpdateService)] - public IActionResult Index() { - return View("../Admin/Upload/Index"); - } -} \ No newline at end of file diff --git a/HaWeb/Controllers/UploadController.cs b/HaWeb/Controllers/UploadController.cs index f09c64a..a2e2b90 100644 --- a/HaWeb/Controllers/UploadController.cs +++ b/HaWeb/Controllers/UploadController.cs @@ -3,27 +3,17 @@ using Microsoft.AspNetCore.Mvc; using HaDocument.Interfaces; using HaXMLReader.Interfaces; using Microsoft.FeatureManagement.Mvc; -using System.IO; -using System.Net; -using System.Text; using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Configuration; -using Microsoft.Net.Http.Headers; using HaWeb.Filters; -using HaWeb.FileHelpers; using HaWeb.XMLParser; using HaWeb.Models; -using System.Xml.Linq; -using System.Text.Json.Serialization; -using System.Text.Json; +using HaWeb.FileHelpers; public class UploadController : Controller { // DI - private ILibrary _lib; + private IHaDocumentWrappper _lib; private IReaderService _readerService; private readonly long _fileSizeLimit; private readonly string _targetFilePath; @@ -34,7 +24,7 @@ public class UploadController : Controller { private static readonly FormOptions _defaultFormOptions = new FormOptions(); - public UploadController(ILibrary lib, IReaderService readerService, IXMLService xmlService, IConfiguration config) { + public UploadController(IHaDocumentWrappper lib, IReaderService readerService, IXMLService xmlService, IConfiguration config) { _lib = lib; _readerService = readerService; _xmlService = xmlService; @@ -48,94 +38,35 @@ public class UploadController : Controller { [HttpGet] [Route("Admin/Upload/{id?}")] - [FeatureGate(Features.UploadService)] + [FeatureGate(Features.AdminService)] [GenerateAntiforgeryTokenCookie] public IActionResult Index(string? id) { - var model = new UploadViewModel(); - model.AvailableRoots = _xmlService.GetRoots(); - model.UsedFiles = _xmlService.GetUsed(); - var hello = "mama"; - return View("../Admin/Upload/Index", model); + if (id != null) { + var root = _xmlService.GetRoot(id); + if (root == null) return error404(); + + var roots = _xmlService.GetRoots(); + if (roots == null) return error404(); + + var usedFiles = _xmlService.GetUsed(); + var availableFiles = _xmlService.GetAvailableFiles(id); + + var model = new UploadViewModel(root.Type, id, roots, availableFiles, usedFiles); + return View("../Admin/Upload/Index", model); + } + else { + var roots = _xmlService.GetRoots(); + if (roots == null) return error404(); + + var usedFiles = _xmlService.GetUsed(); + + var model = new UploadViewModel("Upload", id, roots, null, usedFiles); + return View("../Admin/Upload/Index", model); + } } - - //// UPLOAD //// - [HttpPost] - [Route("Admin/Upload")] - [DisableFormValueModelBinding] - [ValidateAntiForgeryToken] - public async Task Post() { - List? docs = null; - //// 1. Stage: Check Request format and request spec - // Checks the Content-Type Field (must be multipart + Boundary) - if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { - ModelState.AddModelError("Error", $"Wrong / No Content Type on the Request"); - return BadRequest(ModelState); - } - - // Divides the multipart document into it's sections and sets up a reader - var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit); - var reader = new MultipartReader(boundary, HttpContext.Request.Body); - MultipartSection? section = null; - try { - section = await reader.ReadNextSectionAsync(); - } catch (Exception ex) { - ModelState.AddModelError("Error", "The Request is bad: " + ex.Message); - } - - while (section != null) { - // Multipart document content disposition header read for a section: - // Starts with boundary, contains field name, content-dispo, filename, content-type - var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); - if (hasContentDispositionHeader && contentDisposition != null) { - // Checks if it is a section with content-disposition, name, filename - if (!MultipartRequestHelper.HasFileContentDisposition(contentDisposition)) { - ModelState.AddModelError("Error", $"Wrong Content-Dispostion Headers in Multipart Document"); - return BadRequest(ModelState); - } - - //// 2. Stage: Check File. Sanity checks on the file on a byte level, extension checking, is it empty etc. - var streamedFileContent = await XMLFileHelpers.ProcessStreamedFile( - section, contentDisposition, ModelState, - _permittedExtensions, _fileSizeLimit); - if (!ModelState.IsValid || streamedFileContent == null) - return BadRequest(ModelState); - - //// 3. Stage: Valid XML checking using a simple XDocument.Load() - var xdocument = await XDocumentFileHelper.ProcessStreamedFile(streamedFileContent, ModelState); - if (!ModelState.IsValid || xdocument == null) - return UnprocessableEntity(ModelState); - - //// 4. Stage: Is it a Hamann-Document? What kind? - var retdocs = await _xmlService.ProbeHamannFile(xdocument, ModelState); - if (!ModelState.IsValid || retdocs == null || !retdocs.Any()) - return UnprocessableEntity(ModelState); - - //// 5. Stage: Saving the File(s) - foreach (var doc in retdocs) { - await _xmlService.UpdateAvailableFiles(doc, _targetFilePath, ModelState); - if (!ModelState.IsValid) return StatusCode(500, ModelState); - if (docs == null) docs = new List(); - docs.Add(doc); - } - } - - try { - section = await reader.ReadNextSectionAsync(); - } catch (Exception ex) { - ModelState.AddModelError("Error", "The Request is bad: " + ex.Message); - } - } - - // 6. Stage: Success! Returning Ok, and redirecting - JsonSerializerOptions options = new() { - ReferenceHandler = ReferenceHandler.Preserve, - Converters = { - new IdentificationStringJSONConverter() - } - }; - - string json = JsonSerializer.Serialize(docs); - return Created(nameof(UploadController), json); + private IActionResult error404() { + Response.StatusCode = 404; + return Redirect("/Error404"); } } \ No newline at end of file diff --git a/HaWeb/FileHelpers/HaDocumentWrapper.cs b/HaWeb/FileHelpers/HaDocumentWrapper.cs new file mode 100644 index 0000000..4b1a9c7 --- /dev/null +++ b/HaWeb/FileHelpers/HaDocumentWrapper.cs @@ -0,0 +1,19 @@ +namespace HaWeb.FileHelpers; +using HaDocument.Interfaces; + +public class HaDocumentWrapper : IHaDocumentWrappper { + public ILibrary Library; + + public HaDocumentWrapper() { + Library = HaDocument.Document.Create(new HaWeb.Settings.HaDocumentOptions()); + } + + public ILibrary SetLibrary() { + Library = HaDocument.Document.Create(new HaWeb.Settings.HaDocumentOptions()); + return Library; + } + + public ILibrary GetLibrary() { + return Library; + } +} \ No newline at end of file diff --git a/HaWeb/FileHelpers/IHaDocumentWrapper.cs b/HaWeb/FileHelpers/IHaDocumentWrapper.cs new file mode 100644 index 0000000..bfaafb5 --- /dev/null +++ b/HaWeb/FileHelpers/IHaDocumentWrapper.cs @@ -0,0 +1,7 @@ +namespace HaWeb.FileHelpers; +using HaDocument.Interfaces; + +public interface IHaDocumentWrappper { + public ILibrary SetLibrary(); + public ILibrary GetLibrary(); +} \ No newline at end of file diff --git a/HaWeb/Models/SyntaxCheckModel.cs b/HaWeb/Models/SyntaxCheckModel.cs new file mode 100644 index 0000000..5b5f5ee --- /dev/null +++ b/HaWeb/Models/SyntaxCheckModel.cs @@ -0,0 +1,22 @@ +namespace HaWeb.Models; + +public class SyntaxCheckModel { + public string Prefix { get; private set; } + public List? Errors { get; set; } + public List? Warnings { get; set; } + + public SyntaxCheckModel(string prefix) { + Prefix = prefix; + } +} + +public class SyntaxError { + public string Message { get; private set; } + public string? File { get; set; } + public string? Line { get; set; } + public string? Column { get; set; } + + public SyntaxError(string message) { + Message = message; + } +} \ No newline at end of file diff --git a/HaWeb/Models/UploadViewModel.cs b/HaWeb/Models/UploadViewModel.cs index f7f53b8..2c0bad4 100644 --- a/HaWeb/Models/UploadViewModel.cs +++ b/HaWeb/Models/UploadViewModel.cs @@ -2,7 +2,18 @@ namespace HaWeb.Models; using HaWeb.XMLParser; public class UploadViewModel { - public List? AvailableRoots { get; set; } - public List? AvailableFiles { get; set; } - public Dictionary>? UsedFiles { get; set; } + public string ActiveTitle { get; private set; } + public string? Prefix { get; private set; } + public List? AvailableRoots { get; private set; } + public List? AvailableFiles { get; private set; } + public Dictionary>? UsedFiles { get; private set; } + + + public UploadViewModel(string title, string? prefix, List? roots, List? availableFiles, Dictionary>? usedFiles) { + Prefix = prefix; + ActiveTitle = title; + AvailableRoots = roots; + AvailableFiles = availableFiles; + UsedFiles = usedFiles; + } } \ No newline at end of file diff --git a/HaWeb/Program.cs b/HaWeb/Program.cs index 046ab3d..f4ae4bf 100644 --- a/HaWeb/Program.cs +++ b/HaWeb/Program.cs @@ -29,7 +29,7 @@ var physicalProvider = new PhysicalFileProvider(filepath); builder.Services.AddSingleton(physicalProvider); -builder.Services.AddSingleton(HaDocument.Document.Create(new Options())); +builder.Services.AddSingleton(); builder.Services.AddTransient(); builder.Services.AddSingleton(); builder.Services.AddFeatureManagement(); @@ -50,12 +50,3 @@ app.UseAuthorization(); app.UseStaticFiles(); app.MapControllers(); app.Run(); - - - -class Options : IHaDocumentOptions { - public string HamannXMLFilePath { get; set; } = HaWeb.Settings.General.XMLFILEPATH; - public string[] AvailableVolumes { get; set; } = HaWeb.Settings.General.AVAILABLEVOLUMES; - public bool NormalizeWhitespace { get; set; } = HaWeb.Settings.General.NORMALIZEWHITESPACE; - public (int, int) AvailableYearRange {get; set; } = HaWeb.Settings.General.AVAILABLEYEARRANGE; -} \ No newline at end of file diff --git a/HaWeb/Settings/GeneralSettings.cs b/HaWeb/Settings/GeneralSettings.cs index c177c25..7ecc83f 100644 --- a/HaWeb/Settings/GeneralSettings.cs +++ b/HaWeb/Settings/GeneralSettings.cs @@ -2,9 +2,5 @@ namespace HaWeb.Settings; using HaWeb.Settings.XMLRoots; public static class General { - // Classes generated by parsing the XML: - public const string XMLFILEPATH = @"Hamann.xml"; - public const bool NORMALIZEWHITESPACE = true; - public static (int, int) AVAILABLEYEARRANGE = (1751, 1788); - public static string[] AVAILABLEVOLUMES = { }; + } \ No newline at end of file diff --git a/HaWeb/Settings/HaDocumentSettings.cs b/HaWeb/Settings/HaDocumentSettings.cs index 0ffdd02..df197c8 100644 --- a/HaWeb/Settings/HaDocumentSettings.cs +++ b/HaWeb/Settings/HaDocumentSettings.cs @@ -1 +1,9 @@ -// TODO \ No newline at end of file +namespace HaWeb.Settings; +using HaDocument.Interfaces; + +class HaDocumentOptions : IHaDocumentOptions { + public string HamannXMLFilePath { get; set; } = @"Hamann.xml"; + public string[] AvailableVolumes { get; set; } = { }; + public bool NormalizeWhitespace { get; set; } = true; + public (int, int) AvailableYearRange {get; set; } = (1751, 1788); +} \ No newline at end of file diff --git a/HaWeb/Views/Admin/Upload/Index.cshtml b/HaWeb/Views/Admin/Upload/Index.cshtml index 07e46ec..5ccacf3 100644 --- a/HaWeb/Views/Admin/Upload/Index.cshtml +++ b/HaWeb/Views/Admin/Upload/Index.cshtml @@ -1,42 +1,108 @@ -@model UploadViewModel; +@model UploadViewModel?; -
+
@foreach (var item in Model.AvailableRoots.OrderBy(x => x.Type)) { -
-
@item.Type
- @if (Model.UsedFiles != null && Model.UsedFiles.ContainsKey(item.Prefix)) { - @foreach(var file in Model.UsedFiles[item.Prefix]) { - @file.File.Name - } - } -
+ +
@item.Type
+ @if (Model.UsedFiles != null && Model.UsedFiles.ContainsKey(item.Prefix)) { +
+ @foreach(var file in Model.UsedFiles[item.Prefix]) { + @if (file == Model.UsedFiles[item.Prefix].Last()) + { + @file.File.Name + } + else + { + @file.File.Name; + } + } +
+ } + else { +
Keine Datei geladen!
+ } +
} -
- -
- -
-
+
+
+ +
+ Fehler!
+ +
+
+ +
+ +
+ + +
+
+
+
+ + +
+

@Model.ActiveTitle

+ @if (Model.Prefix != null) { +
+
+ @if(Model.UsedFiles != null && Model.UsedFiles.ContainsKey(Model.Prefix)) { + @foreach (var item in Model.UsedFiles[Model.Prefix]) + { +
@item.File.Name
+ } + } + else { +
Keine Datei geladen
+ } + @if (Model.AvailableFiles != null) + { + + } +
+
+ } +
+ +
+@if (Model.UsedFiles != null && Model.Prefix != null && Model.UsedFiles.ContainsKey(Model.Prefix)) { +
+
+ +
+
+ +
+
+ +
+ +
+}
@section Scripts {