diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Auth.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Auth.cs new file mode 100644 index 00000000..f08b6c95 --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Auth.cs @@ -0,0 +1,14 @@ +namespace EnvelopeGenerator.API.Models; + +public record Auth(string? AccessCode = null, string? SmsCode = null, string? AuthenticatorCode = null, bool UserSelectSMS = default) +{ + public bool HasAccessCode => AccessCode is not null; + + public bool HasSmsCode => SmsCode is not null; + + public bool HasAuthenticatorCode => AuthenticatorCode is not null; + + public bool HasMulti => new[] { HasAccessCode, HasSmsCode, HasAuthenticatorCode }.Count(state => state) > 1; + + public bool HasNone => !(HasAccessCode || HasSmsCode || HasAuthenticatorCode); +} \ No newline at end of file diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/ContactLink.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/ContactLink.cs new file mode 100644 index 00000000..691dbc83 --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/ContactLink.cs @@ -0,0 +1,60 @@ +namespace EnvelopeGenerator.API.Models +{ + /// + /// Represents a hyperlink for contact purposes with various HTML attributes. + /// + public class ContactLink + { + /// + /// Gets or sets the label of the hyperlink. + /// + public string Label { get; init; } = "Contact"; + + /// + /// Gets or sets the URL that the hyperlink points to. + /// + public string Href { get; set; } = string.Empty; + + /// + /// Gets or sets the target where the hyperlink should open. + /// Commonly used values are "_blank", "_self", "_parent", "_top". + /// + public string Target { get; set; } = "_blank"; + + /// + /// Gets or sets the relationship of the linked URL as space-separated link types. + /// Examples include "nofollow", "noopener", "noreferrer". + /// + public string Rel { get; set; } = string.Empty; + + /// + /// Gets or sets the filename that should be downloaded when clicking the hyperlink. + /// This attribute will only have an effect if the href attribute is set. + /// + public string Download { get; set; } = string.Empty; + + /// + /// Gets or sets the language of the linked resource. Useful when linking to + /// content in another language. + /// + public string HrefLang { get; set; } = "en"; + + /// + /// Gets or sets the MIME type of the linked URL. Helps browsers to handle + /// the type correctly when the link is clicked. + /// + public string Type { get; set; } = string.Empty; + + /// + /// Gets or sets additional information about the hyperlink, typically viewed + /// as a tooltip when the mouse hovers over the link. + /// + public string Title { get; set; } = string.Empty; + + /// + /// Gets or sets an identifier for the hyperlink, unique within the HTML document. + /// + public string Id { get; set; } = string.Empty; + } + +} diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Culture.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Culture.cs new file mode 100644 index 00000000..b1386088 --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Culture.cs @@ -0,0 +1,17 @@ +using System.Globalization; + +namespace EnvelopeGenerator.API.Models; + +public class Culture +{ + private string _language = string.Empty; + public string Language { get => _language; + init { + _language = value; + Info = new(value); + } + } + public string FIClass { get; init; } = string.Empty; + + public CultureInfo? Info { get; init; } +} diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Cultures.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Cultures.cs new file mode 100644 index 00000000..7b293c13 --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Cultures.cs @@ -0,0 +1,12 @@ +namespace EnvelopeGenerator.API.Models; + +public class Cultures : List +{ + public IEnumerable Languages => this.Select(c => c.Language); + + public IEnumerable FIClasses => this.Select(c => c.FIClass); + + public Culture Default => this.First(); + + public Culture? this[string? language] => language is null ? null : this.Where(c => c.Language == language).FirstOrDefault(); +} \ No newline at end of file diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/CustomImages.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/CustomImages.cs new file mode 100644 index 00000000..fd212acd --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/CustomImages.cs @@ -0,0 +1,6 @@ +namespace EnvelopeGenerator.API.Models; + +public class CustomImages : Dictionary +{ + public new Image this[string key] => TryGetValue(key, out var img) && img is not null ? img : new(); +} \ No newline at end of file diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/EnvelopeReceiverLogin.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/EnvelopeReceiverLogin.cs new file mode 100644 index 00000000..fa53ec61 --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/EnvelopeReceiverLogin.cs @@ -0,0 +1,7 @@ +namespace EnvelopeGenerator.API.Models; + +/// +/// Request body for the envelope-receiver login endpoint. +/// +/// The access code sent to the receiver. +public record EnvelopeReceiverLogin(string? AccessCode = null); diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/ErrorViewModel.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/ErrorViewModel.cs new file mode 100644 index 00000000..b09ffded --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/ErrorViewModel.cs @@ -0,0 +1,10 @@ +namespace EnvelopeGenerator.API.Models; + +public class ErrorViewModel +{ + public string Title { get; init; } = "404"; + + public string Subtitle { get; init; } = "Hmmm..."; + + public string Body { get; init; } = "It looks like one of the developers fell asleep"; +} \ No newline at end of file diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Image.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Image.cs new file mode 100644 index 00000000..d7abd148 --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Image.cs @@ -0,0 +1,10 @@ +namespace EnvelopeGenerator.API.Models; + +public class Image +{ + public string Src { get; init; } = string.Empty; + + public Dictionary Classes { get; init; } = new(); + + public string GetClassIn(string page) => Classes.TryGetValue(page, out var cls) && cls is not null ? cls : string.Empty; +} \ No newline at end of file diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Login.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Login.cs new file mode 100644 index 00000000..926b1811 --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/Login.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace EnvelopeGenerator.API.Models; + +/// +/// Repräsentiert ein Login-Modell mit erforderlichem Passwort und optionaler ID und Benutzername. +/// +/// Das erforderliche Passwort für das Login. +/// Die optionale ID des Benutzers. +/// Der optionale Benutzername. +public record Login([Required] string Password, int? UserId = null, string? Username = null) +{ +} diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/MainViewModel.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/MainViewModel.cs new file mode 100644 index 00000000..d49d323b --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/MainViewModel.cs @@ -0,0 +1,6 @@ +namespace EnvelopeGenerator.API.Models; + +public class MainViewModel +{ + public string? Title { get; init; } +} diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/Annotation.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/Annotation.cs new file mode 100644 index 00000000..5dbb1aed --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/Annotation.cs @@ -0,0 +1,93 @@ +using EnvelopeGenerator.API.Models.PsPdfKitAnnotation; +using System.Text.Json.Serialization; + +namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation; + +public record Annotation : IAnnotation +{ + public required string Name { get; init; } + + #region Bound Annotation + [JsonIgnore] + public string? HorBoundAnnotName { get; init; } + + [JsonIgnore] + public string? VerBoundAnnotName { get; init; } + #endregion + + #region Layout + [JsonIgnore] + public double? MarginLeft { get; set; } + + [JsonIgnore] + public double MarginLeftRatio { get; init; } = 1; + + [JsonIgnore] + public double? MarginTop { get; set; } + + [JsonIgnore] + public double MarginTopRatio { get; init; } = 1; + + public double? Width { get; set; } + + [JsonIgnore] + public double WidthRatio { get; init; } = 1; + + public double? Height { get; set; } + + [JsonIgnore] + public double HeightRatio { get; init; } = 1; + #endregion + + #region Position + public double Left => (MarginLeft ?? 0) + (HorBoundAnnot?.HorBoundary ?? 0); + + public double Top => (MarginTop ?? 0) + (VerBoundAnnot?.VerBoundary ?? 0); + #endregion + + #region Boundary + [JsonIgnore] + public double HorBoundary => Left + (Width ?? 0); + + [JsonIgnore] + public double VerBoundary => Top + (Height ?? 0); + #endregion + + #region BoundAnnot + [JsonIgnore] + public Annotation? HorBoundAnnot { get; set; } + + [JsonIgnore] + public Annotation? VerBoundAnnot { get; set; } + #endregion + + public Color? BackgroundColor { get; init; } + + #region Border + public Color? BorderColor { get; init; } + + public string? BorderStyle { get; init; } + + public int? BorderWidth { get; set; } + #endregion + + [JsonIgnore] + internal Annotation Default + { + set + { + // To set null value, annotation must have null (0) value but null must has non-null value + if (MarginLeft == null && value.MarginLeft != null) + MarginLeft = value.MarginLeft * MarginLeftRatio; + + if (MarginTop == null && value.MarginTop != null) + MarginTop = value.MarginTop * MarginTopRatio; + + if (Width == null && value.Width != null) + Width = value.Width * WidthRatio; + + if (Height == null && value.Height != null) + Height = value.Height * HeightRatio; + } + } +}; \ No newline at end of file diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/AnnotationParams.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/AnnotationParams.cs new file mode 100644 index 00000000..019d872e --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/AnnotationParams.cs @@ -0,0 +1,80 @@ +using EnvelopeGenerator.API.Models.PsPdfKitAnnotation; +using System.Text.Json.Serialization; + +namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation; + +public class AnnotationParams +{ + public AnnotationParams() + { + _AnnotationJSObjectInitor = new(CreateAnnotationJSObject); + } + + public Background? Background { get; init; } + + #region Annotation + [JsonIgnore] + public Annotation? DefaultAnnotation { get; init; } + + private readonly List _annots = new List(); + + public bool TryGet(string name, out Annotation annotation) + { +#pragma warning disable CS8601 // Possible null reference assignment. + annotation = _annots.FirstOrDefault(a => a.Name == name); +#pragma warning restore CS8601 // Possible null reference assignment. + return annotation is not null; + } + + public required IEnumerable Annotations + { + get => _annots; + init + { + _annots = value.ToList(); + + if (DefaultAnnotation is not null) + foreach (var annot in _annots) + annot.Default = DefaultAnnotation; + + for (int i = 0; i < _annots.Count; i++) + { + #region set bound annotations + // horizontal + if (_annots[i].HorBoundAnnotName is string horBoundAnnotName) + if (TryGet(horBoundAnnotName, out var horBoundAnnot)) + _annots[i].HorBoundAnnot = horBoundAnnot; + else + throw new InvalidOperationException($"{horBoundAnnotName} added as bound anotation. However, it is not defined."); + + // vertical + if (_annots[i].VerBoundAnnotName is string verBoundAnnotName) + if (TryGet(verBoundAnnotName, out var verBoundAnnot)) + _annots[i].VerBoundAnnot = verBoundAnnot; + else + throw new InvalidOperationException($"{verBoundAnnotName} added as bound anotation. However, it is not defined."); + #endregion + } + } + } + #endregion + + #region AnnotationJSObject + private Dictionary CreateAnnotationJSObject() + { + var dict = _annots.ToDictionary(a => a.Name.ToLower(), a => a as IAnnotation); + + if (Background is not null) + { + Background.Locate(_annots); + dict.Add(Background.Name.ToLower(), Background); + } + + return dict; + } + + private readonly Lazy> _AnnotationJSObjectInitor; + + public Dictionary AnnotationJSObject => _AnnotationJSObjectInitor.Value; + #endregion +} diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/Background.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/Background.cs new file mode 100644 index 00000000..19298196 --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/Background.cs @@ -0,0 +1,58 @@ +using System.Text.Json.Serialization; + +namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation; + +/// +/// The Background is an annotation for the PSPDF Kit. However, it has no function. +/// It is only the first annotation as a background for other annotations. +/// +public record Background : IAnnotation +{ + [JsonIgnore] + public double Margin { get; init; } + + public string Name { get; } = "Background"; + + public double? Width { get; set; } + + public double? Height { get; set; } + + public double Left { get; set; } + + public double Top { get; set; } + + public Color? BackgroundColor { get; init; } + + #region Border + public Color? BorderColor { get; init; } + + public string? BorderStyle { get; init; } + + public int? BorderWidth { get; set; } + #endregion + + public void Locate(IEnumerable annotations) + { + // set Top + if (annotations.MinBy(a => a.Top)?.Top is double minTop) + Top = minTop; + + // set Left + if (annotations.MinBy(a => a.Left)?.Left is double minLeft) + Left = minLeft; + + // set Width + if(annotations.MaxBy(a => a.GetRight())?.GetRight() is double maxRight) + Width = maxRight - Left; + + // set Height + if (annotations.MaxBy(a => a.GetBottom())?.GetBottom() is double maxBottom) + Height = maxBottom - Top; + + // add margins + Top -= Margin; + Left -= Margin; + Width += Margin * 2; + Height += Margin * 2; + } +} diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/Color.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/Color.cs new file mode 100644 index 00000000..0d3d4056 --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/Color.cs @@ -0,0 +1,10 @@ +namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation; + +public record Color +{ + public int R { get; init; } = 0; + + public int G { get; init; } = 0; + + public int B { get; init; } = 0; +} diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/Extensions.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/Extensions.cs new file mode 100644 index 00000000..1cc508fd --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/Extensions.cs @@ -0,0 +1,8 @@ +namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation; + +public static class Extensions +{ + public static double GetRight(this IAnnotation annotation) => annotation.Left + annotation?.Width ?? 0; + + public static double GetBottom(this IAnnotation annotation) => annotation.Top + annotation?.Height ?? 0; +} diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/IAnnotation.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/IAnnotation.cs new file mode 100644 index 00000000..735bf573 --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/PsPdfKitAnnotation/IAnnotation.cs @@ -0,0 +1,22 @@ +namespace EnvelopeGenerator.API.Models.PsPdfKitAnnotation; + +public interface IAnnotation +{ + string Name { get; } + + double? Width { get; } + + double? Height { get; } + + double Left { get; } + + double Top { get; } + + Color? BackgroundColor { get; } + + Color? BorderColor { get; } + + string? BorderStyle { get; } + + int? BorderWidth { get; } +} diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/TFARegParams.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/TFARegParams.cs new file mode 100644 index 00000000..ff363fdf --- /dev/null +++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/TFARegParams.cs @@ -0,0 +1,17 @@ +namespace EnvelopeGenerator.API.Models; + +/// +/// Represents the parameters for two-factor authentication (2FA) registration. +/// +public class TFARegParams +{ + /// + /// The maximum allowed time for completing the registration process. + /// + public TimeSpan TimeLimit { get; init; } = new(0, 30, 0); + + /// + /// The deadline for registration, calculated as the current time plus the . + /// + public DateTime Deadline => DateTime.Now.AddTicks(TimeLimit.Ticks); +} \ No newline at end of file