Formatted everything; completed upload capabilities

This commit is contained in:
schnulller
2022-06-04 02:42:01 +02:00
parent 743c88a4e5
commit 37b794ea05
61 changed files with 677 additions and 558 deletions

View File

@@ -1,6 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processName": "HaWeb",
},
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
@@ -24,12 +30,7 @@
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}

View File

@@ -4,14 +4,12 @@ using HaDocument.Interfaces;
using HaXMLReader.Interfaces;
using Microsoft.FeatureManagement.Mvc;
public class AdminController : Controller
{
public class AdminController : Controller {
// DI
private ILibrary _lib;
private IReaderService _readerService;
public AdminController(ILibrary lib, IReaderService readerService)
{
public AdminController(ILibrary lib, IReaderService readerService) {
_lib = lib;
_readerService = readerService;
}
@@ -19,8 +17,7 @@ public class AdminController : Controller
[Route("Admin")]
[FeatureGate(Features.AdminService)]
public IActionResult Index()
{
public IActionResult Index() {
return Redirect("/Admin/Upload");
}
}

View File

@@ -7,8 +7,7 @@ using HaDocument.Models;
namespace HaWeb.Controllers;
public class Briefecontroller : Controller
{
public class Briefecontroller : Controller {
[BindProperty(SupportsGet = true)]
public string? id { get; set; }
@@ -16,16 +15,14 @@ public class Briefecontroller : Controller
private ILibrary _lib;
private IReaderService _readerService;
public Briefecontroller(ILibrary lib, IReaderService readerService)
{
public Briefecontroller(ILibrary lib, IReaderService readerService) {
_lib = lib;
_readerService = readerService;
}
[Route("Briefe")]
[Route("Briefe/{id?}")]
public IActionResult Index(string? id)
{
public IActionResult Index(string? id) {
// Setup settings and variables
var url = "/Briefe/";
var defaultID = "1";
@@ -79,19 +76,16 @@ public class Briefecontroller : Controller
return View(model);
}
private IActionResult error404()
{
private IActionResult error404() {
Response.StatusCode = 404;
return Redirect("/Error404");
}
private BriefeMetaViewModel generateMetaViewModel(Meta meta, bool hasMarginals)
{
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>();
var zhstring = meta.ZH != null ? HaWeb.HTMLHelpers.LetterHelpers.CreateZHString(meta) : null;
return new BriefeMetaViewModel(meta, hasMarginals, false)
{
return new BriefeMetaViewModel(meta, hasMarginals, false) {
ParsedZHString = zhstring,
ParsedSenders = HTMLHelpers.StringHelpers.GetEnumerationString(senders),
ParsedReceivers = HTMLHelpers.StringHelpers.GetEnumerationString(recivers)

View File

@@ -5,40 +5,32 @@ using HaWeb.Models;
namespace HaWeb.Controllers;
[Route("Edition/[action]")]
public class EditionController : Controller
{
public class EditionController : Controller {
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
public IActionResult Error() {
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
public IActionResult Kontakt()
{
public IActionResult Kontakt() {
return View();
}
public IActionResult Mitwirkende()
{
public IActionResult Mitwirkende() {
return View();
}
public IActionResult Richtlinien()
{
public IActionResult Richtlinien() {
return View();
}
public IActionResult Werkausgabe()
{
public IActionResult Werkausgabe() {
return View();
}
public IActionResult Editionsgeschichte()
{
public IActionResult Editionsgeschichte() {
return View();
}
public IActionResult Datenschutzerklaerung()
{
public IActionResult Datenschutzerklaerung() {
return View();
}
}

View File

@@ -5,8 +5,7 @@ using HaWeb.Models;
namespace HaWeb.Controllers;
public class ErrorController : Controller
{
public class ErrorController : Controller {
[Route("Error404/")]
public IActionResult ErrorNotFound() {
return View();

View File

@@ -4,18 +4,15 @@ using HaWeb.Models;
namespace HaWeb.Controllers;
public class HomeController : Controller
{
public class HomeController : Controller {
[Route("")]
public IActionResult Index()
{
public IActionResult Index() {
return Redirect("/Suche");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
public IActionResult Error() {
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}

View File

@@ -6,8 +6,7 @@ using HaWeb.Models;
namespace HaWeb.Controllers;
public class LegacyContoller : Controller
{
public class LegacyContoller : Controller {
[Route("Supplementa/")]
[Route("Supplementa/Register")]
[Route("Supplementa/Register/{id?}")]

View File

@@ -12,8 +12,7 @@ using System.Collections.Concurrent;
namespace HaWeb.Controllers;
[Route("Register/[action]/{id?}")]
public class RegisterController : Controller
{
public class RegisterController : Controller {
[BindProperty(SupportsGet = true)]
public string? search { get; set; }
@@ -24,14 +23,12 @@ public class RegisterController : Controller
private ILibrary _lib;
private IReaderService _readerService;
public RegisterController(ILibrary lib, IReaderService readerService)
{
public RegisterController(ILibrary lib, IReaderService readerService) {
_lib = lib;
_readerService = readerService;
}
public IActionResult Register(string? id)
{
public IActionResult Register(string? id) {
// Setup settings and variables
var url = "/Register/Register/";
var category = "neuzeit";
@@ -52,15 +49,12 @@ public class RegisterController : Controller
// Parsing
var res = new List<CommentModel>();
foreach (var comm in comments)
{
foreach (var comm in comments) {
var parsedComment = HTMLHelpers.CommentHelpers.CreateHTML(_lib, _readerService, comm, category, Settings.ParsingState.CommentType.Comment);
List<string>? parsedSubComments = null;
if (comm.Kommentare != null)
{
if (comm.Kommentare != null) {
parsedSubComments = new List<string>();
foreach (var subcomm in comm.Kommentare.OrderBy(x => x.Value.Order))
{
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));
}
}
@@ -68,8 +62,7 @@ public class RegisterController : Controller
}
// Model instantiation
var model = new RegisterViewModel(category, this.id, res, title)
{
var model = new RegisterViewModel(category, this.id, res, title) {
AvailableCategories = availableCategories,
};
@@ -77,8 +70,7 @@ public class RegisterController : Controller
return View("Index", model);
}
public IActionResult Bibelstellen(string? id)
{
public IActionResult Bibelstellen(string? id) {
// Setup settings and variables
var url = "/Register/Bibelstellen/";
var category = "bibel";
@@ -99,15 +91,12 @@ public class RegisterController : Controller
// Parsing
var res = new List<CommentModel>();
foreach (var comm in comments)
{
foreach (var comm in comments) {
var parsedComment = HTMLHelpers.CommentHelpers.CreateHTML(_lib, _readerService, comm, category, Settings.ParsingState.CommentType.Comment);
List<string>? parsedSubComments = null;
if (comm.Kommentare != 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))
{
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));
}
}
@@ -115,8 +104,7 @@ public class RegisterController : Controller
}
// Model instantiation
var model = new RegisterViewModel(category, this.id, res, title)
{
var model = new RegisterViewModel(category, this.id, res, title) {
AvailableCategories = availableCategories,
};
@@ -124,8 +112,7 @@ public class RegisterController : Controller
return View("Index", model);
}
public IActionResult Forschung(string? id)
{
public IActionResult Forschung(string? id) {
// Setup settings and variables
var url = "/Register/Forschung/";
var category = "forschung";
@@ -144,8 +131,7 @@ public class RegisterController : Controller
IOrderedEnumerable<Comment>? comments = null;
if (this.id == "EDITIONEN") {
comments = _lib.CommentsByCategory[this.id.ToLower()].OrderBy(x => x.Index);
}
else {
} else {
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();
@@ -154,15 +140,12 @@ public class RegisterController : Controller
// Parsing
var res = new List<CommentModel>();
foreach (var comm in comments)
{
foreach (var comm in comments) {
var parsedComment = HTMLHelpers.CommentHelpers.CreateHTML(_lib, _readerService, comm, category, Settings.ParsingState.CommentType.Comment);
List<string>? parsedSubComments = null;
if (comm.Kommentare != null)
{
if (comm.Kommentare != null) {
parsedSubComments = new List<string>();
foreach (var subcomm in comm.Kommentare.OrderBy(x => x.Value.Order))
{
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));
}
}
@@ -170,8 +153,7 @@ public class RegisterController : Controller
}
// Model instantiation
var model = new RegisterViewModel(category, this.id, res, title)
{
var model = new RegisterViewModel(category, this.id, res, title) {
AvailableCategories = availableCategories,
AvailableSideCategories = AvailableSideCategories
};
@@ -180,22 +162,18 @@ public class RegisterController : Controller
return View("Index", model);
}
private void normalizeID(string? id, string defaultid)
{
private void normalizeID(string? id, string defaultid) {
this.id = this.id.ToUpper();
}
private bool validationCheck(HashSet<string> permitted)
{
if (!permitted.Contains(id))
{
private bool validationCheck(HashSet<string> permitted) {
if (!permitted.Contains(id)) {
return false;
}
return true;
}
private IActionResult error404()
{
private IActionResult error404() {
Response.StatusCode = 404;
return Redirect("/Error404");
}

View File

@@ -4,18 +4,15 @@ using HaWeb.Models;
namespace HaWeb.Controllers;
public class SucheController : Controller
{
public class SucheController : Controller {
[Route("Suche")]
public IActionResult Index()
{
public IActionResult Index() {
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
public IActionResult Error() {
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}

View File

@@ -4,14 +4,12 @@ using HaDocument.Interfaces;
using HaXMLReader.Interfaces;
using Microsoft.FeatureManagement.Mvc;
public class UpdateController : Controller
{
public class UpdateController : Controller {
// DI
private ILibrary _lib;
private IReaderService _readerService;
public UpdateController(ILibrary lib, IReaderService readerService)
{
public UpdateController(ILibrary lib, IReaderService readerService) {
_lib = lib;
_readerService = readerService;
}
@@ -19,8 +17,7 @@ public class UpdateController : Controller
[Route("Admin/Update")]
[FeatureGate(Features.UpdateService)]
public IActionResult Index()
{
public IActionResult Index() {
return View("../Admin/Upload/Index");
}
}

View File

@@ -21,8 +21,7 @@ using System.Xml.Linq;
using System.Text.Json.Serialization;
using System.Text.Json;
public class UploadController : Controller
{
public class UploadController : Controller {
// DI
private ILibrary _lib;
private IReaderService _readerService;
@@ -35,8 +34,7 @@ public class UploadController : Controller
private static readonly FormOptions _defaultFormOptions = new FormOptions();
public UploadController(ILibrary lib, IReaderService readerService, IXMLService xmlService, IConfiguration config)
{
public UploadController(ILibrary lib, IReaderService readerService, IXMLService xmlService, IConfiguration config) {
_lib = lib;
_readerService = readerService;
_xmlService = xmlService;
@@ -49,27 +47,28 @@ public class UploadController : Controller
}
[HttpGet]
[Route("Admin/Upload")]
[Route("Admin/Upload/{id?}")]
[FeatureGate(Features.UploadService)]
[GenerateAntiforgeryTokenCookie]
public IActionResult Index()
{
public IActionResult Index(string? id) {
var model = new UploadViewModel();
model.AvailableRoots = _xmlService.GetRoots().Select(x => (x.Type, "")).ToList();
model.AvailableRoots = _xmlService.GetRoots();
model.UsedFiles = _xmlService.GetUsed();
var hello = "mama";
return View("../Admin/Upload/Index", model);
}
//// UPLOAD ////
//// UPLOAD ////
[HttpPost]
[Route("Admin/Upload")]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Post() {
//// 1. Stage: Check Request format and request spec
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))
{
if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) {
ModelState.AddModelError("Error", $"Wrong / No Content Type on the Request");
return BadRequest(ModelState);
}
@@ -80,80 +79,63 @@ public class UploadController : Controller
MultipartSection? section = null;
try {
section = await reader.ReadNextSectionAsync();
}
catch (Exception ex) {
} catch (Exception ex) {
ModelState.AddModelError("Error", "The Request is bad: " + ex.Message);
}
while (section != null)
{
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)
{
if (hasContentDispositionHeader && contentDisposition != null) {
// Checks if it is a section with content-disposition, name, filename
if (!MultipartRequestHelper.HasFileContentDisposition(contentDisposition))
{
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.
//// 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()
//// 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 docs = _xmlService.ProbeHamannFile(xdocument, ModelState);
if (!ModelState.IsValid || docs == null || !docs.Any())
//// 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 docs) {
var type = doc.Prefix;
var directory = Path.Combine(_targetFilePath, type);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
var path = Path.Combine(directory, doc.FileName);
try {
using (var targetStream = System.IO.File.Create(path))
await doc.Save(targetStream, 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);
}
catch (Exception ex) {
ModelState.AddModelError("Error", "Speichern der Datei fehlgeschlagen: " + ex.Message);
return StatusCode(500, ModelState);
if (docs == null) docs = new List<XMLRootDocument>();
docs.Add(doc);
}
}
// 6. State: Returning Ok, and redirecting
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
ReferenceHandler = ReferenceHandler.Preserve,
Converters = {
new IdentificationStringJSONConverter()
}
};
string json = JsonSerializer.Serialize(docs);
return Created(nameof(UploadController), json);
}
try
{
section = await reader.ReadNextSectionAsync();
}
catch (Exception ex)
{
ModelState.AddModelError("Error", "The Request is bad: " + ex.Message);
}
}
//// Success! Return Last Created File View
return Created(nameof(UploadController), null);
}
}

View File

@@ -3,12 +3,10 @@ using System;
using System.IO;
using Microsoft.Net.Http.Headers;
public static class MultipartRequestHelper
{
public static class MultipartRequestHelper {
// Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"
// The spec at https://tools.ietf.org/html/rfc2046#section-5.1 states that 70 characters is a reasonable limit.
public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
{
public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit) {
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value;
if (string.IsNullOrWhiteSpace(boundary))
@@ -23,8 +21,7 @@ public static class MultipartRequestHelper
public static bool IsMultipartContentType(string? contentType)
=> !string.IsNullOrEmpty(contentType) && contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0;
public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition)
{
public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition) {
// Content-Disposition: form-data; name="key";
return contentDisposition != null
&& contentDisposition.DispositionType.Equals("form-data")
@@ -32,8 +29,7 @@ public static class MultipartRequestHelper
&& string.IsNullOrEmpty(contentDisposition.FileNameStar.Value);
}
public static bool HasFileContentDisposition(ContentDispositionHeaderValue? contentDisposition)
{
public static bool HasFileContentDisposition(ContentDispositionHeaderValue? contentDisposition) {
return contentDisposition != null
&& contentDisposition.DispositionType.Equals("form-data")
&& (!string.IsNullOrEmpty(contentDisposition.FileName.Value)

View File

@@ -6,8 +6,7 @@ using System.Xml;
public static class XDocumentFileHelper {
private readonly static XmlReaderSettings _Settings = new XmlReaderSettings()
{
private readonly static XmlReaderSettings _Settings = new XmlReaderSettings() {
CloseInput = true,
CheckCharacters = false,
ConformanceLevel = ConformanceLevel.Fragment,
@@ -17,19 +16,14 @@ public static class XDocumentFileHelper {
};
public static async Task<XDocument?> ProcessStreamedFile(byte[] bytes, ModelStateDictionary modelState) {
try
{
using (var stream = new MemoryStream(bytes))
{
using (var xmlreader = XmlReader.Create(stream, _Settings))
{
try {
using (var stream = new MemoryStream(bytes)) {
using (var xmlreader = XmlReader.Create(stream, _Settings)) {
return XDocument.Load(xmlreader, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
}
}
}
catch (Exception ex)
{
} catch (Exception ex) {
modelState.AddModelError("Error", $"Kein gültiges XML-Dokument geladen. Error: {ex.Message}");
}

View File

@@ -12,8 +12,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;
public static class XMLFileHelpers
{
public static class XMLFileHelpers {
// File Signatures Database (https://www.filesignatures.net/)
private static readonly Dictionary<string, List<byte[]>> _fileSignature = new Dictionary<string, List<byte[]>>
{
@@ -142,19 +141,15 @@ public static class XMLFileHelpers
public static async Task<byte[]?> ProcessStreamedFile(
MultipartSection section, ContentDispositionHeaderValue contentDisposition,
ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit)
{
try
{
using (var memoryStream = new MemoryStream())
{
ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit) {
try {
using (var memoryStream = new MemoryStream()) {
await section.Body.CopyToAsync(memoryStream);
// Check if the file is empty or exceeds the size limit.
if (memoryStream.Length == 0)
modelState.AddModelError("Error", "The file is empty.");
else if (memoryStream.Length > sizeLimit)
{
else if (memoryStream.Length > sizeLimit) {
var megabyteSizeLimit = sizeLimit / 1048576;
modelState.AddModelError("Error", $"The file exceeds {megabyteSizeLimit:N1} MB.");
}
@@ -168,17 +163,14 @@ public static class XMLFileHelpers
// Return the File as a byte array
else return memoryStream.ToArray();
}
}
catch (Exception ex)
{
} catch (Exception ex) {
modelState.AddModelError("Error", $"The upload failed. Error: {ex.Message}");
}
return null;
}
private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, string[] permittedExtensions)
{
private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, string[] permittedExtensions) {
if (string.IsNullOrEmpty(fileName) || data == null || data.Length == 0)
return false;
@@ -189,8 +181,7 @@ public static class XMLFileHelpers
data.Position = 0;
using (var reader = new BinaryReader(data))
{
using (var reader = new BinaryReader(data)) {
var signatures = _fileSignature[ext];
var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length));
return signatures.Any(signature =>

View File

@@ -4,10 +4,8 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
public class GenerateAntiforgeryTokenCookieAttribute : ResultFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
public class GenerateAntiforgeryTokenCookieAttribute : ResultFilterAttribute {
public override void OnResultExecuting(ResultExecutingContext context) {
var antiforgery = context.HttpContext.RequestServices.GetService<IAntiforgery>();
// Send the request token as a JavaScript-readable cookie
@@ -19,8 +17,7 @@ public class GenerateAntiforgeryTokenCookieAttribute : ResultFilterAttribute
new CookieOptions() { HttpOnly = false });
}
public override void OnResultExecuted(ResultExecutedContext context)
{
public override void OnResultExecuted(ResultExecutedContext context) {
}
}

View File

@@ -4,17 +4,14 @@ using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter {
public void OnResourceExecuting(ResourceExecutingContext context) {
var factories = context.ValueProviderFactories;
factories.RemoveType<FormValueProviderFactory>();
factories.RemoveType<FormFileValueProviderFactory>();
factories.RemoveType<JQueryFormValueProviderFactory>();
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
public void OnResourceExecuted(ResourceExecutedContext context) {
}
}

View File

@@ -7,16 +7,14 @@ using HaWeb.Settings.ParsingRules;
using HaWeb.Settings.ParsingState;
using System.Text;
public static class CommentHelpers
{
public static class CommentHelpers {
private static readonly string DEFAULTELEMENT = HaWeb.Settings.HTML.DEFAULTELEMENT;
private static readonly string BACKLINKSCLASS = HaWeb.Settings.CSSClasses.BACKLINKSCLASS;
private static readonly string LETLINKCLASS = HaWeb.Settings.CSSClasses.LETLINKCLASS;
private static readonly string COMMENTHEADCLASS = HaWeb.Settings.CSSClasses.COMMENTHEADCLASS;
private static readonly string BACKLINKSHKBCLASS = HaWeb.Settings.CSSClasses.BACKLINKSHKBCLASS;
public static string CreateHTML(ILibrary lib, IReaderService readerService, Comment comment, string category, CommentType type)
{
public static string CreateHTML(ILibrary lib, IReaderService readerService, Comment comment, string category, CommentType type) {
var sb = new StringBuilder();
var rd = readerService.RequestStringReader(comment.Lemma);
var commentState = new CommentState(category, type);
@@ -28,17 +26,13 @@ public static class CommentHelpers
.Where(x => lib.Metas.ContainsKey(x.Letter))
.OrderBy(x => lib.Metas[x.Letter].Sort)
.ThenBy(x => lib.Metas[x.Letter].Order) : null;
if (backlinks != null)
{
if (backlinks != null) {
sb.Append(HTMLHelpers.TagHelpers.CreateElement(DEFAULTELEMENT, BACKLINKSCLASS));
var arrow = false;
foreach (var blk in backlinks)
{
foreach (var blk in backlinks) {
var let = lib.Metas.ContainsKey(blk.Letter) ? lib.Metas[blk.Letter] : null;
if (let != null)
{
if (!arrow)
{
if (let != null) {
if (!arrow) {
sb.Append(HTMLHelpers.TagHelpers.CreateElement(DEFAULTELEMENT, BACKLINKSHKBCLASS));
sb.Append("HKB&nbsp;");
sb.Append(HTMLHelpers.TagHelpers.CreateEndElement(DEFAULTELEMENT));

View File

@@ -14,36 +14,27 @@ public static class ConversionHelpers {
{'M', 1000}
};
public static int RomanToInteger(string roman)
{
public static int RomanToInteger(string roman) {
var ro = roman.ToUpper();
int number = 0;
for (int i = 0; i < roman.Length; i++)
{
if (RomanMap.ContainsKey(ro[i]) && (i + 1 >= ro.Length || RomanMap.ContainsKey(ro[i + 1])))
{
if (i + 1 < ro.Length && RomanMap[ro[i]] < RomanMap[ro[i + 1]])
{
for (int i = 0; i < roman.Length; i++) {
if (RomanMap.ContainsKey(ro[i]) && (i + 1 >= ro.Length || RomanMap.ContainsKey(ro[i + 1]))) {
if (i + 1 < ro.Length && RomanMap[ro[i]] < RomanMap[ro[i + 1]]) {
number -= RomanMap[ro[i]];
}
else
{
} else {
number += RomanMap[ro[i]];
}
}
else return 0;
} else return 0;
}
return number;
}
public static int RomanOrNumberToInt(string number)
{
public static int RomanOrNumberToInt(string number) {
var a = 0;
if (Int32.TryParse(number, out a)) return a;
else return RomanToInteger(number);
}
public static string ToRoman(int number)
{
public static string ToRoman(int number) {
if ((number < 0) || (number > 3999)) return string.Empty;
if (number < 1) return string.Empty;
if (number >= 1000) return "M" + ToRoman(number - 1000);

View File

@@ -11,10 +11,8 @@ using System.Xml.Linq;
using HaWeb.Settings.ParsingState;
using HaWeb.Settings.ParsingRules;
public static class LetterHelpers
{
public static LetterState CreateLetter(ILibrary lib, IReaderService readerService, Meta meta, Letter letter, IEnumerable<Marginal>? marginals, IEnumerable<Hand>? hands, IEnumerable<Editreason>? edits)
{
public static class LetterHelpers {
public static LetterState CreateLetter(ILibrary lib, IReaderService readerService, Meta meta, Letter letter, IEnumerable<Marginal>? marginals, IEnumerable<Hand>? hands, IEnumerable<Editreason>? edits) {
var rd = readerService.RequestStringReader(letter.Element);
var letterState = new LetterState(lib, readerService, meta, marginals, hands, edits);
new HaWeb.HTMLParser.XMLHelper<LetterState>(letterState, rd, letterState.sb_lettertext, LetterRules.OTagRules, LetterRules.STagRules, LetterRules.CTagRules, LetterRules.TextRules, LetterRules.WhitespaceRules);
@@ -23,8 +21,7 @@ public static class LetterHelpers
return letterState;
}
public static TraditionState CreateTraditions(ILibrary lib, IReaderService readerService, IEnumerable<Marginal>? marginals, Tradition tradition, IEnumerable<Hand>? hands, IEnumerable<Editreason>? edits)
{
public static TraditionState CreateTraditions(ILibrary lib, IReaderService readerService, IEnumerable<Marginal>? marginals, Tradition tradition, IEnumerable<Hand>? hands, IEnumerable<Editreason>? edits) {
var rd = readerService.RequestStringReader(tradition.Element);
var traditionState = new TraditionState(lib, rd, readerService, marginals, hands, edits);
new HaWeb.HTMLParser.XMLHelper<TraditionState>(traditionState, rd, traditionState.sb_tradition, TraditionRules.OTagRules, TraditionRules.STagRules, TraditionRules.CTagRules, TraditionRules.TextRules, TraditionRules.WhitespaceRules);
@@ -33,13 +30,11 @@ public static class LetterHelpers
return traditionState;
}
public static List<(string, string, string, string, string, string)> CreateEdits(ILibrary lib, IReaderService readerService, IEnumerable<Editreason> editreasons)
{
public static List<(string, string, string, string, string, string)> CreateEdits(ILibrary lib, IReaderService readerService, IEnumerable<Editreason> editreasons) {
editreasons = editreasons.OrderBy(x => HaWeb.HTMLHelpers.ConversionHelpers.RomanOrNumberToInt(x.StartPage)).ThenBy(x => HaWeb.HTMLHelpers.ConversionHelpers.RomanOrNumberToInt(x.StartLine));
var editstrings = new List<(string, string, string, string, string, string)>();
var editsState = new EditState();
foreach (var edit in editreasons)
{
foreach (var edit in editreasons) {
var currstring = edit.StartPage + "/" + edit.StartLine;
var endstring = "";
var refstring = "";
@@ -49,8 +44,7 @@ public static class LetterHelpers
endstring += edit.EndLine;
editsState.sb_edits.Append(HaWeb.HTMLHelpers.TagHelpers.CreateElement("div", "edit"));
if (!String.IsNullOrWhiteSpace(edit.Reference))
{
if (!String.IsNullOrWhiteSpace(edit.Reference)) {
var sb2 = new StringBuilder();
sb2.Append(HaWeb.HTMLHelpers.TagHelpers.CreateElement("span", "reference"));
var rd = readerService.RequestStringReader(edit.Reference);
@@ -58,8 +52,7 @@ public static class LetterHelpers
rd.Read();
sb2.Append(HaWeb.HTMLHelpers.TagHelpers.CreateEndElement("span"));
// Old: (edit.StartPage != edit.EndPage || edit.StartLine != edit.EndLine) &&
if (XElement.Parse(sb2.ToString()).Value.ToString().Length >= 20)
{
if (XElement.Parse(sb2.ToString()).Value.ToString().Length >= 20) {
var text = XElement.Parse(sb2.ToString()).Value.ToString();
text = text.ToString().Split(' ').Take(1).First() + " [&#x2026;] " + text.ToString().Split(' ').TakeLast(1).First();
var sb3 = new StringBuilder();
@@ -67,12 +60,10 @@ public static class LetterHelpers
sb3.Append(text);
sb3.Append(HaWeb.HTMLHelpers.TagHelpers.CreateEndElement("span"));
refstring = sb3.ToString();
}
else
} else
refstring = sb2.ToString();
}
if (!String.IsNullOrWhiteSpace(edit.Element))
{
if (!String.IsNullOrWhiteSpace(edit.Element)) {
editsState.sb_edits.Append(HaWeb.HTMLHelpers.TagHelpers.CreateElement("span", "corrections"));
var rd = readerService.RequestStringReader(edit.Element);
new HaWeb.HTMLParser.XMLHelper<EditState>(editsState, rd, editsState.sb_edits, EditRules.OTagRules, EditRules.STagRules, EditRules.CTagRules, EditRules.TextRules, EditRules.WhitespaceRules);
@@ -86,11 +77,9 @@ public static class LetterHelpers
return editstrings;
}
public static List<(string, string, string, string, string)> CreateHands(ILibrary lib, ImmutableList<Hand> hands)
{
public static List<(string, string, string, string, string)> CreateHands(ILibrary lib, ImmutableList<Hand> hands) {
var handstrings = new List<(string, string, string, string, string)>();
foreach (var hand in hands.OrderBy(x => x.StartPage.Length).ThenBy(x => x.StartPage).ThenBy(x => x.StartLine.Length).ThenBy(x => x.StartLine))
{
foreach (var hand in hands.OrderBy(x => x.StartPage.Length).ThenBy(x => x.StartPage).ThenBy(x => x.StartLine.Length).ThenBy(x => x.StartLine)) {
var currstring = hand.StartPage + "/" + hand.StartLine;
var endstring = "";
var personstring = "";
@@ -100,8 +89,7 @@ public static class LetterHelpers
if (hand.StartLine != hand.EndLine)
endstring += hand.EndLine;
var persons = lib.HandPersons.Where(x => x.Key == hand.Person);
if (persons.Any())
{
if (persons.Any()) {
personstring += " " + persons.FirstOrDefault().Value.Name;
handstrings.Add((currstring, endstring, personstring, hand.StartPage, hand.StartLine));
}

View File

@@ -37,21 +37,20 @@ public class LinkHelper {
if (tag.Name == "wwwlink" || tag.Name == "intlink" || tag.Name == "link") {
if (tag.EndTag && _followlinksinthis) {
_sb.Append(HTMLHelpers.TagHelpers.CreateEndElement("a"));
}
else {
} else {
if (tag.Name == "wwwlink" && tag.Values.ContainsKey("address") && _followlinksinthis)
_sb.Append(HTMLHelpers.TagHelpers.CreateCustomElement("a",
new HaWeb.HTMLHelpers.TagHelpers.Attribute() { Name = "class", Value = WWWLINKCLASS },
new HaWeb.HTMLHelpers.TagHelpers.Attribute() { Name = "href", Value = tag["address"]},
new HaWeb.HTMLHelpers.TagHelpers.Attribute() { Name = "target", Value = "_blank"},
new HaWeb.HTMLHelpers.TagHelpers.Attribute() { Name = "rel", Value = "noopener noreferrer"}));
new HaWeb.HTMLHelpers.TagHelpers.Attribute() { Name = "href", Value = tag["address"] },
new HaWeb.HTMLHelpers.TagHelpers.Attribute() { Name = "target", Value = "_blank" },
new HaWeb.HTMLHelpers.TagHelpers.Attribute() { Name = "rel", Value = "noopener noreferrer" }));
if (tag.Name == "intlink" && tag.Values.ContainsKey("letter") && _lib.Metas.ContainsKey(tag["letter"])) {
var letter = _lib.Metas[tag["letter"]];
_sb.Append(HTMLHelpers.TagHelpers.CreateElement("a", LETLINKCLASS, "/Briefe/" + letter.Autopsic + "#" + tag["page"] + "-" + tag["line"]));
if (!tag.Values.ContainsKey("linktext") || tag.Values["linktext"] == "true") {
var linkstring = "";
var ZHstring = "";
var pglnstring= "";
var pglnstring = "";
linkstring += "HKB&nbsp;" + letter.Autopsic;
if (tag.Values.ContainsKey("page")) {
pglnstring += tag["page"];

View File

@@ -2,11 +2,9 @@ using System.Web;
namespace HaWeb.HTMLHelpers;
public static class StringHelpers {
public static string GetEnumerationString(IEnumerable<string> strlist)
{
public static string GetEnumerationString(IEnumerable<string> strlist) {
var res = "";
foreach (var str in strlist)
{
foreach (var str in strlist) {
if (str != strlist.First())
if (str == strlist.Last())
res += " und " + str;

View File

@@ -3,16 +3,13 @@ using System;
using System.Collections.Generic;
using System.Linq;
public static class TagHelpers
{
public struct Attribute
{
public static class TagHelpers {
public struct Attribute {
public string Name;
public string Value;
}
public static string CreateElement(string elementname, string classes = "", string ids = "")
{
public static string CreateElement(string elementname, string classes = "", string ids = "") {
string res = "<" + elementname;
if (!String.IsNullOrWhiteSpace(classes))
if (elementname == "button")
@@ -27,13 +24,10 @@ public static class TagHelpers
return res + ">";
}
public static string CreateCustomElement(string elementname, params Attribute[] attributes)
{
public static string CreateCustomElement(string elementname, params Attribute[] attributes) {
string res = "<" + elementname;
if (!(attributes.Length == 0))
{
foreach (var attrib in attributes)
{
if (!(attributes.Length == 0)) {
foreach (var attrib in attributes) {
res += CreateAttribute(attrib);
}
}
@@ -47,8 +41,7 @@ public static class TagHelpers
public static string CreateAttribute(Attribute attr)
=> " " + attr.Name + "=\"" + attr.Value + "\" ";
public static string CreateEmptyElement(string elementname, string classes = "", string ids = "")
{
public static string CreateEmptyElement(string elementname, string classes = "", string ids = "") {
string res = "<" + elementname;
if (!String.IsNullOrWhiteSpace(classes))
res += CreateAttribute(new Attribute() { Name = "class", Value = classes });

View File

@@ -1,8 +1,7 @@
namespace HaWeb.Models;
using HaDocument.Models;
using System.Web;
public class BriefeMetaViewModel
{
public class BriefeMetaViewModel {
public Meta Meta { get; private set; }
public bool HasMarginals { get; private set; }
public bool ShowZHData { get; private set; }
@@ -12,11 +11,9 @@ public class BriefeMetaViewModel
private string? _ParsedReceivers;
private string? _ParsedZHString;
public string? ParsedSenders
{
public string? ParsedSenders {
get => _ParsedSenders;
set
{
set {
if (value != null)
_ParsedSenders = HttpUtility.HtmlEncode(value);
else
@@ -24,11 +21,9 @@ public class BriefeMetaViewModel
}
}
public string? ParsedReceivers
{
public string? ParsedReceivers {
get => _ParsedReceivers;
set
{
set {
if (value != null)
_ParsedReceivers = HttpUtility.HtmlEncode(value);
else
@@ -37,11 +32,9 @@ public class BriefeMetaViewModel
}
}
public string? ParsedZHString
{
public string? ParsedZHString {
get => _ParsedZHString;
set
{
set {
if (value != null)
_ParsedZHString = HttpUtility.HtmlEncode(value);
else
@@ -54,8 +47,7 @@ public class BriefeMetaViewModel
public (BriefeMetaViewModel, string)? Prev { get; set; }
public BriefeMetaViewModel(Meta meta, bool hasMarginals, bool showZHData)
{
public BriefeMetaViewModel(Meta meta, bool hasMarginals, bool showZHData) {
Meta = meta;
HasMarginals = hasMarginals;
ShowZHData = showZHData;

View File

@@ -1,8 +1,7 @@
namespace HaWeb.Models;
using System.Web;
public class BriefeViewModel
{
public class BriefeViewModel {
public string Id { get; private set; }
public string Index { get; private set; }
public BriefeMetaViewModel MetaData { get; private set; }
@@ -17,11 +16,9 @@ public class BriefeViewModel
public bool MinWidthTrad { get; set; } = false;
// From, Until, Reference, Edit, sartpage, startline
public List<(string, string, string, string, string, string)>? ParsedEdits
{
public List<(string, string, string, string, string, string)>? ParsedEdits {
get => _ParsedEdits;
set
{
set {
if (value != null)
_ParsedEdits = value.Select(x => (
HttpUtility.HtmlEncode(x.Item1),
@@ -37,11 +34,9 @@ public class BriefeViewModel
}
// From, Until, Person, startpage, startline
public List<(string, string, string, string, string)>? ParsedHands
{
public List<(string, string, string, string, string)>? ParsedHands {
get => _ParsedHands;
set
{
set {
if (value != null)
_ParsedHands = value.Select(x => (
HttpUtility.HtmlEncode(x.Item1),
@@ -56,11 +51,9 @@ public class BriefeViewModel
}
// Page, Line, Element
public List<(string, string, string)>? ParsedMarginals
{
public List<(string, string, string)>? ParsedMarginals {
get => _ParsedMarginals;
set
{
set {
if (value != null)
_ParsedMarginals = value.Select(x => (
HttpUtility.HtmlEncode(x.Item1),
@@ -72,8 +65,7 @@ public class BriefeViewModel
}
}
public BriefeViewModel(string id, string index, BriefeMetaViewModel meta)
{
public BriefeViewModel(string id, string index, BriefeMetaViewModel meta) {
Id = id;
Index = index;
MetaData = meta;

View File

@@ -5,13 +5,11 @@ using System.Text;
using HaXMLReader.Interfaces;
using HaXMLReader.EvArgs;
public class CommentModel
{
public class CommentModel {
public string ParsedComment { get; private set; }
public List<string>? ParsedSubComments { get; private set; }
public CommentModel(string parsedComment, List<string>? parsedSubComments)
{
public CommentModel(string parsedComment, List<string>? parsedSubComments) {
this.ParsedComment = parsedComment;
this.ParsedSubComments = parsedSubComments;
}

View File

@@ -1,7 +1,6 @@
namespace HaWeb.Models;
public class ErrorViewModel
{
public class ErrorViewModel {
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

View File

@@ -1,8 +1,7 @@
namespace HaWeb.Models;
using System.Web;
public class RegisterViewModel
{
public class RegisterViewModel {
public string Category { get; private set; }
public string Id { get; private set; }
public string Title { get; private set; }
@@ -15,11 +14,9 @@ public class RegisterViewModel
public List<CommentModel> ParsedComments { get; private set; }
// Title, URL
public List<(string, string)>? AvailableCategories
{
public List<(string, string)>? AvailableCategories {
get => _AvailableCategories;
set
{
set {
if (value != null)
_AvailableCategories = value.Select(x => (
HttpUtility.HtmlEncode(x.Item1),
@@ -31,11 +28,9 @@ public class RegisterViewModel
}
// Title, URL
public List<(string, string)>? AvailableSideCategories
{
public List<(string, string)>? AvailableSideCategories {
get => _AvailableSideCategories;
set
{
set {
if (value != null)
_AvailableSideCategories = value.Select(x => (
HttpUtility.HtmlEncode(x.Item1),
@@ -46,8 +41,7 @@ public class RegisterViewModel
}
}
public RegisterViewModel(string category, string id, List<CommentModel> parsedComments, string title)
{
public RegisterViewModel(string category, string id, List<CommentModel> parsedComments, string title) {
this.Category = HttpUtility.HtmlAttributeEncode(category);
this.Id = HttpUtility.HtmlAttributeEncode(id);
this.ParsedComments = parsedComments;

View File

@@ -26,10 +26,8 @@ public class DocumentResult {
}
}
public class LetterComparer : IComparer<DocumentSearchResult>
{
public int Compare(DocumentSearchResult first, DocumentSearchResult second)
{
public class LetterComparer : IComparer<DocumentSearchResult> {
public int Compare(DocumentSearchResult first, DocumentSearchResult second) {
var cmp = new DefaultComparer();
return cmp.Compare(first.MetaData, second.MetaData);
}

View File

@@ -1,5 +1,8 @@
namespace HaWeb.Models;
using HaWeb.XMLParser;
public class UploadViewModel {
public List<(string, string)>? AvailableRoots { get; set; }
public List<IXMLRoot>? AvailableRoots { get; set; }
public List<XMLRootDocument>? AvailableFiles { get; set; }
public Dictionary<string, List<XMLRootDocument>>? UsedFiles { get; set; }
}

View File

@@ -3,9 +3,7 @@ using HaXMLReader.Interfaces;
using HaDocument.Interfaces;
using HaWeb.XMLParser;
using Microsoft.FeatureManagement;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Runtime.InteropServices;
using Microsoft.Extensions.FileProviders;
var builder = WebApplication.CreateBuilder(args);
@@ -14,12 +12,23 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddHttpContextAccessor();
// // To list physical files from a path provided by configuration:
// var physicalProvider = new PhysicalFileProvider(Configuration.GetValue<string>("StoredFilesPath"));
// // To list physical files in the temporary files folder, use:
// //var physicalProvider = new PhysicalFileProvider(Path.GetTempPath());
// services.AddSingleton<IFileProvider>(physicalProvider);
// // To get files from a path provided by configuration:
string? filepath = null;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
filepath = builder.Configuration.GetValue<string>("StoredFilePathWindows");
}
else {
filepath = builder.Configuration.GetValue<string>("StoredFilePathLinux");
}
if (filepath == null) {
throw new Exception("You need to set a specific Filepath, either StoredFilePathWindows or StoredFilePathLinux");
}
var physicalProvider = new PhysicalFileProvider(filepath);
builder.Services.AddSingleton<IFileProvider>(physicalProvider);
builder.Services.AddSingleton<ILibrary>(HaDocument.Document.Create(new Options()));
builder.Services.AddTransient<IReaderService, ReaderService>();
builder.Services.AddSingleton<IXMLService, XMLService>();

View File

@@ -42,3 +42,5 @@ TODO Kommentare und min-size von ha-lettertetx
TODO Word-wrap before align, tabs
TODO pills are not mobile friendly (hover / click)
TODO Evtl alignment von center / right an der letzten oder nächsten zeile
TODO Abhärten des Konstruktors von XMLRootDokument für von außerhalb platzierte Dokumente
TODO XML-Check im Client

View File

@@ -63,7 +63,7 @@ public static class CSSClasses {
// Zeilen:
public const string ZHLINECLASS = "ha-zhline";
public const string FIRSTLINECLASS ="ha-firstline";
public const string FIRSTLINECLASS = "ha-firstline";
public const string ZHBREAKCLASS = "ha-zhbreak";
public const string LINELINECLASS = "ha-hr";
public const string LINEINDENTCLASS = "ha-indent-"; // TODO: GEN

View File

@@ -1,7 +1,6 @@
namespace HaWeb;
public static class Features
{
public static class Features {
public const string AdminService = "AdminService";
public const string UploadService = "UploadService";
public const string UpdateService = "UpdateService";

View File

@@ -7,8 +7,7 @@ using TextFuncList = List<(Func<HaXMLReader.EvArgs.Text, HaWeb.HTMLParser.XMLHel
using WhitespaceFuncList = List<(Func<HaXMLReader.EvArgs.Whitespace, HaWeb.HTMLParser.XMLHelper<HaWeb.Settings.ParsingState.LetterState>, bool>, Action<System.Text.StringBuilder, HaXMLReader.EvArgs.Whitespace, HaWeb.HTMLParser.XMLHelper<HaWeb.Settings.ParsingState.LetterState>>)>;
// TODO: stringbuilder als Rückgabeparameter des XMHelpers ist eigentlich auch Part vom State
public class LetterRules
{
public class LetterRules {
private static readonly string DEFAULTELEMENT = HaWeb.Settings.HTML.DEFAULTELEMENT;
private static readonly string LEMMACLASS = HaWeb.Settings.CSSClasses.LEMMACLASS;

View File

@@ -6,8 +6,7 @@ using TagFuncList = List<(Func<HaXMLReader.EvArgs.Tag, HaWeb.HTMLParser.XMLHelpe
using TextFuncList = List<(Func<HaXMLReader.EvArgs.Text, HaWeb.HTMLParser.XMLHelper<HaWeb.Settings.ParsingState.TraditionState>, bool>, Action<System.Text.StringBuilder, HaXMLReader.EvArgs.Text, HaWeb.HTMLParser.XMLHelper<HaWeb.Settings.ParsingState.TraditionState>>)>;
using WhitespaceFuncList = List<(Func<HaXMLReader.EvArgs.Whitespace, HaWeb.HTMLParser.XMLHelper<HaWeb.Settings.ParsingState.TraditionState>, bool>, Action<System.Text.StringBuilder, HaXMLReader.EvArgs.Whitespace, HaWeb.HTMLParser.XMLHelper<HaWeb.Settings.ParsingState.TraditionState>>)>;
public static class TraditionRules
{
public static class TraditionRules {
private static readonly string DEFAULTELEMENT = HaWeb.Settings.HTML.DEFAULTELEMENT;
private static readonly string LEMMACLASS = HaWeb.Settings.CSSClasses.LEMMACLASS;

View File

@@ -34,8 +34,7 @@ public class TraditionState : HaWeb.HTMLParser.IState {
internal IReader rd_tradition;
public TraditionState(ILibrary lib, IReader reader, IReaderService readerService, IEnumerable<Marginal>? marginals, IEnumerable<Hand>? hands, IEnumerable<Editreason>? edits)
{
public TraditionState(ILibrary lib, IReader reader, IReaderService readerService, IEnumerable<Marginal>? marginals, IEnumerable<Hand>? hands, IEnumerable<Editreason>? edits) {
Lib = lib;
rd_tradition = reader;
Marginals = marginals;

View File

@@ -5,7 +5,7 @@ using HaWeb.XMLParser;
public class DescriptionsRoot : HaWeb.XMLParser.IXMLRoot {
public string Type { get; } = "Metadaten";
public string Prefix { get; } = "metadaten";
public string[] XPathContainer { get; } = {".//data/descriptions", ".//descriptions" };
public string[] XPathContainer { get; } = { ".//data/descriptions", ".//descriptions" };
public Predicate<XElement> IsCollectedObject { get; } = (elem) => {
if (elem.Name == "letterDesc") return true;

View File

@@ -1,57 +1,79 @@
@model UploadViewModel;
Hello from Upload Index!
<form id="uploadForm" action="Upload" method="post"
enctype="multipart/form-data" onsubmit="AJAXSubmit(this);return false;">
<dl>
<dt>
<label for="file">File</label>
</dt>
<dd>
<input id="file" type="file" name="file" />
</dd>
</dl>
<input class="btn" type="submit" value="Upload" />
<div style="margin-top:15px">
<div class="ha-adminuploadfields">
@foreach (var item in Model.AvailableRoots.OrderBy(x => x.Type)) {
<div class="ha-uploadfield">
<div class="ha-uploadfieldname">@item.Type</div>
@if (Model.UsedFiles != null && Model.UsedFiles.ContainsKey(item.Prefix)) {
@foreach(var file in Model.UsedFiles[item.Prefix]) {
<span>@file.File.Name</span>
}
}
</div>
}
<form class="ha-uploadform" id="uploadForm" action="Upload" method="post" enctype="multipart/form-data">
<label class="filelabel" id="dropzone">
<input class="hidden" id="file" type="file" accept=".xml" name="file" />
Upload2 es
</label>
<div class="ha-uploadmessage">
<output form="uploadForm" name="result"></output>
</div>
</form>
@foreach (var item in Model.AvailableRoots.OrderBy(x => x.Item1))
{
<div>@item.Item1</div>
}
</form>
</div>
@section Scripts {
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery"
crossorigin="anonymous"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
</script>
<script>
"use strict";
async function AJAXSubmit (oFormElement) {
const formData = new FormData(oFormElement);
const dropHandler = function (formelement, ev, dropzone) {
ev.preventDefault();
try {
const response = await fetch(oFormElement.action, {
if (ev.dataTransfer.items) {
if (ev.dataTransfer.items[0].kind === 'file') {
var file = ev.dataTransfer.items[0].getAsFile();
AJAXSubmit(formelement, file);
} else {
var file = ev.dataTransfer.files[0];
AJAXSubmit(formelement, file);
}
}
}
const dragOverHandler = function (ev, dropzone) {
ev.preventDefault();
}
const dragLeaveHander = function (ev, dropzone) {
ev.preventDefault();
}
const dragEnterHandler = function (ev, dropzone) {
ev.preventDefault();
}
const AJAXSubmit = 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";
await fetch(oFormElement.action, {
method: 'POST',
headers: {
'RequestVerificationToken': getCookie('RequestVerificationToken')
},
body: formData
});
oFormElement.elements.namedItem("result").value =
'Result: ' + response.status + ' ' + response.statusText;
} catch (error) {
console.error('Error:', error);
body: fd
})
.then(response => response.json())
.then(json => {
if ("Error" in json) {
oFormElement.elements.namedItem("result").value = json.Error;
} else {
oFormElement.elements.namedItem("result").value = "Erfolg!";
}
})
.catch ((e) => console.error('Error:', error))
}
function getCookie(name) {
@@ -59,5 +81,18 @@ Hello from Upload Index!
var parts = value.split("; " + name + "=");
if (parts.length == 2) return parts.pop().split(";").shift();
}
window.addEventListener("load", function () {
var submitelement = document.getElementById("file");
var formelement = document.getElementById("uploadForm");
var dropzone = document.getElementById("dropzone");
submitelement.addEventListener("change", () => AJAXSubmit(formelement));
dropzone.addEventListener("drop", (ev) => dropHandler(formelement, ev, dropzone));
dropzone.addEventListener("dragover", (ev) => dragOverHandler(ev, dropzone));
dropzone.addEventListener("dragleave", (ev) => dragLeaveHander(ev, dropzone));
dropzone.addEventListener("dragenter", (ev) => dragEnterHandler(ev, dropzone));
});
</script>
}

View File

@@ -7,8 +7,7 @@
if (Model.MinWidthTrad)
minwidthtrads = "ha-minwidth";
}
<div class="ha-twilighttogglebar">
<div class="ha-letterheader">
<div class="ha-letterheader">
@await Html.PartialAsync("/Views/Shared/_LetterHead.cshtml", Model.MetaData)
<div class="ha-letterheadernav">
<div class="ha-lettertabs">
@@ -47,7 +46,6 @@
</div>
}
</div>
</div>
</div>

View File

@@ -3,10 +3,8 @@
<div class="flex grow shrink-0">
<div class="ha-themetoggles">
<input type="radio" id="ha-toggledark" name="ha-themetoggle">
<input type="radio" id="ha-toggletwilight" name="ha-themetoggle">
<input type="radio" id="ha-togglebright" name="ha-themetoggle">
<label for="ha-toggledark"></label>
<label for="ha-toggletwilight"></label>
<label for="ha-togglebright"></label>
<div class="ha-themetoggleslider">
</div>

View File

@@ -25,13 +25,14 @@ public interface IXMLRoot {
var elements = root.XPathSelectElements(p);
if (elements != null && elements.Any()) {
if (ret == null) ret = new List<XElement>();
ret.AddRange(elements);
foreach (var e in elements)
if (!ret.Contains(e)) ret.Add(e);
}
}
return ret;
}
// Generate certain metadat fields to display about this root
// Generate certain metadata fields to display about this root
public abstract List<(string, string?)>? GenerateFields(XMLRootDocument document);
// Generate an identification string of which the hash will be the filename.

View File

@@ -5,5 +5,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
public interface IXMLService {
public IXMLRoot? GetRoot(string name);
public List<IXMLRoot>? GetRoots();
public List<XMLRootDocument>? ProbeHamannFile(XDocument document, ModelStateDictionary ModelState);
public Task<List<XMLRootDocument>?> ProbeHamannFile(XDocument document, ModelStateDictionary ModelState);
public Task UpdateAvailableFiles(XMLRootDocument doc, string basefilepath, ModelStateDictionary ModelState);
public Dictionary<string, List<XMLRootDocument>> GetUsed();
}

View File

@@ -0,0 +1,34 @@
namespace HaWeb.XMLParser;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
public class IdentificationStringJSONConverter : JsonConverter<(string?, string?)>
{
public override (string?, string?) Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) {
var s = reader.GetString();
if (s == null) return (null, null);
var split = s.Split('-');
string? str1 = null;
if (!String.IsNullOrWhiteSpace(split[0])) str1 = split[0];
if (s.Length > 1 && !String.IsNullOrWhiteSpace(split[1])) return (str1, split[1]);
else return (str1, null);
}
public override void Write(
Utf8JsonWriter writer,
(string?, string?) value,
JsonSerializerOptions options)
{
if (value.Item1 == null && value.Item2 == null) return;
var res = "";
if (value.Item1 != null) res += value.Item1;
if (value.Item2 != null) res += "-" + value.Item2;
writer.WriteStringValue(res);
}
}

View File

@@ -2,42 +2,45 @@ namespace HaWeb.XMLParser;
using System.Xml.Linq;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.FileProviders;
public class XMLRootDocument {
private XElement? _Element;
private string? _filename;
private string? _path;
private IXMLService _xmlService;
[JsonIgnore]
public XElement Root {
get {
if (_Element == null) {
_Element = GetElement();
_Element = _GetElement();
}
return _Element;
} }
}
}
public string FileName { get {
public string FileName {
get {
if (_filename == null)
_filename = _CreateFilename();
return _filename;
} }
}
}
[JsonIgnore]
public IFileInfo? File { get; private set; }
public string Prefix { get; private set; }
public DateTime Date { get; private set; }
public bool Used { get; private set; } = false;
public (string?, string?) IdentificationString { get; private set; }
[JsonIgnore]
public List<(string, string)>? Fields { get; set; }
// Entry point for file reading
public XMLRootDocument(IXMLService xmlService, string prefix, (string?, string?) idString, DateTime date, string path) {
public XMLRootDocument(IXMLService xmlService, IFileInfo file) {
_xmlService = xmlService;
_path = path;
Prefix = prefix;
IdentificationString = idString;
Date = date;
SetFile(file);
}
// Entry point for XML upload reading
@@ -49,6 +52,19 @@ public class XMLRootDocument {
_Element = element;
}
public void SetFile(IFileInfo file) {
File = file;
Date = file.LastModified.DateTime;
_GenerateFieldsFromFilename(file.Name);
}
public void SetUsed(bool used) {
Used = used;
if (used && _Element == null) {
_GetElement();
}
}
private string _CreateFilename() {
var filename = _removeInvalidChars(Prefix) + "_";
if (!String.IsNullOrWhiteSpace(IdentificationString.Item1)) {
@@ -69,28 +85,39 @@ public class XMLRootDocument {
return s;
}
private XElement GetElement() {
if (_path == null || String.IsNullOrWhiteSpace(_path))
private void _GenerateFieldsFromFilename(string filename) {
var split = filename.Split('_');
Prefix = split[0];
if (split.Length == 3) {
IdentificationString = (null, split[1]);
} else if (split.Length == 4) {
IdentificationString = (split[1], split[2]);
} else {
IdentificationString = (null, null);
}
}
private XElement _GetElement() {
if (File == null || String.IsNullOrWhiteSpace(File.PhysicalPath) || !File.Exists)
throw new Exception("Es ist kein Pfad für die XML-Datei vorhanden.");
var root = _xmlService.GetRoot(Prefix);
if (root == null)
throw new Exception("Kein gültiges Hamann-Dokument: " + _path + "Vom Prefix: " + Prefix);
throw new Exception("Kein gültiges Hamann-Dokument: " + File.PhysicalPath + "Vom Prefix: " + Prefix);
XDocument? doc = null;
try {
doc = XDocument.Load(_path, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
}
catch (Exception ex) {
doc = XDocument.Load(File.PhysicalPath, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
} catch (Exception ex) {
throw new Exception("Fehler beim Lesen des Dokuments: " + ex.Message);
}
if (doc == null || doc.Root == null)
throw new Exception("Das Dokument ist ungültig und kann nicht gelesen werden: " + _path);
throw new Exception("Das Dokument ist ungültig und kann nicht gelesen werden: " + File.PhysicalPath);
var element = root.IsTypeOf(doc.Root);
if (element == null || !element.Any())
throw new Exception("Kein gültiges Hamann-Dokument: " + _path + "Vom Prefix: " + Prefix);
throw new Exception("Kein gültiges Hamann-Dokument: " + File.PhysicalPath + "Vom Prefix: " + Prefix);
return element.First();
}

View File

@@ -2,17 +2,43 @@ namespace HaWeb.XMLParser;
using HaWeb.Settings.XMLRoots;
using System.Xml.Linq;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.FileProviders;
using Microsoft.FeatureManagement;
public class XMLService : IXMLService {
private Dictionary<string, List<XMLRootDocument>?>? _availableFilesObj;
private Dictionary<string, List<XMLRootDocument>?>? _availableFiles {
get {
if (_availableFilesObj == null) {
_availableFilesObj = _GetAvailableFiles();
AutoDetermineUsed();
}
return _availableFilesObj;
} }
private IFileProvider _fileProvider;
private IFeatureManager _featureManager;
private Dictionary<string, IXMLRoot>? _Roots;
public XMLService() {
public bool UploadEnabled = false;
public bool UpdateEnabled = false;
public XMLService(IFileProvider provider, IFeatureManager featureManager) {
_fileProvider = provider;
_featureManager = featureManager;
if (provider == null)
throw new Exception("To Upload Files you need a FileProvider");
// Getting all classes which implement IXMLRoot for possible upload endpoints
var types = _GetAllTypesThatImplementInterface<IXMLRoot>().ToList();
types.ForEach( x => {
if (this._Roots == null) this._Roots = new Dictionary<string, IXMLRoot>();
var instance = (IXMLRoot)Activator.CreateInstance(x)!;
if (instance != null) this._Roots.Add(instance.Prefix, instance);
});
if (_Roots == null || !_Roots.Any())
throw new Exception("No classes for upload endpoints were found!");
}
public IXMLRoot? GetRoot(string name) {
@@ -22,7 +48,7 @@ public class XMLService : IXMLService {
public List<IXMLRoot>? GetRoots() => this._Roots == null ? null : this._Roots.Values.ToList();
public List<XMLRootDocument>? ProbeHamannFile(XDocument document, ModelStateDictionary ModelState) {
public async Task<List<XMLRootDocument>?> ProbeHamannFile(XDocument document, ModelStateDictionary ModelState) {
if (document.Root!.Name != "opus") {
ModelState.AddModelError("Error", "A valid Hamann-Docuemnt must begin with <opus>");
return null;
@@ -43,6 +69,63 @@ public class XMLService : IXMLService {
return res;
}
public void UpdateAvailableFiles() {
_availableFilesObj = _GetAvailableFiles();
}
public void UpdateAvailableFiles(string prefix) {
if (_availableFilesObj == null) {
UpdateAvailableFiles();
return;
}
if (_availableFilesObj.ContainsKey(prefix))
_availableFilesObj.Remove(prefix);
if (_fileProvider.GetDirectoryContents(prefix).Exists) {
_availableFilesObj.Add(prefix, _GetAvailableFiles(prefix));
}
AutoDetermineUsed(prefix);
}
public async Task UpdateAvailableFiles(XMLRootDocument doc, string basefilepath, ModelStateDictionary ModelState) {
await _setEnabled();
if (!UploadEnabled) {
ModelState.AddModelError("Error", "The uploading of files is deactivated");
return;
}
await _Save(doc, basefilepath, ModelState);
if (!ModelState.IsValid) return;
UpdateAvailableFiles(doc.Prefix);
}
private async Task _Save(XMLRootDocument doc, string basefilepath, ModelStateDictionary ModelState) {
var type = doc.Prefix;
var directory = Path.Combine(basefilepath, type);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
var path = Path.Combine(directory, doc.FileName);
try {
using (var targetStream = System.IO.File.Create(path))
await doc.Save(targetStream, ModelState);
}
catch (Exception ex) {
ModelState.AddModelError("Error", "Speichern der Datei fehlgeschlagen: " + ex.Message);
return;
}
var info = _fileProvider.GetFileInfo(Path.Combine(doc.Prefix, doc.FileName));
if (info == null) {
ModelState.AddModelError("Error", "Auf die neu erstellte Dtaei konnte nicht zugegriffen werden");
return;
}
doc.SetFile(info);
UpdateAvailableFiles(type);
}
private XMLRootDocument _createXMLRootDocument(IXMLRoot Root, XElement element) {
var doc = new XMLRootDocument(this, Root.Prefix, Root.GenerateIdentificationString(element), element);
doc.Fields = Root.GenerateFields(doc);
@@ -56,4 +139,63 @@ public class XMLService : IXMLService {
.Where(type => typeof(T).IsAssignableFrom(type) && !type.IsInterface);
}
private async Task _setEnabled() {
if (await _featureManager.IsEnabledAsync(HaWeb.Features.UploadService))
UploadEnabled = true;
if (await _featureManager.IsEnabledAsync(HaWeb.Features.UpdateService))
UpdateEnabled = true;
}
private Dictionary<string, List<XMLRootDocument>?>? _GetAvailableFiles() {
if (_Roots == null) return null;
Dictionary<string, List<XMLRootDocument>?>? res = null;
var dirs = _fileProvider.GetDirectoryContents(string.Empty).Where(x => x.IsDirectory);
foreach(var dir in dirs) {
if(_Roots.ContainsKey(dir.Name)) {
if (res == null) res = new Dictionary<string, List<XMLRootDocument>?>();
res.Add(dir.Name, _GetAvailableFiles(dir.Name));
}
}
return res;
}
private List<XMLRootDocument>? _GetAvailableFiles(string prefix) {
List<XMLRootDocument>? res = null;
var files = _fileProvider.GetDirectoryContents(prefix).Where(x => !x.IsDirectory && x.Name.StartsWith(prefix) && x.Name.EndsWith(".xml"));
foreach (var file in files) {
if (res == null) res = new List<XMLRootDocument>();
res.Add(new XMLRootDocument(this, file));
}
return res;
}
public void AutoDetermineUsed() {
if (_availableFilesObj == null) return;
foreach (var (prefix, _) in _availableFilesObj)
AutoDetermineUsed(prefix);
}
public void AutoDetermineUsed(string prefix) {
if (_Roots == null || _availableFilesObj == null) return;
_Roots.TryGetValue(prefix, out var root);
_availableFilesObj.TryGetValue(prefix, out var files);
if (files == null || root == null) return;
//TODO: Item1
var lookup = files.ToLookup(x => x.IdentificationString.Item2);
foreach (var idstring in lookup) {
var ordered = idstring.OrderBy(x => x.Date);
ordered.Last().SetUsed(true);
ordered.Take(ordered.Count() - 1).ToList().ForEach(x => x.SetUsed(false));
}
}
public Dictionary<string, List<XMLRootDocument>>? GetUsed() {
if (_availableFiles == null) return null;
return _availableFiles
.Where(x => x.Value != null)
.Select(x => x.Value!.Where(x => x.Used).ToList())
.Where(x => x.Any())
.ToDictionary(x => x.First().Prefix);
}
}

16
HaWeb/omnisharp.json Normal file
View File

@@ -0,0 +1,16 @@
{
"FormattingOptions": {
"NewLinesForBracesInLambdaExpressionBody": false,
"NewLinesForBracesInAnonymousMethods": false,
"NewLinesForBracesInAnonymousTypes": false,
"NewLinesForBracesInControlBlocks": false,
"NewLinesForBracesInTypes": false,
"NewLinesForBracesInMethods": false,
"NewLinesForBracesInProperties": false,
"NewLinesForBracesInObjectCollectionArrayInitializers": false,
"NewLinesForBracesInAccessors": false,
"NewLineForElse": false,
"NewLineForCatch": false,
"NewLineForFinally": false
}
}

View File

@@ -468,6 +468,14 @@ Ensure the default browser behavior of the `hidden` attribute.
--tw-backdrop-sepia: ;
}
* {
transition-property: color, background-color, border-color, fill, stroke, -webkit-text-decoration-color;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, -webkit-text-decoration-color;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 100ms;
}
/* TODO: check what can be inlined (eg. used once in the code, has no double paths etc...) */
/* Everything related to theme color */
@@ -570,21 +578,36 @@ body {
--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
transition-property: color, background-color, border-color, fill, stroke, -webkit-text-decoration-color;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, -webkit-text-decoration-color;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 300ms;
}
.ha-footer .ha-themetoggles #ha-toggledark:checked ~ .ha-themetoggleslider {
.dark .ha-footer .ha-themetoggles {
--tw-bg-opacity: 1;
background-color: rgb(30 41 59 / var(--tw-bg-opacity));
}
.ha-footer .ha-themetoggles #ha-toggletwilight:checked ~ .ha-themetoggleslider {
.ha-footer .ha-themetoggles #ha-toggledark:checked ~ .ha-themetoggleslider {
--tw-bg-opacity: 1;
background-color: rgb(100 116 139 / var(--tw-bg-opacity));
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
transition-property: color, background-color, border-color, fill, stroke, -webkit-text-decoration-color;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, -webkit-text-decoration-color;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 300ms;
}
.ha-footer .ha-themetoggles #ha-togglebright:checked ~ .ha-themetoggleslider {
--tw-bg-opacity: 1;
background-color: rgb(248 250 252 / var(--tw-bg-opacity));
transition-property: color, background-color, border-color, fill, stroke, -webkit-text-decoration-color;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, -webkit-text-decoration-color;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 300ms;
}
.ha-static {
@@ -1013,6 +1036,8 @@ body {
}
.ha-tradzhtext .ha-marginalbox.ha-expanded-box .ha-marginallist, .ha-lettertext .ha-marginalbox.ha-expanded-box .ha-marginallist {
--tw-bg-opacity: 1;
background-color: rgb(241 245 249 / var(--tw-bg-opacity));
padding-bottom: 0.25rem;
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
@@ -1020,6 +1045,8 @@ body {
}
.dark .ha-tradzhtext .ha-marginalbox.ha-expanded-box .ha-marginallist, .dark .ha-lettertext .ha-marginalbox.ha-expanded-box .ha-marginallist {
--tw-bg-opacity: 1;
background-color: rgb(71 85 105 / var(--tw-bg-opacity));
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
@@ -1115,6 +1142,8 @@ body {
color: rgb(31 41 55 / var(--tw-text-opacity)) !important;
}
/* Classes */
body {
height: 100%;
width: 100%;
@@ -1258,7 +1287,7 @@ body {
.ha-footer .ha-themetoggles {
position: relative;
height: 1rem;
width: 49px;
width: 34px;
white-space: nowrap;
border-radius: 1.5rem;
padding-left: 0.125rem;
@@ -1305,12 +1334,8 @@ body {
left: 0.25rem;
}
.ha-footer .ha-themetoggles #ha-toggletwilight:checked ~ .ha-themetoggleslider {
left: 19px;
}
.ha-footer .ha-themetoggles #ha-togglebright:checked ~ .ha-themetoggleslider {
left: 34px;
left: 19px;
}
/* Classes for static pages */
@@ -2254,6 +2279,7 @@ body {
flex-wrap: wrap;
-moz-column-gap: 1.5rem;
column-gap: 1.5rem;
padding-right: 0.25rem;
font-size: 0.875rem;
line-height: 1.25rem;
line-height: 1.25;
@@ -2791,6 +2817,10 @@ body {
width: 2rem;
}
.max-w-\[25\%\] {
max-width: 25%;
}
.shrink-0 {
flex-shrink: 0;
}
@@ -2827,6 +2857,10 @@ body {
flex-wrap: wrap;
}
.gap-5 {
gap: 1.25rem;
}
.overflow-hidden {
overflow: hidden;
}

View File

@@ -80,6 +80,9 @@
}
@layer components {
* {
@apply transition-colors duration-100
}
/* TODO: check what can be inlined (eg. used once in the code, has no double paths etc...) */
/* Everything related to theme color */
@@ -118,19 +121,15 @@
}
.ha-footer .ha-themetoggles {
@apply bg-slate-200 shadow-inner
@apply bg-slate-200 dark:bg-slate-800 transition-colors duration-300 shadow-inner
}
.ha-footer .ha-themetoggles #ha-toggledark:checked ~ .ha-themetoggleslider {
@apply bg-slate-800
}
.ha-footer .ha-themetoggles #ha-toggletwilight:checked ~ .ha-themetoggleslider {
@apply bg-slate-500
@apply bg-slate-200 transition-colors duration-300
}
.ha-footer .ha-themetoggles #ha-togglebright:checked ~ .ha-themetoggleslider {
@apply bg-slate-50
@apply bg-slate-50 transition-colors duration-300
}
.ha-static {
@@ -278,7 +277,7 @@
.ha-tradzhtext .ha-marginalbox.ha-expanded-box .ha-marginallist,
.ha-lettertext .ha-marginalbox.ha-expanded-box .ha-marginallist {
@apply shadow-md dark:shadow-lg pb-1
@apply shadow-md dark:shadow-lg pb-1 bg-slate-100 dark:bg-slate-600
}
.ha-tradzhtext .ha-btn-collapsed-box,
@@ -322,7 +321,7 @@
@apply !text-hamannHighlight dark:!text-gray-800
}
/* Classes */
body {
@apply text-base desktop:text-lg w-full h-full;
}
@@ -373,7 +372,7 @@
}
.ha-footer .ha-themetoggles {
@apply whitespace-nowrap relative px-0.5 rounded-3xl h-4 w-[49px]
@apply whitespace-nowrap relative px-0.5 rounded-3xl h-4 w-[34px]
}
.ha-footer .ha-themetoggles * {
@@ -396,12 +395,8 @@
@apply left-1
}
.ha-footer .ha-themetoggles #ha-toggletwilight:checked ~ .ha-themetoggleslider {
@apply left-[19px]
}
.ha-footer .ha-themetoggles #ha-togglebright:checked ~ .ha-themetoggleslider {
@apply left-[34px]
@apply left-[19px]
}
/* Classes for static pages */
@@ -898,7 +893,7 @@
.ha-tradzhtext .ha-marginalbox .ha-marginallist,
.ha-lettertext .ha-marginalbox .ha-marginallist {
@apply text-sm leading-tight flex flex-wrap gap-x-6
@apply text-sm leading-tight flex flex-wrap gap-x-6 pr-1
}
.ha-tradzhtext .ha-marginalbox .ha-marginallist .ha-marginal,

View File

@@ -221,21 +221,8 @@ const go_to_dark = function () {
document.documentElement.classList.add("dark");
};
const go_to_twilight = function () {
document.documentElement.classList.remove("dark");
let elements = document.getElementsByClassName("ha-twilighttogglebar");
for (let el of elements) {
el.classList.add("dark");
}
localStorage.setItem("theme", "ha-toggletwilight");
};
const go_to_bright = function () {
document.documentElement.classList.remove("dark");
let elements = document.getElementsByClassName("ha-twilighttogglebar");
for (let el of elements) {
el.classList.remove("dark");
}
localStorage.setItem("theme", "ha-togglebright");
};
@@ -335,7 +322,6 @@ window.addEventListener("load", function () {
// Register theme toggler
if (
document.getElementById("ha-togglebright") !== null &&
document.getElementById("ha-toggletwilight") !== null &&
this.document.getElementById("ha-toggledark") !== null
) {
document
@@ -344,9 +330,6 @@ window.addEventListener("load", function () {
document
.getElementById("ha-toggledark")
.addEventListener("click", go_to_dark);
document
.getElementById("ha-toggletwilight")
.addEventListener("click", go_to_twilight);
}
get_theme_settings("ha-togglebright");
});

View File

@@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<opus><definitions></definitions></opus>
<opus></opus>