diff --git a/EnvelopeGenerator.ServiceHost/EnvelopeGenerator.ServiceHost.csproj b/EnvelopeGenerator.ServiceHost/EnvelopeGenerator.ServiceHost.csproj index b16aff60..f861646b 100644 --- a/EnvelopeGenerator.ServiceHost/EnvelopeGenerator.ServiceHost.csproj +++ b/EnvelopeGenerator.ServiceHost/EnvelopeGenerator.ServiceHost.csproj @@ -8,6 +8,21 @@ + + + + + + + + + + + + + + + diff --git a/EnvelopeGenerator.ServiceHost/Jobs/ActionService.cs b/EnvelopeGenerator.ServiceHost/Jobs/ActionService.cs new file mode 100644 index 00000000..1c5e4a89 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/ActionService.cs @@ -0,0 +1,31 @@ +using DigitalData.Modules.Database; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.ServiceHost.Jobs; + +public class ActionService : BaseService +{ + public ActionService(State state, MSSQLServer database) : base(state) + { + } + + public bool CreateReport(Envelope envelope) + { + return true; + } + + public bool FinalizeEnvelope(Envelope envelope) + { + return true; + } + + public bool CompleteEnvelope(Envelope envelope) + { + return true; + } + + public bool CompleteEnvelope(Envelope envelope, Receiver receiver) + { + return true; + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/BaseModel.cs b/EnvelopeGenerator.ServiceHost/Jobs/BaseModel.cs new file mode 100644 index 00000000..eaed1e1e --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/BaseModel.cs @@ -0,0 +1,18 @@ +using DigitalData.Modules.Database; +using DigitalData.Modules.Logging; + +namespace EnvelopeGenerator.ServiceHost.Jobs; + +public abstract class BaseModel +{ + protected MSSQLServer Database { get; } + protected Logger Logger { get; } + protected State State { get; } + + protected BaseModel(State state) + { + Logger = state.LogConfig!.GetLogger(); + Database = state.Database!; + State = state; + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/BaseService.cs b/EnvelopeGenerator.ServiceHost/Jobs/BaseService.cs new file mode 100644 index 00000000..481b6e4e --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/BaseService.cs @@ -0,0 +1,13 @@ +using DigitalData.Modules.Base; + +namespace EnvelopeGenerator.ServiceHost.Jobs; + +public class BaseService : BaseClass +{ + protected State State { get; } + + public BaseService(State state) : base(state.LogConfig!) + { + State = state; + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/ConfigModel.cs b/EnvelopeGenerator.ServiceHost/Jobs/ConfigModel.cs new file mode 100644 index 00000000..d0cc76a4 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/ConfigModel.cs @@ -0,0 +1,37 @@ +using DigitalData.Modules.Base; + +namespace EnvelopeGenerator.ServiceHost.Jobs; + +public class ConfigModel : BaseModel +{ + public ConfigModel(State state) : base(state) + { + } + + public DbConfig LoadConfiguration() + { + try + { + const string sql = "SELECT TOP 1 * FROM TBSIG_CONFIG"; + var table = Database.GetDatatable(sql); + var row = table.Rows[0]; + + return new DbConfig + { + DocumentPath = row.ItemEx("DOCUMENT_PATH", string.Empty), + DocumentPathOrigin = row.ItemEx("DOCUMENT_PATH", string.Empty), + ExportPath = row.ItemEx("EXPORT_PATH", string.Empty), + SendingProfile = row.ItemEx("SENDING_PROFILE", 0), + SignatureHost = row.ItemEx("SIGNATURE_HOST", string.Empty), + ExternalProgramName = row.ItemEx("EXTERNAL_PROGRAM_NAME", string.Empty), + Default_Tfa_Enabled = row.ItemEx("DEF_TFA_ENABLED", false), + Default_Tfa_WithPhone = row.ItemEx("DEF_TFA_WITH_PHONE", false) + }; + } + catch (Exception ex) + { + Logger.Error(ex); + return new DbConfig(); + } + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/DbConfig.cs b/EnvelopeGenerator.ServiceHost/Jobs/DbConfig.cs new file mode 100644 index 00000000..823a23e1 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/DbConfig.cs @@ -0,0 +1,13 @@ +namespace EnvelopeGenerator.ServiceHost.Jobs; + +public class DbConfig +{ + public string ExternalProgramName { get; set; } = "signFLOW"; + public string DocumentPathOrigin { get; set; } = string.Empty; + public string DocumentPath { get; set; } = string.Empty; + public string ExportPath { get; set; } = string.Empty; + public int SendingProfile { get; set; } + public string SignatureHost { get; set; } = string.Empty; + public bool Default_Tfa_Enabled { get; set; } + public bool Default_Tfa_WithPhone { get; set; } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/EnvelopeModel.cs b/EnvelopeGenerator.ServiceHost/Jobs/EnvelopeModel.cs new file mode 100644 index 00000000..7c2180a1 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/EnvelopeModel.cs @@ -0,0 +1,42 @@ +using DigitalData.Modules.Base; +using EnvelopeGenerator.Domain.Constants; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.ServiceHost.Jobs; + +public class EnvelopeModel : BaseModel +{ + public EnvelopeModel(State state) : base(state) + { + } + + public Envelope? GetById(int envelopeId) + { + try + { + var sql = $"SELECT * FROM [dbo].[TBSIG_ENVELOPE] WHERE GUID = {envelopeId}"; + var table = Database.GetDatatable(sql); + var row = table.Rows.Cast().SingleOrDefault(); + if (row is null) + { + return null; + } + + return new Envelope + { + Id = row.ItemEx("GUID", 0), + Uuid = row.ItemEx("ENVELOPE_UUID", string.Empty), + FinalEmailToCreator = row.ItemEx("FINAL_EMAIL_TO_CREATOR", 0), + FinalEmailToReceivers = row.ItemEx("FINAL_EMAIL_TO_RECEIVERS", 0), + UserId = row.ItemEx("USER_ID", 0), + User = null!, + EnvelopeReceivers = new List() + }; + } + catch (Exception ex) + { + Logger.Error(ex); + return null; + } + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/PDFBurner.cs b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/PDFBurner.cs new file mode 100644 index 00000000..8a9ac595 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/PDFBurner.cs @@ -0,0 +1,445 @@ +using System.Collections.Immutable; +using System.Drawing; +using System.IO; +using DigitalData.Modules.Base; +using DigitalData.Modules.Logging; +using EnvelopeGenerator.Domain.Entities; +using EnvelopeGenerator.Infrastructure; +using EnvelopeGenerator.PdfEditor; +using EnvelopeGenerator.ServiceHost.Exceptions; +using GdPicture14; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; + +namespace EnvelopeGenerator.ServiceHost.Jobs.FinalizeDocument; + +public class PDFBurner : BaseClass +{ + private readonly AnnotationManager _manager; + private readonly LicenseManager _licenseManager; + private readonly DbContextOptions _dbContextOptions; + + private readonly PDFBurnerParams _pdfBurnerParams; + + public PDFBurner(LogConfig logConfig, string gdPictureLicenseKey, PDFBurnerParams pdfBurnerParams, string connectionString) : base(logConfig) + { + _licenseManager = new LicenseManager(); + _licenseManager.RegisterKEY(gdPictureLicenseKey); + + _manager = new AnnotationManager(); + + _pdfBurnerParams = pdfBurnerParams; + _dbContextOptions = new DbContextOptionsBuilder() + .UseSqlServer(connectionString) + .EnableDetailedErrors() + .EnableSensitiveDataLogging() + .Options; + } + + public byte[] BurnAnnotsToPDF(byte[] sourceBuffer, List instantJsonList, int envelopeId) + { + using var context = new EGDbContext(_dbContextOptions, Options.Create(new DbTriggerParams())); + + var envelope = context.Envelopes.FirstOrDefault(env => env.Id == envelopeId); + if (envelope is null) + { + throw new BurnAnnotationException($"Envelope with Id {envelopeId} not found."); + } + + if (envelope.ReadOnly) + { + return sourceBuffer; + } + + var elements = context.DocumentReceiverElements + .Where(sig => sig.Document.EnvelopeId == envelopeId) + .Include(sig => sig.Annotations) + .ToList(); + + return elements.Any() + ? BurnElementAnnotsToPDF(sourceBuffer, elements) + : BurnInstantJSONAnnotsToPDF(sourceBuffer, instantJsonList); + } + + public byte[] BurnElementAnnotsToPDF(byte[] sourceBuffer, List elements) + { + using (var doc = Pdf.FromMemory(sourceBuffer)) + { + sourceBuffer = doc.Background(elements, 1.9500000000000002 * 0.93, 2.52 * 0.67).ExportStream().ToArray(); + + using var sourceStream = new MemoryStream(sourceBuffer); + var result = _manager.InitFromStream(sourceStream); + if (result != GdPictureStatus.OK) + { + throw new BurnAnnotationException($"Could not open document for burning: [{result}]"); + } + + var margin = 0.2; + var inchFactor = 72d; + + var keys = new[] { "position", "city", "date" }; + var unitYOffsets = 0.2; + var yOffsetsOfFF = keys + .Select((k, i) => new { Key = k, Value = unitYOffsets * i + 1 }) + .ToDictionary(x => x.Key, x => x.Value); + + foreach (var element in elements) + { + var frameX = element.Left - 0.7 - margin; + + var frame = element.Annotations.FirstOrDefault(a => a.Name == "frame"); + var frameY = element.Top - 0.5 - margin; + var frameYShift = (frame?.Y ?? 0) - frameY * inchFactor; + var frameXShift = (frame?.X ?? 0) - frameX * inchFactor; + + foreach (var annot in element.Annotations) + { + var yOffsetOfFF = yOffsetsOfFF.TryGetValue(annot.Name, out var offset) ? offset : 0; + var y = frameY + yOffsetOfFF; + + if (annot.Type == AnnotationType.FormField) + { + AddFormFieldValue(annot.X / inchFactor, y, annot.Width / inchFactor, annot.Height / inchFactor, element.Page, annot.Value); + } + else if (annot.Type == AnnotationType.Image) + { + AddImageAnnotation( + annot.X / inchFactor, + annot.Name == "signature" ? (annot.Y - frameYShift) / inchFactor : y, + annot.Width / inchFactor, + annot.Height / inchFactor, + element.Page, + annot.Value); + } + else if (annot.Type == AnnotationType.Ink) + { + AddInkAnnotation(element.Page, annot.Value); + } + } + } + + using var newStream = new MemoryStream(); + result = _manager.SaveDocumentToPDF(newStream); + if (result != GdPictureStatus.OK) + { + throw new BurnAnnotationException($"Could not save document to stream: [{result}]"); + } + + _manager.Close(); + + return newStream.ToArray(); + } + } + + public byte[] BurnInstantJSONAnnotsToPDF(byte[] sourceBuffer, List instantJsonList) + { + using var sourceStream = new MemoryStream(sourceBuffer); + var result = _manager.InitFromStream(sourceStream); + if (result != GdPictureStatus.OK) + { + throw new BurnAnnotationException($"Could not open document for burning: [{result}]"); + } + + foreach (var json in instantJsonList) + { + try + { + AddInstantJSONAnnotationToPDF(json); + } + catch (Exception ex) + { + Logger.Warn("Error in AddInstantJSONAnnotationToPDF - oJson: "); + Logger.Warn(json); + throw new BurnAnnotationException("Adding Annotation failed", ex); + } + } + + result = _manager.BurnAnnotationsToPage(RemoveInitialAnnots: true, VectorMode: true); + if (result != GdPictureStatus.OK) + { + throw new BurnAnnotationException($"Could not burn annotations to file: [{result}]"); + } + + using var newStream = new MemoryStream(); + result = _manager.SaveDocumentToPDF(newStream); + if (result != GdPictureStatus.OK) + { + throw new BurnAnnotationException($"Could not save document to stream: [{result}]"); + } + + _manager.Close(); + + return newStream.ToArray(); + } + + private void AddInstantJSONAnnotationToPDF(string instantJson) + { + var annotationData = JsonConvert.DeserializeObject(instantJson); + if (annotationData is null) + { + return; + } + + annotationData.annotations.Reverse(); + + foreach (var annotation in annotationData.annotations) + { + Logger.Debug("Adding AnnotationID: " + annotation.id); + + switch (annotation.type) + { + case AnnotationType.Image: + AddImageAnnotation(annotation, annotationData.attachments); + break; + case AnnotationType.Ink: + AddInkAnnotation(annotation); + break; + case AnnotationType.Widget: + var formFieldValue = annotationData.formFieldValues.FirstOrDefault(fv => fv.name == annotation.id); + if (formFieldValue is not null && !_pdfBurnerParams.IgnoredLabels.Contains(formFieldValue.value)) + { + AddFormFieldValue(annotation, formFieldValue); + } + break; + } + } + } + + private void AddImageAnnotation(double x, double y, double width, double height, int page, string base64) + { + _manager.SelectPage(page); + _manager.AddEmbeddedImageAnnotFromBase64(base64, (float) x, (float) y, (float) width, (float) height); + } + + private void AddImageAnnotation(Annotation annotation, Dictionary attachments) + { + var attachment = attachments.Where(a => a.Key == annotation.imageAttachmentId).SingleOrDefault(); + + var bounds = annotation.bbox.Select(ToInches).ToList(); + + var x = bounds[0]; + var y = bounds[1]; + var width = bounds[2]; + var height = bounds[3]; + + _manager.SelectPage(annotation.pageIndex + 1); + _manager.AddEmbeddedImageAnnotFromBase64(attachment.Value.binary, (float) x, (float) y, (float) width, (float) height); + } + + private void AddInkAnnotation(int page, string value) + { + var ink = JsonConvert.DeserializeObject(value); + if (ink is null) + { + return; + } + + var segments = ink.lines.points; + var color = ColorTranslator.FromHtml(ink.strokeColor); + _manager.SelectPage(page); + + foreach (var segment in segments) + { + var points = segment.Select(ToPointF).ToArray(); + _manager.AddFreeHandAnnot(color, points); + } + } + + private void AddInkAnnotation(Annotation annotation) + { + var segments = annotation.lines.points; + var color = ColorTranslator.FromHtml(annotation.strokeColor); + _manager.SelectPage(annotation.pageIndex + 1); + + foreach (var segment in segments) + { + var points = segment.Select(ToPointF).ToArray(); + _manager.AddFreeHandAnnot(color, points); + } + } + + private void AddFormFieldValue(double x, double y, double width, double height, int page, string value) + { + _manager.SelectPage(page); + + var annot = _manager.AddTextAnnot((float) x, (float) y, (float) width, (float) height, value); + annot.FontName = _pdfBurnerParams.FontName; + annot.FontSize = _pdfBurnerParams.FontSize; + annot.FontStyle = _pdfBurnerParams.FontStyle; + _manager.SaveAnnotationsToPage(); + } + + private void AddFormFieldValue(Annotation annotation, FormFieldValue formFieldValue) + { + var ffIndex = EGName.Index[annotation.egName]; + + var bounds = annotation.bbox.Select(ToInches).ToList(); + + var x = bounds[0]; + var y = bounds[1] + _pdfBurnerParams.YOffset * ffIndex + _pdfBurnerParams.TopMargin; + var width = bounds[2]; + var height = bounds[3]; + + _manager.SelectPage(annotation.pageIndex + 1); + var annot = _manager.AddTextAnnot((float) x, (float) y, (float) width, (float) height, formFieldValue.value); + annot.FontName = _pdfBurnerParams.FontName; + annot.FontSize = _pdfBurnerParams.FontSize; + annot.FontStyle = _pdfBurnerParams.FontStyle; + _manager.SaveAnnotationsToPage(); + } + + private static PointF ToPointF(List points) + { + var convertedPoints = points.Select(ToInches).ToList(); + return new PointF(convertedPoints[0], convertedPoints[1]); + } + + private static double ToInches(double value) => value / 72; + + private static float ToInches(float value) => value / 72; + + internal static class AnnotationType + { + public const string Image = "pspdfkit/image"; + public const string Ink = "pspdfkit/ink"; + public const string Widget = "pspdfkit/widget"; + public const string FormField = "pspdfkit/form-field-value"; + } + + internal class AnnotationData + { + public List annotations { get; set; } = new(); + + public IEnumerable> AnnotationsByReceiver => annotations + .Where(annot => annot.hasStructuredID) + .GroupBy(a => a.receiverId) + .Select(g => g.ToList()); + + public IEnumerable> UnstructuredAnnotations => annotations + .Where(annot => !annot.hasStructuredID) + .GroupBy(a => a.receiverId) + .Select(g => g.ToList()); + + public Dictionary attachments { get; set; } = new(); + public List formFieldValues { get; set; } = new(); + } + + internal class Annotation + { + private string? _id; + + public int envelopeId; + public int receiverId; + public int index; + public string egName = EGName.NoName; + public bool hasStructuredID; + + public bool isLabel + { + get + { + if (string.IsNullOrEmpty(egName)) + { + return false; + } + + var parts = egName.Split('_'); + return parts.Length > 1 && parts[1] == "label"; + } + } + + public string id + { + get => _id ?? string.Empty; + set + { + _id = value; + + if (string.IsNullOrWhiteSpace(_id)) + { + throw new BurnAnnotationException("The identifier of annotation is null or empty."); + } + + var parts = value.Split('#'); + + if (parts.Length != 4) + { + return; + } + + if (!int.TryParse(parts[0], out envelopeId)) + { + throw new BurnAnnotationException($"The envelope ID of annotation is not integer. Id: {_id}"); + } + + if (!int.TryParse(parts[1], out receiverId)) + { + throw new BurnAnnotationException($"The receiver ID of annotation is not integer. Id: {_id}"); + } + + if (!int.TryParse(parts[2], out index)) + { + throw new BurnAnnotationException($"The index of annotation is not integer. Id: {_id}"); + } + + egName = parts[3]; + + hasStructuredID = true; + } + } + + public List bbox { get; set; } = new(); + + public string type { get; set; } = string.Empty; + + public bool isSignature { get; set; } + + public string imageAttachmentId { get; set; } = string.Empty; + + public Lines lines { get; set; } = new(); + + public int pageIndex { get; set; } + + public string strokeColor { get; set; } = string.Empty; + } + + internal class Ink + { + public Lines lines { get; set; } = new(); + + public string strokeColor { get; set; } = string.Empty; + } + + public class EGName + { + public static readonly string NoName = Guid.NewGuid().ToString(); + public static readonly string Seal = "signature"; + + public static readonly ImmutableDictionary Index = new Dictionary + { + { NoName, 0 }, + { Seal, 0 }, + { "position", 1 }, + { "city", 2 }, + { "date", 3 } + }.ToImmutableDictionary(); + } + + internal class Lines + { + public List>> points { get; set; } = new(); + } + + internal class Attachment + { + public string binary { get; set; } = string.Empty; + public string contentType { get; set; } = string.Empty; + } + + internal class FormFieldValue + { + public string name { get; set; } = string.Empty; + public string value { get; set; } = string.Empty; + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/PDFBurnerParams.cs b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/PDFBurnerParams.cs new file mode 100644 index 00000000..2d54c50c --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/PDFBurnerParams.cs @@ -0,0 +1,18 @@ +using System.Drawing; + +namespace EnvelopeGenerator.ServiceHost.Jobs.FinalizeDocument; + +public class PDFBurnerParams +{ + public List IgnoredLabels { get; set; } = new() { "Date", "Datum", "ZIP", "PLZ", "Place", "Ort", "Position", "Stellung" }; + + public double TopMargin { get; set; } = 0.1; + + public double YOffset { get; set; } = -0.3; + + public string FontName { get; set; } = "Arial"; + + public int FontSize { get; set; } = 8; + + public FontStyle FontStyle { get; set; } = FontStyle.Italic; +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/PDFMerger.cs b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/PDFMerger.cs new file mode 100644 index 00000000..b2f1556a --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/PDFMerger.cs @@ -0,0 +1,65 @@ +using System.IO; +using DigitalData.Modules.Base; +using DigitalData.Modules.Logging; +using EnvelopeGenerator.ServiceHost.Exceptions; +using GdPicture14; + +namespace EnvelopeGenerator.ServiceHost.Jobs.FinalizeDocument; + +public class PDFMerger : BaseClass +{ + private readonly AnnotationManager _manager; + private readonly LicenseManager _licenseManager; + + private const bool AllowRasterization = true; + private const bool AllowVectorization = true; + + private readonly PdfConversionConformance _pdfaConformanceLevel = PdfConversionConformance.PDF_A_1b; + + public PDFMerger(LogConfig logConfig, string gdPictureLicenseKey) : base(logConfig) + { + _licenseManager = new LicenseManager(); + _licenseManager.RegisterKEY(gdPictureLicenseKey); + + _manager = new AnnotationManager(); + } + + public byte[] MergeDocuments(byte[] document, byte[] report) + { + using var documentStream = new MemoryStream(document); + using var reportStream = new MemoryStream(report); + using var finalStream = new MemoryStream(); + using var documentPdf = new GdPicturePDF(); + using var reportPdf = new GdPicturePDF(); + + documentPdf.LoadFromStream(documentStream, true); + var status = documentPdf.GetStat(); + if (status != GdPictureStatus.OK) + { + throw new MergeDocumentException($"Document could not be loaded: {status}"); + } + + reportPdf.LoadFromStream(reportStream, true); + status = reportPdf.GetStat(); + if (status != GdPictureStatus.OK) + { + throw new MergeDocumentException($"Report could not be loaded: {status}"); + } + + var mergedPdf = documentPdf.Merge2Documents(documentPdf, reportPdf); + status = mergedPdf.GetStat(); + if (status != GdPictureStatus.OK) + { + throw new MergeDocumentException($"Documents could not be merged: {status}"); + } + + mergedPdf.ConvertToPDFA(finalStream, _pdfaConformanceLevel, AllowVectorization, AllowRasterization); + status = documentPdf.GetStat(); + if (status != GdPictureStatus.OK) + { + throw new MergeDocumentException($"Document could not be converted to PDF/A: {status}"); + } + + return finalStream.ToArray(); + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/ReportCreator.cs b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/ReportCreator.cs new file mode 100644 index 00000000..ad31a841 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/ReportCreator.cs @@ -0,0 +1,107 @@ +using System.Data; +using System.IO; +using DigitalData.Modules.Base; +using DigitalData.Modules.Logging; +using EnvelopeGenerator.Domain.Constants; +using EnvelopeGenerator.Domain.Entities; +using EnvelopeGenerator.ServiceHost.Exceptions; +using EnvelopeGenerator.ServiceHost.Jobs; + +namespace EnvelopeGenerator.ServiceHost.Jobs.FinalizeDocument; + +public class ReportCreator : BaseClass +{ + private Envelope? _envelope; + private readonly ReportModel _reportModel; + + public ReportCreator(LogConfig logConfig, State state) : base(logConfig) + { + _reportModel = new ReportModel(state); + } + + public byte[] CreateReport(Envelope envelope) + { + try + { + Logger.Debug("Loading report data.."); + var table = _reportModel.List(envelope.Id); + var items = GetReportSource(table); + + _envelope = envelope; + + if (items.Count == 0) + { + throw new CreateReportException("No report data found!"); + } + + Logger.Debug("Creating report with [{0}] items..", items.Count); + var buffer = DoCreateReport(items); + Logger.Debug("Report created!"); + + return buffer; + } + catch (Exception ex) + { + Logger.Error(ex); + throw new CreateReportException("Could not prepare report data!", ex); + } + } + + private List GetReportSource(DataTable dataTable) + { + Logger.Debug("Preparing report data"); + return dataTable.Rows + .Cast() + .Select(ToReportItem) + .OrderByDescending(r => r.ItemDate) + .ToList(); + } + + private byte[] DoCreateReport(List reportItems) + { + var items = reportItems.Select(MergeEnvelope).ToList(); + var source = new ReportSource { Items = items }; + var report = new rptEnvelopeHistory { DataSource = source, DataMember = "Items" }; + + Logger.Debug("Creating report in memory.."); + report.CreateDocument(); + + Logger.Debug("Exporting report to stream.."); + using var stream = new MemoryStream(); + report.ExportToPdf(stream); + + Logger.Debug("Writing report to buffer.."); + return stream.ToArray(); + } + + private ReportItem MergeEnvelope(ReportItem item) + { + if (item.Envelope is null) + { + item.Envelope = _envelope; + } + + return item; + } + + private ReportItem ToReportItem(DataRow row) + { + try + { + return new ReportItem + { + EnvelopeId = row.ItemEx("ENVELOPE_ID", 0), + EnvelopeTitle = row.ItemEx("HEAD_TITLE", string.Empty), + EnvelopeSubject = row.ItemEx("HEAD_SUBJECT", string.Empty), + ItemDate = row.ItemEx("POS_WHEN", DateTime.MinValue), + ItemStatus = (EnvelopeStatus)row.ItemEx("POS_STATUS", 0), + ItemUserReference = row.ItemEx("POS_WHO", string.Empty) + }; + } + catch (Exception ex) + { + Logger.Error(ex); + throw new CreateReportException("Could not read data from database!", ex); + } + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/ReportItem.cs b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/ReportItem.cs new file mode 100644 index 00000000..40758489 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/ReportItem.cs @@ -0,0 +1,19 @@ +using EnvelopeGenerator.Domain.Constants; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.ServiceHost.Jobs.FinalizeDocument; + +public class ReportItem +{ + public Envelope? Envelope { get; set; } + public int EnvelopeId { get; set; } + public string EnvelopeTitle { get; set; } = string.Empty; + public string EnvelopeSubject { get; set; } = string.Empty; + + public EnvelopeStatus ItemStatus { get; set; } + + public string ItemStatusTranslated => ItemStatus.ToString(); + + public string ItemUserReference { get; set; } = string.Empty; + public DateTime ItemDate { get; set; } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/ReportSource.cs b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/ReportSource.cs new file mode 100644 index 00000000..e65ea1ea --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/ReportSource.cs @@ -0,0 +1,6 @@ +namespace EnvelopeGenerator.ServiceHost.Jobs.FinalizeDocument; + +public class ReportSource +{ + public List Items { get; set; } = new(); +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/rptEnvelopeHistory.cs b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/rptEnvelopeHistory.cs new file mode 100644 index 00000000..2a57be60 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocument/rptEnvelopeHistory.cs @@ -0,0 +1,18 @@ +using System.IO; + +namespace EnvelopeGenerator.ServiceHost.Jobs.FinalizeDocument; + +public class rptEnvelopeHistory +{ + public object? DataSource { get; set; } + public string? DataMember { get; set; } + + public void CreateDocument() + { + } + + public void ExportToPdf(Stream stream) + { + stream.Write(Array.Empty(), 0, 0); + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocumentJob.cs b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocumentJob.cs new file mode 100644 index 00000000..39a9eb88 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocumentJob.cs @@ -0,0 +1,432 @@ +using System.Data; +using System.IO; +using DigitalData.Modules.Base; +using DigitalData.Modules.Database; +using DigitalData.Modules.Logging; +using EnvelopeGenerator.Domain.Constants; +using EnvelopeGenerator.Domain.Entities; +using EnvelopeGenerator.ServiceHost.Exceptions; +using EnvelopeGenerator.ServiceHost.Jobs.FinalizeDocument; +using GdPicture14; +using Microsoft.Data.SqlClient; +using Quartz; + +namespace EnvelopeGenerator.ServiceHost.Jobs; + +public class FinalizeDocumentJob : IJob +{ + private readonly LicenseManager _licenseManager = new(); + private GdViewer? _gdViewer; + + private LogConfig? _logConfig; + private Logger? _logger; + private MSSQLServer? _database; + private DbConfig? _config; + private string _databaseConnectionString = string.Empty; + + private ConfigModel? _configModel; + private EnvelopeModel? _envelopeModel; + private ReportModel? _reportModel; + + private ActionService? _actionService; + + private PDFBurner? _pdfBurner; + private PDFMerger? _pdfMerger; + private ReportCreator? _reportCreator; + + private const int CompleteWaitTime = 1; + private string _parentFolderUid = string.Empty; + private TempFiles? _tempFiles; + + private sealed class EnvelopeData + { + public int EnvelopeId { get; set; } + public string EnvelopeUuid { get; set; } = string.Empty; + public string DocumentPath { get; set; } = string.Empty; + public List AnnotationData { get; set; } = new(); + public byte[]? DocAsByte { get; set; } + } + + public Task Execute(IJobExecutionContext context) + { + var gdPictureKey = (string)context.MergedJobDataMap[Value.GDPICTURE]; + _logConfig = (LogConfig)context.MergedJobDataMap[Value.LOGCONFIG]; + _logger = _logConfig.GetLogger(); + _tempFiles = new TempFiles(_logConfig); + _tempFiles.Create(); + var jobId = context.JobDetail.Key; + _logger.Debug("Starting job {0}", jobId); + + try + { + _logger.Debug("Loading GdViewer.."); + _gdViewer = new GdViewer(); + _licenseManager.RegisterKEY(gdPictureKey); + + _logger.Debug("Loading Database.."); + var connectionString = (string)context.MergedJobDataMap[Value.DATABASE]; + _databaseConnectionString = MSSQLServer.DecryptConnectionString(connectionString); + _database = new MSSQLServer(_logConfig, _databaseConnectionString); + + _logger.Debug("Loading Models & Services"); + var state = GetState(); + InitializeModels(state); + + _logger.Debug("Loading Configuration.."); + _config = _configModel?.LoadConfiguration() ?? new DbConfig(); + state.DbConfig = _config; + + InitializeServices(state); + + _logger.Debug("Loading PDFBurner.."); + var pdfBurnerParams = (PDFBurnerParams)context.MergedJobDataMap[Value.PDF_BURNER_PARAMS]; + _pdfBurner = new PDFBurner(_logConfig, gdPictureKey, pdfBurnerParams, _databaseConnectionString); + + _logger.Debug("Loading PDFMerger.."); + _pdfMerger = new PDFMerger(_logConfig, gdPictureKey); + + _logger.Debug("Loading ReportCreator.."); + _reportCreator = new ReportCreator(_logConfig, state); + + _config.DocumentPath = _config.DocumentPath; + + _logger.Debug("DocumentPath: [{0}]", _config.DocumentPath); + _logger.Debug("ExportPath: [{0}]", _config.ExportPath); + + var completeStatus = EnvelopeStatus.EnvelopeCompletelySigned; + var sql = $"SELECT * FROM TBSIG_ENVELOPE WHERE STATUS = {completeStatus} AND DATEDIFF(minute, CHANGED_WHEN, GETDATE()) >= {CompleteWaitTime} ORDER BY GUID"; + var table = _database.GetDatatable(sql); + + var envelopeIds = table.Rows.Cast() + .Select(r => r.Field("GUID")) + .ToList(); + + if (envelopeIds.Count > 0) + { + _logger.Info("Found [{0}] completed envelopes.", envelopeIds.Count); + } + + var total = envelopeIds.Count; + var current = 1; + + foreach (var id in envelopeIds) + { + _logger.Info("Finalizing Envelope [{0}] ({1}/{2})", id, current, total); + try + { + var envelope = _envelopeModel?.GetById(id); + if (envelope is null) + { + _logger.Warn("Envelope could not be loaded for Id [{0}]!", id); + throw new ArgumentNullException(nameof(EnvelopeData)); + } + + _logger.Debug("Loading Envelope Data.."); + var envelopeData = GetEnvelopeData(id); + + if (envelopeData is null) + { + _logger.Warn("EnvelopeData could not be loaded for Id [{0}]!", id); + throw new ArgumentNullException(nameof(EnvelopeData)); + } + + _logger.Debug("Burning Annotations to pdf ..."); + var burnedDocument = BurnAnnotationsToPdf(envelopeData); + if (burnedDocument is null) + { + _logger.Warn("Document could not be finalized!"); + throw new ApplicationException("Document could not be finalized"); + } + + if (_actionService?.CreateReport(envelope) == false) + { + _logger.Warn("Document Report could not be created!"); + throw new ApplicationException("Document Report could not be created"); + } + + _logger.Debug("Creating report.."); + var report = _reportCreator!.CreateReport(envelope); + _logger.Debug("Report created!"); + + _logger.Debug("Merging documents ..."); + var mergedDocument = _pdfMerger!.MergeDocuments(burnedDocument, report); + _logger.Debug("Documents merged!"); + + var outputDirectoryPath = Path.Combine(_config.ExportPath, _parentFolderUid); + _logger.Debug("oOutputDirectoryPath is {0}", outputDirectoryPath); + if (!Directory.Exists(outputDirectoryPath)) + { + _logger.Debug("Directory not existing. Creating ... "); + Directory.CreateDirectory(outputDirectoryPath); + } + + var outputFilePath = Path.Combine(outputDirectoryPath, $"{envelope.Uuid}.pdf"); + _logger.Debug("Writing finalized Pdf to disk.."); + _logger.Info("Output path is [{0}]", outputFilePath); + + try + { + File.WriteAllBytes(outputFilePath, mergedDocument); + } + catch (Exception ex) + { + _logger.Warn("Could not export final document to disk!"); + throw new ExportDocumentException("Could not export final document to disk!", ex); + } + + _logger.Debug("Writing EB-bytes to database..."); + UpdateFileDb(outputFilePath, envelope.Id); + + if (!SendFinalEmails(envelope)) + { + throw new ApplicationException("Final emails could not be sent!"); + } + + _logger.Info("Report-mails successfully sent!"); + + _logger.Debug("Setting envelope status.."); + if (_actionService?.FinalizeEnvelope(envelope) == false) + { + _logger.Warn("Envelope could not be finalized!"); + throw new ApplicationException("Envelope could not be finalized"); + } + } + catch (Exception ex) + { + _logger.Error(ex); + _logger.Warn(ex, "Unhandled exception while working envelope [{0}]", id); + } + + current += 1; + _logger.Info("Envelope [{0}] finalized!", id); + } + + _logger.Debug("Completed job {0} successfully!", jobId); + } + catch (MergeDocumentException ex) + { + _logger.Warn("Certificate Document job failed at step: Merging documents!"); + _logger.Error(ex); + } + catch (ExportDocumentException ex) + { + _logger.Warn("Certificate Document job failed at step: Exporting document!"); + _logger.Error(ex); + } + catch (Exception ex) + { + _logger.Warn("Certificate Document job failed!"); + _logger.Error(ex); + } + finally + { + _logger.Debug("Job execution for [{0}] ended", jobId); + } + + return Task.FromResult(true); + } + + private void UpdateFileDb(string filePath, long envelopeId) + { + try + { + var imageData = ReadFile(filePath); + if (imageData is null) + { + return; + } + + var query = $"UPDATE TBSIG_ENVELOPE SET DOC_RESULT = @ImageData WHERE GUID = {envelopeId}"; + using var command = new SqlCommand(query, _database!.GetConnection); + command.Parameters.Add(new SqlParameter("@ImageData", imageData)); + command.ExecuteNonQuery(); + } + catch (Exception ex) + { + _logger?.Error(ex); + } + } + + private static byte[]? ReadFile(string path) + { + var fileInfo = new FileInfo(path); + var numBytes = fileInfo.Length; + using var stream = new FileStream(path, FileMode.Open, FileAccess.Read); + using var reader = new BinaryReader(stream); + return reader.ReadBytes((int)numBytes); + } + + private bool SendFinalEmails(Envelope envelope) + { + var mailToCreator = (FinalEmailType)(envelope.FinalEmailToCreator ?? 0); + var mailToReceivers = (FinalEmailType)(envelope.FinalEmailToReceivers ?? 0); + + if (mailToCreator != FinalEmailType.No) + { + _logger?.Debug("Sending email to creator ..."); + SendFinalEmailToCreator(envelope, mailToCreator); + } + else + { + _logger?.Warn("No SendFinalEmailToCreator - oMailToCreator [{0}] <> [{1}] ", mailToCreator, FinalEmailType.No); + } + + if (mailToReceivers != FinalEmailType.No) + { + _logger?.Debug("Sending emails to receivers.."); + SendFinalEmailToReceivers(envelope, mailToReceivers); + } + else + { + _logger?.Warn("No SendFinalEmailToReceivers - oMailToCreator [{0}] <> [{1}] ", mailToReceivers, FinalEmailType.No); + } + + return true; + } + + private bool SendFinalEmailToCreator(Envelope envelope, FinalEmailType mailToCreator) + { + var includeAttachment = SendFinalEmailWithAttachment(mailToCreator); + _logger?.Debug("Attachment included: [{0}]", includeAttachment); + + if (_actionService?.CompleteEnvelope(envelope) == false) + { + _logger?.Error(new Exception("CompleteEnvelope failed"), "Envelope could not be completed for receiver [{0}]", envelope.User?.Email); + return false; + } + + return true; + } + + private bool SendFinalEmailToReceivers(Envelope envelope, FinalEmailType mailToReceivers) + { + var includeAttachment = SendFinalEmailWithAttachment(mailToReceivers); + _logger?.Debug("Attachment included: [{0}]", includeAttachment); + + foreach (var receiver in envelope.EnvelopeReceivers ?? Enumerable.Empty()) + { + if (_actionService?.CompleteEnvelope(envelope, receiver.Receiver) == false) + { + _logger?.Error(new Exception("CompleteEnvelope failed"), "Envelope could not be completed for receiver [{0}]", receiver.Receiver?.EmailAddress); + return false; + } + } + + return true; + } + + private static bool SendFinalEmailWithAttachment(FinalEmailType type) + { + return type == FinalEmailType.YesWithAttachment; + } + + private byte[] BurnAnnotationsToPdf(EnvelopeData envelopeData) + { + var envelopeId = envelopeData.EnvelopeId; + + _logger?.Info("Burning [{0}] signatures", envelopeData.AnnotationData.Count); + var annotations = envelopeData.AnnotationData; + var inputPath = string.Empty; + if (envelopeData.DocAsByte is null) + { + inputPath = envelopeData.DocumentPath; + _logger?.Info("Input path: [{0}]", inputPath); + } + else + { + _logger?.Debug("we got bytes.."); + inputPath = _config!.DocumentPathOrigin; + _logger?.Debug("oInputPath: {0}", _config.DocumentPathOrigin); + } + + if (envelopeData.DocAsByte is null) + { + var directorySource = Path.GetDirectoryName(inputPath) ?? string.Empty; + var split = directorySource.Split('\\'); + _parentFolderUid = split[^1]; + } + else + { + _parentFolderUid = envelopeData.EnvelopeUuid; + } + + _logger?.Info("ParentFolderUID: [{0}]", _parentFolderUid); + byte[] inputDocumentBuffer; + if (envelopeData.DocAsByte is not null) + { + inputDocumentBuffer = envelopeData.DocAsByte; + } + else + { + try + { + inputDocumentBuffer = File.ReadAllBytes(inputPath); + } + catch (Exception ex) + { + throw new BurnAnnotationException("Source document could not be read from disk!", ex); + } + } + + return _pdfBurner!.BurnAnnotsToPDF(inputDocumentBuffer, annotations, envelopeId); + } + + private EnvelopeData? GetEnvelopeData(int envelopeId) + { + var sql = $"SELECT T.GUID, T.ENVELOPE_UUID, T.ENVELOPE_TYPE, T2.FILEPATH, T2.BYTE_DATA FROM [dbo].[TBSIG_ENVELOPE] T\n JOIN TBSIG_ENVELOPE_DOCUMENT T2 ON T.GUID = T2.ENVELOPE_ID\n WHERE T.GUID = {envelopeId}"; + var table = _database!.GetDatatable(sql); + var row = table.Rows.Cast().SingleOrDefault(); + if (row is null) + { + return null; + } + + var annotationData = GetAnnotationData(envelopeId); + var data = new EnvelopeData + { + EnvelopeId = envelopeId, + DocumentPath = row.ItemEx("FILEPATH", string.Empty), + AnnotationData = annotationData, + DocAsByte = row.Field("BYTE_DATA"), + EnvelopeUuid = row.ItemEx("ENVELOPE_UUID", string.Empty) + }; + + _logger?.Debug("Document path: [{0}]", data.DocumentPath); + + return data; + } + + private List GetAnnotationData(int envelopeId) + { + var sql = $"SELECT VALUE FROM TBSIG_DOCUMENT_STATUS WHERE ENVELOPE_ID = {envelopeId}"; + var table = _database!.GetDatatable(sql); + + return table.Rows.Cast() + .Select(r => r.ItemEx("VALUE", string.Empty)) + .ToList(); + } + + private void InitializeServices(State state) + { + _actionService = new ActionService(state, _database!); + } + + private void InitializeModels(State state) + { + _configModel = new ConfigModel(state); + _envelopeModel = new EnvelopeModel(state); + _reportModel = new ReportModel(state); + } + + private State GetState() + { + return new State + { + LogConfig = _logConfig!, + Database = _database!, + UserId = 0, + Config = null, + DbConfig = null + }; + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/Infrastructure/Base.cs b/EnvelopeGenerator.ServiceHost/Jobs/Infrastructure/Base.cs new file mode 100644 index 00000000..abccf9c5 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/Infrastructure/Base.cs @@ -0,0 +1,63 @@ +using System.Data; +using DigitalData.Modules.Logging; + +namespace DigitalData.Modules.Base; + +public abstract class BaseClass +{ + protected BaseClass(LogConfig logConfig) + { + LogConfig = logConfig; + Logger = logConfig.GetLogger(); + } + + protected LogConfig LogConfig { get; } + protected Logger Logger { get; } +} + +public static class ObjectEx +{ + public static T ToEnum(object value) where T : struct, Enum + { + if (value is T enumValue) + { + return enumValue; + } + + if (value is string stringValue && Enum.TryParse(stringValue, true, out var parsed)) + { + return parsed; + } + + if (int.TryParse(Convert.ToString(value), out var intValue)) + { + return (T)Enum.ToObject(typeof(T), intValue); + } + + return default; + } +} + +public static class DataRowExtensions +{ + public static T ItemEx(this DataRow row, string columnName, T defaultValue) + { + if (!row.Table.Columns.Contains(columnName)) + { + return defaultValue; + } + + var value = row[columnName]; + if (value is DBNull or null) + { + return defaultValue; + } + + return (T)Convert.ChangeType(value, typeof(T)); + } + + public static string ItemEx(this DataRow row, string columnName, string defaultValue) + { + return ItemEx(row, columnName, defaultValue); + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/Infrastructure/Database.cs b/EnvelopeGenerator.ServiceHost/Jobs/Infrastructure/Database.cs new file mode 100644 index 00000000..c751d886 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/Infrastructure/Database.cs @@ -0,0 +1,67 @@ +using System.Data; +using Microsoft.Data.SqlClient; +using DigitalData.Modules.Logging; + +namespace DigitalData.Modules.Database; + +public class MSSQLServer +{ + private readonly LogConfig _logConfig; + private readonly string _connectionString; + + public MSSQLServer(LogConfig logConfig, string connectionString) + { + _logConfig = logConfig; + _connectionString = connectionString; + } + + public static string DecryptConnectionString(string connectionString) => connectionString; + + public SqlConnection GetConnection + { + get + { + var connection = new SqlConnection(_connectionString); + if (connection.State != ConnectionState.Open) + { + connection.Open(); + } + + return connection; + } + } + + public DataTable GetDatatable(string sql) + { + using var connection = new SqlConnection(_connectionString); + using var command = new SqlCommand(sql, connection); + using var adapter = new SqlDataAdapter(command); + var table = new DataTable(); + adapter.Fill(table); + return table; + } + + public object? GetScalarValue(string sql) + { + using var connection = new SqlConnection(_connectionString); + using var command = new SqlCommand(sql, connection); + connection.Open(); + return command.ExecuteScalar(); + } + + public bool ExecuteNonQuery(string sql) + { + using var connection = new SqlConnection(_connectionString); + using var command = new SqlCommand(sql, connection); + connection.Open(); + return command.ExecuteNonQuery() > 0; + } + + public bool ExecuteNonQuery(SqlCommand command) + { + using var connection = new SqlConnection(_connectionString); + command.Connection = connection; + connection.Open(); + return command.ExecuteNonQuery() > 0; + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/Infrastructure/Logging.cs b/EnvelopeGenerator.ServiceHost/Jobs/Infrastructure/Logging.cs new file mode 100644 index 00000000..6a3f7a08 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/Infrastructure/Logging.cs @@ -0,0 +1,26 @@ +using System.Globalization; + +namespace DigitalData.Modules.Logging; + +public class LogConfig +{ + public bool Debug { get; set; } + + public Logger GetLogger() => new(); +} + +public class Logger +{ + public void Debug(string message, params object?[] args) => Write("DEBUG", message, args); + public void Info(string message, params object?[] args) => Write("INFO", message, args); + public void Warn(string message, params object?[] args) => Write("WARN", message, args); + public void Warn(Exception exception, string message, params object?[] args) => Write("WARN", message + " " + exception.Message, args); + public void Error(Exception exception) => Write("ERROR", exception.Message, Array.Empty()); + public void Error(Exception exception, string message, params object?[] args) => Write("ERROR", message + " " + exception.Message, args); + + private static void Write(string level, string message, params object?[] args) + { + var formatted = args.Length > 0 ? string.Format(CultureInfo.InvariantCulture, message, args) : message; + Console.WriteLine($"[{level}] {formatted}"); + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/ReportModel.cs b/EnvelopeGenerator.ServiceHost/Jobs/ReportModel.cs new file mode 100644 index 00000000..f98cdf64 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/ReportModel.cs @@ -0,0 +1,16 @@ +using System.Data; + +namespace EnvelopeGenerator.ServiceHost.Jobs; + +public class ReportModel : BaseModel +{ + public ReportModel(State state) : base(state) + { + } + + public DataTable List(int envelopeId) + { + var sql = $"SELECT * FROM VWSIG_ENVELOPE_REPORT WHERE ENVELOPE_ID = {envelopeId}"; + return Database.GetDatatable(sql); + } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/State.cs b/EnvelopeGenerator.ServiceHost/Jobs/State.cs new file mode 100644 index 00000000..74e4f9c1 --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/State.cs @@ -0,0 +1,15 @@ +using DigitalData.Modules.Database; +using DigitalData.Modules.Logging; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.ServiceHost.Jobs; + +public class State +{ + public int UserId { get; set; } + public FormUser? User { get; set; } + public Config? Config { get; set; } + public DbConfig? DbConfig { get; set; } + public LogConfig? LogConfig { get; set; } + public MSSQLServer? Database { get; set; } +} diff --git a/EnvelopeGenerator.ServiceHost/Jobs/TempFiles.cs b/EnvelopeGenerator.ServiceHost/Jobs/TempFiles.cs new file mode 100644 index 00000000..ad9a51db --- /dev/null +++ b/EnvelopeGenerator.ServiceHost/Jobs/TempFiles.cs @@ -0,0 +1,72 @@ +using System.IO; +using DigitalData.Modules.Base; +using DigitalData.Modules.Logging; + +namespace EnvelopeGenerator.ServiceHost.Jobs; + +public class TempFiles : BaseClass +{ + public string TempPath { get; } + + public TempFiles(LogConfig logConfig) : base(logConfig) + { + var tempDirectoryPath = Path.GetTempPath(); + TempPath = Path.Combine(tempDirectoryPath, "EnvelopeGenerator"); + } + + public bool Create() + { + try + { + if (!Directory.Exists(TempPath)) + { + Directory.CreateDirectory(TempPath); + } + else + { + CleanUpFiles(); + } + + return true; + } + catch (Exception ex) + { + Logger.Error(ex); + return false; + } + } + + private bool CleanUpFiles() + { + try + { + foreach (var fileItem in Directory.GetFiles(TempPath)) + { + Logger.Debug("Deleting tempPath-file: {0} ...", fileItem); + File.Delete(fileItem); + } + + return true; + } + catch (Exception ex) + { + Logger.Error(ex); + return false; + } + } + + public bool CleanUp() + { + try + { + Logger.Debug("Deleting tempPath-Data: {0} ...", TempPath); + Directory.Delete(TempPath, true); + return true; + } + catch (Exception ex) + { + Logger.Error(ex); + return false; + } + } +}