mirror of
https://github.com/Theodor-Springmann-Stiftung/hamann-ausgabe-core.git
synced 2025-10-29 17:25:32 +00:00
Creaded Upload Interface; Also added Wrapper around HaDocument to reload the Document
This commit is contained in:
145
HaWeb/Controllers/APIController.cs
Normal file
145
HaWeb/Controllers/APIController.cs
Normal file
@@ -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<long>("FileSizeLimit");
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
|
||||
_targetFilePath = config.GetValue<string>("StoredFilePathWindows");
|
||||
} else {
|
||||
_targetFilePath = config.GetValue<string>("StoredFilePathLinux");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("API/Syntaxcheck/{id}")]
|
||||
[DisableFormValueModelBinding]
|
||||
[ValidateAntiForgeryToken]
|
||||
[FeatureGate(Features.AdminService)]
|
||||
public async Task<IActionResult> SyntaxCheck(string id) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
//// UPLOAD ////
|
||||
[HttpPost]
|
||||
[Route("API/Upload")]
|
||||
[DisableFormValueModelBinding]
|
||||
[ValidateAntiForgeryToken]
|
||||
[FeatureGate(Features.UploadService)]
|
||||
public async Task<IActionResult> Upload(string? id) {
|
||||
List<XMLRootDocument>? 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<XMLRootDocument>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<string>();
|
||||
var recivers = meta.Receivers.Select(x => _lib.Persons[x].Name) ?? new List<string>();
|
||||
private BriefeMetaViewModel generateMetaViewModel(ILibrary lib, Meta meta, bool hasMarginals) {
|
||||
var senders = meta.Senders.Select(x => lib.Persons[x].Name) ?? new List<string>();
|
||||
var recivers = meta.Receivers.Select(x => lib.Persons[x].Name) ?? new List<string>();
|
||||
var zhstring = meta.ZH != null ? HaWeb.HTMLHelpers.LetterHelpers.CreateZHString(meta) : null;
|
||||
return new BriefeMetaViewModel(meta, hasMarginals, false) {
|
||||
ParsedZHString = zhstring,
|
||||
|
||||
@@ -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<CommentModel>();
|
||||
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<string>? parsedSubComments = null;
|
||||
if (comm.Kommentare != null) {
|
||||
parsedSubComments = new List<string>();
|
||||
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<CommentModel>();
|
||||
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<string>? parsedSubComments = null;
|
||||
if (comm.Kommentare != null) {
|
||||
parsedSubComments = new List<string>();
|
||||
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<Comment>? 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<CommentModel>();
|
||||
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<string>? parsedSubComments = null;
|
||||
if (comm.Kommentare != null) {
|
||||
parsedSubComments = new List<string>();
|
||||
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<string> permitted) {
|
||||
private bool validationCheck(string? id, HashSet<string> permitted) {
|
||||
if (id == null) return false;
|
||||
if (!permitted.Contains(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
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();
|
||||
|
||||
//// UPLOAD ////
|
||||
[HttpPost]
|
||||
[Route("Admin/Upload")]
|
||||
[DisableFormValueModelBinding]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Post() {
|
||||
List<XMLRootDocument>? 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<XMLRootDocument>();
|
||||
docs.Add(doc);
|
||||
var model = new UploadViewModel("Upload", id, roots, null, usedFiles);
|
||||
return View("../Admin/Upload/Index", model);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
19
HaWeb/FileHelpers/HaDocumentWrapper.cs
Normal file
19
HaWeb/FileHelpers/HaDocumentWrapper.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
7
HaWeb/FileHelpers/IHaDocumentWrapper.cs
Normal file
7
HaWeb/FileHelpers/IHaDocumentWrapper.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace HaWeb.FileHelpers;
|
||||
using HaDocument.Interfaces;
|
||||
|
||||
public interface IHaDocumentWrappper {
|
||||
public ILibrary SetLibrary();
|
||||
public ILibrary GetLibrary();
|
||||
}
|
||||
22
HaWeb/Models/SyntaxCheckModel.cs
Normal file
22
HaWeb/Models/SyntaxCheckModel.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace HaWeb.Models;
|
||||
|
||||
public class SyntaxCheckModel {
|
||||
public string Prefix { get; private set; }
|
||||
public List<SyntaxError>? Errors { get; set; }
|
||||
public List<SyntaxError>? 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;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,18 @@ namespace HaWeb.Models;
|
||||
using HaWeb.XMLParser;
|
||||
|
||||
public class UploadViewModel {
|
||||
public List<IXMLRoot>? AvailableRoots { get; set; }
|
||||
public List<XMLRootDocument>? AvailableFiles { get; set; }
|
||||
public Dictionary<string, List<XMLRootDocument>>? UsedFiles { get; set; }
|
||||
public string ActiveTitle { get; private set; }
|
||||
public string? Prefix { get; private set; }
|
||||
public List<IXMLRoot>? AvailableRoots { get; private set; }
|
||||
public List<XMLRootDocument>? AvailableFiles { get; private set; }
|
||||
public Dictionary<string, List<XMLRootDocument>>? UsedFiles { get; private set; }
|
||||
|
||||
|
||||
public UploadViewModel(string title, string? prefix, List<IXMLRoot>? roots, List<XMLRootDocument>? availableFiles, Dictionary<string, List<XMLRootDocument>>? usedFiles) {
|
||||
Prefix = prefix;
|
||||
ActiveTitle = title;
|
||||
AvailableRoots = roots;
|
||||
AvailableFiles = availableFiles;
|
||||
UsedFiles = usedFiles;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ var physicalProvider = new PhysicalFileProvider(filepath);
|
||||
|
||||
|
||||
builder.Services.AddSingleton<IFileProvider>(physicalProvider);
|
||||
builder.Services.AddSingleton<ILibrary>(HaDocument.Document.Create(new Options()));
|
||||
builder.Services.AddSingleton<HaWeb.FileHelpers.IHaDocumentWrappper, HaWeb.FileHelpers.HaDocumentWrapper>();
|
||||
builder.Services.AddTransient<IReaderService, ReaderService>();
|
||||
builder.Services.AddSingleton<IXMLService, XMLService>();
|
||||
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;
|
||||
}
|
||||
@@ -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 = { };
|
||||
|
||||
}
|
||||
@@ -1 +1,9 @@
|
||||
// TODO
|
||||
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);
|
||||
}
|
||||
@@ -1,42 +1,108 @@
|
||||
@model UploadViewModel;
|
||||
@model UploadViewModel?;
|
||||
|
||||
<div class="ha-adminuploadfields">
|
||||
<div class="ha-adminuploadfields" id="ha-adminuploadfields">
|
||||
@foreach (var item in Model.AvailableRoots.OrderBy(x => x.Type)) {
|
||||
<div class="ha-uploadfield">
|
||||
<a class="ha-uploadfield" asp-controller="Upload" asp-action="Index" asp-route-id="@item.Prefix">
|
||||
<div class="ha-uploadfieldname">@item.Type</div>
|
||||
@if (Model.UsedFiles != null && Model.UsedFiles.ContainsKey(item.Prefix)) {
|
||||
<div class="ha-uploadusedfiles">
|
||||
@foreach(var file in Model.UsedFiles[item.Prefix]) {
|
||||
<span>@file.File.Name</span>
|
||||
@if (file == Model.UsedFiles[item.Prefix].Last())
|
||||
{
|
||||
<span class="ha-uploadusedfile">@file.File.Name</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="ha-uploadusedfile">@file.File.Name;</span>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<form class="ha-uploadform" id="uploadForm" action="Upload" method="post" enctype="multipart/form-data">
|
||||
<label class="filelabel" id="dropzone">
|
||||
else {
|
||||
<div class="ha-uploadusedfiles ha-uploadusedfilesnotfound">Keine Datei geladen!</div>
|
||||
}
|
||||
</a>
|
||||
}
|
||||
<div class="ha-uploadpublishforms">
|
||||
<form class="ha-uploadform" id="uploadForm" asp-controller="API" asp-action="Upload" method="post" enctype="multipart/form-data">
|
||||
<label class="ha-uploadfilelabel" id="dropzone">
|
||||
<input class="hidden" id="file" type="file" accept=".xml" name="file" />
|
||||
Upload2 es
|
||||
<div class="ha-uploadtext">Upload</div>
|
||||
<div class="ha-lds-ellipsis" id="ha-lds-ellipsis"><div></div><div></div><div></div><div></div></div>
|
||||
</label>
|
||||
<div class="ha-uploadmessage" id="ha-uploadmessage">
|
||||
Fehler!<br/>
|
||||
<output form="uploadForm" name="upload-result"></output>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form class="ha-publishform" action="Upload" method="post" enctype="multipart/form-data">
|
||||
<label class="filelabel">
|
||||
<input class="hidden" type="file" accept=".xml" name="file" />
|
||||
Veröffentlichen
|
||||
</label>
|
||||
<div class="ha-uploadmessage">
|
||||
|
||||
<output form="uploadForm" name="result"></output>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ha-uploadheader">
|
||||
<h1 class="ha-uploadtitle">@Model.ActiveTitle</h1>
|
||||
@if (Model.Prefix != null) {
|
||||
<div class="ha-usedfilesheader">
|
||||
<div class="ha-usedfilesheaderlist">
|
||||
@if(Model.UsedFiles != null && Model.UsedFiles.ContainsKey(Model.Prefix)) {
|
||||
@foreach (var item in Model.UsedFiles[Model.Prefix])
|
||||
{
|
||||
<div class="ha-usedfilesheaderfile">@item.File.Name</div>
|
||||
}
|
||||
}
|
||||
else {
|
||||
<div class="ha-usedfilesnone">Keine Datei geladen</div>
|
||||
}
|
||||
@if (Model.AvailableFiles != null)
|
||||
{
|
||||
<div class="ha-availablefilechooser"><div class="ha-plussign"></div><a class="ha-loadotherfilesbtn">Andere Dateien laden...</a></div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="ha-uploadcontainer">
|
||||
@if (Model.UsedFiles != null && Model.Prefix != null && Model.UsedFiles.ContainsKey(Model.Prefix)) {
|
||||
<div class="ha-errorswarnings">
|
||||
<div class="ha-criticalerrors">
|
||||
|
||||
</div>
|
||||
<div class="ha-warnings">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ha-crossfilechecking">
|
||||
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const dropHandler = function (formelement, ev, dropzone) {
|
||||
ev.preventDefault();
|
||||
|
||||
if (ev.dataTransfer.items) {
|
||||
if (ev.dataTransfer.items[0].kind === 'file') {
|
||||
var file = ev.dataTransfer.items[0].getAsFile();
|
||||
AJAXSubmit(formelement, file);
|
||||
UPLOADSubmit(formelement, file);
|
||||
} else {
|
||||
var file = ev.dataTransfer.files[0];
|
||||
AJAXSubmit(formelement, file);
|
||||
UPLOADSubmit(formelement, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,11 +119,12 @@
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
const AJAXSubmit = async function (oFormElement, file = null) {
|
||||
const UPLOADSubmit = async function (oFormElement, file = null) {
|
||||
var fd = new FormData();
|
||||
if (file !== null) fd.append("file", file);
|
||||
else fd = new FormData(oFormElement);
|
||||
oFormElement.elements.namedItem("result").value = "Wait";
|
||||
document.getElementById("ha-lds-ellipsis").style.display = "inline-block";
|
||||
document.getElementById("ha-uploadmessage").style.opacity = "0";
|
||||
await fetch(oFormElement.action, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -68,12 +135,18 @@
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
if ("Error" in json) {
|
||||
oFormElement.elements.namedItem("result").value = json.Error;
|
||||
document.getElementById("ha-lds-ellipsis").style.display = "none";
|
||||
document.getElementById("ha-uploadmessage").style.opacity = "1";
|
||||
oFormElement.elements.namedItem("upload-result").value = json.Error;
|
||||
} else {
|
||||
oFormElement.elements.namedItem("result").value = "Erfolg!";
|
||||
document.getElementById("ha-lds-ellipsis").style.display = "none";
|
||||
oFormElement.elements.namedItem("upload-result").value = "Erfolg!";
|
||||
if ("Prefix" in json[0]) {
|
||||
window.location.replace("/Admin/Upload/" + json[0].Prefix);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch ((e) => console.error('Error:', error))
|
||||
.catch ((e) => console.log('Error:', e))
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
@@ -86,7 +159,7 @@
|
||||
var submitelement = document.getElementById("file");
|
||||
var formelement = document.getElementById("uploadForm");
|
||||
var dropzone = document.getElementById("dropzone");
|
||||
submitelement.addEventListener("change", () => AJAXSubmit(formelement));
|
||||
submitelement.addEventListener("change", () => UPLOADSubmit(formelement));
|
||||
dropzone.addEventListener("drop", (ev) => dropHandler(formelement, ev, dropzone));
|
||||
dropzone.addEventListener("dragover", (ev) => dragOverHandler(ev, dropzone));
|
||||
dropzone.addEventListener("dragleave", (ev) => dragLeaveHander(ev, dropzone));
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<div class="pb-24">
|
||||
@await Html.PartialAsync("/Views/Shared/_Menu.cshtml")
|
||||
|
||||
<main role="main" class="pb-3 w-full desktop:max-w-screen-desktop mx-auto">
|
||||
<main role="main" class="pb-3 w-full desktop:max-w-screen-desktop mx-auto h-full">
|
||||
@RenderBody()
|
||||
</main>
|
||||
|
||||
|
||||
@@ -8,4 +8,5 @@ public interface IXMLService {
|
||||
public Task<List<XMLRootDocument>?> ProbeHamannFile(XDocument document, ModelStateDictionary ModelState);
|
||||
public Task UpdateAvailableFiles(XMLRootDocument doc, string basefilepath, ModelStateDictionary ModelState);
|
||||
public Dictionary<string, List<XMLRootDocument>> GetUsed();
|
||||
public List<XMLRootDocument>? GetAvailableFiles(string prefix);
|
||||
}
|
||||
@@ -87,6 +87,13 @@ public class XMLService : IXMLService {
|
||||
AutoDetermineUsed(prefix);
|
||||
}
|
||||
|
||||
public List<XMLRootDocument>? GetAvailableFiles(string prefix) {
|
||||
if (_Roots == null || _availableFiles == null) return null;
|
||||
if(!_Roots.ContainsKey(prefix) || !_availableFiles.ContainsKey(prefix)) return null;
|
||||
|
||||
return _availableFiles[prefix];
|
||||
}
|
||||
|
||||
public async Task UpdateAvailableFiles(XMLRootDocument doc, string basefilepath, ModelStateDictionary ModelState) {
|
||||
await _setEnabled();
|
||||
if (!UploadEnabled) {
|
||||
|
||||
@@ -1835,6 +1835,243 @@ body {
|
||||
line-height: 1.375;
|
||||
}
|
||||
|
||||
/* Classes for Upload View */
|
||||
|
||||
.ha-adminuploadfields {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
-moz-column-gap: 1rem;
|
||||
column-gap: 1rem;
|
||||
row-gap: 1rem;
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadfield {
|
||||
display: block;
|
||||
max-width: 20rem;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
flex-basis: 16rem;
|
||||
border-radius: 0.25rem;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(248 250 252 / var(--tw-bg-opacity));
|
||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadfield:hover {
|
||||
--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.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);
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadfield .ha-uploadfieldname {
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadusedfiles {
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
border-top-width: 1px;
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(203 213 225 / var(--tw-border-opacity));
|
||||
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
|
||||
--tw-bg-opacity: 0.3;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.125rem;
|
||||
padding-bottom: 0.125rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadusedfiles.ha-uploadusedfilesnotfound {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(100 116 139 / var(--tw-border-opacity));
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(100 116 139 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadpublishforms {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: row;
|
||||
-moz-column-gap: 1rem;
|
||||
column-gap: 1rem;
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadform {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
border-radius: 0.25rem;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(248 250 252 / var(--tw-bg-opacity));
|
||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadform .ha-uploadtext {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadform .ha-lds-ellipsis {
|
||||
left: 50%;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadform .ha-uploadfilelabel {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-bottom: 0.25rem;
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadform .ha-uploadmessage {
|
||||
border-radius: 0.125rem;
|
||||
background-color: rgb(51 65 85 / var(--tw-bg-opacity));
|
||||
--tw-bg-opacity: 0.3;
|
||||
padding-left: 0.25rem;
|
||||
padding-right: 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-publishform {
|
||||
flex-grow: 1;
|
||||
border-radius: 0.25rem;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(248 250 252 / var(--tw-bg-opacity));
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.ha-uploadheader {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(248 250 252 / var(--tw-bg-opacity));
|
||||
padding-left: 4rem;
|
||||
padding-right: 4rem;
|
||||
padding-top: 3rem;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.ha-uploadheader h1 {
|
||||
font-size: 3rem;
|
||||
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%;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
row-gap: 0.5rem;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(248 250 252 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.ha-uploadcontainer .ha-errorswarnings {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
-moz-column-gap: 0.5rem;
|
||||
column-gap: 0.5rem;
|
||||
}
|
||||
|
||||
.ha-uploadcontainer .ha-errorswarnings .ha-criticalerrors, .ha-uploadcontainer .ha-errorswarnings .ha-warnings {
|
||||
min-height: 400px;
|
||||
min-width: 40%;
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
flex-basis: 50%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.ha-uploadcontainer .ha-errorswarnings .ha-criticalerrors {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(254 202 202 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.ha-uploadcontainer .ha-errorswarnings .ha-warnings {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(254 215 170 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.ha-uploadcontainer .ha-crossfilechecking {
|
||||
height: 100%;
|
||||
min-height: 400px;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(165 243 252 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
/* Classes for Letter View */
|
||||
|
||||
.ha-letterheader {
|
||||
@@ -2801,6 +3038,10 @@ body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.h-8 {
|
||||
height: 2rem;
|
||||
}
|
||||
@@ -2817,10 +3058,6 @@ body {
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.max-w-\[25\%\] {
|
||||
max-width: 25%;
|
||||
}
|
||||
|
||||
.shrink-0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -2857,10 +3094,6 @@ body {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.gap-5 {
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -3063,6 +3296,8 @@ body {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
|
||||
.ha-lettertext .ha-marginalbox:before {
|
||||
content: "";
|
||||
}
|
||||
@@ -3107,6 +3342,8 @@ body {
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Not implemented in tailwindcss */
|
||||
|
||||
* {
|
||||
-webkit-text-decoration-skip-ink: all;
|
||||
text-decoration-skip-ink: all;
|
||||
@@ -3117,10 +3354,6 @@ body {
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
||||
/* ul {
|
||||
list-style-type:circle;
|
||||
} */
|
||||
|
||||
.ha-tradzhtext .ha-marginalbox.ha-collapsed-box .ha-marginallist .ha-marginal,
|
||||
.ha-lettertext .ha-marginalbox.ha-collapsed-box .ha-marginallist .ha-marginal {
|
||||
display: -webkit-inline-box;
|
||||
@@ -3214,6 +3447,8 @@ body {
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
/* Symbols, Icons, Drawings, Tooltips */
|
||||
|
||||
.ha-menu-arrowsymbol::after {
|
||||
display: inline-block;
|
||||
margin-left: 0.2em;
|
||||
@@ -3276,11 +3511,154 @@ body {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.ha-text {
|
||||
-webkit-user-select: contain;
|
||||
-moz-user-select: contain;
|
||||
-ms-user-select: element;
|
||||
user-select: contain;
|
||||
.ha-uploadheader .ha-usedfilesheaderlist .ha-plussign {
|
||||
content: "\200E+ ";
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
background-color: lightslategray;
|
||||
padding-left: 0.25rem;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
|
||||
/* Tooltip text */
|
||||
|
||||
.ha-uploadform .ha-uploadmessage {
|
||||
visibility: visible;
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
border-radius: 6px;
|
||||
/* Position the tooltip text - see examples below! */
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
margin-top: 0.5rem;
|
||||
width: 360px;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -180px;
|
||||
/* Use half of the width (120/2 = 60), to center the tooltip */
|
||||
opacity: 0;
|
||||
transition: opacity 1s;
|
||||
}
|
||||
|
||||
.ha-uploadform .ha-uploadmessage::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
/* At the top of the tooltip */
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent grey transparent;
|
||||
}
|
||||
|
||||
.ha-lds-ellipsis {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
}
|
||||
|
||||
.ha-lds-ellipsis div {
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: #000;
|
||||
-webkit-animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
||||
animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
||||
}
|
||||
|
||||
.ha-lds-ellipsis div:nth-child(1) {
|
||||
left: 6px;
|
||||
-webkit-animation: ha-lds-ellipsis1 0.6s infinite;
|
||||
animation: ha-lds-ellipsis1 0.6s infinite;
|
||||
}
|
||||
|
||||
.ha-lds-ellipsis div:nth-child(2) {
|
||||
left: 4px;
|
||||
-webkit-animation: ha-lds-ellipsis2 0.6s infinite;
|
||||
animation: ha-lds-ellipsis2 0.6s infinite;
|
||||
}
|
||||
|
||||
.ha-lds-ellipsis div:nth-child(3) {
|
||||
left: 16px;
|
||||
-webkit-animation: ha-lds-ellipsis2 0.6s infinite;
|
||||
animation: ha-lds-ellipsis2 0.6s infinite;
|
||||
}
|
||||
|
||||
.ha-lds-ellipsis div:nth-child(4) {
|
||||
left: 30px;
|
||||
-webkit-animation: ha-lds-ellipsis3 0.6s infinite;
|
||||
animation: ha-lds-ellipsis3 0.6s infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes ha-lds-ellipsis1 {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ha-lds-ellipsis1 {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes ha-lds-ellipsis3 {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ha-lds-ellipsis3 {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes ha-lds-ellipsis2 {
|
||||
0% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(16px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ha-lds-ellipsis2 {
|
||||
0% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(16px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.ha-cross::before,
|
||||
.ha-cross::after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.ha-cross::before {
|
||||
transform: skewY(-27deg);
|
||||
}
|
||||
|
||||
/* Classes for tables */
|
||||
@@ -3819,15 +4197,6 @@ body {
|
||||
min-width: 8.333%;
|
||||
}
|
||||
|
||||
.ha-cross::before,
|
||||
.ha-cross::after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.ha-cross::before {
|
||||
transform: skewY(-27deg);
|
||||
}
|
||||
|
||||
.hover\:text-black:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(0 0 0 / var(--tw-text-opacity));
|
||||
|
||||
@@ -664,6 +664,116 @@
|
||||
@apply flex leading-snug
|
||||
}
|
||||
|
||||
/* Classes for Upload View */
|
||||
.ha-adminuploadfields {
|
||||
@apply flex flex-row flex-wrap gap-x-4 gap-y-4
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadfield {
|
||||
@apply block shrink-0 grow bg-slate-50 rounded shadow basis-64 max-w-xs
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadfield:hover {
|
||||
@apply brightness-110
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadfield.active {
|
||||
@apply !text-black shadow-red-100
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadfield .ha-uploadfieldname {
|
||||
@apply px-3 pt-2 pb-1
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadusedfiles {
|
||||
@apply text-sm whitespace-nowrap overflow-hidden text-ellipsis w-auto bg-slate-200 border-t border-slate-300 bg-opacity-30 px-2 py-0.5
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadusedfiles.ha-uploadusedfilesnotfound {
|
||||
@apply bg-slate-500 border-slate-500
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadpublishforms {
|
||||
@apply flex flex-row gap-x-4 grow
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadform {
|
||||
@apply bg-slate-50 rounded shadow grow relative
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadform .ha-uploadtext {
|
||||
@apply text-center
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadform .ha-lds-ellipsis {
|
||||
@apply left-1/2 -ml-[20px]
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadform .ha-uploadfilelabel {
|
||||
@apply inline-block px-2 py-1 pt-2 cursor-pointer w-full h-full
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-uploadform .ha-uploadmessage {
|
||||
@apply text-sm bg-slate-700 bg-opacity-30 px-1 rounded-sm
|
||||
}
|
||||
|
||||
.ha-adminuploadfields .ha-publishform {
|
||||
@apply bg-slate-50 rounded shadow grow px-3 py-2
|
||||
}
|
||||
|
||||
.ha-uploadheader {
|
||||
@apply bg-slate-50 w-full mt-4 px-16 pt-12 pb-8 flex flex-row
|
||||
}
|
||||
|
||||
.ha-uploadheader h1 {
|
||||
@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-errorswarnings {
|
||||
@apply flex flex-row gap-x-2
|
||||
}
|
||||
|
||||
.ha-uploadcontainer .ha-errorswarnings .ha-criticalerrors,
|
||||
.ha-uploadcontainer .ha-errorswarnings .ha-warnings {
|
||||
@apply basis-1/2 min-w-[40%] min-h-[400px] overflow-x-hidden overflow-y-scroll grow shrink
|
||||
}
|
||||
|
||||
.ha-uploadcontainer .ha-errorswarnings .ha-criticalerrors {
|
||||
@apply bg-red-200
|
||||
}
|
||||
|
||||
.ha-uploadcontainer .ha-errorswarnings .ha-warnings {
|
||||
@apply bg-orange-200
|
||||
}
|
||||
|
||||
.ha-uploadcontainer .ha-crossfilechecking {
|
||||
@apply w-full bg-cyan-200 grow shrink-0 h-full min-h-[400px]
|
||||
}
|
||||
|
||||
/* Classes for Letter View */
|
||||
|
||||
.ha-letterheader {
|
||||
@@ -1137,6 +1247,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Content */
|
||||
.ha-lettertext .ha-marginalbox:before {
|
||||
content: "";
|
||||
}
|
||||
@@ -1181,6 +1292,7 @@
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Not implemented in tailwindcss */
|
||||
* {
|
||||
text-decoration-skip-ink: all;
|
||||
}
|
||||
@@ -1190,10 +1302,6 @@ body {
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
||||
/* ul {
|
||||
list-style-type:circle;
|
||||
} */
|
||||
|
||||
.ha-tradzhtext .ha-marginalbox.ha-collapsed-box .ha-marginallist .ha-marginal,
|
||||
.ha-lettertext .ha-marginalbox.ha-collapsed-box .ha-marginallist .ha-marginal {
|
||||
display: -webkit-inline-box;
|
||||
@@ -1282,6 +1390,7 @@ body {
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
/* Symbols, Icons, Drawings, Tooltips */
|
||||
.ha-menu-arrowsymbol::after {
|
||||
display: inline-block;
|
||||
margin-left: 0.2em;
|
||||
@@ -1344,9 +1453,121 @@ body {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.ha-text {
|
||||
user-select: contain;
|
||||
.ha-uploadheader .ha-usedfilesheaderlist .ha-plussign {
|
||||
content: "\200E+ ";
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
background-color: lightslategray;
|
||||
padding-left: 0.25rem;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
|
||||
/* Tooltip text */
|
||||
.ha-uploadform .ha-uploadmessage {
|
||||
visibility: visible;
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
border-radius: 6px;
|
||||
|
||||
/* Position the tooltip text - see examples below! */
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
|
||||
margin-top: 0.5rem;
|
||||
width: 360px;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -180px; /* Use half of the width (120/2 = 60), to center the tooltip */
|
||||
|
||||
opacity: 0;
|
||||
transition: opacity 1s;
|
||||
}
|
||||
|
||||
.ha-uploadform .ha-uploadmessage::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
bottom: 100%; /* At the top of the tooltip */
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent grey transparent;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.ha-lds-ellipsis {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
}
|
||||
|
||||
.ha-lds-ellipsis div {
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: #000;
|
||||
animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
||||
}
|
||||
|
||||
.ha-lds-ellipsis div:nth-child(1) {
|
||||
left: 6px;
|
||||
animation: ha-lds-ellipsis1 0.6s infinite;
|
||||
}
|
||||
|
||||
.ha-lds-ellipsis div:nth-child(2) {
|
||||
left: 4px;
|
||||
animation: ha-lds-ellipsis2 0.6s infinite;
|
||||
}
|
||||
|
||||
.ha-lds-ellipsis div:nth-child(3) {
|
||||
left: 16px;
|
||||
animation: ha-lds-ellipsis2 0.6s infinite;
|
||||
}
|
||||
|
||||
.ha-lds-ellipsis div:nth-child(4) {
|
||||
left: 30px;
|
||||
animation: ha-lds-ellipsis3 0.6s infinite;
|
||||
}
|
||||
|
||||
@keyframes ha-lds-ellipsis1 {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ha-lds-ellipsis3 {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ha-lds-ellipsis2 {
|
||||
0% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translate(16px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.ha-cross::before,
|
||||
.ha-cross::after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.ha-cross::before {
|
||||
-webkit-transform: skewY(-27deg);
|
||||
transform: skewY(-27deg);
|
||||
}
|
||||
|
||||
/* Classes for tables */
|
||||
.ha-table {
|
||||
overflow: hidden;
|
||||
@@ -1881,13 +2102,3 @@ body {
|
||||
left: 91.666%;
|
||||
min-width: 8.333%;
|
||||
}
|
||||
|
||||
.ha-cross::before,
|
||||
.ha-cross::after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.ha-cross::before {
|
||||
-webkit-transform: skewY(-27deg);
|
||||
transform: skewY(-27deg);
|
||||
}
|
||||
@@ -259,6 +259,8 @@ window.addEventListener("load", function () {
|
||||
markactive_startswith(document.getElementById("ha-topnav"));
|
||||
if (document.getElementById("ha-register-nav") != null)
|
||||
markactive_exact(document.getElementById("ha-register-nav"));
|
||||
if (this.document.getElementById("ha-adminuploadfields") != null)
|
||||
markactive_startswith(document.getElementById("ha-adminuploadfields"));
|
||||
|
||||
// Letter / Register View: Collapse all unfit boxes + resize observer
|
||||
collapseboxes();
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<opus></opus>
|
||||
<opus><lol></lol>></opus>
|
||||
Reference in New Issue
Block a user