From d8155e26f67c60bd6d2c0fbb2291579e0b899a68 Mon Sep 17 00:00:00 2001 From: schnulller Date: Tue, 7 Jun 2022 16:14:27 +0200 Subject: [PATCH] Created FileList with ability to set used files --- HaDocumentNew/Document.cs | 5 + HaDocumentNew/HaDocumentNew.csproj | 9 + HaDocumentNew/Interfaces/IDocument.cs | 5 + .../Interfaces/IDocumentSyntaxErrors.cs | 0 .../Interfaces/IHaDocumentOptions.cs | 8 + HaDocumentNew/Interfaces/ILibrary.cs | 29 +++ HaDocumentNew/Models/Backlink.cs | 24 +++ HaDocumentNew/Models/Comment.cs | 36 ++++ HaDocumentNew/Models/Editreason.cs | 32 ++++ HaDocumentNew/Models/HaModel.cs | 15 ++ HaDocumentNew/Models/Hand.cs | 26 +++ HaDocumentNew/Models/IntermediateLibrary.cs | 70 +++++++ HaDocumentNew/Models/Letter.cs | 16 ++ HaDocumentNew/Models/Library.cs | 150 +++++++++++++++ HaDocumentNew/Models/Location.cs | 14 ++ HaDocumentNew/Models/Marginal.cs | 23 +++ HaDocumentNew/Models/Meta.cs | 49 +++++ HaDocumentNew/Models/OptionalBool.cs | 13 ++ HaDocumentNew/Models/Person.cs | 20 ++ HaDocumentNew/Models/Tradition.cs | 14 ++ HaDocumentNew/Models/ZHInfo.cs | 22 +++ .../Settings/XMLRoots/CommentRoot.cs | 50 +++++ .../Settings/XMLRoots/DescriptionsRoot.cs | 49 +++++ .../Settings/XMLRoots/DocumentRoot.cs | 50 +++++ HaDocumentNew/Settings/XMLRoots/EditsRoot.cs | 50 +++++ .../Settings/XMLRoots/MarginalsRoot.cs | 49 +++++ .../Settings/XMLRoots/ReferencesRoot.cs | 48 +++++ .../Settings/XMLRoots/TraditionsRoot.cs | 49 +++++ HaWeb/Controllers/APIController.cs | 159 +++++++++++++++- HaWeb/Controllers/UploadController.cs | 19 +- HaWeb/FileHelpers/HaDocumentWrapper.cs | 14 +- HaWeb/FileHelpers/IXMLProvider.cs | 2 + HaWeb/FileHelpers/XMLFileHelpers.cs | 15 ++ HaWeb/FileHelpers/XMLProvider.cs | 7 +- HaWeb/Models/FileModel.cs | 6 +- HaWeb/Models/UploadViewModel.cs | 2 +- HaWeb/Program.cs | 2 +- HaWeb/Views/Admin/Upload/Index.cshtml | 85 ++++++--- HaWeb/Views/Shared/_FileList.cshtml | 71 +++++++ HaWeb/XMLParser/IXMLService.cs | 2 + HaWeb/XMLParser/XMLService.cs | 7 + HaWeb/tailwind.config.js | 1 + HaWeb/wwwroot/css/output.css | 178 +++++++++++++----- HaWeb/wwwroot/css/site.css | 92 ++++++--- 44 files changed, 1468 insertions(+), 119 deletions(-) create mode 100644 HaDocumentNew/Document.cs create mode 100644 HaDocumentNew/HaDocumentNew.csproj create mode 100644 HaDocumentNew/Interfaces/IDocument.cs create mode 100644 HaDocumentNew/Interfaces/IDocumentSyntaxErrors.cs create mode 100644 HaDocumentNew/Interfaces/IHaDocumentOptions.cs create mode 100644 HaDocumentNew/Interfaces/ILibrary.cs create mode 100644 HaDocumentNew/Models/Backlink.cs create mode 100644 HaDocumentNew/Models/Comment.cs create mode 100644 HaDocumentNew/Models/Editreason.cs create mode 100644 HaDocumentNew/Models/HaModel.cs create mode 100644 HaDocumentNew/Models/Hand.cs create mode 100644 HaDocumentNew/Models/IntermediateLibrary.cs create mode 100644 HaDocumentNew/Models/Letter.cs create mode 100644 HaDocumentNew/Models/Library.cs create mode 100644 HaDocumentNew/Models/Location.cs create mode 100644 HaDocumentNew/Models/Marginal.cs create mode 100644 HaDocumentNew/Models/Meta.cs create mode 100644 HaDocumentNew/Models/OptionalBool.cs create mode 100644 HaDocumentNew/Models/Person.cs create mode 100644 HaDocumentNew/Models/Tradition.cs create mode 100644 HaDocumentNew/Models/ZHInfo.cs create mode 100644 HaDocumentNew/Settings/XMLRoots/CommentRoot.cs create mode 100644 HaDocumentNew/Settings/XMLRoots/DescriptionsRoot.cs create mode 100644 HaDocumentNew/Settings/XMLRoots/DocumentRoot.cs create mode 100644 HaDocumentNew/Settings/XMLRoots/EditsRoot.cs create mode 100644 HaDocumentNew/Settings/XMLRoots/MarginalsRoot.cs create mode 100644 HaDocumentNew/Settings/XMLRoots/ReferencesRoot.cs create mode 100644 HaDocumentNew/Settings/XMLRoots/TraditionsRoot.cs create mode 100644 HaWeb/Views/Shared/_FileList.cshtml diff --git a/HaDocumentNew/Document.cs b/HaDocumentNew/Document.cs new file mode 100644 index 0000000..b402d7d --- /dev/null +++ b/HaDocumentNew/Document.cs @@ -0,0 +1,5 @@ +namespace HaDocument; +public class Document +{ + +} diff --git a/HaDocumentNew/HaDocumentNew.csproj b/HaDocumentNew/HaDocumentNew.csproj new file mode 100644 index 0000000..132c02c --- /dev/null +++ b/HaDocumentNew/HaDocumentNew.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/HaDocumentNew/Interfaces/IDocument.cs b/HaDocumentNew/Interfaces/IDocument.cs new file mode 100644 index 0000000..4ae6f29 --- /dev/null +++ b/HaDocumentNew/Interfaces/IDocument.cs @@ -0,0 +1,5 @@ +namespace HaDocument.Interfaces; + +public interface IDocument { + +} \ No newline at end of file diff --git a/HaDocumentNew/Interfaces/IDocumentSyntaxErrors.cs b/HaDocumentNew/Interfaces/IDocumentSyntaxErrors.cs new file mode 100644 index 0000000..e69de29 diff --git a/HaDocumentNew/Interfaces/IHaDocumentOptions.cs b/HaDocumentNew/Interfaces/IHaDocumentOptions.cs new file mode 100644 index 0000000..808bbb4 --- /dev/null +++ b/HaDocumentNew/Interfaces/IHaDocumentOptions.cs @@ -0,0 +1,8 @@ +namespace HaDocument.Interfaces; + +public interface IHaDocumentOptions { + string HamannXMLFilePath { get; set; } + string[] AvailableVolumes { get; set; } + bool NormalizeWhitespace { get; set; } + (int, int) AvailableYearRange { get; set; } +} diff --git a/HaDocumentNew/Interfaces/ILibrary.cs b/HaDocumentNew/Interfaces/ILibrary.cs new file mode 100644 index 0000000..d63a7c5 --- /dev/null +++ b/HaDocumentNew/Interfaces/ILibrary.cs @@ -0,0 +1,29 @@ +namespace HaDocument.Interfaces; +using System; +using System.Collections.Immutable; +using HaDocument.Models; +using System.Linq; + +public interface ILibrary { + IHaDocumentOptions Options { get; } + ImmutableDictionary Traditions { get; } + ImmutableDictionary Persons { get; } + ImmutableDictionary Metas { get; } + ImmutableDictionary Marginals { get; } + ImmutableDictionary Locations { get; } + ImmutableDictionary Letters { get; } + ImmutableDictionary HandPersons { get; } + ImmutableDictionary Editreasons { get; } + ImmutableDictionary Comments { get; } + ImmutableDictionary> Backlinks { get; } + ImmutableDictionary> Hands { get; } + ImmutableDictionary>> Structure { get; } + + ImmutableDictionary> CommentsByCategoryLetter { get; } + Lookup CommentsByCategory { get; } + Lookup MarginalsByLetter { get; } + Lookup EditreasonsByLetter { get; } + ImmutableSortedSet MetasByDate { get; } + ILookup MetasByYear { get; } + ImmutableDictionary SubCommentsByID { get; } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/Backlink.cs b/HaDocumentNew/Models/Backlink.cs new file mode 100644 index 0000000..51f1eec --- /dev/null +++ b/HaDocumentNew/Models/Backlink.cs @@ -0,0 +1,24 @@ +namespace HaDocument.Models { + public class Backlink { + public string Index { get; } = ""; + + public string Letter { get; } = ""; + public string Page { get; } = ""; + public string Line { get; } = ""; + public string MarginalIndex { get; } = ""; + + public Backlink( + string index, + string letter, + string page, + string line, + string marginalindex + ) { + Index = index; + Letter = letter; + Page = page; + Line = line; + MarginalIndex = marginalindex; + } + } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/Comment.cs b/HaDocumentNew/Models/Comment.cs new file mode 100644 index 0000000..d072eef --- /dev/null +++ b/HaDocumentNew/Models/Comment.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace HaDocument.Models { + public class Comment{ + public string Entry { get; } = ""; + public string Index { get; } = ""; + public string Type { get; } = ""; + public string Lemma { get; } = ""; + public string Parent { get; } = ""; + public int Order { get; } = -1; + public ImmutableSortedDictionary Kommentare { get; } + + public Comment( + string entry, + string index, + string type, + string lemma, + int order, + SortedDictionary subComments, + string parent="" + ) { + Entry = entry; + Index = index; + Type = type; + Lemma = lemma; + Order = order; + Parent = parent; + if (subComments != null) + Kommentare = ImmutableSortedDictionary.ToImmutableSortedDictionary(subComments); + else + Kommentare = null; + } + } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/Editreason.cs b/HaDocumentNew/Models/Editreason.cs new file mode 100644 index 0000000..0a974a5 --- /dev/null +++ b/HaDocumentNew/Models/Editreason.cs @@ -0,0 +1,32 @@ +namespace HaDocument.Models { + public class Editreason { + public string Index { get; } = ""; + public string Element { get; } = ""; + public string Letter { get; } = ""; + public string StartPage { get; } = ""; + public string StartLine { get; } = ""; + public string EndPage { get; } = ""; + public string EndLine { get; } = ""; + public string Reference { get; } = ""; + + public Editreason( + string index, + string element, + string letter = "", + string startpage = "", + string startline = "", + string endpage = "", + string endline = "", + string reference = "" + ) { + Index = index; + Element = element; + Letter = letter; + StartPage = startpage; + StartLine = startline; + EndPage = endpage; + EndLine = endline; + Reference = reference; + } + } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/HaModel.cs b/HaDocumentNew/Models/HaModel.cs new file mode 100644 index 0000000..9d944cd --- /dev/null +++ b/HaDocumentNew/Models/HaModel.cs @@ -0,0 +1,15 @@ +using System; +using HaXMLReader.EvArgs; +using System.Collections.Generic; + +namespace HaDocument.Models { + public abstract class HaModel { + protected static List<(Func, Action)> FieldActions = null; + + internal static void AddAction(Func If, Action Then) { + if (If == null || Then == null) throw new ArgumentNullException(); + if (FieldActions == null) FieldActions = new List<(Func, Action)>(); + FieldActions.Add((If, Then)); + } + } +} diff --git a/HaDocumentNew/Models/Hand.cs b/HaDocumentNew/Models/Hand.cs new file mode 100644 index 0000000..944a40d --- /dev/null +++ b/HaDocumentNew/Models/Hand.cs @@ -0,0 +1,26 @@ +namespace HaDocument.Models { + public class Hand : HaModel { + public string Letter { get; } = ""; + public string Person { get; } = ""; + public string StartPage { get; } = ""; + public string StartLine { get; } = ""; + public string EndPage { get; } = ""; + public string EndLine {get; } = ""; + + public Hand( + string letter, + string person, + string startpage, + string startline, + string endpage, + string endline + ) { + Letter = letter; + Person = person; + StartPage = startpage; + StartLine = startline; + EndPage = endpage; + EndLine = endline; + } + } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/IntermediateLibrary.cs b/HaDocumentNew/Models/IntermediateLibrary.cs new file mode 100644 index 0000000..a9fbd9e --- /dev/null +++ b/HaDocumentNew/Models/IntermediateLibrary.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using HaDocument.Interfaces; + +namespace HaDocument.Models +{ + public class IntermediateLibrary + { + public Dictionary Traditions; + public Dictionary Persons; + public Dictionary Metas; + public Dictionary Marginals; + public Dictionary Locations; + public Dictionary Letters; + public Dictionary HandPersons; + public Dictionary Editreasons; + public Dictionary Comments; + public Dictionary> Backlinks; + public Dictionary> Hands; + + // Helper Library for precalculationg the Structure of the Document: + public Dictionary>> LetterPageLines; + + public Library GetLibrary(IHaDocumentOptions options) + { + var Structure = new Dictionary>>(); + foreach (var letter in LetterPageLines) + { + if (Metas.ContainsKey(letter.Key) && + Metas[letter.Key].ZH != null) + { + if (!Structure.ContainsKey(Metas[letter.Key].ZH.Volume)) + { + Structure.Add(Metas[letter.Key].ZH.Volume, new Dictionary>()); + } + foreach (var page in letter.Value) + { + if (!Structure[Metas[letter.Key].ZH.Volume].ContainsKey(page.Key)) + { + Structure[Metas[letter.Key].ZH.Volume].Add(page.Key, new Dictionary()); + } + foreach (var line in page.Value) + { + if (!Structure[Metas[letter.Key].ZH.Volume][page.Key].ContainsKey(line)) + { + Structure[Metas[letter.Key].ZH.Volume][page.Key].Add(line, letter.Key); + } + } + } + } + + } + + return new Library( + Traditions, + Persons, + Metas, + Marginals, + Locations, + Letters, + HandPersons, + Editreasons, + Comments, + Backlinks, + Hands, + Structure, + options + ); + } + } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/Letter.cs b/HaDocumentNew/Models/Letter.cs new file mode 100644 index 0000000..87c013b --- /dev/null +++ b/HaDocumentNew/Models/Letter.cs @@ -0,0 +1,16 @@ +namespace HaDocument.Models { + public class Letter : HaModel { + public string Index { get; } = ""; + public string Element { get; } = ""; + public string Value { get; } = ""; + + public Letter( + string index, + string element, + string value + ) { + Index = index; + Element = element; + } + } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/Library.cs b/HaDocumentNew/Models/Library.cs new file mode 100644 index 0000000..968452e --- /dev/null +++ b/HaDocumentNew/Models/Library.cs @@ -0,0 +1,150 @@ +using System; +using HaDocument.Interfaces; +using System.Collections.Immutable; +using System.Collections.Generic; +using HaDocument.Models; +using HaDocument.Comparers; +using System.Linq; + +namespace HaDocument.Models +{ + public class Library : ILibrary + { + public IHaDocumentOptions Options { get; } + public ImmutableDictionary Traditions { get; } + public ImmutableDictionary Persons { get; } + public ImmutableDictionary Metas { get; } + public ImmutableDictionary Marginals { get; } + public ImmutableDictionary Locations { get; } + public ImmutableDictionary Letters { get; } + public ImmutableDictionary HandPersons { get; } + public ImmutableDictionary Editreasons { get; } + public ImmutableDictionary Comments { get; } + public ImmutableDictionary> Backlinks { get; } + public ImmutableDictionary> Hands { get; } + + // Structure for lookups from ZH: + public ImmutableDictionary>> Structure { get; } + + // Lookups: + // Auswählen von Kommentaren nach (1) Kategorie, (2) Anfangsbuchstaben vom Lemma. + // So: _ = CommentsByCategoryLetter['neuzeit']['A'] + public ImmutableDictionary> CommentsByCategoryLetter { get; } + public Lookup CommentsByCategory { get; } + // Auswählen von Subkommentaren nach ID + public ImmutableDictionary SubCommentsByID { get; } + // Auswählen von Marginalien nach Briefen + public Lookup MarginalsByLetter { get; } + // Ausw?hlen von Edits nach Briefen + public Lookup EditreasonsByLetter { get; } + // Auswählen von Briefen nach autoptischer Numemr und in zeitlich sortierter Reihenfolge. + public ImmutableSortedSet MetasByDate { get; } + // Auswählen von Briefen nach dem Jahr, sortiert nach Datum + public ILookup MetasByYear { get; } + + + public Library( + Dictionary traditions, + Dictionary persons, + Dictionary meta, + Dictionary marginals, + Dictionary locations, + Dictionary letters, + Dictionary handPersons, + Dictionary editReasons, + Dictionary comments, + Dictionary> backlinks, + Dictionary> hands, + Dictionary>> Structure, + IHaDocumentOptions options + ) + { + // Dictionaries + Traditions = ImmutableDictionary.ToImmutableDictionary(traditions); + Persons = ImmutableDictionary.ToImmutableDictionary(persons); + Metas = ImmutableDictionary.ToImmutableDictionary(meta); + Marginals = ImmutableDictionary.ToImmutableDictionary(marginals); + Locations = ImmutableDictionary.ToImmutableDictionary(locations); + Letters = ImmutableDictionary.ToImmutableDictionary(letters); + HandPersons = ImmutableDictionary.ToImmutableDictionary(handPersons); + Editreasons = ImmutableDictionary.ToImmutableDictionary(editReasons); + Comments = ImmutableDictionary.ToImmutableDictionary(comments); + + var backbuilder = ImmutableDictionary.CreateBuilder>(); + foreach (var entry in backlinks) + backbuilder.Add(entry.Key, ImmutableList.ToImmutableList(entry.Value)); + Backlinks = backbuilder.ToImmutableDictionary(); + + var handbuilder = ImmutableDictionary.CreateBuilder>(); + foreach (var entry in hands) + handbuilder.Add(entry.Key, ImmutableList.ToImmutableList(entry.Value)); + Hands = handbuilder.ToImmutableDictionary(); + + // Lookups + CommentsByCategory = (Lookup)Comments.Values.ToLookup(x => x.Type); + var CommentsByLetter_builder = ImmutableDictionary.CreateBuilder>(); + foreach (var ts in CommentsByCategory) + { + CommentsByLetter_builder.Add(ts.Key, (Lookup)ts.ToLookup(x => x.Index.Substring(0, 1).ToUpper())); + } + CommentsByCategoryLetter = CommentsByLetter_builder.ToImmutableDictionary(); + MarginalsByLetter = (Lookup)Marginals.Values.ToLookup(x => x.Letter); + EditreasonsByLetter = (Lookup)Editreasons.Values.ToLookup(x => x.Letter); + MetasByDate = Metas.Values.ToImmutableSortedSet(new DefaultComparer()); + MetasByYear = Metas.Values.ToLookup(x => x.Sort.Year.ToString()); + + var tempbuilder = ImmutableDictionary.CreateBuilder(); + foreach (var comm in Comments) + if (comm.Value.Kommentare != null) + foreach (var subcomm in comm.Value.Kommentare) + if (!tempbuilder.ContainsKey(subcomm.Key)) + tempbuilder.Add(subcomm.Key, subcomm.Value); + SubCommentsByID = tempbuilder.ToImmutableDictionary(); + + var tempstructurebuilder = ImmutableDictionary.CreateBuilder>>(); + foreach (var volume in Structure) + { + if (volume.Value != null) + { + var tempvolbuilder = ImmutableDictionary.CreateBuilder>(); + foreach (var page in volume.Value) + { + if (page.Value != null) + { + tempvolbuilder.Add(page.Key, page.Value.ToImmutableDictionary()); + } + } + if (tempvolbuilder.Any()) + { + tempstructurebuilder.Add(volume.Key, tempvolbuilder.ToImmutableDictionary()); + } + } + } + + this.Structure = tempstructurebuilder.ToImmutableDictionary(); + + Options = options; + + } + + // public List MetasByDate() { + // var ret = Metas.OrderBy(x => x.Value, new DefaultComparer()).ToLookup(x => x.Value.Autopsic, x => x.Value); + // ret.Sort(new DefaultComparer()); + // return ret; + // } + + public ImmutableList MetasByZH() + { + return Metas.Values.ToImmutableList().Sort(new Comparers.ZHComparer()); + } + + public List PersonByAlphabet() + { + var ret = Persons.Values.ToList(); + ret.Sort(new PersonComparer()); + return ret; + } + + + } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/Location.cs b/HaDocumentNew/Models/Location.cs new file mode 100644 index 0000000..8e1db38 --- /dev/null +++ b/HaDocumentNew/Models/Location.cs @@ -0,0 +1,14 @@ +namespace HaDocument.Models { + public class Location { + public string Index { get; } = ""; + public string Name { get; } = ""; + + public Location( + string index, + string name + ) { + Index = index; + Name = name; + } + } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/Marginal.cs b/HaDocumentNew/Models/Marginal.cs new file mode 100644 index 0000000..d536a77 --- /dev/null +++ b/HaDocumentNew/Models/Marginal.cs @@ -0,0 +1,23 @@ +namespace HaDocument.Models { + public class Marginal { + public string Index { get; } = ""; + public string Letter { get; } = ""; + public string Page { get; } = ""; + public string Line { get; } = ""; + public string Element { get; } = ""; + + public Marginal( + string index, + string letter, + string page, + string line, + string elemnt + ) { + Index = index; + Letter = letter; + Page = page; + Line = line; + Element = elemnt; + } + } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/Meta.cs b/HaDocumentNew/Models/Meta.cs new file mode 100644 index 0000000..2c13b0c --- /dev/null +++ b/HaDocumentNew/Models/Meta.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using HaXMLReader.EvArgs; + +namespace HaDocument.Models { + public class Meta { + public string Index { get; } = ""; + public string Autopsic { get; } = ""; + public string Date { get; } = ""; + public DateTime Sort { get; } = new DateTime(1700, 1, 1); + public int Order { get; } = -1; + public string Location { get; } = ""; + public List Senders { get; } = null; + public List Receivers { get; } = null; + public OptionalBool hasOriginal { get; } = OptionalBool.None; + public OptionalBool isProofread { get; } = OptionalBool.None; + public OptionalBool isDraft { get; } = OptionalBool.None; + public ZHInfo ZH { get; } = null; + + public Meta( + string index, + string autopsic, + string date, + DateTime sort, + int order, + OptionalBool hasOriginal, + OptionalBool isProofread, + OptionalBool isDraft, + string location, + List senders, + List receivers, + ZHInfo ZH + ) { + Index = index; + Autopsic = autopsic; + Date = date; + Sort = sort; + Order = order; + Location = location; + Senders = senders; + Receivers = receivers; + this.hasOriginal = hasOriginal; + this.isProofread = isProofread; + this.isDraft = isDraft; + this.ZH = ZH; + } + + } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/OptionalBool.cs b/HaDocumentNew/Models/OptionalBool.cs new file mode 100644 index 0000000..bb37fdc --- /dev/null +++ b/HaDocumentNew/Models/OptionalBool.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace HaDocument.Models +{ + public enum OptionalBool + { + True, + False, + None + } +} diff --git a/HaDocumentNew/Models/Person.cs b/HaDocumentNew/Models/Person.cs new file mode 100644 index 0000000..cc4b104 --- /dev/null +++ b/HaDocumentNew/Models/Person.cs @@ -0,0 +1,20 @@ +namespace HaDocument.Models { + public class Person { + public string Index { get; } = ""; + public string Name { get; } = ""; + public string Prename { get; } = ""; + public string Surname { get; } = ""; + + public Person( + string index, + string name, + string prename, + string surname + ) { + Index = index; + Name = name; + Prename = prename; + Surname = surname; + } + } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/Tradition.cs b/HaDocumentNew/Models/Tradition.cs new file mode 100644 index 0000000..e7154ff --- /dev/null +++ b/HaDocumentNew/Models/Tradition.cs @@ -0,0 +1,14 @@ +namespace HaDocument.Models { + public class Tradition { + public string Index { get; } = ""; + public string Element { get; } = ""; + + public Tradition( + string index, + string element + ) { + Index = index; + Element = element; + } + } +} \ No newline at end of file diff --git a/HaDocumentNew/Models/ZHInfo.cs b/HaDocumentNew/Models/ZHInfo.cs new file mode 100644 index 0000000..0cd8274 --- /dev/null +++ b/HaDocumentNew/Models/ZHInfo.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace HaDocument.Models +{ + + public class ZHInfo + { + public bool alternativeLineNumbering { get; } = false; + public bool dateChanged { get; } = false; + public string Volume { get; } = ""; + public string Page { get; } = ""; + + public ZHInfo(bool alternativeLineNumbering, bool dateChanged, string Volume, string Page) { + this.alternativeLineNumbering = alternativeLineNumbering; + this.dateChanged = dateChanged; + this.Volume = Volume; + this.Page = Page; + } + } +} diff --git a/HaDocumentNew/Settings/XMLRoots/CommentRoot.cs b/HaDocumentNew/Settings/XMLRoots/CommentRoot.cs new file mode 100644 index 0000000..6615185 --- /dev/null +++ b/HaDocumentNew/Settings/XMLRoots/CommentRoot.cs @@ -0,0 +1,50 @@ +namespace HaDocument.Settings.XMLRoots; +using System.Xml.Linq; + +public class CommentRoot : HaWeb.XMLParser.IXMLRoot { + public string Type { get; } = "Register"; + public string Prefix { get; } = "register"; + public string[] XPathContainer { get; } = { ".//data//kommentare/kommcat", ".//kommentare/kommcat" }; + + public Predicate IsCollectedObject { get; } = (elem) => { + if (elem.Name == "kommentar") return true; + else return false; + }; + + public Func GetKey { get; } = (elem) => { + var index = elem.Attribute("id"); + if (index != null && !String.IsNullOrWhiteSpace(index.Value)) + return index.Value; + else return null; + }; + + public List<(string, string?)>? GenerateFields(XMLRootDocument document) { + return null; + } + + public (string?, string?) GenerateIdentificationString(XElement element) { + var kat = element.Attribute("value"); + if (kat != null && !String.IsNullOrWhiteSpace(kat.Value)) + return (null, kat.Value); + return (null, null); + } + + public bool Replaces(XMLRootDocument doc1, XMLRootDocument doc2) { + return true; + } + + public XElement CreateHamannDocument(XElement element) { + var opus = new XElement("opus"); + var kommentare = new XElement("kommentare"); + kommentare.AddFirst(element); + opus.AddFirst(kommentare); + return opus; + } + + public void MergeIntoFile(XElement file, XMLRootDocument document) { + if (file.Element("kommentare") == null) + file.AddFirst(new XElement("kommentare")); + file.Element("kommentare")!.AddFirst(document.Root); + } + +} \ No newline at end of file diff --git a/HaDocumentNew/Settings/XMLRoots/DescriptionsRoot.cs b/HaDocumentNew/Settings/XMLRoots/DescriptionsRoot.cs new file mode 100644 index 0000000..c1728e3 --- /dev/null +++ b/HaDocumentNew/Settings/XMLRoots/DescriptionsRoot.cs @@ -0,0 +1,49 @@ +namespace HaDocument.Settings.XMLRoots; +using System.Xml.Linq; + +public class DescriptionsRoot : HaWeb.XMLParser.IXMLRoot { + public string Type { get; } = "Metadaten"; + public string Prefix { get; } = "metadaten"; + public string[] XPathContainer { get; } = { ".//data/descriptions", ".//descriptions" }; + + public Predicate IsCollectedObject { get; } = (elem) => { + if (elem.Name == "letterDesc") return true; + return false; + }; + + // public Func GetKey { get; } = (elem) => { + // var index = elem.Attribute("ref"); + // if (index != null && !String.IsNullOrWhiteSpace(index.Value)) + // return index.Value; + // else return null; + // }; + + public List<(string, string?)>? GenerateFields(XMLRootDocument document) { + return null; + } + + public (string?, string?) GenerateIdentificationString(XElement element) { + return (null, null); + } + + public bool Replaces(XMLRootDocument doc1, XMLRootDocument doc2) { + return true; + } + + public XElement CreateHamannDocument(XElement element) { + var opus = new XElement("opus"); + opus.AddFirst(element); + return opus; + } + + public void MergeIntoFile(XElement file, XMLRootDocument document) { + if (file.Element("descriptions") == null) + file.AddFirst(new XElement("descriptions")); + var elements = document.Root.Elements().Where(x => IsCollectedObject(x)); + var root = file.Element("descriptions"); + foreach (var element in elements) { + root!.Add(element); + } + } + +} \ No newline at end of file diff --git a/HaDocumentNew/Settings/XMLRoots/DocumentRoot.cs b/HaDocumentNew/Settings/XMLRoots/DocumentRoot.cs new file mode 100644 index 0000000..d894cb5 --- /dev/null +++ b/HaDocumentNew/Settings/XMLRoots/DocumentRoot.cs @@ -0,0 +1,50 @@ +namespace HaDocument.Settings.XMLRoots; +using System.Xml.Linq; + + +public class DocumentRoot : HaWeb.XMLParser.IXMLRoot { + public string Type { get; } = "Brieftext"; + public string Prefix { get; } = "brieftext"; + public string[] XPathContainer { get; } = { ".//data/document", ".//document" }; + + public Predicate IsCollectedObject { get; } = (elem) => { + if (elem.Name == "letterText") return true; + else return false; + }; + + public Func GetKey { get; } = (elem) => { + var index = elem.Attribute("index"); + if (index != null && !String.IsNullOrWhiteSpace(index.Value)) + return index.Value; + else return null; + }; + + public List<(string, string?)>? GenerateFields(XMLRootDocument document) { + return null; + } + + public (string?, string?) GenerateIdentificationString(XElement element) { + return (null, null); + } + + public bool Replaces(XMLRootDocument doc1, XMLRootDocument doc2) { + return true; + } + + public XElement CreateHamannDocument(XElement element) { + var opus = new XElement("opus"); + opus.AddFirst(element); + return opus; + } + + public void MergeIntoFile(XElement file, XMLRootDocument document) { + if (file.Element("document") == null) + file.AddFirst(new XElement("document")); + var elements = document.Root.Elements().Where(x => IsCollectedObject(x)); + var root = file.Element("document"); + foreach (var element in elements) { + root!.Add(element); + } + } + +} \ No newline at end of file diff --git a/HaDocumentNew/Settings/XMLRoots/EditsRoot.cs b/HaDocumentNew/Settings/XMLRoots/EditsRoot.cs new file mode 100644 index 0000000..30d7953 --- /dev/null +++ b/HaDocumentNew/Settings/XMLRoots/EditsRoot.cs @@ -0,0 +1,50 @@ +namespace HaDocument.Settings.XMLRoots; +using System.Xml.Linq; + + +public class EditsRoot : HaWeb.XMLParser.IXMLRoot { + public string Type { get; } = "Texteingriffe"; + public string Prefix { get; } = "texteingriffe"; + public string[] XPathContainer { get; } = { ".//data/edits", ".//edits" }; + + public Predicate IsCollectedObject { get; } = (elem) => { + if (elem.Name == "editreason") return true; + else return false; + }; + + public Func GetKey { get; } = (elem) => { + var index = elem.Attribute("index"); + if (index != null && !String.IsNullOrWhiteSpace(index.Value)) + return index.Value; + else return null; + }; + + public List<(string, string?)>? GenerateFields(XMLRootDocument document) { + return null; + } + + public (string?, string?) GenerateIdentificationString(XElement element) { + return (null, null); + } + + public bool Replaces(XMLRootDocument doc1, XMLRootDocument doc2) { + return true; + } + + public XElement CreateHamannDocument(XElement element) { + var opus = new XElement("opus"); + opus.AddFirst(element); + return opus; + } + + public void MergeIntoFile(XElement file, XMLRootDocument document) { + if (file.Element("edits") == null) + file.AddFirst(new XElement("edits")); + var elements = document.Root.Elements().Where(x => IsCollectedObject(x)); + var root = file.Element("edits"); + foreach (var element in elements) { + root!.Add(element); + } + } + +} \ No newline at end of file diff --git a/HaDocumentNew/Settings/XMLRoots/MarginalsRoot.cs b/HaDocumentNew/Settings/XMLRoots/MarginalsRoot.cs new file mode 100644 index 0000000..d715152 --- /dev/null +++ b/HaDocumentNew/Settings/XMLRoots/MarginalsRoot.cs @@ -0,0 +1,49 @@ +namespace HaDocument.Settings.XMLRoots; +using System.Xml.Linq; + +public class MarginalsRoot : HaWeb.XMLParser.IXMLRoot { + public string Type { get; } = "Stellenkommentar"; + public string Prefix { get; } = "stellenkommentar"; + public string[] XPathContainer { get; } = { ".//data/marginalien", ".//marginalien" }; + + public Predicate IsCollectedObject { get; } = (elem) => { + if (elem.Name == "marginal") return true; + else return false; + }; + + public Func GetKey { get; } = (elem) => { + var index = elem.Attribute("index"); + if (index != null && !String.IsNullOrWhiteSpace(index.Value)) + return index.Value; + else return null; + }; + + public List<(string, string?)>? GenerateFields(XMLRootDocument document) { + return null; + } + + public (string?, string?) GenerateIdentificationString(XElement element) { + return (null, null); + } + + public bool Replaces(XMLRootDocument doc1, XMLRootDocument doc2) { + return true; + } + + public XElement CreateHamannDocument(XElement element) { + var opus = new XElement("opus"); + opus.AddFirst(element); + return opus; + } + + public void MergeIntoFile(XElement file, XMLRootDocument document) { + if (file.Element("marginalien") == null) + file.AddFirst(new XElement("marginalien")); + var elements = document.Root.Elements().Where(x => IsCollectedObject(x)); + var root = file.Element("marginalien"); + foreach (var element in elements) { + root!.Add(element); + } + } + +} \ No newline at end of file diff --git a/HaDocumentNew/Settings/XMLRoots/ReferencesRoot.cs b/HaDocumentNew/Settings/XMLRoots/ReferencesRoot.cs new file mode 100644 index 0000000..f4d1925 --- /dev/null +++ b/HaDocumentNew/Settings/XMLRoots/ReferencesRoot.cs @@ -0,0 +1,48 @@ +namespace HaDocument.Settings.XMLRoots; +using System.Xml.Linq; + + +public class ReferencesRoot : HaWeb.XMLParser.IXMLRoot { + public string Type { get; } = "Personen / Orte"; + public string Prefix { get; } = "personenorte"; + public string[] XPathContainer { get; } = { ".//data/definitions", ".//definitions" }; + + public Predicate IsCollectedObject { get; } = (elem) => { + if (elem.Name == "personDefs" || elem.Name == "structureDefs" || elem.Name == "handDefs" || elem.Name == "locationDefs") + return true; + return false; + }; + + public Func GetKey { get; } = (elem) => { + return elem.Name.ToString(); + }; + + public List<(string, string?)>? GenerateFields(XMLRootDocument document) { + return null; + } + + public (string?, string?) GenerateIdentificationString(XElement element) { + return (null, null); + } + + public bool Replaces(XMLRootDocument doc1, XMLRootDocument doc2) { + return true; + } + + public XElement CreateHamannDocument(XElement element) { + var opus = new XElement("opus"); + opus.AddFirst(element); + return opus; + } + + public void MergeIntoFile(XElement file, XMLRootDocument document) { + if (file.Element("definitions") == null) + file.AddFirst(new XElement("definitions")); + var elements = document.Root.Elements().Where(x => IsCollectedObject(x)); + var root = file.Element("definitions"); + foreach (var element in elements) { + root!.Add(element); + } + } + +} \ No newline at end of file diff --git a/HaDocumentNew/Settings/XMLRoots/TraditionsRoot.cs b/HaDocumentNew/Settings/XMLRoots/TraditionsRoot.cs new file mode 100644 index 0000000..8b8bb84 --- /dev/null +++ b/HaDocumentNew/Settings/XMLRoots/TraditionsRoot.cs @@ -0,0 +1,49 @@ +namespace HaDocument.Settings.XMLRoots; +using System.Xml.Linq; + + +public class TraditionsRoot : HaWeb.XMLParser.IXMLRoot { + public string Type { get; } = "Überlieferung"; + public string Prefix { get; } = "ueberlieferung"; + public string[] XPathContainer { get; } = { ".//data/traditions", ".//traditions" }; + + public Predicate IsCollectedObject { get; } = (elem) => { + if (elem.Name == "letterTradition") return true; + else return false; + }; + + public Func GetKey { get; } = (elem) => { + var index = elem.Attribute("ref"); + if (index != null && !String.IsNullOrWhiteSpace(index.Value)) + return index.Value; + else return null; + }; + + public List<(string, string?)>? GenerateFields(XMLRootDocument document) { + return null; + } + + public (string?, string?) GenerateIdentificationString(XElement element) { + return (null, null); + } + + public bool Replaces(XMLRootDocument doc1, XMLRootDocument doc2) { + return true; + } + + public XElement CreateHamannDocument(XElement element) { + var opus = new XElement("opus"); + opus.AddFirst(element); + return opus; + } + + public void MergeIntoFile(XElement file, XMLRootDocument document) { + if (file.Element("traditions") == null) + file.AddFirst(new XElement("traditions")); + var elements = document.Root.Elements().Where(x => IsCollectedObject(x)); + var root = file.Element("traditions"); + foreach (var element in elements) { + root!.Add(element); + } + } +} \ No newline at end of file diff --git a/HaWeb/Controllers/APIController.cs b/HaWeb/Controllers/APIController.cs index ba662c3..6752cf1 100644 --- a/HaWeb/Controllers/APIController.cs +++ b/HaWeb/Controllers/APIController.cs @@ -16,6 +16,7 @@ using HaXMLReader.Interfaces; using Microsoft.FeatureManagement.Mvc; using System.Runtime.InteropServices; using Microsoft.AspNetCore.Http.Features; +using System.Text; // Controlling all the API-Endpoints public class APIController : Controller { @@ -78,6 +79,7 @@ public class APIController : Controller { section = await reader.ReadNextSectionAsync(); } catch (Exception ex) { ModelState.AddModelError("Error", "The Request is bad: " + ex.Message); + return BadRequest(ModelState); } while (section != null) { @@ -178,7 +180,82 @@ public class APIController : Controller { [ValidateAntiForgeryToken] [FeatureGate(Features.UploadService, Features.AdminService)] public async Task SetUsed(string id) { - return Ok(); + var f = _xmlProvider.GetFiles(id); + if (f == null) { + ModelState.AddModelError("Error", "Wrong Endpoint"); + return BadRequest(ModelState); + } + + var files = f.GetFileList(); + if (files == null) { + ModelState.AddModelError("Error", "Wrong Endpoint"); + return BadRequest(ModelState); + } + + List? newUsed = null; + + if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { + ModelState.AddModelError("Error", $"Wrong / No Content Type on the Request"); + return BadRequest(ModelState); + } + + // Same as above, check Upload() + 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); + return BadRequest(ModelState); + } + + while (section != null) { + 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; + } + + var filename = ""; + if (hasContentDispositionHeader && contentDisposition != null) { + if (!MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition)) { + ModelState.AddModelError("Error", $"Wrong Content-Dispostion Headers in Multipart Document"); + return BadRequest(ModelState); + } + + filename = XMLFileHelpers.StreamToString(section.Body, ModelState); + if (!ModelState.IsValid) return BadRequest(ModelState); + + var isFile = files.Where(x => x.FileName == filename); + if (isFile == null || !isFile.Any()) { + ModelState.AddModelError("Error", "Tried to add a file that does not exist."); + return BadRequest(ModelState); + } + + if (newUsed == null) newUsed = new List(); + newUsed.Add(isFile.First()); + } + + try { + section = await reader.ReadNextSectionAsync(); + } catch (Exception ex) { + ModelState.AddModelError("Error", "The Request is bad: " + ex.Message); + return BadRequest(ModelState); + } + } + + if (newUsed != null && newUsed.Any()) { + _xmlService.UnUse(id); + newUsed.ForEach(x => _xmlService.Use(x)); + } + + return Created("/", newUsed); } @@ -188,6 +265,84 @@ public class APIController : Controller { [ValidateAntiForgeryToken] [FeatureGate(Features.UploadService, Features.AdminService)] public async Task SetUsedHamann() { - return Ok(); + var hF = _xmlProvider.GetHamannFiles(); + if (hF == null) { + ModelState.AddModelError("Error", "There are no Hamman.xml files available."); + return BadRequest(ModelState); + } + + if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { + ModelState.AddModelError("Error", $"Wrong / No Content Type on the Request"); + return BadRequest(ModelState); + } + + // Same as above, check Upload() + string? filename = null; + 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); + return BadRequest(ModelState); + } + + while (section != null) { + 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; + } + + filename = XMLFileHelpers.StreamToString(section.Body, ModelState); + if (!ModelState.IsValid) return BadRequest(ModelState); + + if (hasContentDispositionHeader && contentDisposition != null) { + if (!MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition)) { + ModelState.AddModelError("Error", $"Wrong Content-Dispostion Headers in Multipart Document"); + return BadRequest(ModelState); + } + + filename = XMLFileHelpers.StreamToString(section.Body, ModelState); + + } + + try { + section = await reader.ReadNextSectionAsync(); + } catch (Exception ex) { + ModelState.AddModelError("Error", "The Request is bad: " + ex.Message); + return BadRequest(ModelState); + } + } + + if (filename == null) { + ModelState.AddModelError("Error", "No filename given"); + return BadRequest(ModelState); + } + + var newFile = hF.Where(x => x.Name == filename); + if (newFile == null || !newFile.Any()) { + ModelState.AddModelError("Error", "Trying to set a unavailable file."); + return BadRequest(ModelState); + } + + try { + _ = _lib.SetLibrary(newFile.First().PhysicalPath, ModelState); + } + catch (Exception ex) { + ModelState.AddModelError("Error", "Error parsing the file: " + ex.Message); + return BadRequest(ModelState); + } + + _xmlProvider.SetInProduction(newFile.First()); + _xmlService.UnUseProduction(); + + return Created("/", null); } } \ No newline at end of file diff --git a/HaWeb/Controllers/UploadController.cs b/HaWeb/Controllers/UploadController.cs index 97a8cc5..18d9211 100644 --- a/HaWeb/Controllers/UploadController.cs +++ b/HaWeb/Controllers/UploadController.cs @@ -48,18 +48,18 @@ public class UploadController : Controller { if (roots == null) return error404(); var hF = _xmlProvider.GetHamannFiles(); - List<(string, DateTime)>? hamannFiles = null; + List? hamannFiles = null; if (hF != null) hamannFiles = hF .OrderByDescending(x => x.LastModified) - .Select(x => (x.Name, x.LastModified.LocalDateTime)) + .Select(x => new FileModel(x.Name, string.Empty, x.LastModified.LocalDateTime, false, x == _xmlProvider.GetInProduction())) .ToList(); - + var uF = _xmlService.GetUsedDictionary(); var pF = _xmlService.GetInProduction(); Dictionary?>? usedFiles = null; - if (uF != null) { + if (uF != null) { usedFiles = new Dictionary?>(); foreach (var kv in uF) { if (kv.Value == null) continue; @@ -68,7 +68,7 @@ public class UploadController : Controller { } Dictionary?>? productionFiles = null; - if (pF != null) { + if (pF != null) { productionFiles = new Dictionary?>(); foreach (var kv in pF) { if (kv.Value == null) continue; @@ -81,16 +81,15 @@ public class UploadController : Controller { var root = _xmlService.GetRoot(id); if (root == null) return error404(); - + var model = new UploadViewModel(root.Type, id, roots, usedFiles); model.ProductionFiles = productionFiles; model.HamannFiles = hamannFiles; model.AvailableFiles = XMLFileHelpers.ToFileModel(_xmlProvider.GetFiles(id), pF, uF); - + return View("../Admin/Upload/Index", model); - } - else { - var model = new UploadViewModel("Upload", id, roots, usedFiles); + } else { + var model = new UploadViewModel("Upload & Veröffentlichen", id, roots, usedFiles); model.ProductionFiles = productionFiles; model.HamannFiles = hamannFiles; diff --git a/HaWeb/FileHelpers/HaDocumentWrapper.cs b/HaWeb/FileHelpers/HaDocumentWrapper.cs index 4592a9a..e2f06d1 100644 --- a/HaWeb/FileHelpers/HaDocumentWrapper.cs +++ b/HaWeb/FileHelpers/HaDocumentWrapper.cs @@ -15,15 +15,13 @@ public class HaDocumentWrapper : IHaDocumentWrappper { _startYear = configuration.GetValue("AvailableStartYear"); _endYear = configuration.GetValue("AvailableEndYear"); - var filelist = xmlProvider.GetHamannFiles(); - if (filelist != null && filelist.Any()) + if (filelist != null && filelist.Any()) { _AutoLoad(filelist); - + } // Use Fallback library - if (Library == null) + if (Library == null) Library = HaDocument.Document.Create(new HaWeb.Settings.HaDocumentOptions() { AvailableYearRange = (_startYear, _endYear) }); - } public ILibrary ResetLibrary() { @@ -38,6 +36,7 @@ public class HaDocumentWrapper : IHaDocumentWrappper { } catch (Exception ex) { if (ModelState != null) ModelState.AddModelError("Error:", "Das Dokument konnte nicht geparst werden: " + ex.Message); + Console.WriteLine(ex.Message); return null; } return Library; @@ -50,7 +49,10 @@ public class HaDocumentWrapper : IHaDocumentWrappper { private void _AutoLoad(List files) { var orderdlist = files.OrderByDescending(x => x.LastModified); foreach(var item in orderdlist) { - if (SetLibrary(item.PhysicalPath) != null) return; + if (SetLibrary(item.PhysicalPath) != null) { + _xmlProvider.SetInProduction(item); + return; + } } } } \ No newline at end of file diff --git a/HaWeb/FileHelpers/IXMLProvider.cs b/HaWeb/FileHelpers/IXMLProvider.cs index 7b669a3..5c0af13 100644 --- a/HaWeb/FileHelpers/IXMLProvider.cs +++ b/HaWeb/FileHelpers/IXMLProvider.cs @@ -10,4 +10,6 @@ public interface IXMLProvider { public Task Save(XMLRootDocument doc, string basefilepath, ModelStateDictionary ModelState); public Task SaveHamannFile(XElement element, string basefilepath, ModelStateDictionary ModelState); public List? GetHamannFiles(); + public IFileInfo? GetInProduction(); + public void SetInProduction(IFileInfo info); } \ No newline at end of file diff --git a/HaWeb/FileHelpers/XMLFileHelpers.cs b/HaWeb/FileHelpers/XMLFileHelpers.cs index efc14c0..a0d795e 100644 --- a/HaWeb/FileHelpers/XMLFileHelpers.cs +++ b/HaWeb/FileHelpers/XMLFileHelpers.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Net.Http.Headers; using HaWeb.Models; +using System.Text; public static class XMLFileHelpers { // File Signatures Database (https://www.filesignatures.net/) @@ -199,6 +200,20 @@ public static class XMLFileHelpers { return null; } + public static string? StreamToString(System.IO.Stream stream, ModelStateDictionary modelState) { + string? ret = null; + try { + using (var rd = new StreamReader(stream, Encoding.UTF8)) { + ret = rd.ReadToEnd(); + return ret; + } + } + catch (Exception ex) { + modelState.AddModelError("Error", "Reading of the message failed with " + ex.Message); + return null; + } + } + private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, string[] permittedExtensions) { if (string.IsNullOrEmpty(fileName) || data == null || data.Length == 0) return false; diff --git a/HaWeb/FileHelpers/XMLProvider.cs b/HaWeb/FileHelpers/XMLProvider.cs index 31e8dcd..aec7ab0 100644 --- a/HaWeb/FileHelpers/XMLProvider.cs +++ b/HaWeb/FileHelpers/XMLProvider.cs @@ -17,7 +17,7 @@ public class XMLProvider : IXMLProvider { _Roots = xmlservice.GetRootsDictionary(); _Files = _ScanFiles(); _HamannFiles = _ScanHamannFiles(); - + if (_Files != null) foreach(var category in _Files) if (category.Value != null) @@ -26,6 +26,10 @@ public class XMLProvider : IXMLProvider { public List? GetHamannFiles() => this._HamannFiles; + public IFileInfo? GetInProduction() => this._InProduction; + + public void SetInProduction(IFileInfo info) => _InProduction = info; + public FileList? GetFiles(string prefix) => _Files != null && _Files.ContainsKey(prefix) ? _Files[prefix] : null; @@ -83,6 +87,7 @@ public class XMLProvider : IXMLProvider { } if (_HamannFiles == null) _HamannFiles = new List(); + _HamannFiles.RemoveAll(x => x.Name == info.Name); _HamannFiles.Add(info); _InProduction = info; return info; diff --git a/HaWeb/Models/FileModel.cs b/HaWeb/Models/FileModel.cs index 657d1d3..7d57c8b 100644 --- a/HaWeb/Models/FileModel.cs +++ b/HaWeb/Models/FileModel.cs @@ -8,10 +8,10 @@ public class FileModel { public bool InProduction { get; private set; } public List<(string, string?)>? Fields { get; set; } - public FileModel(string name, string prefix, DateTime lastModified, bool isUSed, bool inProduction) { + public FileModel(string name, string prefix, DateTime lastModified, bool isUsed, bool inProduction) { FileName = name; - IsUsed = IsUsed; + IsUsed = isUsed; LastModified = lastModified; - InProduction = InProduction; + InProduction = inProduction; } } \ No newline at end of file diff --git a/HaWeb/Models/UploadViewModel.cs b/HaWeb/Models/UploadViewModel.cs index bc50ee2..2742b01 100644 --- a/HaWeb/Models/UploadViewModel.cs +++ b/HaWeb/Models/UploadViewModel.cs @@ -11,7 +11,7 @@ public class UploadViewModel { public Dictionary?>? UsedFiles { get; private set; } public Dictionary?>? ProductionFiles { get; set; } - public List<(string, DateTime)>? HamannFiles { get; set; } + public List? HamannFiles { get; set; } public UploadViewModel(string title, string? prefix, List? roots, Dictionary?>? usedFiles) { Prefix = prefix; diff --git a/HaWeb/Program.cs b/HaWeb/Program.cs index f692602..9aaedc5 100644 --- a/HaWeb/Program.cs +++ b/HaWeb/Program.cs @@ -30,10 +30,10 @@ if (filepath == null) { var physicalProvider = new PhysicalFileProvider(filepath); builder.Services.AddSingleton(physicalProvider); -builder.Services.AddSingleton(); builder.Services.AddTransient(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddFeatureManagement(); var app = builder.Build(); diff --git a/HaWeb/Views/Admin/Upload/Index.cshtml b/HaWeb/Views/Admin/Upload/Index.cshtml index 3ada090..af442ed 100644 --- a/HaWeb/Views/Admin/Upload/Index.cshtml +++ b/HaWeb/Views/Admin/Upload/Index.cshtml @@ -44,7 +44,7 @@
@* Fehler!
*@ - +
@@ -52,31 +52,42 @@

@Model.ActiveTitle

- - @if (Model.Prefix != null) { -
-
- @if(Model.UsedFiles != null && Model.UsedFiles.ContainsKey(Model.Prefix)) { - @foreach (var item in Model.UsedFiles[Model.Prefix]!) - { -
@item.FileName
- } - } - else { -
Keine Datei geladen
- } - @if (Model.AvailableFiles != null) - { - - } -
-
- } -
+@* File Category Page File List *@ +@if (Model.AvailableFiles != null && Model.AvailableFiles.Any()) { +
+
+
Datei(en)
+ @if(Model.UsedFiles != null && Model.UsedFiles.ContainsKey(Model.Prefix)) { +
+ @foreach (var item in Model.UsedFiles[Model.Prefix]!) + { + if(item == Model.UsedFiles[Model.Prefix]!.Last()) { + @item.FileName + } + else { + @item.FileName, + } + } +
+ } +
+ +
+} +@* Start Page File List *@ +else { +
+ @await Html.PartialAsync("/Views/Shared/_FileList.cshtml", (Model.HamannFiles, "Verfügbare Hamann-Dateien", "API", "SetUsedHamann", string.Empty, "/Download/XML/", false)) +
+} + +@* File Category Page Syntax Check *@ @if (Model.UsedFiles != null && Model.Prefix != null && Model.UsedFiles.ContainsKey(Model.Prefix)) {
@@ -92,17 +103,25 @@
} -else { -
- -
-} -
@section Scripts { \ No newline at end of file diff --git a/HaWeb/XMLParser/IXMLService.cs b/HaWeb/XMLParser/IXMLService.cs index fd64b5e..d687e5d 100644 --- a/HaWeb/XMLParser/IXMLService.cs +++ b/HaWeb/XMLParser/IXMLService.cs @@ -14,4 +14,6 @@ public interface IXMLService { public void AutoUse(string prefix); public void AutoUse(FileList filelist); public Dictionary? GetInProduction(); + public void UnUse(string prefix); + public void UnUseProduction(); } \ No newline at end of file diff --git a/HaWeb/XMLParser/XMLService.cs b/HaWeb/XMLParser/XMLService.cs index 91bfda7..f2df2a9 100644 --- a/HaWeb/XMLParser/XMLService.cs +++ b/HaWeb/XMLParser/XMLService.cs @@ -34,6 +34,8 @@ public class XMLService : IXMLService { public Dictionary? GetInProduction() => this._InProduction; + public void UnUseProduction() => this._InProduction = null; + public List? ProbeHamannFile(XDocument document, ModelStateDictionary ModelState) { if (document.Root!.Name != "opus") { ModelState.AddModelError("Error", "A valid Hamann-Docuemnt must begin with "); @@ -66,6 +68,11 @@ public class XMLService : IXMLService { _Used[doc.Prefix]!.Add(doc); } + public void UnUse(string prefix) { + if (_Used != null && _Used.ContainsKey(prefix)) _Used.Remove(prefix); + return; + } + // Performs detection of using on the specified document type public void AutoUse(string prefix) { if (_Used == null || !_Used.ContainsKey(prefix)) return; diff --git a/HaWeb/tailwind.config.js b/HaWeb/tailwind.config.js index 5c19b4e..b40c6a5 100644 --- a/HaWeb/tailwind.config.js +++ b/HaWeb/tailwind.config.js @@ -19,6 +19,7 @@ module.exports = { sans: ['Biolinum', 'sans-serif'], serif: ['Libertine', 'serif'], classy: ['Playfair', 'serif'], + mono: ['ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace', 'mono'] }, screens: { 'sm': '700px', diff --git a/HaWeb/wwwroot/css/output.css b/HaWeb/wwwroot/css/output.css index 79d6be9..78468f0 100644 --- a/HaWeb/wwwroot/css/output.css +++ b/HaWeb/wwwroot/css/output.css @@ -123,7 +123,7 @@ code, kbd, samp, pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace, mono; /* 1 */ font-size: 1em; /* 2 */ @@ -478,6 +478,8 @@ Ensure the default browser behavior of the `hidden` attribute. /* TODO: check what can be inlined (eg. used once in the code, has no double paths etc...) */ +/* TODO: Copy color classes for all thing upload to colors on top */ + /* Everything related to theme color */ body { @@ -1868,8 +1870,8 @@ body { .ha-adminuploadfields .ha-uploadfield.active { --tw-text-opacity: 1 !important; color: rgb(0 0 0 / var(--tw-text-opacity)) !important; - --tw-shadow-color: #fee2e2; - --tw-shadow: var(--tw-shadow-colored); + --tw-brightness: brightness(1.1); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } .ha-adminuploadfields .ha-uploadfield .ha-uploadfieldname { @@ -2022,46 +2024,6 @@ body { line-height: 1; } -.ha-uploadheader .ha-usedfilesheader { - display: flex; -} - -.ha-uploadheader .ha-usedfilesheaderlist { - margin-left: 1.5rem; - margin-bottom: 0.25rem; - display: flex; - flex-direction: row; - flex-wrap: wrap; - align-content: flex-end; - -moz-column-gap: 0.5rem; - column-gap: 0.5rem; - row-gap: 0.25rem; - align-self: flex-end; -} - -.ha-uploadheader .ha-usedfilesheaderlist .ha-usedfilesheaderfile { - border-radius: 0.25rem; - background-color: rgb(51 65 85 / var(--tw-bg-opacity)); - --tw-bg-opacity: 0.3; - padding-left: 0.5rem; - padding-right: 0.5rem; - font-size: 0.875rem; - line-height: 1.25rem; -} - -.ha-uploadheader .ha-usedfilesheaderlist .ha-availablefilechooser { - border-radius: 0.25rem; - background-color: rgb(51 65 85 / var(--tw-bg-opacity)); - --tw-bg-opacity: 0.5; - padding-right: 0.25rem; - font-size: 0.875rem; - line-height: 1.25rem; -} - -.ha-uploadheader .ha-usedfilesheaderlist .ha-availablefilechooser .ha-loadotherfilesbtn { - display: inline-block; -} - .ha-uploadcontainer { display: flex; height: 100%; @@ -2072,6 +2034,40 @@ body { background-color: rgb(248 250 252 / var(--tw-bg-opacity)); } +.ha-uploadcontainer .ha-availablefiles { + cursor: pointer; + border-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(226 232 240 / var(--tw-border-opacity)); + padding-left: 4rem; + padding-right: 4rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.ha-uploadcontainer .ha-availablefiles:hover { + --tw-border-opacity: 1; + border-color: rgb(30 41 59 / var(--tw-border-opacity)); +} + +.ha-uploadcontainer .ha-availablefiles .ha-availablefilestitle { + font-size: 1.5rem; + line-height: 2rem; +} + +.ha-uploadcontainer .ha-availablefiles .ha-usedfilelist { +} + +.ha-filesheader { + margin-bottom: 2rem; +} + +.ha-availablefileslist { + padding-left: 4rem; + padding-right: 4rem; + padding-top: 1rem; +} + .ha-uploadcontainer .ha-errorswarnings { display: flex; flex-direction: row; @@ -2109,6 +2105,98 @@ body { background-color: rgb(165 243 252 / var(--tw-bg-opacity)); } +.ha-uploadcontainer .ha-hamannfilechooser { + padding-left: 4rem; + padding-right: 4rem; + padding-bottom: 4rem; +} + +/* Classes for FileList Component */ + +.ha-filelistfieldset { +} + +.ha-filelistfieldset .ha-filelistlegend { + margin-bottom: 0.5rem; + font-size: 1.25rem; + line-height: 1.75rem; +} + +.ha-selectfilesform { +} + +.ha-selectfilesform .ha-filelistfile { + display: flex; + flex-direction: row; + -moz-column-gap: 1rem; + column-gap: 1rem; +} + +.ha-selectfilesform .ha-filelistlist { + height: 24rem; + overflow-x: hidden; + overflow-y: scroll; + padding-right: 1rem; +} + +.ha-selectfilesform .ha-filelistfile .ha-filelistname { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace, mono; +} + +.ha-selectfilesform .ha-filelistfile .ha-filelistusedproduction { + align-self: flex-start; + font-size: 0.875rem; + line-height: 1.25rem; +} + +.ha-selectfilesform .ha-filelistfile .ha-filelistusedproduction .ha-filelistproduction { + margin-right: 0.5rem; + display: inline-block; + border-radius: 0.375rem; + border-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(13 148 136 / var(--tw-border-opacity)); + padding-left: 0.5rem; + padding-right: 0.5rem; + --tw-text-opacity: 1; + color: rgb(13 148 136 / var(--tw-text-opacity)); +} + +.ha-selectfilesform .ha-filelistfile .ha-filelistusedproduction .ha-filelistused { + display: inline-block; + border-radius: 0.375rem; + border-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(79 70 229 / var(--tw-border-opacity)); + padding-left: 0.5rem; + padding-right: 0.5rem; + --tw-text-opacity: 1; + color: rgb(79 70 229 / var(--tw-text-opacity)); +} + +.ha-selectfilesform .ha-filelistfile .ha-filelistmodified { + flex-grow: 1; + text-align: right; +} + +.ha-selectfilesform .ha-filelistbutton { + float: right; + margin-top: 0.5rem; + margin-left: 1.5rem; + cursor: pointer; + border-radius: 0.375rem; + border-width: 2px; + --tw-border-opacity: 1; + border-color: rgb(29 78 216 / var(--tw-border-opacity)); + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.ha-selectfilesform .ha-filelistbutton:hover { + --tw-bg-opacity: 1; + background-color: rgb(147 197 253 / var(--tw-bg-opacity)); +} + /* Classes for Letter View */ .ha-letterheader { @@ -3055,6 +3143,10 @@ body { margin-right: 0px !important; } +.block { + display: block; +} + .inline-block { display: inline-block; } diff --git a/HaWeb/wwwroot/css/site.css b/HaWeb/wwwroot/css/site.css index 01826d9..d91f061 100644 --- a/HaWeb/wwwroot/css/site.css +++ b/HaWeb/wwwroot/css/site.css @@ -84,6 +84,7 @@ @apply transition-colors duration-100 } /* TODO: check what can be inlined (eg. used once in the code, has no double paths etc...) */ + /* TODO: Copy color classes for all thing upload to colors on top */ /* Everything related to theme color */ body { @@ -678,7 +679,7 @@ } .ha-adminuploadfields .ha-uploadfield.active { - @apply !text-black shadow-red-100 + @apply !text-black brightness-110 } .ha-adminuploadfields .ha-uploadfield .ha-uploadfieldname { @@ -745,30 +746,30 @@ @apply text-5xl } - .ha-uploadheader .ha-usedfilesheader { - @apply flex - } - - .ha-uploadheader .ha-usedfilesheaderlist { - @apply flex flex-row flex-wrap ml-6 gap-x-2 gap-y-1 self-end content-end mb-1 - } - - .ha-uploadheader .ha-usedfilesheaderlist .ha-usedfilesheaderfile { - @apply text-sm px-2 bg-slate-700 bg-opacity-30 rounded - } - - .ha-uploadheader .ha-usedfilesheaderlist .ha-availablefilechooser { - @apply text-sm pr-1 bg-slate-700 bg-opacity-50 rounded - } - - .ha-uploadheader .ha-usedfilesheaderlist .ha-availablefilechooser .ha-loadotherfilesbtn { - @apply inline-block - } - .ha-uploadcontainer { @apply w-full bg-slate-50 flex flex-col gap-y-2 h-full } + .ha-uploadcontainer .ha-availablefiles { + @apply px-16 border border-slate-200 hover:border-slate-800 py-2 cursor-pointer + } + + .ha-uploadcontainer .ha-availablefiles .ha-availablefilestitle { + @apply text-2xl + } + + .ha-uploadcontainer .ha-availablefiles .ha-usedfilelist { + + } + + .ha-filesheader { + @apply mb-8 + } + + .ha-availablefileslist { + @apply px-16 pt-4 + } + .ha-uploadcontainer .ha-errorswarnings { @apply flex flex-row gap-x-2 } @@ -790,6 +791,55 @@ @apply w-full bg-cyan-200 grow shrink-0 h-full min-h-[400px] } + .ha-uploadcontainer .ha-hamannfilechooser { + @apply px-16 pb-16 + } + + /* Classes for FileList Component */ + .ha-filelistfieldset { + + } + + .ha-filelistfieldset .ha-filelistlegend { + @apply mb-2 text-xl + } + + .ha-selectfilesform { + + } + + .ha-selectfilesform .ha-filelistfile { + @apply flex flex-row gap-x-4 + } + + .ha-selectfilesform .ha-filelistlist { + @apply h-96 overflow-x-hidden overflow-y-scroll pr-4 + } + + .ha-selectfilesform .ha-filelistfile .ha-filelistname { + @apply font-mono + } + + .ha-selectfilesform .ha-filelistfile .ha-filelistusedproduction { + @apply text-sm self-start + } + + .ha-selectfilesform .ha-filelistfile .ha-filelistusedproduction .ha-filelistproduction { + @apply inline-block border rounded-md text-teal-600 border-teal-600 px-2 mr-2 + } + + .ha-selectfilesform .ha-filelistfile .ha-filelistusedproduction .ha-filelistused { + @apply inline-block border rounded-md text-indigo-600 border-indigo-600 px-2 + } + + .ha-selectfilesform .ha-filelistfile .ha-filelistmodified { + @apply grow text-right + } + + .ha-selectfilesform .ha-filelistbutton { + @apply mt-2 ml-6 rounded-md px-3 border-2 border-blue-700 hover:bg-blue-300 cursor-pointer float-right; + } + /* Classes for Letter View */ .ha-letterheader {