Compare commits

...

95 Commits

Author SHA1 Message Date
35b7b1a080 refactor(AddReportBehavior): update to add history record. 2025-11-10 17:05:35 +01:00
67d0980c63 move the behaviours about pdf to pdf dir 2025-11-10 16:46:10 +01:00
c6a99b56a2 add GdPicture packages via nuget 2025-11-10 16:18:42 +01:00
31f5d1f340 add SaveBurnedPdfBehavior to services 2025-11-10 15:29:16 +01:00
a2df5d7691 create SaveBurnedPdfBehavior 2025-11-10 15:13:57 +01:00
f8c586dd31 feat(FinishEnvelopeJob): add PDF burning with concurrency control
- Introduced PDFBurnerParams configuration for concurrency limit.
- Added SemaphoreSlim to handle concurrent PDF burning for envelopes.
- Updated job to call _mediator.BurnPdf for each envelope with error handling.
- Preserved logging of job execution and envelope UUIDs.
2025-11-10 15:01:07 +01:00
8aea3c8301 refactor(BurnPdfCommand): add extension methods for ISender
- Introduce BurnPdfCommandExtensions to simplify sending BurnPdfCommand via ISender
- Enables calling `sender.BurnPdf(envelopeId, cancel)` or `sender.BurnPdf(envelopeUuid, cancel)`
- No changes to the underlying PDF burning logic in BurnPdfCommandHandler
2025-11-10 14:00:21 +01:00
495adb8c31 feat(dependency-injection): add optional usePdfBurner flag to AddServices
- Updated EGConfiguration.AddServices to accept a new `usePdfBurner` boolean parameter.
- Pass `usePdfBurner` through to AddEnvelopeGeneratorServices to enable optional PDF burner functionality.
- Maintains existing behavior when parameter is not provided (default false).
2025-11-10 13:53:26 +01:00
86c0a65540 refactor: add conditional PDFBurner and GdPicture service registration 2025-11-10 13:43:44 +01:00
d2e8f1fc5e refactor: simplify GdPicture license resolution in dependency injection 2025-11-10 13:29:02 +01:00
f8369e350f refactor(BurnPdfCommand): replace manual JSON serialization with ToJson extension
- Replaced `System.Text.Json.JsonSerializer.Serialize(request, Format.Json.ForDiagnostics)`
  with `request.ToJson(Format.Json.ForDiagnostics)` for better readability and consistency.
- No functional changes, purely a code style and maintainability improvement.
2025-11-10 13:01:33 +01:00
b8a2ad97ef refactor(BurnPdfCommand): simplify BurnPdfCommand and improve envelope/document handling
- Updated `BurnPdfCommand` to accept `EnvelopeId` or `EnvelopeUuid` instead of full `EnvelopeQueryBase` and document data.
- Reworked `BurnPdfCommandHandler` to fetch envelope and document from repositories, including proper validations and exceptions.
- Removed direct dependency on `Signature` repository; annotations now retrieved via document elements.
- Added detailed exception handling for missing envelope, document, or byte data.
- Minor namespace and using cleanup.
2025-11-10 12:58:04 +01:00
ffffc2d470 refactor(BurnPdfCommand): update BurnPdfCommand to use EnvelopeQueryBase and enhance request structure
- Replaced record-based BurnPdfCommand with class-based implementation
- Added Envelope property of type EnvelopeQueryBase
- Changed parameters to nullable (Document, InstantJSONList)
- Added ByCronService flag for cron-based PDF burning control
- Updated query logic to use request.Envelope.Id instead of EnvelopeId
- Improved code structure for flexibility and service integration
2025-11-10 09:30:04 +01:00
36b03da084 fix(DependencyInjection): change AnnotationManager registration from Scoped to Transient 2025-11-07 15:21:36 +01:00
defa53fa26 refactor(BurnPdfCommand): rename SourceBuffer to Document in BurnPdfCommand and handler
- Renamed parameter `SourceBuffer` to `Document` in BurnPdfCommand record
- Updated all usages of `request.SourceBuffer` to `request.Document`
- Reorganized method order for better readability (Handle moved up)
- No functional or behavioral changes introduced
2025-11-07 14:56:34 +01:00
6134b58a4c refactor(BurnPdfCommand): extract GdPicture and math helpers into extension classes and simplify BurnPdfCommandHandler
- Refactored BurnPdfCommandHandler to use new extension methods for cleaner annotation handling.
- Introduced ITextStyle interface to generalize font styling for text annotations.
- Updated PDFBurnerParams to implement ITextStyle for consistent font settings reuse.
- Added MathematExtensions for coordinate and unit conversion (ToInches, ToPointF).
- Added GdPictureExtensions to encapsulate annotation-related logic (form fields, images, ink).
- Improved readability and maintainability by removing redundant helper methods.
2025-11-07 14:46:55 +01:00
5299016b43 move PSDPFKitModels namespace to Dto and rename as PSPDFKitInstant 2025-11-07 13:51:53 +01:00
55c20e83d8 refactor: convert BurnPdfCommand to record and update handler to return byte[]
- Changed `BurnPdfCommand` from empty class to a `record` with properties: `SourceBuffer`, `InstantJSONList`, `EnvelopeId`.
- Updated `BurnPdfCommandHandler` to implement `IRequestHandler<BurnPdfCommand, byte[]>`.
- Modified `Handle` method to be async and use the `request` data to return PDF bytes.
- Removed obsolete `BurnPdfCommand` class and updated related logic accordingly.
2025-11-07 13:44:53 +01:00
f1a140faa7 feat(BurnPdfCommandHandler): implement AddInkAnnotation for Annotation parameter
- Added implementation for AddInkAnnotation(Annotation) to handle ink annotations from Annotation objects.
- Retained existing AddInkAnnotation(int, string) method for backward compatibility.
- Refactored PDF burning logic to support both Annotation-based and JSON-based annotations.
2025-11-07 13:24:26 +01:00
b20d25e5b9 feat(BurnPdfCommandHandler): implement AddInkAnnotation for freehand annotations
- Added implementation for AddInkAnnotation(int page, string value) to render ink annotations from JSON.
- Deserialize ink lines and stroke color, convert points to PDF coordinates, and add as freehand annotations.
- Retained placeholder for AddInkAnnotation(Annotation pAnnotation) for future use.
2025-11-07 13:21:33 +01:00
e21eb2c0d6 refactor(BurnPdfCommandHandler): rename AddInstantJSONAnnotationToPdf to AddInstantJsonAnnotationToPdf for consistent casing 2025-11-07 13:16:14 +01:00
d237b4ab95 feat(BurnPdfCommand): implement AddImageAnnotation with attachment handling
- Added logic to AddImageAnnotation to handle image attachments from a dictionary.
- Removed NotImplementedException placeholder for AddImageAnnotation with attachments.
- Refactored code to convert annotation bounds from pixels to inches before adding images.
2025-11-07 13:14:02 +01:00
dff99b163d refactor: simplify AddFormFieldValue method and remove redundant parameters 2025-11-07 13:12:01 +01:00
70fbf31b14 feat(BurnPdfCommandHandler): implement AddImageAnnotation for base64 images
- Added implementation for AddImageAnnotation(double x, double y, double width, double height, int page, string base64)
- Allows embedding images directly from base64 into PDF pages
- Existing annotation burning logic remains unchanged
2025-11-07 13:09:54 +01:00
339e0e81cd feat(PDFBurnerParams): add DefaultIndexOfAnnot and GetAnnotationIndex
- add `DefaultIndexOfAnnot` property to provide a fallback index for annotations
 - add `GetAnnotationIndex(string name)` method to safely retrieve an annotation index, returning default if not found
2025-11-07 13:03:50 +01:00
a386ad72bb refactor(BurnPdfCommandHandler): use IOptions<PDFBurnerParams> and improve form field handling
- Updated BurnPdfCommandHandler constructor to accept IOptions<PDFBurnerParams> instead of raw PDFBurnerParams.
- Updated AddFormFieldValue to calculate coordinates based on PDFBurnerParams offsets and margins.
- Added helper methods for converting pixels to inches (ToInches, ToPointF).
- Removed unused NotImplemented methods in AddFormFieldValue overload.
- Improved code readability and maintainability for annotation burning logic.
2025-11-07 12:59:13 +01:00
02c5f286ec feat(pdf): implement AddFormFieldValue for PDF annotations
- Added implementation for AddFormFieldValue method to properly add text annotations to PDF pages using AnnotationManager.
- Preserves font settings from PDFBurnerParams.
- Other annotation methods remain unimplemented.
2025-11-07 12:44:05 +01:00
5fa358ca79 refactor(BurnPdfCommandHandler): deserialize and process instant JSON annotations
- Added Newtonsoft.Json to parse instant JSON into `InstantData`.
- Replaced `AddInstantJSONAnnotationToPDF` logic with `AddInstantJsonAnnotationToPdf`.
- Process annotations by type (Image, Ink, Widget) and apply attachments/form fields.
- Introduced checks for ignored form field values using `_pdfBurnerParams.IgnoredLabels`.
- Added logging for annotation IDs when processing instant JSON.
2025-11-07 12:36:19 +01:00
7233d2ce98 add InstantData 2025-11-07 11:37:28 +01:00
5784cc7a97 refactor(Application.Pdf.PSPDFKit): rename as Application.Pdf.PSPDFKitModels 2025-11-07 11:28:55 +01:00
f31f680f91 add PSPDFKit.Annotation 2025-11-07 11:27:07 +01:00
e7c2d46ef0 add PSPDFKit.Ink 2025-11-07 11:23:04 +01:00
85d70c1db4 feat(PDFBurnerParams): add IndexOfAnnot-dictionary 2025-11-07 11:16:22 +01:00
fb340fb08a create PSPDFKit.Lines 2025-11-07 10:59:13 +01:00
7d3959ae51 add PSPDFKit.Attachment 2025-11-07 10:56:53 +01:00
9d4890b10d add PSPDFKit.FormFieldValue 2025-11-07 10:52:15 +01:00
b64f4d71f5 feat(BurnPdfCommandHandler): add logging and implement BurnInstantJSONAnnotsToPDF
- Added ILogger<BurnPdfCommandHandler> to log warnings during annotation processing
- Implemented BurnInstantJSONAnnotsToPDF method to handle instant JSON annotations
- Added AddInstantJSONAnnotationToPDF stub for future implementation
- Updated PDF burn flow to include error handling and logging
2025-11-07 10:40:44 +01:00
b7d146ddb5 refactor: standardize annotation types and clean up PDF burn handler
- Introduced `AnnotationType.PSPDFKit` constants for consistent annotation type references.
- Updated `BurnPdfCommandHandler` to use new PSPDFKit annotation types.
- Refactored method signatures and local variables for clarity (e.g., using 'var', null-coalescing for annotation properties).
- Streamlined `using` statements and memory management in PDF burning process.
- Added placeholder methods for form fields, image, and ink annotations to centralize future implementations.
2025-11-07 10:11:48 +01:00
6a5c9a3489 fix(BurnPdfCommand): improve JSON serialization and null safety in Signature entity and BurnPdfCommandHandler
- Added System.Text.Json.Serialization namespace and [JsonIgnore] attributes to Top and Left properties in Signature entity for .NET builds
- Applied null-forgiving operator (!) and null-coalescing defaults for frame.X and frame.Y in BurnPdfCommandHandler
- Improved runtime safety and prevented unintended JSON serialization of computed properties
2025-11-07 09:51:02 +01:00
Developer 02
15f6ee7be0 add ExportDocumentException 2025-11-06 21:53:36 +01:00
Developer 02
1051c5356f add CreateReportException 2025-11-06 21:52:35 +01:00
Developer 02
7b13350fbf add BurnAnnotationException 2025-11-06 21:51:20 +01:00
Developer 02
2bada327d8 add MergeDocumentException 2025-11-06 21:50:17 +01:00
Developer 02
a47729ebca feat(pdf): implement annotation burning logic in BurnPdfCommandHandler
- Added full implementation of BurnElementAnnotsToPDF method
- Integrated PdfEditor for background rendering
- Added annotation handling for form fields, images, and ink types
- Included error handling for PDF loading and saving
- Removed unused DigitalData.Core.Abstractions namespace
2025-11-06 21:49:24 +01:00
Developer 02
fcfed963b7 feat(pdf): add annotation burning logic and repository dependency to BurnPdfCommandHandler
- Inject IRepository<Signature> for accessing signature and annotation data
- Implement BurnAnnotsToPDF method to process annotations from DB or JSON
- Add BurnElementAnnotsToPDF and BurnInstantJSONAnnotsToPDF stubs
- Integrate EF Core Include for loading related entities
2025-11-06 21:30:51 +01:00
Developer 02
b2ace61cd4 refactor(BurnPdfCommandHandler): inject AnnotationManager 2025-11-06 21:20:57 +01:00
Developer 02
77cdc83a4e add AnnotationManager as scoped 2025-11-06 21:19:05 +01:00
Developer 02
a611df4914 inject LicenseManager 2025-11-06 21:09:46 +01:00
Developer 02
1341f69ab1 move gdpucture parmas configuration to application layer 2025-11-06 20:57:10 +01:00
Developer 02
0480513288 add gdpicture dependencies 2025-11-06 20:10:41 +01:00
Developer 02
9dbd1b16b5 inject pdf burner params 2025-11-06 19:57:56 +01:00
Developer 02
c649c93921 move pdf burner to application layer 2025-11-06 19:51:15 +01:00
Developer 02
1843119b1b add PDFBurnerParams 2025-11-06 19:39:43 +01:00
Developer 02
bc4371cb99 feat(PDFBurner): init for only .net 2025-11-06 19:34:39 +01:00
Developer 02
05130d6163 feat(FinishEnvelopeJob): enhance FinishEnvelopeJob logging for finalized envelopes
- Removed redundant initial info log.
- Added detailed logging after job execution:
  - Logs total finalized envelope count.
  - Logs UUIDs of finalized envelopes when available.
  - Logs success message when no envelopes were finalized.
2025-11-06 09:59:16 +01:00
567b9c9565 refactor(Envelope): remove unnecessary [NotMapped] attribute from DocResult property 2025-11-05 16:49:39 +01:00
b798181f91 feat(notification): implement handler to remove document result on signature removal 2025-11-05 15:26:48 +01:00
7e8fc25ec9 refactor: update core inf and abst.app 2025-11-05 14:31:24 +01:00
db76162697 Refactor: Convert envelope query filters to LINQ query syntax
- Replaced multiple sequential 'Where' calls with a single LINQ query expression
- Preserved all status filters (Include, Ignore, Min, Max)
- Preserved DocResult filtering logic
- Improves readability while keeping behavior identical
2025-11-05 13:39:10 +01:00
2b4573ea73 feat(ReadEnvelopeQuery): implement ReadEnvelopeQueryHandler with filtering support
- Added `ReadEnvelopeQueryHandler` to handle `ReadEnvelopeQuery`.
- Supports filtering envelopes by:
  - Included and ignored `EnvelopeStatus` values.
  - Minimum and maximum status.
  - Presence or absence of `DocResult`.
- Uses repository query extension methods and AutoMapper to map to `EnvelopeDto`.
2025-11-05 13:32:30 +01:00
4a043ed247 feat(FinishEnvelopeJob): update Execute to add cancelation token 2025-11-05 12:58:35 +01:00
a62a035ec6 feat(FinishEnvelopeJob): enhance FinishEnvelopeJob to fetch signed envelopes
- Added MediatR dependency to query envelopes
- Injected GdPictureOptions via IOptions
- Updated Execute method to fetch envelopes with status 'EnvelopeCompletelySigned'
- Preserved logging with job details
- Prepared loop for further processing of envelopes
2025-11-05 12:55:49 +01:00
1713a65014 refactor(Program): simplify Quartz job registration and add EnvelopeTaskApiJob
- Replaced manual FinishEnvelopeJob Quartz setup with ScheduleJobDefault extension
- Added scheduling for EnvelopeTaskApiJob
- Updated using directives to include EnvelopeGenerator.Finalizer namespace
- Improved maintainability by removing redundant Quartz configuration logic
2025-11-05 11:24:35 +01:00
695d7c83e0 feat(Extensions): add Quartz job scheduling extension methods
- Introduced ScheduleJobDefault<TJob> extension for IServiceCollectionQuartzConfigurator
- Allows scheduling jobs using either a cron expression or configuration-based cron settings
- Includes validation for missing or invalid cron expressions
2025-11-05 10:43:25 +01:00
428f71863d create EnvelopeTaskApi 2025-11-05 09:41:02 +01:00
cce2f8f90e refactor(Worker): rename as FinishEnvelopeJob 2025-11-05 09:34:56 +01:00
Developer 02
86c9fdfcd7 refactor: inject IScheduler via DI instead of using StdSchedulerFactory directly 2025-11-04 17:34:36 +01:00
Developer 02
89ec887510 feat(quartz): integrate Quartzmon dashboard and CommandDotNet
- Added `Quartzmon` package and configured its middleware for job monitoring.
- Integrated `CommandDotNet.Execution` for command-line execution support.
- Updated using directives and service registrations accordingly.
- Preserved existing Serilog logging, DB context, and EnvelopeGenerator setup.
2025-11-04 17:29:07 +01:00
Developer 02
7d5b988842 fix(middleware): add UseRouting before UseAuthorization
Added `app.UseRouting()` in the middleware pipeline to ensure proper endpoint routing before authorization and controller mapping.
2025-11-04 17:18:29 +01:00
Developer 02
3c456562cc refactor: replace QuartzHostedService with QuartzServer and remove unnecessary using
- Replaced `AddQuartzHostedService` with `AddQuartzServer` for better Quartz integration.
- Removed `Microsoft.Extensions.Options` using as it was unused.
- Updated Quartz job naming to remove GUID and simplify identity.
- Minor code cleanup in using statements and regions.
2025-11-04 17:06:00 +01:00
Developer 02
4d6b01030c refactor(startup): migrate from generic Host to WebApplication and integrate Web API support
- Replaced Host.CreateApplicationBuilder with WebApplication.CreateBuilder
- Added Web API service registrations (Controllers, Swagger)
- Organized startup into clear regions: Logging, Configuration, Worker, Services, Middleware
- Introduced Swagger and HTTPS middleware for API
- Improved structure and readability of Program.cs
2025-11-04 16:12:21 +01:00
Developer 02
75e7e9925b feat(Program): make Quartz cron schedule configurable via appsettings
- Replaced hardcoded cron expression with configuration-based `Worker:CronExpression`.
- Throws descriptive exception if cron expression is missing.
- Keeps previous worker and DB context setup unchanged.
2025-11-04 15:37:20 +01:00
Developer 02
0a175b9e9d refactor: remove unnecessary while loop in Worker.Execute 2025-11-04 15:18:38 +01:00
Developer 02
f611e74de1 refactor(config): allow GdPicture license key from configuration
- Updated GdPictureOptions setup to read license key from `GdPictureLicenseKey` config value.
- Falls back to reading from third-party module if config key is not set.
2025-11-04 14:56:44 +01:00
Developer 02
08ca116628 refactor(worker): replace BackgroundService with Quartz IJob for scheduled execution
- Removed inheritance from BackgroundService
- Implemented Quartz IJob interface for better scheduling control
- Replaced ExecuteAsync with Execute(IJobExecutionContext)
- Updated cancellation handling to use context.CancellationToken
2025-11-04 14:49:56 +01:00
Developer 02
4997f7d75c feat: add in-memory database support via appsettings UseInMemoryDb flag
- Introduced conditional EF Core configuration to support InMemoryDatabase for testing or lightweight runs.
- Added `UseInMemoryDb` config flag read from appsettings.
- Retained SQL Server as the default when the flag is false.
- Added missing Quartz namespace import.
2025-11-04 13:44:39 +01:00
b5cd42b6fa add WorkerOptions.
- bind IntervalInMin with worker task delay
2025-11-03 16:38:41 +01:00
187f4a42fc feat(GdPictureOptions): Created to configure parameters related to GdPicture.
- Configure the GdPicture license key via a third-party module entity.
2025-11-03 15:36:11 +01:00
23d4b2f31e chore: move finalizer to presentation layer 2025-11-03 14:51:13 +01:00
8e71e5b4bb feat(third-party-modules): add query and handler for reading third-party modules with filtering by name and active status 2025-11-03 14:49:54 +01:00
b693615561 feat(ThirdPartyModuleDto): create DTO of ThirdPartyModule with mapping profile 2025-11-03 14:30:45 +01:00
36dc9266bc Add ThirdPartyModule entity for 3rd party module tracking
- Created ThirdPartyModule class in EnvelopeGenerator.Domain.Entities
- Mapped to TBDD_3RD_PARTY_MODULES table with appropriate columns
- Added properties: Id, Active, Name, Description, License, Version, AddedWho, AddedWhen, ChangedWho, ChangedWhen
- Configured data annotations for primary key, required fields, string lengths, and nullable support
- Conditional nullable support based on compilation symbols
2025-11-03 13:05:24 +01:00
9aabe270b4 add appsettings for PDF burner 2025-11-03 11:56:10 +01:00
b303b7be06 chore(Finalizer.Program): enhance configuration and environment-specific JSON loading
- Changed Serilog configuration file from `appsettings.json` to `appsettings.Logging.json`.
- Added logging for application startup.
- Dynamically load environment-specific appsettings JSON files, excluding `Development` and `migration` files.
2025-11-03 11:41:55 +01:00
02937360ea chore(Finalizer.appsettings): update Serilog configuration to increase log verbosity and retention
- Changed default minimum log level from Information to Verbose.
- Updated console sink to use Verbose level.
- Renamed and consolidated log files for Verbose, Debug, Info, Warning, Error, and Fatal levels.
- Increased retained file count from 7 to 30 for all log levels.
2025-11-03 11:26:14 +01:00
c2735b92e0 feat(logging): enhance Serilog configuration with separate level-based log files 2025-11-03 10:48:35 +01:00
568f43186c refactor(Finalizer.Program): simplify EnvelopeGenerator service registration using AddEnvelopeGenerator extension
- Replaced manual service setup with AddEnvelopeGenerator fluent configuration
- Added EnvelopeGenerator.DependencyInjection namespace
- Integrated distributed SQL Server cache for EG services
- Improved DI structure and reduced obsolete warnings
2025-11-03 10:24:09 +01:00
3bc5439b5a feat(dependency-injection): add service configuration validation and simplify setup
- Removed unnecessary IConfiguration and SqlServerCacheOptions parameters from AddEnvelopeGenerator
- Added EnsureAllServicesConfigured() to validate that all required service methods are invoked
- Introduced _addingStatus dictionary to track configuration status
- Renamed internal queue field to _serviceRegs for consistency
- Added AddServices() method to explicitly register EnvelopeGenerator services
- Improved AddLocalization() to support optional custom localization options
2025-11-03 09:30:43 +01:00
22b494a262 refactor(di): unify Application and Infrastructure DI registrations under a central method
- Added central AddEnvelopeGenerator extension to aggregate existing DI setups
- Introduced EGConfiguration for modular service registration
- Standardized configuration pattern for Application and Infrastructure layers
- Simplified distributed cache and localization registration
2025-11-03 08:44:22 +01:00
209785dda5 refactor(DependencyInjection): created to handle DependencyInjection 2025-10-31 11:31:39 +01:00
44ea893f05 move memory-cache injection to Application-layer 2025-10-31 10:58:22 +01:00
b4aa7984aa chore(Application): Update your packages with vulnerabilities
- Added `SixLabors.ImageSharp` (v3.1.12) to main package references.
- Added `System.Formats.Asn1` for all target frameworks:
  - net7.0 → v8.0.2
  - net8.0, net9.0 → v9.0.10
2025-10-31 10:34:25 +01:00
7c5a505ad1 add serilog 2025-10-31 10:23:11 +01:00
bd6d57e1e8 feat: integrate EnvelopeGenerator infrastructure, DB context, and services
- Added references to EnvelopeGenerator.Application, Infrastructure, and EF Core.
- Configured DB context with SQL Server connection string from configuration.
- Registered EnvelopeGenerator services and infrastructure with dependency injection.
- Preserved warnings suppression for obsolete members.
- Structured DI registration under a dedicated region for clarity.
2025-10-30 17:05:45 +01:00
8403ce2c6a init EnvelopeGenerator.Finalizer 2025-10-30 16:31:22 +01:00
59 changed files with 2024 additions and 38 deletions

View File

@@ -0,0 +1,12 @@
namespace EnvelopeGenerator.Application.Common.Configurations;
/// <summary>
///
/// </summary>
public class GdPictureParams
{
/// <summary>
///
/// </summary>
public string License { get; set; } = null!;
}

View File

@@ -0,0 +1,78 @@
using EnvelopeGenerator.Application.Common.Interfaces.Model;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
namespace EnvelopeGenerator.Application.Common.Configurations;
/// <summary>
///
/// </summary>
public class PDFBurnerParams : ITextStyle
{
/// <summary>
///
/// </summary>
public int ConcurrencyLimit { get; set; } = 5;
/// <summary>
///
/// </summary>
public IEnumerable<string> IgnoredLabels { get; set; } = new List<string>
{
"Date", "Datum", "ZIP", "PLZ", "Place", "Ort", "Position", "Stellung"
};
/// <summary>
///
/// </summary>
public double TopMargin { get; set; } = 0.1;
/// <summary>
///
/// </summary>
public double YOffset { get; set; } = -0.3;
/// <summary>
///
/// </summary>
public string FontName { get; set; } = "Arial";
/// <summary>
///
/// </summary>
public int FontSize { get; set; } = 8;
/// <summary>
///
/// </summary>
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
public FontStyle FontStyle { get; set; } = FontStyle.Italic;
/// <summary>
///
/// </summary>
public Dictionary<string, int> IndexOfAnnot { get; init; } = new()
{
{ string.Empty, 0 },
{ "seal", 0 },
{ "position", 1 },
{ "city", 2 },
{ "date", 3 }
};
/// <summary>
///
/// </summary>
public int DefaultIndexOfAnnot { get; set; } = 0;
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public int GetAnnotationIndex(string name) => IndexOfAnnot.TryGetValue(name, out var value) ? value : DefaultIndexOfAnnot;
}

View File

@@ -36,6 +36,7 @@ public class MappingProfile : Profile
CreateMap<Domain.Entities.Receiver, ReceiverDto>();
CreateMap<Domain.Entities.EnvelopeReceiverReadOnly, EnvelopeReceiverReadOnlyDto>();
CreateMap<ElementAnnotation, AnnotationDto>();
CreateMap<ThirdPartyModule, ThirdPartyModuleDto>();
// DTO to Entity mappings
CreateMap<ConfigDto, Config>();

View File

@@ -0,0 +1,122 @@
using EnvelopeGenerator.Application.Exceptions;
namespace EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
/// <summary>
///
/// </summary>
public class Annotation
{
private string? _id;
/// <summary>
///
/// </summary>
public int EnvelopeId { get; private set; } = 0;
/// <summary>
///
/// </summary>
public int ReceiverId { get; private set; } = 0;
/// <summary>
///
/// </summary>
public int Index { get; private set; } = 0;
/// <summary>
///
/// </summary>
public string EgName { get; private set; } = string.Empty;
/// <summary>
///
/// </summary>
public bool HasStructuredID { get; private set; } = false;
/// <summary>
///
/// </summary>
public bool IsLabel
{
get
{
if (string.IsNullOrEmpty(EgName))
return false;
var parts = EgName.Split('_');
return parts.Length > 1 && parts[1] == "label";
}
}
/// <summary>
///
/// </summary>
public string? Id
{
get => _id;
set
{
_id = value;
if (string.IsNullOrWhiteSpace(value))
throw new BurnAnnotationException("The identifier of annotation is null or empty.");
var parts = value.Split('#');
if (parts.Length != 4)
return;
// throw new BurnAnnotationException($"The identifier of annotation has more or less than 4 sub-part. Id: {_id}");
if (!int.TryParse(parts[0], out int envelopeId))
throw new BurnAnnotationException($"The envelope ID of annotation is not integer. Id: {_id}");
EnvelopeId = envelopeId;
if (!int.TryParse(parts[1], out int receiverId))
throw new BurnAnnotationException($"The receiver ID of annotation is not integer. Id: {_id}");
ReceiverId = receiverId;
if (!int.TryParse(parts[2], out int index))
throw new BurnAnnotationException($"The index of annotation is not integer. Id: {_id}");
Index = index;
EgName = parts[3];
HasStructuredID = true;
}
}
/// <summary>
///
/// </summary>
public List<double>? Bbox { get; set; }
/// <summary>
///
/// </summary>
public string? Type { get; set; }
/// <summary>
///
/// </summary>
public bool IsSignature { get; set; }
/// <summary>
///
/// </summary>
public string? ImageAttachmentId { get; set; }
/// <summary>
///
/// </summary>
public Lines? Lines { get; set; }
/// <summary>
///
/// </summary>
public int PageIndex { get; set; }
/// <summary>
///
/// </summary>
public string? StrokeColor { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
/// <summary>
///
/// </summary>
/// <param name="Binary"></param>
/// <param name="ContentType"></param>
public record Attachment(string Binary, string ContentType);

View File

@@ -0,0 +1,8 @@
namespace EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
/// <param name="Value"></param>
public record FormFieldValue(string Name, string? Value = null);

View File

@@ -0,0 +1,8 @@
namespace EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
/// <summary>
///
/// </summary>
/// <param name="Lines"></param>
/// <param name="StrokeColor"></param>
public record Ink(Lines Lines, string? StrokeColor = null);

View File

@@ -0,0 +1,50 @@
namespace EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
/// <summary>
///
/// </summary>
public class InstantData
{
/// <summary>
///
/// </summary>
public List<Annotation>? Annotations { get; set; }
/// <summary>
///
/// </summary>
public IEnumerable<List<Annotation>>? AnnotationsByReceiver
{
get
{
return Annotations?
.Where(a => a.HasStructuredID)
.GroupBy(a => a.ReceiverId)
.Select(g => g.ToList());
}
}
/// <summary>
///
/// </summary>
public IEnumerable<List<Annotation>>? UnstructuredAnnotations
{
get
{
return Annotations?
.Where(a => !a.HasStructuredID)
.GroupBy(a => a.ReceiverId)
.Select(g => g.ToList());
}
}
/// <summary>
///
/// </summary>
public Dictionary<string, Attachment>? Attachments { get; set; }
/// <summary>
///
/// </summary>
public List<FormFieldValue>? FormFieldValues { get; set; }
}

View File

@@ -0,0 +1,7 @@
namespace EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
/// <summary>
///
/// </summary>
/// <param name="Points"></param>
public record Lines(List<List<List<float>>> Points);

View File

@@ -0,0 +1,57 @@
namespace EnvelopeGenerator.Application.Common.Dto;
/// <summary>
///
/// </summary>
public record ThirdPartyModuleDto
{
/// <summary>
///
/// </summary>
public int Id { get; init; }
/// <summary>
///
/// </summary>
public bool Active { get; init; }
/// <summary>
///
/// </summary>
public string Name { get; init; } = default!;
/// <summary>
///
/// </summary>
public string? Description { get; init; }
/// <summary>
///
/// </summary>
public string License { get; init; } = default!;
/// <summary>
///
/// </summary>
public string Version { get; init; } = default!;
/// <summary>
///
/// </summary>
public string? AddedWho { get; init; }
/// <summary>
///
/// </summary>
public DateTime? AddedWhen { get; init; }
/// <summary>
///
/// </summary>
public string? ChangedWho { get; init; }
/// <summary>
///
/// </summary>
public DateTime? ChangedWhen { get; init; }
}

View File

@@ -0,0 +1,176 @@
using EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
using EnvelopeGenerator.Domain.Constants;
using GdPicture14;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using SixLabors.ImageSharp;
using System.Drawing;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Interfaces.Model;
using EnvelopeGenerator.Application.Common.Configurations;
namespace EnvelopeGenerator.Application.Common.Extensions;
/// <summary>
///
/// </summary>
public static class GdPictureExtensions
{
/// <summary>
///
/// </summary>
/// <param name="manager"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="page"></param>
/// <param name="value"></param>
/// <param name="textStyle"></param>
public static void AddFormFieldValue(this AnnotationManager manager, double x, double y, double width, double height, int page, string value, ITextStyle textStyle)
{
manager.SelectPage(page);
// Add the text annotation
var ant = manager.AddTextAnnot((float)x, (float)y, (float)width, (float)height, value);
// Set the font properties
ant.FontName = textStyle.FontName;
ant.FontSize = textStyle.FontSize;
ant.FontStyle = textStyle.FontStyle;
manager.SaveAnnotationsToPage();
}
/// <summary>
///
/// </summary>
/// <param name="manager"></param>
/// <param name="pAnnotation"></param>
/// <param name="formFieldValue"></param>
/// <param name="options"></param>
public static void AddFormFieldValue(this AnnotationManager manager, Annotation pAnnotation, FormFieldValue formFieldValue, PDFBurnerParams options)
{
var ffIndex = options.GetAnnotationIndex(pAnnotation.EgName);
// Convert pixels to Inches
var oBounds = pAnnotation.Bbox?.Select(points => points.ToInches()).ToList();
if (oBounds is null || oBounds.Count < 4)
return;
double oX = oBounds[0];
double oY = oBounds[1] + options.YOffset * ffIndex + options.TopMargin;
double oWidth = oBounds[2];
double oHeight = oBounds[3];
manager.SelectPage(pAnnotation.PageIndex + 1);
// Add the text annotation
var ant = manager.AddTextAnnot((float)oX, (float)oY, (float)oWidth, (float)oHeight, formFieldValue.Value);
// Set the font properties
ant.FontName = options.FontName;
ant.FontSize = options.FontSize;
ant.FontStyle = options.FontStyle;
manager.SaveAnnotationsToPage();
}
/// <summary>
///
/// </summary>
/// <param name="manager"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="page"></param>
/// <param name="base64"></param>
public static void AddImageAnnotation(this AnnotationManager manager, 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);
}
/// <summary>
///
/// </summary>
/// <param name="manager"></param>
/// <param name="pAnnotation"></param>
/// <param name="pAttachments"></param>
public static void AddImageAnnotation(this AnnotationManager manager, Annotation pAnnotation, Dictionary<string, Attachment> pAttachments)
{
var oAttachment = pAttachments
.Where(a => a.Key == pAnnotation.ImageAttachmentId)
.SingleOrDefault();
if (oAttachment.Value == null)
return;
// Convert pixels to Inches
var oBounds = pAnnotation.Bbox?.Select(post => post.ToInches()).ToList();
if (oBounds is null || oBounds.Count < 4)
return;
var oX = oBounds[0];
var oY = oBounds[1];
var oWidth = oBounds[2];
var oHeight = oBounds[3];
manager.SelectPage(pAnnotation.PageIndex + 1);
manager.AddEmbeddedImageAnnotFromBase64(oAttachment.Value.Binary, (float)oX, (float)oY, (float)oWidth, (float)oHeight);
}
/// <summary>
///
/// </summary>
/// <param name="manager"></param>
/// <param name="page"></param>
/// <param name="value"></param>
public static void AddInkAnnotation(this AnnotationManager manager, int page, string value)
{
var ink = JsonConvert.DeserializeObject<Ink>(value);
var oSegments = ink?.Lines.Points;
var oColor = ColorTranslator.FromHtml(ink?.StrokeColor ?? "#000000");
manager.SelectPage(page);
if (oSegments is null)
return;
foreach (var oSegment in oSegments)
{
var oPoints = oSegment
.Select(points => points.ToPointF())
.ToArray();
manager.AddFreeHandAnnot(oColor, oPoints);
}
}
/// <summary>
///
/// </summary>
/// <param name="manager"></param>
/// <param name="pAnnotation"></param>
public static void AddInkAnnotation(this AnnotationManager manager, Annotation pAnnotation)
{
var oSegments = pAnnotation.Lines?.Points;
var oColor = ColorTranslator.FromHtml(pAnnotation.StrokeColor ?? "#000000");
manager.SelectPage(pAnnotation.PageIndex + 1);
if (oSegments is null)
return;
foreach (var oSegment in oSegments)
{
var oPoints = oSegment
.Select(points => points.ToPointF())
.ToArray();
manager.AddFreeHandAnnot(oColor, oPoints);
}
}
}

View File

@@ -0,0 +1,40 @@
using System.Drawing;
namespace EnvelopeGenerator.Application.Common.Extensions;
/// <summary>
///
/// </summary>
public static class MathematExtensions
{
/// <summary>
///
/// </summary>
/// <param name="points"></param>
/// <returns></returns>
public static PointF ToPointF(this List<float> points)
{
var pointsInch = points.Select(ToInches).ToList();
return new PointF(pointsInch[0], pointsInch[1]);
}
/// <summary>
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static double ToInches(this double value)
{
return value / 72.0;
}
/// <summary>
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static float ToInches(this float value)
{
return value / 72f;
}
}

View File

@@ -0,0 +1,27 @@
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
namespace EnvelopeGenerator.Application.Common.Interfaces.Model;
/// <summary>
///
/// </summary>
public interface ITextStyle
{
/// <summary>
///
/// </summary>
public string FontName { get; set; }
/// <summary>
///
/// </summary>
public int FontSize { get; set; }
/// <summary>
///
/// </summary>
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
public FontStyle FontStyle { get; set; }
}

View File

@@ -0,0 +1,47 @@
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature.Handlers;
/// <summary>
///
/// </summary>
public class RemoveDocResult : INotificationHandler<RemoveSignatureNotification>
{
private readonly IRepository<Envelope> _repo;
/// <summary>
///
/// </summary>
/// <param name="repository"></param>
public RemoveDocResult(IRepository<Envelope> repository)
{
_repo = repository;
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public Task Handle(RemoveSignatureNotification notification, CancellationToken cancel)
{
if(notification.EnvelopeId is null && notification.EnvelopeUuid is null)
return Task.CompletedTask;
return _repo.UpdateAsync(
envelope => envelope.DocResult = null,
query => {
if (notification.EnvelopeId is int envelopeId)
query = query.Where(envelope => envelope.Id == envelopeId);
if (notification.EnvelopeUuid is string uuid)
query = query.Where(envelope => envelope.Uuid == uuid);
return query;
}, cancel);
}
}

View File

@@ -2,11 +2,16 @@
using EnvelopeGenerator.Application.Common.Configurations;
using EnvelopeGenerator.Application.Common.Interfaces.Services;
using EnvelopeGenerator.Application.Services;
using EnvelopeGenerator.Application.ThirdPartyModules.Queries;
using MediatR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using QRCoder;
using System.Reflection;
using GdPicture14;
using EnvelopeGenerator.Application.Pdf.Behaviors;
namespace EnvelopeGenerator.Application;
@@ -20,9 +25,10 @@ public static class DependencyInjection
/// </summary>
/// <param name="services"></param>
/// <param name="config"></param>
/// <param name="usePdfBurner"></param>
/// <returns></returns>
[Obsolete("Use MediatR")]
public static IServiceCollection AddEnvelopeGeneratorServices(this IServiceCollection services, IConfiguration config)
public static IServiceCollection AddEnvelopeGeneratorServices(this IServiceCollection services, IConfiguration config, bool usePdfBurner = false)
{
//Inject CRUD Service and repositoriesad
services.TryAddScoped<IConfigService, ConfigService>();
@@ -47,6 +53,31 @@ public static class DependencyInjection
services.Configure<AuthenticatorParams>(config.GetSection(nameof(AuthenticatorParams)));
services.Configure<TotpSmsParams>(config.GetSection(nameof(TotpSmsParams)));
if (usePdfBurner)
{
services.Configure<PDFBurnerParams>(config.GetSection(nameof(PDFBurnerParams)));
services.AddOptions<GdPictureParams>()
.Configure((GdPictureParams opt, IServiceProvider provider) =>
{
opt.License = config["GdPictureLicenseKey"]
?? provider.GetRequiredService<IMediator>().ReadThirdPartyModuleLicenseAsync("GDPICTURE").GetAwaiter().GetResult()
?? throw new InvalidOperationException($"License record not found for key: {"GDPICTURE"}");
});
services.AddSingleton(provider =>
{
var license = provider.GetRequiredService<IOptions<GdPictureParams>>().Value.License;
var licenseManager = new LicenseManager();
licenseManager.RegisterKEY(license);
return licenseManager;
});
services.AddTransient(provider =>
{
// Ensure LicenseManager is resolved so that its constructor is called
_ = provider.GetRequiredService<LicenseManager>();
return new AnnotationManager();
});
}
services.AddHttpClientService<GtxMessagingParams>(config.GetSection(nameof(GtxMessagingParams)));
services.TryAddSingleton<ISmsSender, GTXSmsSender>();
services.TryAddSingleton<IEnvelopeSmsHandler, EnvelopeSmsHandler>();
@@ -56,8 +87,15 @@ public static class DependencyInjection
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
cfg.AddOpenBehaviors(new Type[] { typeof(AddReportBehavior), typeof(SavePdfBehavior) });
});
// Add memory cache
services.AddMemoryCache();
// Register mail services
services.AddScoped<IEnvelopeMailService, EnvelopeMailService>();
return services;
}
}

View File

@@ -14,7 +14,7 @@
<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.4.0" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.5.0" />
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
<PackageReference Include="DigitalData.Core.Client" Version="2.1.0" />
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
@@ -25,23 +25,35 @@
<PackageReference Include="Otp.NET" Version="1.4.0" />
<PackageReference Include="QRCoder" Version="1.6.0" />
<PackageReference Include="QRCoder-ImageSharp" Version="0.10.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
<PackageReference Include="UserManager" Version="1.1.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="System.Formats.Asn1" Version="8.0.2" />
<PackageReference Include="GdPicture" Version="14.3.19.1" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageReference Include="System.Formats.Asn1" Version="9.0.10" />
<PackageReference Include="GdPicture" Version="14.3.19.1" />
<PackageReference Include="GdPicture.runtimes.windows" Version="14.3.19.1" />
<PackageReference Include="GdPicture.runtimes.linux" Version="14.3.19.1" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageReference Include="System.Formats.Asn1" Version="9.0.10" />
<PackageReference Include="GdPicture" Version="14.3.19.1" />
<PackageReference Include="GdPicture.runtimes.windows" Version="14.3.19.1" />
<PackageReference Include="GdPicture.runtimes.linux" Version="14.3.19.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,18 +1,29 @@
using MediatR;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.Common.Query;
using EnvelopeGenerator.Application.Common.Dto;
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Application.Envelopes.Queries;
/// <summary>
/// Repräsentiert eine Abfrage für Umschläge.
/// </summary>
public record ReadEnvelopeQuery : EnvelopeQueryBase, IRequest
public record ReadEnvelopeQuery : EnvelopeQueryBase, IRequest<IEnumerable<EnvelopeDto>>
{
/// <summary>
/// Abfrage des Include des Umschlags
/// </summary>
public EnvelopeStatusQuery? Status { get; init; }
/// <summary>
///
/// </summary>
public bool? HasDocResult { get; init; }
}
/// <summary>
@@ -65,4 +76,53 @@ public record EnvelopeStatusQuery
/// Eine Liste von Statuswerten, die ignoriert werden werden.
/// </summary>
public EnvelopeStatus[]? Ignore { get; init; }
}
/// <summary>
///
/// </summary>
public class ReadEnvelopeQueryHandler : IRequestHandler<ReadEnvelopeQuery, IEnumerable<EnvelopeDto>>
{
private readonly IMapper _mapper;
private readonly IRepository<Envelope> _repo;
/// <summary>
///
/// </summary>
/// <param name="mapper"></param>
/// <param name="repo"></param>
public ReadEnvelopeQueryHandler(IMapper mapper, IRepository<Envelope> repo)
{
_mapper = mapper;
_repo = repo;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<IEnumerable<EnvelopeDto>> Handle(ReadEnvelopeQuery request, CancellationToken cancel)
{
var envelopesQ = _repo.Query.Where(request, notnull: false);
EnvelopeStatusQuery? statusQ = request.Status;
bool? hasDocResult = request.HasDocResult;
var filtered =
from envelope in envelopesQ
where (statusQ == null || statusQ.Include == null || statusQ.Include.Contains(envelope.Status))
&& (statusQ == null || statusQ.Ignore == null || !statusQ.Ignore.Contains(envelope.Status))
&& (statusQ == null || statusQ.Min == null || envelope.Status > statusQ.Min)
&& (statusQ == null || statusQ.Max == null || envelope.Status < statusQ.Max)
&& (!hasDocResult.HasValue || (hasDocResult.Value ? envelope.DocResult != null : envelope.DocResult == null))
select envelope;
var envelopes = await filtered.ToListAsync(cancel);
return _mapper.Map<IEnumerable<EnvelopeDto>>(envelopes);
}
}

View File

@@ -0,0 +1,19 @@
namespace EnvelopeGenerator.Application.Exceptions;
/// <summary>
///
/// </summary>
public class BurnAnnotationException : ApplicationException
{
/// <summary>
///
/// </summary>
public BurnAnnotationException(string message)
: base(message) { }
/// <summary>
///
/// </summary>
public BurnAnnotationException(string message, Exception innerException)
: base(message, innerException) { }
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EnvelopeGenerator.Application.Exceptions;
/// <summary>
///
/// </summary>
public class CreateReportException : ApplicationException
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public CreateReportException(string message)
: base(message) { }
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <param name="innerException"></param>
public CreateReportException(string message, Exception innerException)
: base(message, innerException) { }
}

View File

@@ -0,0 +1,22 @@
namespace EnvelopeGenerator.Application.Exceptions;
/// <summary>
///
/// </summary>
public class ExportDocumentException : ApplicationException
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public ExportDocumentException(string message)
: base(message) { }
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <param name="innerException"></param>
public ExportDocumentException(string message, Exception innerException)
: base(message, innerException) { }
}

View File

@@ -0,0 +1,22 @@
namespace EnvelopeGenerator.Application.Exceptions;
/// <summary>
///
/// </summary>
public class MergeDocumentException : ApplicationException
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public MergeDocumentException(string message)
: base(message) { }
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <param name="innerException"></param>
public MergeDocumentException(string message, Exception innerException)
: base(message, innerException) { }
}

View File

@@ -34,7 +34,7 @@ public record CreateHistoryCommand : EnvelopeReceiverQueryBase, IRequest<History
/// <summary>
///
/// </summary>
public DateTime AddedWhen { get; } = DateTime.Now;
public DateTime AddedWhen { get; } = DateTime.UtcNow;
/// <summary>
///

View File

@@ -0,0 +1,44 @@
using MediatR;
using EnvelopeGenerator.Application.Histories.Commands;
using EnvelopeGenerator.Domain.Constants;
namespace EnvelopeGenerator.Application.Pdf.Behaviors;
/// <summary>
///
/// </summary>
public class AddReportBehavior : IPipelineBehavior<BurnPdfCommand, byte[]>
{
private readonly ISender _sender;
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
public AddReportBehavior(ISender sender)
{
_sender = sender;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="next"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task<byte[]> Handle(BurnPdfCommand request, RequestHandlerDelegate<byte[]> next, CancellationToken cancel)
{
var docResult = await next(cancel);
var base64 = Convert.ToBase64String(docResult);
await _sender.Send(new CreateHistoryCommand()
{
EnvelopeId = request.EnvelopeId,
UserReference = "System",
Status = EnvelopeStatus.EnvelopeReportCreated,
}, cancel);
return docResult;
}
}

View File

@@ -0,0 +1,23 @@
using MediatR;
namespace EnvelopeGenerator.Application.Pdf.Behaviors;
/// <summary>
///
/// </summary>
public class SavePdfBehavior : IPipelineBehavior<BurnPdfCommand, byte[]>
{
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="next"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task<byte[]> Handle(BurnPdfCommand request, RequestHandlerDelegate<byte[]> next, CancellationToken cancel)
{
var docResult = await next(cancel);
var base64 = Convert.ToBase64String(docResult);
return docResult;
}
}

View File

@@ -0,0 +1,288 @@
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Application.Common.Configurations;
using EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Exceptions;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Domain.Entities;
using GdPicture14;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
namespace EnvelopeGenerator.Application.Pdf;
/// <summary>
///
/// </summary>
public record BurnPdfCommand(int? EnvelopeId = null, string? EnvelopeUuid = null) : IRequest<byte[]>;
/// <summary>
///
/// </summary>
public static class BurnPdfCommandExtensions
{
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="envelopeId"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public static Task<byte[]> BurnPdf(this ISender sender, int envelopeId, CancellationToken cancel = default)
=> sender.Send(new BurnPdfCommand(EnvelopeId: envelopeId), cancel);
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="envelopeUuid"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public static Task<byte[]> BurnPdf(this ISender sender, string envelopeUuid, CancellationToken cancel = default)
=> sender.Send(new BurnPdfCommand(EnvelopeUuid: envelopeUuid), cancel);
}
/// <summary>
///
/// </summary>
public class BurnPdfCommandHandler : IRequestHandler<BurnPdfCommand, byte[]>
{
private readonly PDFBurnerParams _options;
private readonly AnnotationManager _manager;
private readonly ILogger<BurnPdfCommandHandler> _logger;
private readonly IRepository<Envelope> _envRepo;
private readonly IRepository<Domain.Entities.DocumentStatus> _docStatusRepo;
/// <summary>
///
/// </summary>
/// <param name="pdfBurnerParams"></param>
/// <param name="manager"></param>
/// <param name="logger"></param>
/// <param name="envRepo"></param>
/// <param name="docStatusRepo"></param>
public BurnPdfCommandHandler(IOptions<PDFBurnerParams> pdfBurnerParams, AnnotationManager manager, ILogger<BurnPdfCommandHandler> logger, IRepository<Envelope> envRepo, IRepository<Domain.Entities.DocumentStatus> docStatusRepo)
{
_options = pdfBurnerParams.Value;
_manager = manager;
_docStatusRepo = docStatusRepo;
_logger = logger;
_envRepo = envRepo;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<byte[]> Handle(BurnPdfCommand request, CancellationToken cancel)
{
var envQuery =
request.EnvelopeId is not null ? _envRepo.Where(env => env.Id == request.EnvelopeId) :
request.EnvelopeUuid is not null ? _envRepo.Where(env => env.Uuid == request.EnvelopeUuid) :
throw new BadRequestException("Request validation failed: Either Envelope Id or Envelope Uuid must be provided.");
var envelope = await envQuery
.Include(env => env.Documents!).ThenInclude(doc => doc.Elements!).ThenInclude(element => element.Annotations)
.FirstOrDefaultAsync(cancel)
?? throw new BadRequestException($"Envelope could not be found. Request details:\n" +
request.ToJson(Format.Json.ForDiagnostics));
var doc = envelope.Documents?.FirstOrDefault()
?? throw new NotFoundException($"Document could not be located within the specified envelope. Request details:\n" +
request.ToJson(Format.Json.ForDiagnostics));
if (doc.ByteData is null)
throw new InvalidOperationException($"Document byte data is missing, indicating a potential data integrity issue. Request details:\n" +
request.ToJson(Format.Json.ForDiagnostics));
return doc.Elements?.SelectMany(e => e.Annotations ?? Enumerable.Empty<ElementAnnotation>()).Where(annot => annot is not null).Any() ?? false
? BurnElementAnnotsToPDF(doc.ByteData, doc.Elements)
: BurnInstantJSONAnnotsToPDF(doc.ByteData, await _docStatusRepo
.Where(status => status.EnvelopeId == envelope.Id)
.Select(status => status.Value)
.ToListAsync(cancel));
}
private byte[] BurnElementAnnotsToPDF(byte[] pSourceBuffer, List<Signature> elements)
{
// Add background
using var doc = PdfEditor.Pdf.FromMemory(pSourceBuffer);
// TODO: take the length from the largest y
pSourceBuffer = doc.Background(elements, 1.9500000000000002 * 0.93, 2.52 * 0.67)
.ExportStream()
.ToArray();
GdPictureStatus oResult;
using var oSourceStream = new MemoryStream(pSourceBuffer);
// Open PDF
oResult = _manager.InitFromStream(oSourceStream);
if (oResult != GdPictureStatus.OK)
throw new BurnAnnotationException($"Could not open document for burning: [{oResult}]");
// Imported from background (add to configuration)
var margin = 0.2;
var inchFactor = 72;
// Y offset of form fields
var keys = new[] { "position", "city", "date" }; // add to configuration
var unitYOffsets = 0.2;
var yOffsetsOfFF = keys
.Select((k, i) => new { Key = k, Value = unitYOffsets * i + 1 })
.ToDictionary(x => x.Key, x => x.Value);
// Add annotations
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 ?? default) - frameY * inchFactor;
var frameXShift = (frame.X ?? default) - frameX * inchFactor;
foreach (var annot in element.Annotations!)
{
if (!yOffsetsOfFF.TryGetValue(annot.Name, out var yOffsetofFF))
yOffsetofFF = 0;
var y = frameY + yOffsetofFF;
if (annot.Type == AnnotationType.PSPDFKit.FormField)
{
_manager.AddFormFieldValue(
(annot.X ?? default) / inchFactor,
y,
(annot.Width ?? default) / inchFactor,
(annot.Height ?? default) / inchFactor,
element.Page,
annot.Value,
_options
);
}
else if (annot.Type == AnnotationType.PSPDFKit.Image)
{
_manager.AddImageAnnotation(
(annot.X ?? default) / inchFactor,
annot.Name == "signature" ? ((annot.Y ?? default) - frameYShift) / inchFactor : y,
(annot.Width ?? default) / inchFactor,
(annot.Height ?? default) / inchFactor,
element.Page,
annot.Value
);
}
else if (annot.Type == AnnotationType.PSPDFKit.Ink)
{
_manager.AddInkAnnotation(element.Page, annot.Value);
}
}
}
// Save PDF
using var oNewStream = new MemoryStream();
oResult = _manager.SaveDocumentToPDF(oNewStream);
if (oResult != GdPictureStatus.OK)
throw new BurnAnnotationException($"Could not save document to stream: [{oResult}]");
_manager.Close();
return oNewStream.ToArray();
}
private byte[] BurnInstantJSONAnnotsToPDF(byte[] pSourceBuffer, List<string> pInstantJSONList)
{
GdPictureStatus oResult;
using var oSourceStream = new MemoryStream(pSourceBuffer);
// Open PDF
oResult = _manager.InitFromStream(oSourceStream);
if (oResult != GdPictureStatus.OK)
{
throw new BurnAnnotationException($"Could not open document for burning: [{oResult}]");
}
// Add annotation to PDF
foreach (var oJSON in pInstantJSONList)
{
try
{
if (oJSON is string json)
AddInstantJsonAnnotationToPdf(json);
}
catch (Exception ex)
{
_logger.LogWarning("Error in AddInstantJSONAnnotationToPDF - oJson: ");
_logger.LogWarning(oJSON);
throw new BurnAnnotationException("Adding Annotation failed", ex);
}
}
oResult = _manager.BurnAnnotationsToPage(RemoveInitialAnnots: true, VectorMode: true);
if (oResult != GdPictureStatus.OK)
{
throw new BurnAnnotationException($"Could not burn annotations to file: [{oResult}]");
}
// Save PDF
using var oNewStream = new MemoryStream();
oResult = _manager.SaveDocumentToPDF(oNewStream);
if (oResult != GdPictureStatus.OK)
{
throw new BurnAnnotationException($"Could not save document to stream: [{oResult}]");
}
_manager.Close();
return oNewStream.ToArray();
}
private void AddInstantJsonAnnotationToPdf(string instantJson)
{
var annotationData = JsonConvert.DeserializeObject<InstantData>(instantJson);
annotationData?.Annotations?.Reverse();
if (annotationData?.Annotations is null)
return;
foreach (var annotation in annotationData.Annotations)
{
_logger.LogDebug("Adding AnnotationID: {id}", annotation.Id);
switch (annotation.Type)
{
case AnnotationType.PSPDFKit.Image:
if (annotationData?.Attachments is not null)
_manager.AddImageAnnotation(annotation, annotationData.Attachments);
break;
case AnnotationType.PSPDFKit.Ink:
_manager.AddInkAnnotation(annotation);
break;
case AnnotationType.PSPDFKit.Widget:
// Add form field values
var formFieldValue = annotationData?.FormFieldValues?
.FirstOrDefault(fv => fv.Name == annotation.Id);
if (formFieldValue != null && !_options.IgnoredLabels.Contains(formFieldValue.Value))
{
_manager.AddFormFieldValue(annotation, formFieldValue, _options);
}
break;
}
}
}
}

View File

@@ -0,0 +1,92 @@
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Application.ThirdPartyModules.Queries;
/// <summary>
///
/// </summary>
public record ReadThirdPartyModuleQuery : IRequest<IEnumerable<ThirdPartyModuleDto>>
{
/// <summary>
///
/// </summary>
public string? Name { get; init; }
/// <summary>
///
/// </summary>
public bool? Active { get; init; }
}
/// <summary>
///
/// </summary>
public record ReadThirdPartyModuleQueryHandler : IRequestHandler<ReadThirdPartyModuleQuery, IEnumerable<ThirdPartyModuleDto>>
{
private readonly IMapper _mapper;
private readonly IRepository<ThirdPartyModule> _repo;
/// <summary>
///
/// </summary>
/// <param name="mapper"></param>
/// <param name="repo"></param>
public ReadThirdPartyModuleQueryHandler(IMapper mapper, IRepository<ThirdPartyModule> repo)
{
_mapper = mapper;
_repo = repo;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<IEnumerable<ThirdPartyModuleDto>> Handle(ReadThirdPartyModuleQuery request, CancellationToken cancel)
{
var query = _repo.Query;
if(request.Name is string name)
query = query.Where(m => m.Name == name);
if (request.Active is bool active)
query = query.Where(m => m.Active == active);
var modules = await query.ToListAsync(cancel);
return _mapper.Map<IEnumerable<ThirdPartyModuleDto>>(modules);
}
}
/// <summary>
///
/// </summary>
public static class ReadThirdPartyModuleQueryExtensions
{
/// <summary>
///
/// </summary>
/// <param name="mediator"></param>
/// <param name="name"></param>
/// <param name="active"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public static async Task<string?> ReadThirdPartyModuleLicenseAsync(this IMediator mediator, string name, bool active = true, CancellationToken cancel = default)
{
var modules = await mediator.Send(new ReadThirdPartyModuleQuery()
{
Name = name,
Active = active,
}, cancel);
return modules.FirstOrDefault()?.License;
}
}

View File

@@ -70,8 +70,8 @@
<Reference Include="DigitalData.Controls.DocumentViewer, Version=1.9.8.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Controls.DocumentViewer.1.9.8\lib\net462\DigitalData.Controls.DocumentViewer.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.Abstraction.Application, Version=1.4.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Core.Abstraction.Application.1.4.0\lib\net462\DigitalData.Core.Abstraction.Application.dll</HintPath>
<Reference Include="DigitalData.Core.Abstraction.Application, Version=1.5.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Core.Abstraction.Application.1.5.0\lib\net462\DigitalData.Core.Abstraction.Application.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.Abstractions, Version=4.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Core.Abstractions.4.3.0\lib\net462\DigitalData.Core.Abstractions.dll</HintPath>

View File

@@ -32,7 +32,7 @@ Public Class frmFinalizePDF
#Disable Warning BC40000 ' Type or member is obsolete
Factory.Shared _
.BehaveOnPostBuild(PostBuildBehavior.Ignore) _
.AddEnvelopeGeneratorInfrastructureServices(
.AddEGInfrastructureServices(
Sub(opt)
opt.AddDbTriggerParams(
Sub(triggers)

View File

@@ -3,7 +3,7 @@
<package id="AutoMapper" version="10.1.1" targetFramework="net462" />
<package id="BouncyCastle.Cryptography" version="2.5.0" targetFramework="net462" />
<package id="DigitalData.Controls.DocumentViewer" version="1.9.8" targetFramework="net462" />
<package id="DigitalData.Core.Abstraction.Application" version="1.4.0" targetFramework="net462" />
<package id="DigitalData.Core.Abstraction.Application" version="1.5.0" targetFramework="net462" />
<package id="DigitalData.Core.Abstractions" version="4.3.0" targetFramework="net462" />
<package id="DigitalData.Modules.Base" version="1.3.8" targetFramework="net462" />
<package id="DigitalData.Modules.Config" version="1.3.0" targetFramework="net462" />

View File

@@ -72,8 +72,8 @@
<Reference Include="DevExpress.XtraEditors.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.XtraGauges.v21.2.Core, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a, processorArchitecture=MSIL" />
<Reference Include="DevExpress.XtraReports.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a, processorArchitecture=MSIL" />
<Reference Include="DigitalData.Core.Abstraction.Application, Version=1.4.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Core.Abstraction.Application.1.4.0\lib\net462\DigitalData.Core.Abstraction.Application.dll</HintPath>
<Reference Include="DigitalData.Core.Abstraction.Application, Version=1.5.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Core.Abstraction.Application.1.5.0\lib\net462\DigitalData.Core.Abstraction.Application.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.Abstractions, Version=4.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Core.Abstractions.4.3.0\lib\net462\DigitalData.Core.Abstractions.dll</HintPath>

View File

@@ -72,7 +72,7 @@ Namespace Jobs
#Disable Warning BC40000 ' Type or member is obsolete
Factory.Shared _
.BehaveOnPostBuild(PostBuildBehavior.Ignore) _
.AddEnvelopeGeneratorInfrastructureServices(
.AddEGInfrastructureServices(
Sub(opt)
opt.AddDbTriggerParams(
Sub(triggers)

View File

@@ -2,7 +2,7 @@
<packages>
<package id="AutoMapper" version="10.1.1" targetFramework="net462" />
<package id="BouncyCastle.Cryptography" version="2.5.0" targetFramework="net462" />
<package id="DigitalData.Core.Abstraction.Application" version="1.4.0" targetFramework="net462" />
<package id="DigitalData.Core.Abstraction.Application" version="1.5.0" targetFramework="net462" />
<package id="DigitalData.Core.Abstractions" version="4.3.0" targetFramework="net462" />
<package id="DigitalData.Modules.Base" version="1.3.8" targetFramework="net462" />
<package id="DigitalData.Modules.Config" version="1.3.0" targetFramework="net462" />

View File

@@ -0,0 +1,98 @@
using DigitalData.EmailProfilerDispatcher;
using DigitalData.UserManager.DependencyInjection;
using EnvelopeGenerator.Application;
using EnvelopeGenerator.Infrastructure;
using Microsoft.Extensions.Caching.SqlServer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using static EnvelopeGenerator.Infrastructure.DependencyInjection;
namespace EnvelopeGenerator.DependencyInjection;
public static class DependencyInjection
{
public static IServiceCollection AddEnvelopeGenerator(this IServiceCollection services, Action<EGConfiguration> options)
{
var egConfig = new EGConfiguration();
options.Invoke(egConfig);
egConfig.EnsureAllServicesConfigured();
egConfig.RegisterAll(services);
// Add envelope generator services
#pragma warning disable CS0618
services.AddUserManager<EGDbContext>();
#pragma warning restore CS0618
services.AddDispatcher<EGDbContext>();
return services;
}
public record EGConfiguration
{
internal readonly Queue<Action<IServiceCollection>> _serviceRegs = new();
internal void RegisterAll(IServiceCollection services)
{
while (_serviceRegs.Count > 0)
_serviceRegs.Dequeue().Invoke(services);
}
// TODO: update to use attributes and reflections instead of _addingStatus-dictionary
private readonly Dictionary<string, bool> _addingStatus = new ()
{
{ nameof(AddLocalization), false },
{ nameof(AddDistributedSqlServerCache), false },
{ nameof(AddInfrastructure), false },
{ nameof(AddServices), false },
};
public EGConfiguration AddLocalization(Action<IServiceCollection>? customLocalizationOptions = null)
{
_serviceRegs.Enqueue(customLocalizationOptions ?? (s => s.AddLocalization()));
_addingStatus[nameof(AddLocalization)] = true;
return this;
}
public EGConfiguration AddDistributedSqlServerCache(Action<SqlServerCacheOptions> setupAction)
{
_serviceRegs.Enqueue(s => s.AddDistributedSqlServerCache(setupAction));
_addingStatus[nameof(AddDistributedSqlServerCache)] = true;
return this;
}
public EGConfiguration AddInfrastructure(Action<EGInfrastructureConfiguration> options)
{
#pragma warning disable CS0618
_serviceRegs.Enqueue(s => s.AddEGInfrastructureServices(options));
#pragma warning restore CS0618
_addingStatus[nameof(AddInfrastructure)] = true;
return this;
}
public EGConfiguration AddServices(IConfiguration config, bool usePdfBurner = false)
{
#pragma warning disable CS0618
_serviceRegs.Enqueue(s => s.AddEnvelopeGeneratorServices(config, usePdfBurner));
#pragma warning restore CS0618
_addingStatus[nameof(AddServices)] = true;
return this;
}
internal void EnsureAllServicesConfigured()
{
var missingServices = _addingStatus
.Where(kv => !kv.Value)
.Select(kv => kv.Key)
.ToList();
if (missingServices.Count > 0)
{
var missingList = string.Join(", ", missingServices);
throw new InvalidOperationException(
$"Service configuration incomplete. The following required service methods were not called: {missingList}. " +
"Please ensure all necessary configuration methods are invoked before building the application.");
}
}
}
}

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.10" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,13 @@
namespace EnvelopeGenerator.Domain.Constants
{
public static class AnnotationType
{
public static class PSPDFKit
{
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";
}
}
}

View File

@@ -2,7 +2,6 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using EnvelopeGenerator.Domain.Constants;
#if NETFRAMEWORK
using System;
using System.Collections.Generic;
@@ -134,14 +133,12 @@ public class Envelope
= false;
#endif
[NotMapped]
[Column("DOC_RESULT")]
public byte[]
#if NET
?
#endif
DocResult
{ get; set; }
DocResult { get; set; }
[ForeignKey("EnvelopeTypeId")]
public virtual EnvelopeType

View File

@@ -5,6 +5,8 @@ using System.ComponentModel.DataAnnotations.Schema;
#if NETFRAMEWORK
using System;
using System.Collections.Generic;
#elif NET
using System.Text.Json.Serialization;
#endif
namespace EnvelopeGenerator.Domain.Entities
@@ -120,13 +122,17 @@ public class Signature : ISignature, IHasReceiver
#endif
Annotations { get; set; }
#if NETFRAMEWORK
#if NET
[JsonIgnore]
#endif
[NotMapped]
public double Top => Math.Round(Y, 5);
#if NET
[JsonIgnore]
#endif
[NotMapped]
public double Left => Math.Round(X, 5);
#endif
}
#if NETFRAMEWORK

View File

@@ -0,0 +1,73 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
#if NETFRAMEWORK
using System;
#endif
namespace EnvelopeGenerator.Domain.Entities
{
[Table("TBDD_3RD_PARTY_MODULES")]
public class ThirdPartyModule
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("GUID")]
public int Id { get; set; }
[Required]
[Column("ACTIVE")]
public bool Active { get; set; }
[Required]
[StringLength(50)]
[Column("NAME")]
public string Name { get; set; }
[StringLength(500)]
[Column("DESCRIPTION")]
public string
#if nullable
?
#endif
Description { get; set; }
[Required]
[Column("LICENSE", TypeName = "varchar(max)")]
public string License { get; set; }
[Required]
[StringLength(20)]
[Column("VERSION")]
public string Version { get; set; }
[StringLength(50)]
[Column("ADDED_WHO")]
public string
#if nullable
?
#endif
AddedWho { get; set; }
[Column("ADDED_WHEN")]
public DateTime
#if nullable
?
#endif
AddedWhen { get; set; }
[StringLength(50)]
[Column("CHANGED_WHO")]
public string
#if nullable
?
#endif
ChangedWho { get; set; }
[Column("CHANGED_WHEN")]
public DateTime
#if nullable
?
#endif
ChangedWhen { get; set; }
}
}

View File

@@ -0,0 +1,44 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-EnvelopeGenerator.Finalizer-6d5cc618-4159-4ff2-b600-8a15fbfa8099</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Quartz.AspNetCore" Version="3.15.1" />
<PackageReference Include="Quartzmon" Version="1.0.5" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="Quartz" Version="3.15.1" />
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.15.1" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.DependencyInjection\EnvelopeGenerator.DependencyInjection.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Controllers\" />
<Folder Include="Controllers\" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Database.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Logging.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,35 @@
using EnvelopeGenerator.Finalizer.Job;
using Quartz;
namespace EnvelopeGenerator.Finalizer;
public static class Extensions
{
public static IServiceCollectionQuartzConfigurator ScheduleJobDefault<TJob>(this IServiceCollectionQuartzConfigurator q,
string croneEpression)
where TJob : IJob
{
var name = $"{typeof(TJob).FullName}";
var jobKey = new JobKey(name);
return q.ScheduleJob<TJob>(trigger => trigger
.WithIdentity(name + "-trigger")
.WithCronSchedule(croneEpression),
job => job.WithIdentity(jobKey)
);
}
public static IServiceCollectionQuartzConfigurator ScheduleJobDefault<TJob>(this IServiceCollectionQuartzConfigurator q,
IConfiguration configuration)
where TJob : IJob
{
var expression = configuration[$"{typeof(TJob).Name}:CronExpression"];
if (string.IsNullOrWhiteSpace(expression))
throw new InvalidOperationException(
"Cron expression for the Worker job is not configured. " +
"Please provide a valid cron schedule in the configuration under " +
$"'{typeof(TJob).FullName}:CronExpression'.");
return q.ScheduleJobDefault<TJob>(expression);
}
}

View File

@@ -0,0 +1,24 @@
using Quartz;
namespace EnvelopeGenerator.Finalizer.Job
{
public class EnvelopeTaskApiJob : IJob
{
private readonly ILogger<EnvelopeTaskApiJob> _logger;
public EnvelopeTaskApiJob(ILogger<EnvelopeTaskApiJob> logger)
{
_logger = logger;
}
public Task Execute(IJobExecutionContext context)
{
if (_logger.IsEnabled(LogLevel.Information))
{
_logger.LogInformation("{jobName} running at: {time}", context.JobDetail.Key, DateTimeOffset.Now);
}
return Task.CompletedTask;
}
}
}

View File

@@ -0,0 +1,72 @@
using EnvelopeGenerator.Application.Common.Configurations;
using EnvelopeGenerator.Application.Envelopes.Queries;
using EnvelopeGenerator.Application.Pdf;
using EnvelopeGenerator.Domain.Constants;
using MediatR;
using Microsoft.Extensions.Options;
using Quartz;
namespace EnvelopeGenerator.Finalizer.Job
{
public class FinishEnvelopeJob : IJob
{
private readonly ILogger<FinishEnvelopeJob> _logger;
private readonly IMediator _mediator;
private readonly PDFBurnerParams _options;
public FinishEnvelopeJob(ILogger<FinishEnvelopeJob> logger, IMediator mediator, IOptions<PDFBurnerParams> options)
{
_logger = logger;
_mediator = mediator;
_options = options.Value;
}
public async Task Execute(IJobExecutionContext context)
{
var cancel = context.CancellationToken;
var envelopes = await _mediator.Send(new ReadEnvelopeQuery()
{
Status = new() { Include = [ EnvelopeStatus.EnvelopeCompletelySigned ] },
HasDocResult = false
}, cancel);
using var semaphore = new SemaphoreSlim(_options.ConcurrencyLimit);
await Task.WhenAll(envelopes.Select(async envelope =>
{
await semaphore.WaitAsync(cancel);
try
{
await _mediator.BurnPdf(envelope.Id, cancel);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error burning envelope {EnvelopeId}", envelope.Id);
}
finally
{
semaphore.Release();
}
}));
var envelopeCount = envelopes.Count();
if (envelopeCount > 0)
{
_logger.LogInformation(
"Job '{JobName}' executed at {Timestamp}. {EnvelopeCount} envelope(s) successfully finalized. UUID(s): {EnvelopeUuids}",
context.JobDetail.Key.Name,
context.FireTimeUtc.ToLocalTime(),
envelopeCount,
string.Join(", ", envelopes.Select(e => e.Uuid))
);
}
else
_logger.LogInformation("Job '{JobName}' executed successfully at {Timestamp}. No envelopes were finalized.",
context.JobDetail.Key.Name,
context.FireTimeUtc.ToLocalTime()
);
}
}
}

View File

@@ -0,0 +1,129 @@
using EnvelopeGenerator.DependencyInjection;
using EnvelopeGenerator.Finalizer;
using EnvelopeGenerator.Finalizer.Job;
using EnvelopeGenerator.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Quartz;
using Quartz.AspNetCore;
using Quartzmon;
using Serilog;
// Load Serilog from appsettings.json
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(new ConfigurationBuilder()
.AddJsonFile("appsettings.Logging.json", optional: false, reloadOnChange: true)
.Build())
.CreateLogger();
try
{
Log.Information("Application is starting...");
var builder = WebApplication.CreateBuilder(args);
#region Logging
builder.Logging.ClearProviders();
builder.Logging.AddSerilog();
#endregion
#region Configuration
var config = builder.Configuration;
Directory
.GetFiles(builder.Environment.ContentRootPath, "appsettings.*.json", SearchOption.TopDirectoryOnly)
.Where(file => Path.GetFileName(file) != $"appsettings.Development.json")
.Where(file => Path.GetFileName(file) != $"appsettings.migration.json")
.ToList()
.ForEach(file => config.AddJsonFile(file, true, true));
#endregion
#region Web API Services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
#endregion
#region Quartz
builder.Services.AddQuartz(q =>
{
q.ScheduleJobDefault<FinishEnvelopeJob>(config);
q.ScheduleJobDefault<EnvelopeTaskApiJob>(config);
});
builder.Services.AddQuartzServer(options =>
{
options.WaitForJobsToComplete = true;
});
builder.Services.AddQuartzmon();
builder.Services.AddSingleton(provider =>
provider.GetRequiredService<ISchedulerFactory>().GetScheduler().Result
);
#endregion
#region Add DB Context, EG Inf. and Services
var cnnStrName = "Default";
var connStr = config.GetConnectionString(cnnStrName)
?? throw new InvalidOperationException($"Connection string '{cnnStrName}' is missing in the application configuration.");
builder.Services.AddEnvelopeGenerator(egOptions => egOptions
.AddLocalization()
.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = connStr;
options.SchemaName = "dbo";
options.TableName = "TBDD_CACHE";
})
.AddInfrastructure(opt =>
{
opt.AddDbTriggerParams(config);
opt.AddDbContext((provider, options) =>
{
var logger = provider.GetRequiredService<ILogger<EGDbContext>>();
var useInMemoryDb = config.GetValue<bool>("UseInMemoryDb");
var dbCtxOpt = useInMemoryDb ? options.UseInMemoryDatabase("EGInMemoryDb") : options.UseSqlServer(connStr);
dbCtxOpt.LogTo(log => logger.LogInformation("{log}", log), LogLevel.Trace)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
});
})
.AddServices(config, true)
);
#endregion Add DB Context, EG Inf. and Services
var app = builder.Build();
#region Web API Middleware
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseQuartzmon(new QuartzmonOptions()
{
Scheduler = app.Services.GetRequiredService<IScheduler>(),
VirtualPathRoot = "/quartz"
});
app.MapControllers();
#endregion
app.Run();
Log.Information("The worker was stopped.");
}
catch (Exception ex)
{
Log.Fatal(ex, "Worker could not be started!");
}
finally
{
Log.CloseAndFlush();
}

View File

@@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:17119",
"sslPort": 44321
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5010",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "quartz",
"applicationUrl": "https://localhost:7141;http://localhost:5010",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,22 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"UseDbMigration": false,
"ConnectionStrings": {
"Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;",
"DbMigrationTest": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM_DATA_MIGR_TEST;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
},
"DbTriggerParams": {
"Envelope": [ "TBSIG_ENVELOPE_AFT_INS" ],
"History": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ],
"EmailOut": [ "TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD" ],
"EnvelopeReceiverReadOnly": [ "TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD" ],
"Receiver": [],
"EmailTemplate": [ "TBSIG_EMAIL_TEMPLATE_AFT_UPD" ]
},
"UseInMemoryDb": false
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View File

@@ -0,0 +1,8 @@
{
"FinishEnvelopeJob": {
"CronExpression": "* * * * * ?"
},
"EnvelopeTaskApiJob": {
"CronExpression": "* * * * * ?"
}
}

View File

@@ -0,0 +1,81 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Verbose",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "E:/LogFiles/Digital Data/signFlow.Finalizer/log.Verbose-.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Verbose",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "E:/LogFiles/Digital Data/signFlow.Finalizer/log.Debug-.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Debug",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "E:/LogFiles/Digital Data/signFlow.Finalizer/log.Info-.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Information",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "E:/LogFiles/Digital Data/signFlow.Finalizer/log.Warning-.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Warning",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "E:/LogFiles/Digital Data/signFlow.Finalizer/log.Error-.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Error",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "E:/LogFiles/Digital Data/signFlow.Finalizer/log.Fatal-.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Fatal",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@@ -0,0 +1,14 @@
{
"IgnoredLabels": {
"Label": [
"Date",
"Datum",
"ZIP",
"PLZ",
"Place",
"Ort",
"Position",
"Stellung"
]
}
}

View File

@@ -0,0 +1,3 @@
{
"GdPictureLicenseKey": "kG1Qf9PwmqgR8aDmIW2zI_ebj48RzqAJegRxcystEmkbTGQqfkNBdFOXIb6C_A00Ra8zZkrHdfjqzOPXK7kgkF2YDhvrqKfqh4WDug2vOt0qO31IommzkANSuLjZ4zmraoubyEVd25rE3veQ2h_j7tGIoH_LyIHmy24GaXsxdG0yCzIBMdiLbMMMDwcPY-809KeZ83Grv76OVhFvcbBWyYc251vou1N-kGg5_ZlHDgfWoY85gTLRxafjD3KS_i9ARW4BMiy36y8n7UP2jN8kGRnW_04ubpFtfjJqvtsrP_J9D0x7bqV8xtVtT5JI6dpKsVTiMgDCrIcoFSo5gCC1fw9oUopX4TDCkBQttO4-WHBlOeq9dG5Yb0otonVmJKaQA2tP6sMR-lZDs3ql_WI9t91yPWgpssrJUxSHDd27_LMTH_owJIqkF3NOJd9mYQuAv22oNKFYbH8e41pVKb8cT33Y9CgcQ_sy6YDA5PTuIRi67mjKge_nD9rd0IN213Ir9M_EFWqg9e4haWzIdHXQUo0md70kVhPX4UIH_BKJnxEEnFfoFRNMh77bB0N4jkcBEHPl-ghOERv8dOztf4vCnNpzzWvcLD2cqWIm6THy8XGGq9h4hp8aEreRleSMwv9QQAC7mjLwhQ1rBYkpUHlpTjhTLnMwHknl6HH0Z6zzmsgkRKVyfquv94Pd7QbQfZrRka0ss_48pf9p8hAywEn81Q=="
}

View File

@@ -105,6 +105,7 @@ try
});
builder.Services.AddOpenApi();
// TODO: Update to configure with EnvelopeGenerator.DependencyInjection
//AddEF Core dbcontext
var useDbMigration = Environment.GetEnvironmentVariable("MIGRATION_TEST_MODE") == true.ToString() || config.GetValue<bool>("UseDbMigration");
var cnnStrName = useDbMigration ? "DbMigrationTest" : "Default";
@@ -185,7 +186,7 @@ try
// Envelope generator serives
#pragma warning disable CS0618 // Type or member is obsolete
builder.Services
.AddEnvelopeGeneratorInfrastructureServices(opt =>
.AddEGInfrastructureServices(opt =>
{
opt.AddDbTriggerParams(config);
opt.AddDbContext((provider, options) =>

View File

@@ -38,10 +38,10 @@ namespace EnvelopeGenerator.Infrastructure
/// will be created per HTTP request (or per scope) within the dependency injection container.
/// </remarks>
[Obsolete("Use IRepository")]
public static IServiceCollection AddEnvelopeGeneratorInfrastructureServices(this IServiceCollection services, Action<Config> options)
public static IServiceCollection AddEGInfrastructureServices(this IServiceCollection services, Action<EGInfrastructureConfiguration> options)
{
// configure custom options
options(new Config(services));
options(new EGInfrastructureConfiguration(services));
#if NET
services.TryAddScoped<IConfigRepository, ConfigRepository>();
services.TryAddScoped<IDocumentReceiverElementRepository, DocumentReceiverElementRepository>();
@@ -160,11 +160,11 @@ namespace EnvelopeGenerator.Infrastructure
}
#endif
public class Config
public class EGInfrastructureConfiguration
{
private readonly IServiceCollection _services;
internal Config(IServiceCollection services)
internal EGInfrastructureConfiguration(IServiceCollection services)
{
_services = services;
}

View File

@@ -79,6 +79,8 @@ public abstract class EGDbContextBase : DbContext
public DbSet<ClientUser> ClientUsers { get; set; }
public DbSet<ThirdPartyModule> ThirdPartyModules { get; set; }
private readonly DbTriggerParams _triggers;
private readonly ILogger

View File

@@ -22,8 +22,8 @@
<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.4.0" />
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.4.5" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.5.0" />
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.5.0" />
<PackageReference Include="QuestPDF" Version="2025.7.1" />
</ItemGroup>

View File

@@ -525,7 +525,7 @@ namespace EnvelopeGenerator.Infrastructure.Migrations
});
});
modelBuilder.Entity("EnvelopeGenerator.Domain.Entities.Config", b =>
modelBuilder.Entity("EnvelopeGenerator.Domain.Entities.EGInfrastructureConfiguration", b =>
{
b.Property<string>("ExportPath")
.HasColumnType("nvarchar(256)")

View File

@@ -522,7 +522,7 @@ namespace EnvelopeGenerator.Infrastructure.Migrations
});
});
modelBuilder.Entity("EnvelopeGenerator.Domain.Entities.Config", b =>
modelBuilder.Entity("EnvelopeGenerator.Domain.Entities.EGInfrastructureConfiguration", b =>
{
b.Property<string>("ExportPath")
.HasColumnType("nvarchar(256)")

View File

@@ -20,7 +20,7 @@
<ItemGroup>
<PackageReference Include="Bogus" Version="35.6.3" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.4.0" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.5.0" />
<PackageReference Include="DigitalData.Core.Abstractions" Version="4.3.0" />
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />

View File

@@ -1,4 +1,3 @@
using EnvelopeGenerator.Application.Services;
using Microsoft.EntityFrameworkCore;
using NLog;
using Quartz;
@@ -16,7 +15,6 @@ using EnvelopeGenerator.Web.Sanitizers;
using EnvelopeGenerator.Web.Models.Annotation;
using DigitalData.UserManager.DependencyInjection;
using EnvelopeGenerator.Web.Middleware;
using EnvelopeGenerator.Application.Common.Interfaces.Services;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Info("Logging initialized!");
@@ -88,6 +86,7 @@ try
builder.Services.AddSwaggerGen();
}
// TODO: Update to configure with EnvelopeGenerator.DependencyInjection
//AddEF Core dbcontext
var useDbMigration = Environment.GetEnvironmentVariable("MIGRATION_TEST_MODE") == true.ToString() || config.GetValue<bool>("UseDbMigration");
var cnnStrName = useDbMigration ? "DbMigrationTest" : "Default";
@@ -103,7 +102,7 @@ try
// Add envelope generator services
#pragma warning disable CS0618 // Type or member is obsolete
builder.Services.AddEnvelopeGeneratorInfrastructureServices(
builder.Services.AddEGInfrastructureServices(
opt =>
{
opt.AddDbTriggerParams(config);
@@ -186,15 +185,8 @@ try
builder.Services.Configure<Cultures>(config.GetSection("Cultures"));
builder.Services.AddSingleton(sp => sp.GetRequiredService<IOptions<Cultures>>().Value);
// Register mail services
#pragma warning disable CS0618 // Type or member is obsolete
builder.Services.AddScoped<IEnvelopeMailService, EnvelopeMailService>();
#pragma warning restore CS0618 // Type or member is obsolete
builder.Services.AddDispatcher<EGDbContext>();
builder.Services.AddMemoryCache();
builder.ConfigureBySection<CustomImages>();
builder.ConfigureBySection<AnnotationParams>();

View File

@@ -37,6 +37,10 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EnvelopeGenerator.Form", "E
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.PdfEditor", "EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj", "{211619F5-AE25-4BA5-A552-BACAFE0632D3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.DependencyInjection", "EnvelopeGenerator.DependencyInjection\EnvelopeGenerator.DependencyInjection.csproj", "{B97DE7DD-3190-4A84-85E9-E57AD735BE61}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Finalizer", "EnvelopeGenerator.Finalizer\EnvelopeGenerator.Finalizer.csproj", "{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -91,6 +95,14 @@ Global
{211619F5-AE25-4BA5-A552-BACAFE0632D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{211619F5-AE25-4BA5-A552-BACAFE0632D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{211619F5-AE25-4BA5-A552-BACAFE0632D3}.Release|Any CPU.Build.0 = Release|Any CPU
{B97DE7DD-3190-4A84-85E9-E57AD735BE61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B97DE7DD-3190-4A84-85E9-E57AD735BE61}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B97DE7DD-3190-4A84-85E9-E57AD735BE61}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B97DE7DD-3190-4A84-85E9-E57AD735BE61}.Release|Any CPU.Build.0 = Release|Any CPU
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -111,6 +123,8 @@ Global
{A9F9B431-BB9B-49B8-9E2C-0703634A653A} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
{6D56C01F-D6CB-4D8A-BD3D-4FD34326998C} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
{211619F5-AE25-4BA5-A552-BACAFE0632D3} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
{B97DE7DD-3190-4A84-85E9-E57AD735BE61} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {73E60370-756D-45AD-A19A-C40A02DACCC7}