Compare commits

...

227 Commits

Author SHA1 Message Date
e66c46767e refactor: convert markdown parsing to IIFE and preserve indentation 2025-11-18 11:34:37 +01:00
bc732d311c feat: enable async marked parsing and improve markdown rendering
- Configure marked with async, breaks, and GFM options
- Update markdown processing to use textContent and async marked.parse
- Replace synchronous innerHTML parsing with awaited async parsing
2025-11-18 10:00:38 +01:00
c90d29d654 chore: remove IISProfileNetMulti 2025-11-17 16:20:27 +01:00
47a2e950ca chore: add IIS profile for .net 8 2025-11-17 16:19:51 +01:00
6ef989213e chore(Web): add multi-targeting for net7.0, net8.0, and net9.0 with framework-specific package references
Refactored the project file to support multiple target frameworks and added
conditional ItemGroups. Updated several package versions for net8.0 and net9.0
targets to ensure compatibility while keeping net7.0 dependencies unchanged.
2025-11-17 15:23:04 +01:00
2a27b6161b chore(Web): bump to 3.7.0 2025-11-17 12:50:49 +01:00
efdc372b04 feat(envelope): bypass access-code flow when UseAccessCode is disabled
- Add logic to skip access-code workflow if UseAccessCode is false
- Auto-authenticate receiver and show envelope immediately
- Preserve rejection and signed checks
- Keep existing behavior for access-code enabled envelopes
2025-11-17 12:49:15 +01:00
698b7ca1ac feat: render markdown content using marked library 2025-11-15 00:56:19 +01:00
bf6947a28c chore(Web): bump to 3.6.0 2025-11-14 23:05:47 +01:00
e2e31e2e69 feat(ShowEnvelope): render envelope message using Markdown parser 2025-11-14 23:02:37 +01:00
73f6221c3c chore(Web): add marked.js 2025-11-14 15:13:25 +01:00
10f730a833 chore(Web): bump to 3.5.1 2025-11-14 14:31:31 +01:00
cf5a301942 refactor(EnvelopeController): add cancellation token to LogInEnvelope POST action
- Updated `LogInEnvelope` method to accept `CancellationToken` for better async operation handling.
- No functional changes to other methods.
- Maintains backwards compatibility with obsolete MediatR services.
2025-11-14 14:29:25 +01:00
e364f1f592 fix: handle rejected documents correctly with info alert and auto-reload 2025-11-14 14:20:34 +01:00
8a488d4e71 fix: improve finish handler error handling for unavailable envelopes
- Update FINISH flow to handle status codes 409 and 423
- Reload page on 423 responses
- Show proper warning when envelope is no longer available (409)
2025-11-14 14:12:21 +01:00
f0be1a5b03 feat(annotation): update response status codes for signed/rejected checks 2025-11-14 14:08:10 +01:00
773721b634 fix(AnnotationController): enforce proper rejection and history checks in CreateOrUpdate
- Updated `AnyHistoryAsync` call to filter by `EnvelopeRejected` and `DocumentRejected` statuses, returning 403 instead of 200 when applicable.
- Ensures users cannot proceed if envelope was previously rejected.
- Minor cleanup in CreateOrUpdate logic to better handle authorization and signed checks.
2025-11-14 13:49:58 +01:00
99e3e4c24d refactor: update CountHistoryQueryExtensions to use IEnumerable<EnvelopeStatus> and improve status handling 2025-11-14 13:47:34 +01:00
b9c86ce3c6 refactor(CountHistoryQuery): replace single status filter with status range in CountHistoryQuery
- Updated AnyHistoryAsync extension to use EnvelopeStatusQuery instead of single EnvelopeStatus.
- Modified CountHistoryQuery to support multiple statuses (Min, Max, Include, Ignore).
- Preserved backward compatibility with obsolete single Status and EnvelopeId.
2025-11-14 13:39:39 +01:00
637b45efe0 feat(history-query): support multiple status filters in CountHistoryQueryHandler
- Added handling for `Statuses` property to filter by min, max, include, and ignore lists.
- Deprecated single `Status` handling is now wrapped with pragma warnings for backward compatibility.
- Ensures `AnyHistoryAsync` extension works correctly with enhanced query filtering.
2025-11-14 13:36:44 +01:00
28b8c311f9 refactor(CountHistoryQuery): simplify AnyHistoryAsync extension to use UUID and status
- Replace Action-based query configuration with direct parameters (uuid, status)
- Automatically constructs CountHistoryQuery from provided parameters
- Keeps existing EnvelopeId/UUID validation in handler
- Improves readability and usability of history count checks
2025-11-14 13:27:26 +01:00
00c7fe5316 feat(history): enhance ReadHistoryQuery to support Envelope object and UUID
- Replace nullable `OnlyLast` with non-nullable default `true`
- Support filtering by Envelope object (Id or Uuid) in addition to deprecated EnvelopeId
- Throw `BadRequestException` if no valid Envelope reference is provided
- Preserve status filtering and ordering for latest history entries
2025-11-14 13:24:02 +01:00
e5a061d5b5 feat(ReadHistoryQuery): enhance ReadHistoryQuery to support Envelope object and UUID
- Replace nullable `OnlyLast` with non-nullable default `true`
- Support filtering by Envelope object (Id or Uuid) in addition to deprecated EnvelopeId
- Throw `BadRequestException` if no valid Envelope reference is provided
- Preserve status filtering and ordering for latest history entries
2025-11-14 13:19:56 +01:00
629b02863b refactor(history): introduce extension method and remove AutoMapper dependency
- Added `CountHistoryQueryExtensions.AnyHistoryAsync` for cleaner query usage via `ISender`.
- Removed `IMapper` from `CountHistoryQueryHandler` as it was unused.
- Removed unnecessary `using` directives to simplify imports.
- Kept query handling logic intact.
2025-11-14 11:57:16 +01:00
3b24755c35 feat(CountHistoryQuery): add CountHistoryQuery to retrieve envelope history count
- Introduced `CountHistoryQuery` record for querying the count of history entries of an envelope.
- Implemented `CountHistoryQueryHandler` using repository pattern and AutoMapper.
- Supports optional filtering by `Status`.
- Throws `NotFoundException` if necessary (placeholder for future extension).
2025-11-14 11:49:51 +01:00
864e9e8164 refactor(HistoryQueryBase): created to handle basic history queries.
- imp to ReadHistoryQuery
2025-11-14 11:46:15 +01:00
7eff958d0a refactor(ReadHistoryQueryHandler): move to ReadHistoryQuery 2025-11-14 11:33:57 +01:00
c3deaae63b refactor(Service): add UserConfig 2025-10-30 14:39:15 +01:00
bb0197e6ba refactor: wrap repository access in a DI scope in PDFBurner
- Updated `BurnAnnotsToPDF` to create a scope using `Factory.Shared.ScopeFactory.CreateScope()`
  before accessing the `Signature` repository.
- Ensures proper disposal of services and improves dependency injection usage.
- No functional changes to PDF burning logic.
2025-10-29 17:40:42 +01:00
ec2935b524 refactor: update EnvelopeGenerator factory initialization with PostBuildBehavior 2025-10-29 17:14:49 +01:00
4fd7982cba refactor: update EnvelopeGenerator infrastructure service initialization
- Added `BehaveOnPostBuild(PostBuildBehavior.Ignore)` in the Factory initialization
- Adjusted formatting for better readability of `AddEnvelopeGeneratorInfrastructureServices` call
- No functional changes to job execution or PDF/email processing
2025-10-29 17:01:24 +01:00
ddcf5edc00 update DigitalData.Core.Abstractions 2025-10-29 16:54:12 +01:00
74d207caa3 refactor(EnvelopeGenerator.Service): update Microsoft.Extensions.Logging.Abstractions to 7.0.0 2025-10-29 16:29:07 +01:00
a367c12551 chore(Web): bump to 3.5 2025-10-28 16:57:20 +01:00
35a328f8dc feat(pdfburner): adjust background rendering in BurnElementAnnotsToPDF
- Added scaling factors (1.95 * 0.93, 2.52 * 0.67) to the background generation step
- Included TODO note to calculate length dynamically based on largest Y value
- Improved visual accuracy of PDF background rendering for element annotations
2025-10-28 16:56:00 +01:00
d259a15b4b refactor: add DigitalData.Core.Abstractions.Interfaces import and make Background method width/height configurable 2025-10-28 16:43:59 +01:00
23e0e5ddbe fix(pdfburner): correct annotation X-coordinate handling in BurnElementAnnotsToPDF
Adjusted logic in `BurnElementAnnotsToPDF` to properly use annotation X position
(`annot.X / inchFactor`) instead of offset-based calculation with `frameX` and
`frameXShift`. This ensures more accurate placement of form fields and images
when burning annotations into PDFs.
2025-10-28 16:40:06 +01:00
0bb85c28c1 fix: adjust annotation position calculation in PDFBurner
- Updated BurnElementAnnotsToPDF method to correctly align image annotations relative to the frame position.
- Introduced frame-based coordinate shifting (frameXShift and frameYShift) to handle signature positioning accurately.
- Replaced variable `x` with `frameX` for better clarity and consistency.
2025-10-28 16:27:55 +01:00
a11d9a0e0e refactor(PDFBurner): adjust annotation positioning and unit conversions in PDFBurner
- Renamed variable `magin` → `margin` for clarity
- Revised coordinate calculations for annotation positioning (`x`, `y`)
- Adjusted width and height units to use inches instead of fixed inchFactor multipliers
- Updated y-offset logic for form fields (`yOffsetsOfFF`) to improve layout alignment
- Simplified image annotation scaling (removed division by 100)
- Improved code readability and comment consistency
2025-10-28 15:27:52 +01:00
b9bb058137 fix(AnnotationCreateDto): rename WIDTH and HEIGHT properties to Width and Height in AnnotationCreateDto 2025-10-28 14:42:14 +01:00
0818d7d9eb feat(dto): add position and size properties to AnnotationCreateDto 2025-10-28 14:14:09 +01:00
9d200800c5 feat: add position and size properties to ElementAnnotation entity
- Added `X`, `Y`, `Width`, and `Height` properties with `float` database mapping.
- Retains existing fields and database annotations.
- Prepares the entity for spatial layout handling in documents.
2025-10-28 14:10:32 +01:00
6feb601670 fix(annotations): include bounding box coordinates in mapped signature and frame data
- Added extraction of `x`, `y`, `width`, and `height` from annotations and frames in `mapSignature`.
- Ensures positional data is correctly preserved when mapping signatures and frames from PSPDFKit annotations.
- No functional changes to annotation creation, deletion, or validation.
2025-10-28 13:58:03 +01:00
39c12ada45 refactor(annotations): include bounding box info in mapped signature fields
- Added `x`, `y`, `width`, and `height` properties to form field mapping in `mapSignature`.
- Preserves original form field values while enriching them with annotation coordinates.
- No functional changes to annotation creation, deletion, or validation.
2025-10-28 13:49:26 +01:00
985ad4dc29 fix(pdf-burner): correct image annotation type and scaling
- Fixed conditional check in BurnElementAnnotsToPDF to correctly handle AnnotationType.Image.
- Adjusted AddImageAnnotation method to scale width and height by dividing by 100.
- No functional changes to other PDF burning or annotation logic.
2025-10-28 13:00:43 +01:00
038ac2aed0 fix(annotations): correct signature frame mapping by filtering by pageIndex
- Updated `mapSignature` to filter signature annotations by `pageIndex` when finding nearest elements.
- Ensures correct association of frames and signature data for each page.
- Prevents errors caused by cross-page annotation references.
2025-10-28 12:59:49 +01:00
5e74de0ce7 refactor(pdf-burner): adjust annotation positioning logic in BurnElementAnnotsToPDF
- Introduced configurable inch factor, margins, width, and height for annotations
- Added dynamic Y-offsets for form fields based on annotation names
- Simplified AddFormFieldValue logic for direct coordinates
- Removed hardcoded offsets previously embedded in the method
2025-10-28 11:48:19 +01:00
0ce7ae9494 fix(pdfburner): correct page indexing for annotation rendering
- Removed unnecessary +1 offset in Manager.SelectPage calls for image and ink annotations
- Ensured correct page index is used when adding annotations and form field values
- Improved annotation rendering accuracy when burning to PDF
2025-10-28 10:48:15 +01:00
7041a4694a fix: ensure annotations are properly burned after background addition in PDFBurner 2025-10-28 10:43:19 +01:00
75e47d10e3 feat(pdfburner): add ink annotation support and refactor annotation handling
- Added new AddInkAnnotation(page As Integer, value As String) method to handle ink annotations from element data.
- Introduced new Ink model class for deserializing ink annotation data (lines and strokeColor).
- Updated BurnElementAnnotsToPDF to support Ink annotation type.
- Refactored annotation type handling for better extensibility.
- Improved annotation burning logic for mixed annotation types (form fields, images, inks).
2025-10-28 10:06:38 +01:00
7f9125b3aa refactor: add AddImageAnnotation-method 2025-10-28 09:43:43 +01:00
fee256a51a add y offset to AddFormFieldValue metot 2025-10-28 09:25:48 +01:00
8ad7c37261 refactor(PdfBurner): update BurnAnnotsToPDF to add form field 2025-10-28 09:09:53 +01:00
ef28bbaaf1 refactor(pdf-burner): replace annotation type constants with AnnotationType class
- Removed individual annotation type constants (ANNOTATION_TYPE_IMAGE, ANNOTATION_TYPE_INK, ANNOTATION_TYPE_WIDGET)
- Introduced new Friend Class `AnnotationType` to encapsulate annotation type string constants
- Updated all references to use `AnnotationType.Image`, `AnnotationType.Ink`, and `AnnotationType.Widget`
- Improved code organization by grouping related constants and removing redundant declarations
2025-10-24 13:50:24 +02:00
258de6244c refactor(pdf-burner): organize code with regions for better readability
- Grouped related methods and classes into logical #Region blocks:
  - "Burn PDF"
  - "Add Value"
  - "Helpers"
  - "Model"
- Improved code structure and readability without changing functionality.
- No logic or behavior modifications were made.
2025-10-24 12:30:16 +02:00
a845b85a5c refactor(PDFBurner): separate element annotation burning into dedicated method and simplify BurnAnnotsToPDF logic 2025-10-24 12:21:04 +02:00
02a7b706cf refactor(PDFBurner): extract instant JSON annotation burning logic into separate method
- Moved annotation burning logic from `BurnAnnotsToPDF` into new method `BurnInstantJSONAnnotsToPDF`
- Simplified `BurnAnnotsToPDF` to handle only background generation and call the new method
- Improved code readability and separation of concerns
2025-10-23 17:05:37 +02:00
7912469709 refactor(pdf): add output stream disposal management to Pdf class
- Added `disposeOutputStream` parameter to Pdf constructor
- Updated `FromMemory` to set output stream disposal based on null value
- Ensured proper cleanup of output stream in Dispose and DisposeAsync methods
- Improved resource management to prevent potential memory leaks
2025-10-23 14:34:57 +02:00
75d975223e feat(pdf): add optional output stream parameter to FromMemory overload
- Updated Pdf.FromMemory(MemoryStream) to accept an optional outputStream parameter
- Ensures flexibility when reusing existing MemoryStreams for output
- Added conditional nullability support for .NET builds
2025-10-23 14:30:30 +02:00
c456d67d03 feat(pdf): add option to preserve input stream on disposal
- Introduced `disposeInputStream` parameter in `Pdf<TInputStream, TOutputStream>` constructor.
- Updated `Dispose` and `DisposeAsync` to conditionally dispose the input stream based on this flag.
- Updated `Pdf.FromMemory(MemoryStream)` to not dispose the provided MemoryStream by default.
- Ensures better control over resource management when reusing input streams.
2025-10-23 13:51:40 +02:00
241e59fc7e feat(Pdf): add FromMemory method to be able to create via stream 2025-10-23 13:39:59 +02:00
f0d101bb23 refactor(pdf-burner): rename BurnInstantJSONAnnotationsToPDF to BurnAnnotsToPDF
- Updated method name to better reflect its functionality.
- No changes to logic or behavior; purely a rename for clarity.
2025-10-23 13:36:32 +02:00
8db5afae40 feat(PDFBurner): add background rendering using PdfEditor before burning annotations 2025-10-23 12:54:53 +02:00
b62cca5961 feat(pdf-burner): integrate repository to fetch envelope annotations
- Added repository access to retrieve signatures and their annotations for a given envelope.
- Updated `BurnInstantJSONAnnotationsToPDF` to accept `envelopeId` and load elements from the database.
- Updated imports to include `DigitalData.Core.Abstractions` and Entity Framework references.
2025-10-23 12:14:35 +02:00
0e7b120ded refactor(Entities.Annotation): rename it to ElementAnnotation to prevent name conflicts. 2025-10-23 10:51:19 +02:00
d8cbdb0c65 chore:
- upg Core.Abstraction.Application to 1.4.0
 - upg Core.Abstractions to 4.2.0
2025-10-23 10:37:15 +02:00
0107602a84 upg Abstraction.Application and Application projects 2025-10-22 18:34:40 +02:00
02ecd88758 feat(TestAnnotationController): include receiver signature in RemoveSignatureNotification
Updated TestAnnotationController.Delete to extract the receiver signature from the envelope key
and pass it along with the envelope UUID when publishing RemoveSignatureNotification.
Returns BadRequest if the signature is missing.
2025-10-22 14:29:38 +02:00
17c7e46388 fix(AnnotationController): use PublishSafely for docSignedNotification
Replaced `await _mediator.Publish(docSignedNotification, cancel)` with
`await _mediator.PublishSafely(docSignedNotification, cancel)` to ensure
safe publishing of notifications without unhandled exceptions.
2025-10-22 14:25:50 +02:00
f3af30c67d feat(DocSignedNotificationExtensions): add PublishSafely extension for DocSignedNotification
- Introduced PublishSafely method to safely publish DocSignedNotification events.
- Added fallback logic to publish RemoveSignatureNotification when publishing fails.
- Added missing using directive for RemoveSignatureNotification namespace.
2025-10-22 13:10:38 +02:00
90e10d3d04 refactor(RemoveSignatureNotification): update handlers to throw exception if there is no filter 2025-10-22 12:50:57 +02:00
af14ef7ce5 feat(RemoveSignatureNotification): add validation and filter logic to RemoveSignatureNotification
- Added `HasFilter` property to check if any filter parameters are provided.
- Implemented `ThrowIfHasNoFilter()` method to enforce at least one filter parameter.
- Improves robustness and validation of RemoveSignatureNotification.
2025-10-22 11:02:10 +02:00
1edcfed318 refactor(RemoveSignatureNotification): Alle Filter von Handlern an die Bedingung binden, dass sie nicht null sind. 2025-10-22 10:38:08 +02:00
2004c7ced2 feat(RemoveSignatureNotification): extend RemoveSignatureNotification with EnvelopeId and ReceiverId
Added optional parameters `EnvelopeId` and `ReceiverId` to RemoveSignatureNotification
to provide additional context when removing receiver signatures.
2025-10-21 16:43:01 +02:00
40135fb8a2 feat(RemoveHistoryHandler): add filtering by receiver signature in RemoveHistoryHandler
Added logic to filter history records by receiver signature when provided in
RemoveSignatureNotification. This ensures only relevant signed entries are deleted.
2025-10-21 15:51:55 +02:00
b57c0aa9c7 refactor(RemoveDocStatusHandler): update RemoveDocStatusHandler to support conditional receiver signature filtering
Updated the RemoveDocStatusHandler to refine document status deletion logic.
Now filters by EnvelopeUuid and optionally by ReceiverSignature when provided.
2025-10-21 15:41:09 +02:00
2c4c18935f feat(RemoveSignatureNotification): add ReceiverSignature to RemoveSignatureNotification
Added a new nullable ReceiverSignature property to the RemoveSignatureNotification record
to support handling receiver-specific signature removal events.
2025-10-21 15:25:04 +02:00
d8ed06fdb6 feat(RemoveAnnotationHandler): add RemoveAnnotationHandler to handle signature removal notifications
- Implements INotificationHandler<RemoveSignatureNotification>
- Deletes Annotation entities matching the Envelope UUID from the repository
- Uses IRepository<Annotation> for data access
2025-10-21 12:43:59 +02:00
09bf8db884 refactor(RemoveAnnotationHandler): rename as RemoveDocStatusHandler 2025-10-21 12:27:54 +02:00
911c812b19 refactor(Annotation): change Id column type from int to bigint in Annotation entity 2025-10-21 11:45:23 +02:00
8ae0f79365 refactor(AnnotationDto): split AnnotationDto into AnnotationCreateDto and AnnotationDto
- Introduced AnnotationCreateDto for creation-specific properties
- AnnotationDto now inherits from AnnotationCreateDto and includes Id, AddedWhen, ChangedWhen, and ChangedWho
- Added Type property to AnnotationCreateDto
 - remove CreateAnnotationCommand
2025-10-21 11:42:01 +02:00
0ca54fe1fe feat(DocSignedNotification): replace Annotations with PsPdfKitAnnotation in DocSignedNotification
- Introduced new record `PsPdfKitAnnotation` to encapsulate both Instant and Structured annotation data
- Updated `DocSignedNotification` to use `PsPdfKitAnnotation` instead of `ExpandoObject Annotations`
- Modified extension methods to accept and map `PsPdfKitAnnotation`
- Added reference to `EnvelopeGenerator.Application.Annotations.Commands` for `CreateAnnotationCommand`
2025-10-21 10:11:36 +02:00
a1d6b5347f refactor(annotation): simplify mapSignature function to return a flat array
Reworked mapSignature to return a single flattened array combining formFields,
frames, and signatures instead of a nested object. This simplifies downstream
processing and improves readability.
2025-10-21 09:48:06 +02:00
6cc631111c refactor(annotation): simplify field mapping and return structured objects
Refactored the mapSignature function to:
- Return cleaner structured objects for formFields, frames, and signatures
- Include `type` and `value` properties in returned objects
- Remove direct mutation of field and annotation objects
- Improve readability and maintainability of data mapping logic
2025-10-21 09:38:47 +02:00
9d6074874f fix(annotation): correctly assign elementId for signature annotations
Previously, signature annotations did not include elementId mapping logic, which caused issues when linking annotations to their corresponding elements. This update adds logic to extract elementId from the nearest signature annotation (similar to frame annotations) to ensure proper association.
2025-10-21 09:25:01 +02:00
26bdb0806d feat(MappingProfile): add AutoMapper profile for CreateAnnotationCommand to Annotation 2025-10-21 09:09:56 +02:00
7919f02ffd feat(annotation): enhance mapSignature to support drawn signatures
- Added support for mapping drawn (non-image) signatures by including `lines` and `strokeColor` data.
- Added error handling for incompatible or missing signature data structures.
- Ensures compatibility with third-party annotation libraries that return vector-based signatures.
2025-10-20 16:35:53 +02:00
04ae14c660 refactor(annotation): update mapSignature to handle frames and signatures separately
- Modified `mapSignature` to set `name` as 'frame' for FRAME annotations and 'signature' for signature annotations.
- Added processing for annotations with `isSignature` to include their `value` from attachments if present.
- Ensures consistent mapping of form fields, frames, and signatures.
2025-10-20 14:29:39 +02:00
cff79730b0 fix(annotations): correctly decode base64 for FRAME annotations
Updated `mapSignature` function to use `fixBase64` when assigning `value`
to FRAME annotations. This ensures any escaped Base64 strings in attachments
are properly converted to standard Base64 format.
2025-10-20 14:22:19 +02:00
188cb67306 feat(annotations): add fixBase64 and map FRAME annotations to nearest signature
- Added `fixBase64` utility to unescape Base64 strings.
- Updated `mapSignature` to link FRAME annotations to their nearest signature element and include `elementId`.
- Ensures proper mapping of annotation frames and consistent data structure for form fields.
2025-10-20 14:05:11 +02:00
abaa315b24 feat(utils): add findNearest function to calculate closest point 2025-10-20 13:40:38 +02:00
4f463c27e6 feat(annotation): extend mapSignature to include FRAME annotations
- Updated `mapSignature` function to also map image/frame annotations.
- For each annotation with `description === 'FRAME'`, add `name` and `value` based on corresponding attachment.
- Preserves existing mapping of form fields, filtering out labels.
- Ensures signature data and associated frames are returned in a structured object.
2025-10-20 11:28:05 +02:00
d6f17ec4e8 feat(annotations): add mapSignature function for mapping form field data
- Introduced new function `mapSignature(iJSON)` to process formFieldValues
- Filters out label fields and maps form field data to include elementId and simplified name
- Enhances annotation handling for signature mapping
2025-10-20 10:59:57 +02:00
e3e2831da1 feat(Annotation): add Type property to Annotation entity and update CHANGED_WHO column type
- Added new required `Type` field with nvarchar(50) to `Annotation` entity.
- Updated `CHANGED_WHO` column type from nchar(100) to nvarchar(50).
- Corresponding database migration required.
2025-10-20 10:37:29 +02:00
52306d481f refactor(app): simplify annotation creation and improve signature handling
- Updated `createImageAnnotation` to use pre-generated id instead of multiple parameters
- Replaced hardcoded annotation ID generation during signature creation
- Added `fixBase64` helper to handle escaped base64 strings
- Temporarily added logging in `handleFinish` for debugging exported InstantJSON
- Simplified `createImageAnnotation` signature and usage
- Improved readability and consistency in annotation ID generation logic
2025-10-17 13:46:07 +02:00
f046be240b refactor(annotations): replace global element index with element.id in generateId
- Removed global __elementIndex counter and updated generateId() to use element.id for unique annotation IDs.
- Updated createAnnotations() and createImageAnnotation() to pass element.id to generateId().
- Simplified ID generation logic for better consistency and traceability across annotations.
2025-10-17 09:33:58 +02:00
16e5d5c692 feat(AnnotationHandler): add handler for DocSignedNotification to create annotations
- Implemented AnnotationHandler to process DocSignedNotification events
- Sends CreateAnnotationCommand via MediatR with envelope, receiver, and annotations data
2025-10-16 14:28:25 +02:00
e64ad44b71 refactor(annotation): simplify PSPDFKitInstantJSON deserialization in CreateAnnotationCommand
Refactored CreateAnnotationCommand to remove the lazy initialization pattern
and replaced it with a direct property setter for PSPDFKitInstantJSON.
This simplifies JSON deserialization and removes unnecessary complexity
in managing the PSPDFKitInstant object.
2025-10-16 11:24:23 +02:00
e88bd55198 refactor(AnnotationHandler): rename as DocStatusHandler 2025-10-16 10:18:32 +02:00
4abed0e1bc fix(data): define navigation property for Envelope–Document relationship
Explicitly specify the navigation property `d.Envelope` in the relationship
between Envelope and Document to ensure proper entity mapping and navigation
in EF Core.
2025-10-15 18:23:43 +02:00
69821e64c6 refactor(Document): update Document entity structure and file properties
- Moved file-related properties (Filename, Filepath, FileNameOriginal) into dedicated region
- Added column mappings for file-related properties
- Changed Elements type to IEnumerable<Signature> for better abstraction
- Adjusted EnvelopeId default initialization for NETFRAMEWORK only
- Updated ByteData and nullable types for NET compatibility
- Removed obsolete default value and simplified property definitions
2025-10-14 10:53:19 +02:00
f13a2434f7 refactor(CreateAnnotationCommand): make JsonSerializerOptions static. 2025-10-14 09:34:59 +02:00
ecc7552951 refactor: move CreateAnnotationCommand extension to separate static class 2025-10-13 17:15:00 +02:00
d10f19d92a refactor(annotation): add lazy JSON deserialization for PSPDFKitInstantJSON
- Introduced Lazy<dynamic> field for deferred deserialization of PSPDFKitInstantJSON
- Added ExpandoObject property (PSPDFKitInstant) for dynamic access
- Updated handler to use ParsePSPDFKitInstant instead of ParsePSPDFKitInstantJSON
- Improved JsonSerializerOptions for case-insensitive property handling
2025-10-13 16:56:00 +02:00
5e53f2b691 refactor(CreateAnnotationCommand): update to return IEnumerable<Signature> as result 2025-10-13 15:51:53 +02:00
f56928f44f feat(CreateAnnotationCommand): implement CreateAnnotationCommand and handler logic
Added full implementation for CreateAnnotationCommand and its handler:
- Introduced `PSPDFKitInstantJSON` property to the command
- Injected repositories for `Signature` and `Annotation`
- Implemented query filtering for Envelope and Receiver
- Added annotation creation from parsed PSPDFKit JSON
- Created helper method `ParsePSPDFKitInstantJSON` for JSON parsing
2025-10-13 15:28:38 +02:00
faa37e0dcd add CreateAnnotationCommandHandler without implementation 2025-10-13 11:17:50 +02:00
e51470a449 create AnnotationDto 2025-10-13 11:16:37 +02:00
adce61fead feat: add CreateAnnotationCommand record for annotation creation 2025-10-13 10:59:58 +02:00
e052bf56f4 feat(EGDbContext): add annotations mapping and clean up constructor initialization
- Added model configuration for Signature → Annotation relationship
- Removed redundant DbSet initializations from EGDbContextBase constructor
- Updated Config DbSet type from Domain.Entities.Config to Config
- Simplified using directives and removed unnecessary configuration imports
- Maintained existing trigger registration and entity configurations
2025-10-13 10:02:26 +02:00
22a7619627 feat(Signature): add Annotations navigation property to Signature entity
- Added `IEnumerable<Annotation>? Annotations` to Signature class
- Added `using System.Collections.Generic;` for .NET Framework builds
2025-10-13 09:48:24 +02:00
281cf47834 refactor(Annotation): rename Guid as Id
- convert type to long
2025-10-13 09:43:08 +02:00
a258dcdad0 feat(Annotation): add Annotation entity for document receiver element annotations
- Added Annotation entity mapped to table TBSIG_DOCUMENT_RECEIVER_ELEMENT_ANNOTATION
- Included data annotations for key, required fields, and relationships
- Added conditional compilation for .NET and .NET Framework compatibility
2025-10-13 09:40:41 +02:00
79c26eb5b5 refactor(PDFBurner): replace fieldName with egName and simplify form field handling
- Removed formFieldIndex tracking and inline dictionary
- Introduced EGName class with centralized index mapping
- Updated AddFormFieldValue to resolve Y-offset using EGName.Index
- Replaced fieldName property with egName for annotations
- Changed helper methods from Function(Void) to Sub for clarity
2025-10-10 15:00:04 +02:00
ce7ca39c39 add isLabel property 2025-10-09 17:42:34 +02:00
7b6f916486 refactor(PDFBurner): rename Annotation.internalType to fieldName for clarity 2025-10-09 12:19:19 +02:00
57422a481c feat(PDFBurner): support unstructured annotations and simplify processing
- Added `UnstructuredAnnotations` property to handle annotations without structured IDs
- Changed default `hasStructuredID` to `False`, set to `True` only after successful parsing
- Updated `AddInstantJSONAnnotationToPDF` to process annotations directly instead of grouping by receiver
- Simplified logic for reversing annotations and applying seal positioning
2025-10-08 16:27:28 +02:00
e96523b786 update to use IRepository<T> 2025-10-08 13:36:09 +02:00
3b7d0e1321 refactor(RemoveSignatureNotification): create with handlers to remove signatures of a document 2025-10-08 12:41:14 +02:00
9adc1ea4e7 refactor(annotation): remove unnecessary loop 2025-10-08 10:26:25 +02:00
510f5e9ddd fix: update to set to hold the value of id in a varable to be able to use form fields 2025-10-08 00:47:17 +02:00
44b204ca68 add structred id to image annotations 2025-10-08 00:36:20 +02:00
b72ac68daf refactor(annotations.js): move generateId method to outsite of createAnnotations method 2025-10-08 00:29:37 +02:00
7d85d59ace add hasStructuredID property isntead of throwing exception and filter it 2025-10-08 00:19:28 +02:00
4fad41bd0b update to use # as seperator instead of _ 2025-10-07 22:19:01 +02:00
39936792aa update to use annotations-by-receiver for iteration 2025-10-07 22:16:52 +02:00
74f444a8d6 feat(PDFBurner): add AnnotationsByReceiver method to AnnotationData 2025-10-07 21:54:18 +02:00
b67f26cc21 refactor(PDFBurner): enhance annotation ID handling and validation
- Added parsing of annotation `id` into `envelopeId`, `receiverId`, `index`, and `internalType`.
- Added validation for `id` format, ensuring it has exactly 4 parts and numeric checks for relevant fields.
- Throws `BurnAnnotationException` if `id` is null, empty, or incorrectly formatted.
- Maintains existing functionality for adding image, ink, and widget annotations.
2025-10-07 21:31:38 +02:00
a29f918125 fix(DependencyInjection): update to scan the assembly of EnvelopeReceiver instead of Config 2025-10-07 18:09:14 +02:00
3f116ce11a refactor(annotation): generate deterministic annotation IDs based on envelopeId, receiverId, and element index 2025-10-07 09:41:02 +02:00
41738bb36c refactor(annotation): add envelope id and receiver id input parameters 2025-10-06 17:12:06 +02:00
320b2ecc77 refactor(AnnotationData): add SignatureAnnotations-getter method to split signatures 2025-10-06 17:08:44 +02:00
b02cc3d7a4 refactor(PDFBurner): convert to Sub 2025-10-06 14:39:55 +02:00
14f2d9b6af refactor(PDFBurner): remove sigAnnotType parameter from AddInstantJSONAnnotationToPDF method 2025-10-06 13:54:56 +02:00
df74267616 refactor(PDFBurner): Vereinfachung der PDFBurner-Annotationsmethoden und Verbesserung der Fehlerbehandlung
- Methoden für Annotationen (AddInstantJSONAnnotationToPDF, AddImageAnnotation, AddInkAnnotation, AddFormFieldValue) auf Rückgabe-Typ Void umgestellt.
- Interne Try/Catch-Blöcke und das Unterdrücken von Ausnahmen in Hilfsmethoden entfernt.
- Fehlerbehandlung in BurnInstantJSONAnnotationsToPDF zentralisiert, mit Try/Catch für das Hinzufügen von Annotationen.
- Exit Select-Anweisungen für bessere Lesbarkeit in Select Case-Blöcken hinzugefügt.
- Import von DevExpress.DataProcessing ergänzt.
2025-10-06 10:29:29 +02:00
42870b973d refactor(frmFinalizePDF): configure Factory.Shared 2025-10-01 16:52:05 +02:00
33041a8b96 update to add triggers as dictionary 2025-10-01 15:39:08 +02:00
79db05be26 update to use Factory.Shared 2025-10-01 15:18:58 +02:00
f24a218d12 refactor(FinalizeDocumentJob): add EnvelopeGeneratorInfrastructure-Services 2025-10-01 14:44:47 +02:00
44a9971cf8 refactor(DependencyInjection.Config): add AddSQLExecutor method 2025-10-01 14:08:41 +02:00
82d4b7ec6a feat(infrastructure): refactor DIExtensions for DbContext registration
* Removed `dbContextOptions` parameter from `AddEnvelopeGeneratorInfrastructureServices`
* Moved `AddDbContext` registration into `Config` class with overloads for both `Action<DbContextOptionsBuilder>` and `Action<IServiceProvider, DbContextOptionsBuilder>`
* Simplified service registration flow and improved extensibility
2025-10-01 13:25:52 +02:00
9472322c1d fix(dbcontext): add conditional nullable annotation for ILogger parameter on .NET builds 2025-10-01 13:04:44 +02:00
53a656f6ee move DbTriggerParams to Infrastructure layer.
- createConfig for flexable configuration
2025-10-01 12:59:55 +02:00
ca24afe3c6 refactor(dbcontext): add NET/NETFRAMEWORK conditional compilation
- Wrapped `using` directives with `#if NET` and `#elif NETFRAMEWORK`
- Applied conditional inheritance for `IUserManagerDbContext` and `IMailDbContext`
- Made `EmailOut` DbSet and triggers only available in `NET` builds
- Adjusted `_logger` nullability for NET vs NETFRAMEWORK targets
- Ensured `modelBuilder.ConfigureUserManager()` only runs in NET builds
2025-10-01 11:17:27 +02:00
9c867ac8aa refactor(DIExtensions): Hinzufügen einer bedingten NET/NETFRAMEWORK-Unterstützung in DIExtensions
- Umschlossene Repository- und Executor-Registrierungen mit `#if NET`, um die Kompatibilität zwischen .NET und .NET Framework sicherzustellen
- Hinzufügen des Abschnitts `#elif NETFRAMEWORK` für frameworkspezifische Importe
- Verschieben der SQLExecutor- und Dapper-Typzuordnung unter bedingte Kompilierung
- Anpassen der Methodensignaturen zur Unterstützung bedingter nullbarer Parameter
2025-10-01 10:51:50 +02:00
12063f36de refactor(Infrastructure): update Executor, Migrations and Repositories to be compiled only in .NET 2025-10-01 10:36:57 +02:00
167ea1444b update core packages 2025-10-01 10:00:11 +02:00
794029f0e5 chore(Infrastructure): update csproj to support NET 4.6.2 2025-10-01 09:57:47 +02:00
04b3d630fe update to ignore appsettings.migration.json 2025-09-29 16:45:55 +02:00
d39a3d283d bump to 3.4.1 2025-09-29 16:18:51 +02:00
7cae9a5291 feat(pdf): add overload for Page to apply design across all pages
- Introduced Page(Action<PdfPage>) method to apply design logic to every page in the document
- Simplified Background method by replacing per-page iteration with new Page overload
- Improved readability and maintainability by removing duplicate page loop logic
2025-09-29 15:43:55 +02:00
6f31d7b1d0 refactor(pdf): optimize background rendering with single concat per page
- Added `System.Linq` import to enable distinct and ordered page index selection
- Applied `ConcatMatrix` transformation once per page instead of per signature
- Improved performance and reduced redundant operations in `Background` method
2025-09-29 15:06:24 +02:00
06431028cb refactor(EnvelopeLocked): add if smsExpiration-statetment to timer script 2025-09-29 14:04:24 +02:00
eafcd79749 create appsettings.Logging 2025-09-29 10:58:34 +02:00
78473a45f1 move TFARegParams to appsettings.Security 2025-09-29 10:55:31 +02:00
5c10636e37 refactor(appsettings.GSM): create and move GtxMessagingParams 2025-09-29 10:50:42 +02:00
91c8043a23 refactor: move TFARegParams to appsettings.UI 2025-09-29 10:48:36 +02:00
5e77b300d6 fix: add PSPDFKitLicenseKey 2025-09-29 10:46:01 +02:00
ec30c86da1 move DisableMultiLanguage parameter to UI 2025-09-29 10:37:27 +02:00
341cb175a9 refactor(appsettings): add appsettings.Security 2025-09-29 10:34:52 +02:00
cccbb36f94 refactor(appsettings.UI): add appsettings for UI 2025-09-29 10:26:21 +02:00
c75877b19e add appsettings.Database 2025-09-29 10:08:57 +02:00
dde855a08f refactor(appsettings.Mail): create to hold mail and message configurations 2025-09-29 09:54:17 +02:00
247ab38536 refactor(appsettings.Annotation): create to handle Annotation para,eters 2025-09-29 09:39:47 +02:00
737df03f01 refactor(Program): update to scan all appsettings.*.json's 2025-09-29 09:26:23 +02:00
7e1ef838d7 chore(Web): bump to 3.4 2025-09-26 16:38:35 +02:00
16657f6a31 refactor(Pdf): Add Background-method to place a background behind the signatures. 2025-09-26 16:00:25 +02:00
bf0bd8e9e7 refactor(ISignature): create to handle common properties of a signature
- implement to entity and dto
2025-09-25 16:14:59 +02:00
94ce416aa1 refactor(Pdf); rename OutputStream as ExportStream
- rename ToBytes extension method as ExportAsBytes
2025-09-25 12:52:50 +02:00
7964cc44fb refactor(Extensions): add ToBytes shortcut method 2025-09-25 12:47:03 +02:00
b0b734ecfb refactor(Edit): rename as Pdf
- add Page and Design methods as shortcut methods
2025-09-25 11:06:03 +02:00
2d6347ffa6 refactor(Edit): Dokument-Methode zum Bearbeiten von PDF-Dokumenten hinzufügen 2025-09-25 10:19:11 +02:00
ac29fac88d add OutputStream getter 2025-09-24 17:49:15 +02:00
54c21556f6 refactor(Edit): add Finalizer region 2025-09-24 16:08:59 +02:00
ed4fd6ce96 feat(Edit): created to handle pdf edit operations fluently 2025-09-24 16:07:51 +02:00
0fa641c15d feat(Extensions): Add PDF editing extension methods using iText
- Introduced `Edit` extension methods for `Stream` and `byte[]` to allow
  in-place PDF modifications via a `PdfDocument` action.
- Supports both .NET and .NET Framework projects.
- Ensures proper disposal of streams and PdfDocument instances.
2025-09-24 14:12:35 +02:00
8709bd5c2e feat(PdfEditor): Erstellen ein PDF-Bearbeitungsprojekt, das sowohl .NET als auch das .NET Framework unterstützt. 2025-09-24 13:08:56 +02:00
7a6f2a3304 refactor(EnvelopeController): remove unnecessary document assigment 2025-09-24 10:07:51 +02:00
b5d744c1cd fix(annotation): invoke sigature background creation to fix z-index problem 2025-09-23 19:25:09 +02:00
a1f4898c35 refactor(annotation.js): split to regions 2025-09-23 18:08:08 +02:00
841cc4fd8d refactor(annotations.js): update createAnnotations to return signatures and remove instance input 2025-09-22 17:19:00 +02:00
e2df610544 refactor(EnvelopeController): update EnvelopeReceiverReadOnly to use without then-calback-method 2025-09-22 13:26:09 +02:00
95fd16fff0 Download the relevant Google font locally 2025-09-22 12:52:12 +02:00
4fcef41fc0 minimize return statement 2025-09-19 15:28:11 +02:00
9a2959b307 remvoe createElementFromHTML 2025-09-19 15:26:20 +02:00
d51c7ac5ae convert to function 2025-09-19 15:23:40 +02:00
925187e294 remove UI class to simplify 2025-09-19 15:22:48 +02:00
80c6b5bc64 update signatureCount to init in constructor 2025-09-19 15:18:50 +02:00
7d4106d0a5 refactor(app): simplify App initialization and rename PSPDFKit instance
- Initialize `currentDocument` and `currentReceiver` directly in constructor
- Replace `Instance` with `pdfKit` for clearer naming
- Remove redundant envelope/document loading in `init`
- Update all method references to use `this.pdfKit`
2025-09-19 15:16:59 +02:00
3b9b930b82 remove ActionType object 2025-09-19 15:07:24 +02:00
9ec6fcf272 update to use roload instead of rejection redirection 2025-09-19 15:03:23 +02:00
c38a50af34 make env.xsrfToken lazy loading
- remove env.envKey
2025-09-19 14:58:14 +02:00
877c88d52b refactor: replace repeated GET+JSON parsing with reusable getJson helper 2025-09-19 13:58:07 +02:00
9c730e8f42 refactor(api-service): rename as envelope-api 2025-09-19 13:53:12 +02:00
7a1c669fb0 move getAnnotationParams to api-service
- remove network.js
2025-09-19 13:50:19 +02:00
62411b4d2b move logout-method to api-service 2025-09-19 13:36:47 +02:00
f04fcde7b9 move setLanguage to api-service 2025-09-19 13:32:01 +02:00
2a352265a8 freeze env and url objects 2025-09-19 13:18:45 +02:00
ceff62cb64 refactor: unify and simplify request helper methods
This commit:
- Replaces `createRequest`, `createPost`, and `submitForm` with a single `sendRequest` helper.
- Adds `getRequest` and `postRequest` wrappers for clarity.
- Removes redundant content type constants and parameters.
- Updates envelope-related methods (`signEnvelope`, `rejectEnvelope`, `shareEnvelope`) to use the new request helpers.
- Simplifies `redirect` and `redirRejected` functions.
2025-09-19 13:16:48 +02:00
f1f4c6eaef create envelope region 2025-09-19 11:52:37 +02:00
f8297808ec create request helper methods region 2025-09-19 11:50:25 +02:00
0fc6fd650c refactor(postAnnotation): rename as signEnvelope 2025-09-19 11:49:14 +02:00
13af2ae3e1 refactor: move postAnnotations to ap-service 2025-09-19 11:47:49 +02:00
3914b827fb refactor(url): convert getter methods to variables 2025-09-19 11:44:23 +02:00
a5ab217ac6 refactor(env): remove lazy loading to simplfy 2025-09-19 11:42:37 +02:00
4075739522 Split the URL into two objects as URL and environment 2025-09-19 11:41:41 +02:00
8abf8260bf update to use lazy loading 2025-09-19 11:35:07 +02:00
451e7e7daa feat(lazy): created to handle lazy loading 2025-09-19 10:36:14 +02:00
6622442d95 convert to camel case 2025-09-19 10:20:23 +02:00
dcd5dc71de covnert Url to object 2025-09-19 10:07:38 +02:00
3fe09f8382 rename API as Url 2025-09-19 10:06:09 +02:00
bac9aebbc3 refactor(api): replace arrow functions with function declarations and simplify defaults
- Removed unused Content class
- Converted async arrow functions to standard function declarations
- Added default 'application/json' for createRequest and createPost
- Improved readability and consistency across API helpers
2025-09-19 09:59:57 +02:00
a8a73724e6 refactor(EnvelopeController): add logic to check the claims with role 2025-09-18 20:48:09 +02:00
8059e0aae4 remove old routes 2025-09-18 19:53:09 +02:00
4c5116695b remove envelope receiver id input 2025-09-18 19:51:43 +02:00
ffa31fbebc change LogInEnvelope route 2025-09-18 18:37:10 +02:00
e536e523b8 fix(EnvelopeController): rename CreateEnvelopeLockedView as EnvelopeLocked 2025-09-18 18:35:37 +02:00
1d64c19605 add cancelation token 2025-09-18 18:19:22 +02:00
29e6ba8733 Refactoring (EnvelopeController): Vereinheitlichung der Verarbeitung von gesperrten Ansichten
- Methode „CreateEnvelopeLockedView” hinzugefügt, um die Logik für die Anzeige gesperrter/umschlossener Ansichten zu zentralisieren
- Aktion „Main“ aktualisiert, um „CreateEnvelopeLockedView“ anstelle von Redirect zu verwenden
- Veraltete Methode „EnvelopeLocked“ entfernt
- TFA-bezogene Ansichten und Handler aktualisiert, um auf „CreateEnvelopeLockedView“ anstelle von „EnvelopeLocked“ zu verweisen
- Dokumentenlade- und Anmeldelogik in wiederverwendbare Methoden vereinfacht
2025-09-18 18:18:59 +02:00
2e6eeafd74 refactor(EnvelopeController): add read-only region 2025-09-18 17:22:40 +02:00
643501f484 refactor(EnvelopeController): simplify EnvelopeController actions and view handling
- Added overload of CreateShowEnvelopeView(EnvelopeReceiverDto) to remove redundant id parsing logic
- Refactored EnvelopeLocked action to receive EnvelopeReceiverDto directly instead of fetching inside
- Centralized ViewData assignments for cleaner envelope rendering
- Improved error logging to include envelope UUID and receiver signature where applicable
- Adjusted EnvelopeLocked GET to return explicit "EnvelopeLocked" view for unauthenticated users
2025-09-18 17:14:28 +02:00
0c900d219c refactor(EnvelopeController): add Send Access Code-region 2025-09-18 16:19:40 +02:00
8d8757810c refactor(EnvelopeController): move rejection and signing check to Main-endpoint 2025-09-18 16:15:33 +02:00
9dec681ce5 Home und View in Home und Envelope unterteilen 2025-09-18 14:01:21 +02:00
01ec2b3df2 refactor(HomeController): Als Home- und Envelope-Controller aufteilen 2025-09-18 13:16:33 +02:00
d64d46920a refactor(api-services): change wrong url route 2025-09-18 12:59:01 +02:00
6147a619a8 refactor(EnvelopeReceiverReadOnly): fix nullability problem 2025-09-18 12:33:00 +02:00
118 changed files with 6111 additions and 3874 deletions

View File

@@ -1,8 +0,0 @@
namespace EnvelopeGenerator.Application.Common.Configurations;
/// <summary>
///
/// </summary>
public class DbTriggerParams : Dictionary<string, ICollection<string>>
{
}

View File

@@ -0,0 +1,73 @@
namespace EnvelopeGenerator.Application.Common.Dto;
/// <summary>
///
/// </summary>
public record AnnotationCreateDto
{
/// <summary>
///
/// </summary>
public int ElementId { get; init; }
/// <summary>
///
/// </summary>
public string Name { get; init; } = null!;
/// <summary>
///
/// </summary>
public string Value { get; init; } = null!;
/// <summary>
///
/// </summary>
public string Type { get; init; } = null!;
/// <summary>
///
/// </summary>
public double? X { get; init; }
/// <summary>
///
/// </summary>
public double? Y { get; init; }
/// <summary>
///
/// </summary>
public double? Width { get; init; }
/// <summary>
///
/// </summary>
public double? Height { get; init; }
}
/// <summary>
///
/// </summary>
public record AnnotationDto : AnnotationCreateDto
{
/// <summary>
///
/// </summary>
public long Id { get; init; }
/// <summary>
///
/// </summary>
public DateTime AddedWhen { get; init; }
/// <summary>
///
/// </summary>
public DateTime? ChangedWhen { get; init; }
/// <summary>
///
/// </summary>
public string? ChangedWho { get; init; }
}

View File

@@ -82,7 +82,7 @@ public record EnvelopeDto
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public bool? UseAccessCode { get; set; } public bool UseAccessCode { get; set; } = true;
/// <summary> /// <summary>
/// ///

View File

@@ -35,6 +35,7 @@ public class MappingProfile : Profile
CreateMap<EnvelopeType, EnvelopeTypeDto>(); CreateMap<EnvelopeType, EnvelopeTypeDto>();
CreateMap<Domain.Entities.Receiver, ReceiverDto>(); CreateMap<Domain.Entities.Receiver, ReceiverDto>();
CreateMap<Domain.Entities.EnvelopeReceiverReadOnly, EnvelopeReceiverReadOnlyDto>(); CreateMap<Domain.Entities.EnvelopeReceiverReadOnly, EnvelopeReceiverReadOnlyDto>();
CreateMap<ElementAnnotation, AnnotationDto>();
// DTO to Entity mappings // DTO to Entity mappings
CreateMap<ConfigDto, Config>(); CreateMap<ConfigDto, Config>();
@@ -50,6 +51,8 @@ public class MappingProfile : Profile
CreateMap<ReceiverDto, Domain.Entities.Receiver>().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore()); CreateMap<ReceiverDto, Domain.Entities.Receiver>().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore());
CreateMap<EnvelopeReceiverReadOnlyCreateDto, Domain.Entities.EnvelopeReceiverReadOnly>(); CreateMap<EnvelopeReceiverReadOnlyCreateDto, Domain.Entities.EnvelopeReceiverReadOnly>();
CreateMap<EnvelopeReceiverReadOnlyUpdateDto, Domain.Entities.EnvelopeReceiverReadOnly>(); CreateMap<EnvelopeReceiverReadOnlyUpdateDto, Domain.Entities.EnvelopeReceiverReadOnly>();
CreateMap<AnnotationCreateDto, ElementAnnotation>()
.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.UtcNow));
// Messaging mappings // Messaging mappings
// for GTX messaging // for GTX messaging

View File

@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc; using EnvelopeGenerator.Domain.Interfaces;
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.Common.Dto; namespace EnvelopeGenerator.Application.Common.Dto;
@@ -6,7 +7,7 @@ namespace EnvelopeGenerator.Application.Common.Dto;
/// Data Transfer Object representing a positioned element assigned to a document receiver. /// Data Transfer Object representing a positioned element assigned to a document receiver.
/// </summary> /// </summary>
[ApiExplorerSettings(IgnoreApi = true)] [ApiExplorerSettings(IgnoreApi = true)]
public class SignatureDto public class SignatureDto : ISignature
{ {
/// <summary> /// <summary>
/// Gets or sets the unique identifier of the element. /// Gets or sets the unique identifier of the element.

View File

@@ -1,12 +1,20 @@
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver; using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using EnvelopeGenerator.Application.Common.Extensions; using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Notifications.RemoveSignature;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Constants;
using MediatR; using MediatR;
using Newtonsoft.Json;
using System.Dynamic; using System.Dynamic;
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned; namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned;
/// <summary>
///
/// </summary>
/// <param name="Instant"></param>
/// <param name="Structured"></param>
public record PsPdfKitAnnotation(ExpandoObject Instant, IEnumerable<AnnotationCreateDto> Structured);
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
@@ -16,7 +24,7 @@ public record DocSignedNotification(EnvelopeReceiverDto Original) : EnvelopeRece
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public required ExpandoObject Annotations { get; init; } public PsPdfKitAnnotation PsPdfKitAnnotation { get; init; } = null!;
/// <summary> /// <summary>
/// ///
@@ -40,17 +48,41 @@ public static class DocSignedNotificationExtensions
/// Converts an <see cref="EnvelopeReceiverDto"/> to a <see cref="DocSignedNotification"/>. /// Converts an <see cref="EnvelopeReceiverDto"/> to a <see cref="DocSignedNotification"/>.
/// </summary> /// </summary>
/// <param name="dto">The DTO to convert.</param> /// <param name="dto">The DTO to convert.</param>
/// <param name="annotations"></param> /// <param name="psPdfKitAnnotation"></param>
/// <returns>A new <see cref="DocSignedNotification"/> instance.</returns> /// <returns>A new <see cref="DocSignedNotification"/> instance.</returns>
public static DocSignedNotification ToDocSignedNotification(this EnvelopeReceiverDto dto, ExpandoObject annotations) public static DocSignedNotification ToDocSignedNotification(this EnvelopeReceiverDto dto, PsPdfKitAnnotation psPdfKitAnnotation)
=> new(dto) { Annotations = annotations }; => new(dto) { PsPdfKitAnnotation = psPdfKitAnnotation };
/// <summary> /// <summary>
/// Asynchronously converts a <see cref="Task{EnvelopeReceiverDto}"/> to a <see cref="DocSignedNotification"/>. ///
/// </summary> /// </summary>
/// <param name="dtoTask">The task that returns the DTO to convert.</param> /// <param name="dtoTask"></param>
/// <param name="annotations"></param> /// <param name="psPdfKitAnnotation"></param>
/// <returns>A task that represents the asynchronous conversion operation.</returns> /// <returns></returns>
public static async Task<DocSignedNotification?> ToDocSignedNotification(this Task<EnvelopeReceiverDto?> dtoTask, ExpandoObject annotations) public static async Task<DocSignedNotification?> ToDocSignedNotification(this Task<EnvelopeReceiverDto?> dtoTask, PsPdfKitAnnotation psPdfKitAnnotation)
=> await dtoTask is EnvelopeReceiverDto dto ? new(dto) { Annotations = annotations } : null; => await dtoTask is EnvelopeReceiverDto dto ? new(dto) { PsPdfKitAnnotation = psPdfKitAnnotation } : null;
/// <summary>
///
/// </summary>
/// <param name="publisher"></param>
/// <param name="notification"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public static async Task PublishSafely(this IPublisher publisher, DocSignedNotification notification, CancellationToken cancel = default)
{
try
{
await publisher.Publish(notification, cancel);
}
catch (Exception)
{
await publisher.Publish(new RemoveSignatureNotification()
{
EnvelopeId = notification.EnvelopeId,
ReceiverId = notification.ReceiverId
}, cancel);
throw;
}
}
} }

View File

@@ -1,7 +1,6 @@
using EnvelopeGenerator.Application.DocStatus.Commands; using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Entities;
using MediatR; using MediatR;
using System.Text.Json;
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers; namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
@@ -10,15 +9,18 @@ namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
/// </summary> /// </summary>
public class AnnotationHandler : INotificationHandler<DocSignedNotification> public class AnnotationHandler : INotificationHandler<DocSignedNotification>
{ {
private readonly ISender _sender; /// <summary>
///
/// </summary>
private readonly IRepository<ElementAnnotation> _repo;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="repository"></param>
public AnnotationHandler(ISender sender) public AnnotationHandler(IRepository<ElementAnnotation> repository)
{ {
_sender = sender; _repo = repository;
} }
/// <summary> /// <summary>
@@ -27,13 +29,6 @@ public class AnnotationHandler : INotificationHandler<DocSignedNotification>
/// <param name="notification"></param> /// <param name="notification"></param>
/// <param name="cancel"></param> /// <param name="cancel"></param>
/// <returns></returns> /// <returns></returns>
public async Task Handle(DocSignedNotification notification, CancellationToken cancel) public Task Handle(DocSignedNotification notification, CancellationToken cancel)
{ => _repo.CreateAsync(notification.PsPdfKitAnnotation.Structured, cancel);
await _sender.Send(new SaveDocStatusCommand()
{
Envelope = new() { Id = notification.EnvelopeId },
Receiver = new() { Id = notification.ReceiverId},
Value = JsonSerializer.Serialize(notification.Annotations, Format.Json.ForAnnotations)
}, cancel);
}
} }

View File

@@ -0,0 +1,39 @@
using EnvelopeGenerator.Application.DocStatus.Commands;
using EnvelopeGenerator.Domain.Constants;
using MediatR;
using System.Text.Json;
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
/// <summary>
///
/// </summary>
public class DocStatusHandler : INotificationHandler<DocSignedNotification>
{
private readonly ISender _sender;
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
public DocStatusHandler(ISender sender)
{
_sender = sender;
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
{
await _sender.Send(new SaveDocStatusCommand()
{
Envelope = new() { Id = notification.EnvelopeId },
Receiver = new() { Id = notification.ReceiverId},
Value = JsonSerializer.Serialize(notification.PsPdfKitAnnotation.Instant, Format.Json.ForAnnotations)
}, cancel);
}
}

View File

@@ -2,7 +2,6 @@
using EnvelopeGenerator.Application.Histories.Commands; using EnvelopeGenerator.Application.Histories.Commands;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Constants;
using MediatR; using MediatR;
using Newtonsoft.Json;
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers; namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;

View File

@@ -0,0 +1,53 @@
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature.Handlers;
/// <summary>
///
/// </summary>
public class RemoveAnnotationHandler : INotificationHandler<RemoveSignatureNotification>
{
private readonly IRepository<ElementAnnotation> _repo;
/// <summary>
///
/// </summary>
/// <param name="repository"></param>
public RemoveAnnotationHandler(IRepository<ElementAnnotation> repository)
{
_repo = repository;
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public Task Handle(RemoveSignatureNotification notification, CancellationToken cancel)
{
notification.ThrowIfHasNoFilter();
return _repo.DeleteAsync(annots =>
{
// envelope ID filter
if (notification.EnvelopeId is int envelopeId)
annots = annots.Where(annot => annot.Element!.Document.EnvelopeId == envelopeId);
// envelope UUID filter
if (notification.EnvelopeUuid is string envelopeUuid)
annots = annots.Where(annot => annot.Element!.Document.Envelope!.Uuid == envelopeUuid);
// receiver ID
if (notification.ReceiverId is int receiverId)
annots = annots.Where(annot => annot.Element!.ReceiverId == receiverId);
// receiver signature
if (notification.ReceiverSignature is string receiverSignature)
annots = annots.Where(annot => annot.Element!.Receiver!.Signature == receiverSignature);
return annots;
}, cancel);
}
}

View File

@@ -0,0 +1,53 @@
using AngleSharp.Html;
using DigitalData.Core.Abstraction.Application.Repository;
using MediatR;
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature.Handlers;
/// <summary>
///
/// </summary>
public class RemoveDocStatusHandler : INotificationHandler<RemoveSignatureNotification>
{
private readonly IRepository<Domain.Entities.DocumentStatus> _repo;
/// <summary>
///
/// </summary>
/// <param name="repository"></param>
public RemoveDocStatusHandler(IRepository<Domain.Entities.DocumentStatus> repository)
{
_repo = repository;
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public Task Handle(RemoveSignatureNotification notification, CancellationToken cancel)
{
notification.ThrowIfHasNoFilter();
return _repo.DeleteAsync(statuses =>
{
// envelope ID filter
if (notification.EnvelopeId is int envelopeId)
statuses = statuses.Where(status => status.EnvelopeId == envelopeId);
// envelope UUID filter
if (notification.EnvelopeUuid is string envelopeUuid)
statuses = statuses.Where(status => status.Envelope!.Uuid == envelopeUuid);
// receiver Id filter
if (notification.ReceiverId is int receiverId)
statuses = statuses.Where(status => status.ReceiverId == receiverId);
// receiver signature filter
if (notification.ReceiverSignature is string receiverSignature)
statuses = statuses.Where(status => status.Receiver!.Signature == receiverSignature);
return statuses;
}, cancel);
}
}

View File

@@ -0,0 +1,56 @@
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Domain.Constants;
using MediatR;
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature.Handlers;
/// <summary>
///
/// </summary>
public class RemoveHistoryHandler : INotificationHandler<RemoveSignatureNotification>
{
private readonly IRepository<Domain.Entities.History> _repo;
/// <summary>
///
/// </summary>
/// <param name="repository"></param>
public RemoveHistoryHandler(IRepository<Domain.Entities.History> repository)
{
_repo = repository;
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public Task Handle(RemoveSignatureNotification notification, CancellationToken cancel)
{
notification.ThrowIfHasNoFilter();
return _repo.DeleteAsync(hists =>
{
hists = hists.Where(hist => hist.Status == EnvelopeStatus.DocumentSigned);
// envelope ID filter
if (notification.EnvelopeId is int envelopeId)
hists = hists.Where(hist => hist.EnvelopeId == envelopeId);
// envelope UUID filter
if (notification.EnvelopeUuid is string envelopeUuid)
hists = hists.Where(hist => hist.Envelope!.Uuid == envelopeUuid);
// receiver ID filter
if (notification.ReceiverId is int receiverId)
hists = hists.Where(hist => hist.Receiver!.Id == receiverId);
// receiver signature filter
if (notification.ReceiverSignature is string receiverSignature)
hists = hists.Where(hist => hist.Receiver!.Signature == receiverSignature);
return hists;
}, cancel);
}
}

View File

@@ -0,0 +1,37 @@
using MediatR;
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature;
/// <summary>
///
/// </summary>
/// <param name="EnvelopeId"></param>
/// <param name="ReceiverId"></param>
/// <param name="EnvelopeUuid"></param>
/// <param name="ReceiverSignature"></param>
public record RemoveSignatureNotification(
int? EnvelopeId = null,
int? ReceiverId = null,
string? EnvelopeUuid = null,
string? ReceiverSignature = null
) : INotification
{
/// <summary>
///
/// </summary>
public bool HasFilter =>
EnvelopeId is not null
|| ReceiverId is not null
|| EnvelopeUuid is not null
|| ReceiverSignature is not null;
/// <summary>
///
/// </summary>
/// <exception cref="InvalidOperationException"></exception>
public void ThrowIfHasNoFilter()
{
if (!HasFilter)
throw new InvalidOperationException("At least one filter parameter must be provided.");
}
}

View File

@@ -46,7 +46,6 @@ public static class DependencyInjection
services.Configure<MailParams>(config.GetSection(nameof(MailParams))); services.Configure<MailParams>(config.GetSection(nameof(MailParams)));
services.Configure<AuthenticatorParams>(config.GetSection(nameof(AuthenticatorParams))); services.Configure<AuthenticatorParams>(config.GetSection(nameof(AuthenticatorParams)));
services.Configure<TotpSmsParams>(config.GetSection(nameof(TotpSmsParams))); services.Configure<TotpSmsParams>(config.GetSection(nameof(TotpSmsParams)));
services.Configure<DbTriggerParams>(config.GetSection(nameof(DbTriggerParams)));
services.AddHttpClientService<GtxMessagingParams>(config.GetSection(nameof(GtxMessagingParams))); services.AddHttpClientService<GtxMessagingParams>(config.GetSection(nameof(GtxMessagingParams)));
services.TryAddSingleton<ISmsSender, GTXSmsSender>(); services.TryAddSingleton<ISmsSender, GTXSmsSender>();

View File

@@ -14,7 +14,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapper" Version="2.1.66" /> <PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.3.1" /> <PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.4.0" />
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" /> <PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
<PackageReference Include="DigitalData.Core.Client" Version="2.1.0" /> <PackageReference Include="DigitalData.Core.Client" Version="2.1.0" />
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" /> <PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />

View File

@@ -0,0 +1,101 @@
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Application.Histories.Queries;
//TODO: Add sender query
/// <summary>
/// Repräsentiert eine Abfrage für die Verlaufshistorie eines Umschlags.
/// </summary>
public record CountHistoryQuery : HistoryQueryBase, IRequest<int>;
/// <summary>
///
/// </summary>
public static class CountHistoryQueryExtensions
{
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="uuid"></param>
/// <param name="statuses"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public static async Task<bool> AnyHistoryAsync(this ISender sender, string uuid, IEnumerable<EnvelopeStatus> statuses, CancellationToken cancel = default)
{
var count = await sender.Send(new CountHistoryQuery
{
Envelope = new() { Uuid = uuid },
Statuses = new() { Include = statuses }
}, cancel);
return count > 0;
}
}
/// <summary>
///
/// </summary>
public class CountHistoryQueryHandler : IRequestHandler<CountHistoryQuery, int>
{
private readonly IRepository<History> _repo;
/// <summary>
///
/// </summary>
/// <param name="repo"></param>
public CountHistoryQueryHandler(IRepository<History> repo)
{
_repo = repo;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
/// <exception cref="NotFoundException"></exception>
public Task<int> Handle(CountHistoryQuery request, CancellationToken cancel = default)
{
var query = _repo.Query;
if (request.Envelope.Id is int envId)
query = query.Where(e => e.Id == envId);
else if (request.Envelope.Uuid is string uuid)
query = query.Where(e => e.Envelope!.Uuid == uuid);
#pragma warning disable CS0618 // Type or member is obsolete
else if (request.EnvelopeId is not null)
query = query.Where(h => h.EnvelopeId == request.EnvelopeId);
#pragma warning restore CS0618 // Type or member is obsolete
else
throw new BadRequestException("Invalid request: An Envelope object or a valid EnvelopeId/UUID must be supplied.");
#pragma warning disable CS0618 // Type or member is obsolete
if (request.Status is not null)
query = query.Where(h => h.Status == request.Status);
#pragma warning restore CS0618 // Type or member is obsolete
if (request.Statuses is not null)
{
var status = request.Statuses;
if (status.Min is not null)
query = query.Where(er => er.Envelope!.Status >= status.Min);
if (status.Max is not null)
query = query.Where(er => er.Envelope!.Status <= status.Max);
if (status.Include?.Count() > 0)
query = query.Where(er => status.Include.Contains(er.Envelope!.Status));
if (status.Ignore is not null)
query = query.Where(er => !status.Ignore.Contains(er.Envelope!.Status));
}
return query.CountAsync(cancel);
}
}

View File

@@ -0,0 +1,60 @@
using EnvelopeGenerator.Application.Common.Query;
using EnvelopeGenerator.Domain.Constants;
using System.ComponentModel.DataAnnotations;
namespace EnvelopeGenerator.Application.Histories.Queries;
//TODO: Add sender query
/// <summary>
///
/// </summary>
public record HistoryQueryBase
{
/// <summary>
/// Die eindeutige Kennung des Umschlags.
/// </summary>
[Obsolete("Use Envelope property")]
public int? EnvelopeId { get; set; }
/// <summary>
/// Der Include des Umschlags, der abgefragt werden soll. Kann optional angegeben werden, um die Ergebnisse zu filtern.
/// </summary>
[Obsolete("Use statuses")]
public EnvelopeStatus? Status { get; set; }
/// <summary>
///
/// </summary>
public EnvelopeStatusQuery Statuses { get; set; } = new();
/// <summary>
///
/// </summary>
public EnvelopeQueryBase Envelope { get; set; } = new EnvelopeQueryBase();
}
/// <summary>
///
/// </summary>
public record EnvelopeStatusQuery
{
/// <summary>
/// Der minimale Statuswert, der berücksichtigt werden.
/// </summary>
public EnvelopeStatus? Min { get; init; }
/// <summary>
/// Der maximale Statuswert, der berücksichtigt werden.
/// </summary>
public EnvelopeStatus? Max { get; init; }
/// <summary>
/// Eine Liste von Statuswerten, die einbezogen werden.
/// </summary>
public IEnumerable<EnvelopeStatus>? Include { get; init; }
/// <summary>
/// Eine Liste von Statuswerten, die ignoriert werden werden.
/// </summary>
public IEnumerable<EnvelopeStatus>? Ignore { get; init; }
}

View File

@@ -1,7 +1,10 @@
using EnvelopeGenerator.Application.Common.Dto.History; using AutoMapper;
using EnvelopeGenerator.Domain.Constants; using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Application.Common.Dto.History;
using MediatR; using MediatR;
using System.ComponentModel.DataAnnotations; using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Application.Histories.Queries; namespace EnvelopeGenerator.Application.Histories.Queries;
@@ -9,21 +12,81 @@ namespace EnvelopeGenerator.Application.Histories.Queries;
/// <summary> /// <summary>
/// Repräsentiert eine Abfrage für die Verlaufshistorie eines Umschlags. /// Repräsentiert eine Abfrage für die Verlaufshistorie eines Umschlags.
/// </summary> /// </summary>
public record ReadHistoryQuery : IRequest<IEnumerable<HistoryDto>> public record ReadHistoryQuery : HistoryQueryBase, IRequest<IEnumerable<HistoryDto>>
{ {
/// <summary>
/// Die eindeutige Kennung des Umschlags.
/// </summary>
[Required]
public int EnvelopeId { get; init; }
/// <summary>
/// Der Include des Umschlags, der abgefragt werden soll. Kann optional angegeben werden, um die Ergebnisse zu filtern.
/// </summary>
public EnvelopeStatus? Status { get; init; }
/// <summary> /// <summary>
/// Abfrage zur Steuerung, ob nur der aktuelle Include oder der gesamte Datensatz zurückgegeben wird. /// Abfrage zur Steuerung, ob nur der aktuelle Include oder der gesamte Datensatz zurückgegeben wird.
/// </summary> /// </summary>
public bool? OnlyLast { get; init; } = true; public bool OnlyLast { get; init; } = true;
}
/// <summary>
///
/// </summary>
public class ReadHistoryQueryHandler : IRequestHandler<ReadHistoryQuery, IEnumerable<HistoryDto>>
{
private readonly IRepository<History> _repo;
private readonly IMapper _mapper;
/// <summary>
///
/// </summary>
/// <param name="repo"></param>
/// <param name="mapper"></param>
public ReadHistoryQueryHandler(IRepository<History> repo, IMapper mapper)
{
_repo = repo;
_mapper = mapper;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
/// <exception cref="NotFoundException"></exception>
public async Task<IEnumerable<HistoryDto>> Handle(ReadHistoryQuery request, CancellationToken cancel = default)
{
var query = _repo.Query;
if (request.Envelope.Id is int envId)
query = query.Where(e => e.Id == envId);
else if (request.Envelope.Uuid is string uuid)
query = query.Where(e => e.Envelope!.Uuid == uuid);
#pragma warning disable CS0618 // Type or member is obsolete
else if (request.EnvelopeId is not null)
query = query.Where(h => h.EnvelopeId == request.EnvelopeId);
#pragma warning restore CS0618 // Type or member is obsolete
else
throw new BadRequestException("Invalid request: An Envelope object or a valid EnvelopeId/UUID must be supplied.");
#pragma warning disable CS0618 // Type or member is obsolete
if (request.Status is not null)
query = query.Where(h => h.Status == request.Status);
#pragma warning restore CS0618 // Type or member is obsolete
if (request.Statuses is not null)
{
var status = request.Statuses;
if (status.Min is not null)
query = query.Where(er => er.Envelope!.Status >= status.Min);
if (status.Max is not null)
query = query.Where(er => er.Envelope!.Status <= status.Max);
if (status.Include?.Count() > 0)
query = query.Where(er => status.Include.Contains(er.Envelope!.Status));
if (status.Ignore is not null)
query = query.Where(er => !status.Ignore.Contains(er.Envelope!.Status));
}
if (request.OnlyLast)
query = query.OrderByDescending(x => x.AddedWhen);
var hists = await query.ToListAsync(cancel);
return _mapper.Map<List<HistoryDto>>(hists);
}
} }

View File

@@ -1,47 +0,0 @@
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Application.Common.Dto.History;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Application.Histories.Queries;
/// <summary>
///
/// </summary>
public class ReadHistoryQueryHandler : IRequestHandler<ReadHistoryQuery, IEnumerable<HistoryDto>>
{
private readonly IRepository<History> _repo;
private readonly IMapper _mapper;
/// <summary>
///
/// </summary>
/// <param name="repo"></param>
/// <param name="mapper"></param>
public ReadHistoryQueryHandler(IRepository<History> repo, IMapper mapper)
{
_repo = repo;
_mapper = mapper;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
/// <exception cref="NotFoundException"></exception>
public async Task<IEnumerable<HistoryDto>> Handle(ReadHistoryQuery request, CancellationToken cancel = default)
{
var query = _repo.ReadOnly().Where(h => h.EnvelopeId == request.EnvelopeId);
if (request.Status is not null)
query = query.Where(h => h.Status == request.Status);
var hists = await query.ToListAsync(cancel);
return _mapper.Map<List<HistoryDto>>(hists);
}
}

View File

@@ -45,6 +45,9 @@
<StartupObject>EnvelopeGenerator.My.MyApplication</StartupObject> <StartupObject>EnvelopeGenerator.My.MyApplication</StartupObject>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="AutoMapper, Version=10.0.0.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
<HintPath>..\packages\AutoMapper.10.1.1\lib\net461\AutoMapper.dll</HintPath>
</Reference>
<Reference Include="BouncyCastle.Cryptography, Version=2.0.0.0, Culture=neutral, PublicKeyToken=072edcf4a5328938, processorArchitecture=MSIL"> <Reference Include="BouncyCastle.Cryptography, Version=2.0.0.0, Culture=neutral, PublicKeyToken=072edcf4a5328938, processorArchitecture=MSIL">
<HintPath>..\packages\BouncyCastle.Cryptography.2.5.0\lib\net461\BouncyCastle.Cryptography.dll</HintPath> <HintPath>..\packages\BouncyCastle.Cryptography.2.5.0\lib\net461\BouncyCastle.Cryptography.dll</HintPath>
</Reference> </Reference>
@@ -67,6 +70,12 @@
<Reference Include="DigitalData.Controls.DocumentViewer, Version=1.9.8.0, Culture=neutral, processorArchitecture=MSIL"> <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> <HintPath>..\packages\DigitalData.Controls.DocumentViewer.1.9.8\lib\net462\DigitalData.Controls.DocumentViewer.dll</HintPath>
</Reference> </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>
<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>
</Reference>
<Reference Include="DigitalData.Modules.Base, Version=1.3.8.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="DigitalData.Modules.Base, Version=1.3.8.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Modules.Base.1.3.8\lib\net462\DigitalData.Modules.Base.dll</HintPath> <HintPath>..\packages\DigitalData.Modules.Base.1.3.8\lib\net462\DigitalData.Modules.Base.dll</HintPath>
</Reference> </Reference>
@@ -176,12 +185,84 @@
<Reference Include="Microsoft.Bcl.Cryptography, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="Microsoft.Bcl.Cryptography, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.Cryptography.9.0.0\lib\net462\Microsoft.Bcl.Cryptography.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Cryptography.9.0.0\lib\net462\Microsoft.Bcl.Cryptography.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Bcl.HashCode, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.HashCode.1.1.1\lib\net461\Microsoft.Bcl.HashCode.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Data.SqlClient, Version=1.13.20136.2, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Data.SqlClient.1.1.3\lib\net46\Microsoft.Data.SqlClient.dll</HintPath>
</Reference>
<Reference Include="Microsoft.EntityFrameworkCore, Version=3.1.32.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.EntityFrameworkCore.3.1.32\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll</HintPath>
</Reference>
<Reference Include="Microsoft.EntityFrameworkCore.Abstractions, Version=3.1.32.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.EntityFrameworkCore.Abstractions.3.1.32\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.EntityFrameworkCore.Relational, Version=3.1.32.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.EntityFrameworkCore.Relational.3.1.32\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Relational.dll</HintPath>
</Reference>
<Reference Include="Microsoft.EntityFrameworkCore.SqlServer, Version=3.1.32.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.EntityFrameworkCore.SqlServer.3.1.32\lib\netstandard2.0\Microsoft.EntityFrameworkCore.SqlServer.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Caching.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Caching.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.Caching.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Caching.Memory, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Caching.Memory.7.0.0\lib\net462\Microsoft.Extensions.Caching.Memory.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Configuration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Configuration.7.0.0\lib\net462\Microsoft.Extensions.Configuration.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Configuration.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Configuration.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.Configuration.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Configuration.Binder, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Configuration.Binder.7.0.0\lib\net462\Microsoft.Extensions.Configuration.Binder.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.DependencyInjection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.7.0.0\lib\net462\Microsoft.Extensions.DependencyInjection.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Localization.Abstractions, Version=7.0.16.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Localization.Abstractions.7.0.16\lib\net462\Microsoft.Extensions.Localization.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Logging, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.7.0.0\lib\net462\Microsoft.Extensions.Logging.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Options, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Options.7.0.0\lib\net462\Microsoft.Extensions.Options.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Options.ConfigurationExtensions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Options.ConfigurationExtensions.7.0.0\lib\net462\Microsoft.Extensions.Options.ConfigurationExtensions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Primitives, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Primitives.7.0.0\lib\net462\Microsoft.Extensions.Primitives.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Identity.Client, Version=4.77.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae, processorArchitecture=MSIL"> <Reference Include="Microsoft.Identity.Client, Version=4.77.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Identity.Client.4.77.0\lib\net462\Microsoft.Identity.Client.dll</HintPath> <HintPath>..\packages\Microsoft.Identity.Client.4.77.0\lib\net462\Microsoft.Identity.Client.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.IdentityModel.Abstractions, Version=8.14.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="Microsoft.IdentityModel.Abstractions, Version=8.14.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Abstractions.8.14.0\lib\net462\Microsoft.IdentityModel.Abstractions.dll</HintPath> <HintPath>..\packages\Microsoft.IdentityModel.Abstractions.8.14.0\lib\net462\Microsoft.IdentityModel.Abstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.IdentityModel.JsonWebTokens, Version=7.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.JsonWebTokens.7.5.1\lib\net462\Microsoft.IdentityModel.JsonWebTokens.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Logging, Version=7.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Logging.7.5.1\lib\net462\Microsoft.IdentityModel.Logging.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Protocols, Version=5.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Protocols.5.5.0\lib\net461\Microsoft.IdentityModel.Protocols.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect, Version=5.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.5.5.0\lib\net461\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Tokens, Version=7.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Tokens.7.5.1\lib\net462\Microsoft.IdentityModel.Tokens.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
@@ -216,19 +297,31 @@
<Reference Include="System.Collections.Immutable, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Collections.Immutable, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Collections.Immutable.9.0.0\lib\net462\System.Collections.Immutable.dll</HintPath> <HintPath>..\packages\System.Collections.Immutable.9.0.0\lib\net462\System.Collections.Immutable.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.ComponentModel.Annotations, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.ComponentModel.Annotations.4.7.0\lib\net461\System.ComponentModel.Annotations.dll</HintPath>
</Reference>
<Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Configuration" /> <Reference Include="System.Configuration" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Data.Common, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Data.Common.4.3.0\lib\net451\System.Data.Common.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Data.Odbc, Version=6.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Data.Odbc, Version=6.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Data.Odbc.6.0.1\lib\net461\System.Data.Odbc.dll</HintPath> <HintPath>..\packages\System.Data.Odbc.6.0.1\lib\net461\System.Data.Odbc.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Diagnostics.DiagnosticSource, Version=6.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Diagnostics.DiagnosticSource, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.6.0.1\lib\net461\System.Diagnostics.DiagnosticSource.dll</HintPath> <HintPath>..\packages\System.Diagnostics.DiagnosticSource.7.0.0\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.DirectoryServices.AccountManagement" />
<Reference Include="System.Formats.Asn1, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Formats.Asn1, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Formats.Asn1.9.0.0\lib\net462\System.Formats.Asn1.dll</HintPath> <HintPath>..\packages\System.Formats.Asn1.9.0.0\lib\net462\System.Formats.Asn1.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.IdentityModel" /> <Reference Include="System.IdentityModel" />
<Reference Include="System.IdentityModel.Tokens.Jwt, Version=7.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\System.IdentityModel.Tokens.Jwt.7.5.1\lib\net462\System.IdentityModel.Tokens.Jwt.dll</HintPath>
</Reference>
<Reference Include="System.IO.Compression" /> <Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Packaging, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.IO.Packaging, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Packaging.9.0.0\lib\net462\System.IO.Packaging.dll</HintPath> <HintPath>..\packages\System.IO.Packaging.9.0.0\lib\net462\System.IO.Packaging.dll</HintPath>
@@ -252,9 +345,25 @@
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath> <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Security" /> <Reference Include="System.Security" />
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net461\System.Security.Cryptography.Algorithms.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Cng, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Cng.5.0.0\lib\net462\System.Security.Cryptography.Cng.dll</HintPath>
</Reference>
<Reference Include="System.Security.Cryptography.Pkcs, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Security.Cryptography.Pkcs, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Pkcs.9.0.0\lib\net462\System.Security.Cryptography.Pkcs.dll</HintPath> <HintPath>..\packages\System.Security.Cryptography.Pkcs.9.0.0\lib\net462\System.Security.Cryptography.Pkcs.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Principal.Windows, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll</HintPath>
</Reference>
<Reference Include="System.Text.Encodings.Web, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Text.Encodings.Web, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.Encodings.Web.9.0.0\lib\net462\System.Text.Encodings.Web.dll</HintPath> <HintPath>..\packages\System.Text.Encodings.Web.9.0.0\lib\net462\System.Text.Encodings.Web.dll</HintPath>
</Reference> </Reference>
@@ -361,6 +470,10 @@
<Project>{4F32A98D-E6F0-4A09-BD97-1CF26107E837}</Project> <Project>{4F32A98D-E6F0-4A09-BD97-1CF26107E837}</Project>
<Name>EnvelopeGenerator.Domain</Name> <Name>EnvelopeGenerator.Domain</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj">
<Project>{63e32615-0eca-42dc-96e3-91037324b7c7}</Project>
<Name>EnvelopeGenerator.Infrastructure</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="GdPicture.NET.14.barcode.1d.reader.64.dll" /> <Content Include="GdPicture.NET.14.barcode.1d.reader.64.dll" />
@@ -391,8 +504,10 @@
<Error Condition="!Exists('..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets'))" /> <Error Condition="!Exists('..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets'))" />
<Error Condition="!Exists('..\packages\EntityFramework.6.5.1\build\EntityFramework.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\EntityFramework.6.5.1\build\EntityFramework.props'))" /> <Error Condition="!Exists('..\packages\EntityFramework.6.5.1\build\EntityFramework.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\EntityFramework.6.5.1\build\EntityFramework.props'))" />
<Error Condition="!Exists('..\packages\EntityFramework.6.5.1\build\EntityFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\EntityFramework.6.5.1\build\EntityFramework.targets'))" /> <Error Condition="!Exists('..\packages\EntityFramework.6.5.1\build\EntityFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\EntityFramework.6.5.1\build\EntityFramework.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Data.SqlClient.SNI.1.1.0\build\net46\Microsoft.Data.SqlClient.SNI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Data.SqlClient.SNI.1.1.0\build\net46\Microsoft.Data.SqlClient.SNI.targets'))" />
</Target> </Target>
<Import Project="..\packages\EntityFramework.6.5.1\build\EntityFramework.targets" Condition="Exists('..\packages\EntityFramework.6.5.1\build\EntityFramework.targets')" /> <Import Project="..\packages\EntityFramework.6.5.1\build\EntityFramework.targets" Condition="Exists('..\packages\EntityFramework.6.5.1\build\EntityFramework.targets')" />
<Import Project="..\packages\Microsoft.Data.SqlClient.SNI.1.1.0\build\net46\Microsoft.Data.SqlClient.SNI.targets" Condition="Exists('..\packages\Microsoft.Data.SqlClient.SNI.1.1.0\build\net46\Microsoft.Data.SqlClient.SNI.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@@ -107,6 +107,78 @@
<assemblyIdentity name="GdPicture.NET.14.Imaging" publicKeyToken="f52a2e60ad468dbb" culture="neutral" /> <assemblyIdentity name="GdPicture.NET.14.Imaging" publicKeyToken="f52a2e60ad468dbb" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-14.3.3.0" newVersion="14.3.3.0" /> <bindingRedirect oldVersion="0.0.0.0-14.3.3.0" newVersion="14.3.3.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Identity.Client" publicKeyToken="0a613f4dd989e8ae" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.77.0.0" newVersion="4.77.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Abstractions" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.14.0.0" newVersion="8.14.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Caching.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Caching.Memory" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Options" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Configuration.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Logging" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Tokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IdentityModel.Tokens.Jwt" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.JsonWebTokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
<entityFramework> <entityFramework>

View File

@@ -1,10 +1,14 @@
Imports DigitalData.Modules.Database Imports System.IO
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Logging
Imports EnvelopeGenerator.CommonServices.Jobs
Imports EnvelopeGenerator.CommonServices.Jobs.FinalizeDocument
Imports GdPicture14 Imports GdPicture14
Imports Newtonsoft.Json.Linq Imports Newtonsoft.Json.Linq
Imports EnvelopeGenerator.CommonServices.Jobs Imports EnvelopeGenerator.Infrastructure
Imports System.IO Imports Microsoft.EntityFrameworkCore
Imports EnvelopeGenerator.CommonServices.Jobs.FinalizeDocument Imports System.Text
Imports DigitalData.Core.Abstractions
Public Class frmFinalizePDF Public Class frmFinalizePDF
Private Const CONNECTIONSTRING = "Server=sDD-VMP04-SQL17\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=+bk8oAbbQP1AzoHtvZUbd+Mbok2f8Fl4miEx1qssJ5yEaEWoQJ9prg4L14fURpPnqi1WMNs9fE4=;" Private Const CONNECTIONSTRING = "Server=sDD-VMP04-SQL17\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=+bk8oAbbQP1AzoHtvZUbd+Mbok2f8Fl4miEx1qssJ5yEaEWoQJ9prg4L14fURpPnqi1WMNs9fE4=;"
@@ -20,10 +24,35 @@ Public Class frmFinalizePDF
Private Sub frmFinalizePDF_Load(sender As Object, e As EventArgs) Handles MyBase.Load Private Sub frmFinalizePDF_Load(sender As Object, e As EventArgs) Handles MyBase.Load
LogConfig = New LogConfig(LogConfig.PathType.CustomPath, Application.StartupPath) LogConfig = New LogConfig(LogConfig.PathType.CustomPath, Application.StartupPath)
Database = New MSSQLServer(LogConfig, MSSQLServer.DecryptConnectionString(CONNECTIONSTRING))
Dim dCnnStr As String = MSSQLServer.DecryptConnectionString(CONNECTIONSTRING)
PDFBurner = New FinalizeDocument.PDFBurner(LogConfig, pGDPictureLicenseKey, _pdfBurnerParams) Database = New MSSQLServer(LogConfig, dCnnStr)
#Disable Warning BC40000 ' Type or member is obsolete
Factory.Shared _
.BehaveOnPostBuild(PostBuildBehavior.Ignore) _
.AddEnvelopeGeneratorInfrastructureServices(
Sub(opt)
opt.AddDbTriggerParams(
Sub(triggers)
triggers("Envelope") = New List(Of String) From {"TBSIG_ENVELOPE_AFT_INS"}
triggers("History") = New List(Of String) From {"TBSIG_ENVELOPE_HISTORY_AFT_INS"}
triggers("EmailOut") = New List(Of String) From {"TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD"}
triggers("EnvelopeReceiverReadOnly") = New List(Of String) From {"TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD"}
triggers("Receiver") = New List(Of String)() ' no tigger
triggers("EmailTemplate") = New List(Of String) From {"TBSIG_EMAIL_TEMPLATE_AFT_UPD"}
End Sub)
opt.AddDbContext(
Sub(options)
options.UseSqlServer(dCnnStr) _
.EnableSensitiveDataLogging() _
.EnableDetailedErrors()
End Sub)
End Sub)
#Enable Warning BC40000 ' Type or member is obsolete
PDFBurner = New PDFBurner(LogConfig, pGDPictureLicenseKey, _pdfBurnerParams)
Viewer = New GdViewer() Viewer = New GdViewer()
Manager = New AnnotationManager() Manager = New AnnotationManager()
@@ -72,8 +101,9 @@ Public Class frmFinalizePDF
Select(Function(r As DataRow) r.Item("VALUE").ToString()). Select(Function(r As DataRow) r.Item("VALUE").ToString()).
ToList() ToList()
Dim oBuffer As Byte() = ReadEnvelope(CInt(txtEnvelope.Text)) Dim envelopeId As Integer = CInt(txtEnvelope.Text)
Dim oNewBuffer = PDFBurner.BurnInstantJSONAnnotationsToPDF(oBuffer, oJsonList) Dim oBuffer As Byte() = ReadEnvelope(envelopeId)
Dim oNewBuffer = PDFBurner.BurnAnnotsToPDF(oBuffer, oJsonList, envelopeId)
Dim desktopPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) Dim desktopPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
Dim oNewPath = Path.Combine(desktopPath, $"E{txtEnvelope.Text}R{txtReceiver.Text}.burned.pdf") Dim oNewPath = Path.Combine(desktopPath, $"E{txtEnvelope.Text}R{txtReceiver.Text}.burned.pdf")
@@ -81,7 +111,15 @@ Public Class frmFinalizePDF
Process.Start(oNewPath) Process.Start(oNewPath)
Catch ex As Exception Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical) Dim exMsg As StringBuilder = New StringBuilder(ex.Message).AppendLine()
Dim innerEx = ex.InnerException
While (innerEx IsNot Nothing)
exMsg.AppendLine(innerEx.Message)
innerEx = innerEx.InnerException
End While
MsgBox(exMsg.ToString(), MsgBoxStyle.Critical)
End Try End Try
End Sub End Sub

View File

@@ -1,7 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="AutoMapper" version="10.1.1" targetFramework="net462" />
<package id="BouncyCastle.Cryptography" version="2.5.0" 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.Controls.DocumentViewer" version="1.9.8" targetFramework="net462" />
<package id="DigitalData.Core.Abstraction.Application" version="1.4.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.Base" version="1.3.8" targetFramework="net462" />
<package id="DigitalData.Modules.Config" version="1.3.0" targetFramework="net462" /> <package id="DigitalData.Modules.Config" version="1.3.0" targetFramework="net462" />
<package id="DigitalData.Modules.Database" version="2.3.5.4" targetFramework="net462" /> <package id="DigitalData.Modules.Database" version="2.3.5.4" targetFramework="net462" />
@@ -18,9 +21,35 @@
<package id="Microsoft.AspNet.WebApi.Client" version="6.0.0" targetFramework="net462" /> <package id="Microsoft.AspNet.WebApi.Client" version="6.0.0" targetFramework="net462" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="9.0.0" targetFramework="net462" /> <package id="Microsoft.Bcl.AsyncInterfaces" version="9.0.0" targetFramework="net462" />
<package id="Microsoft.Bcl.Cryptography" version="9.0.0" targetFramework="net462" /> <package id="Microsoft.Bcl.Cryptography" version="9.0.0" targetFramework="net462" />
<package id="Microsoft.Bcl.HashCode" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.CSharp" version="4.7.0" targetFramework="net462" /> <package id="Microsoft.CSharp" version="4.7.0" targetFramework="net462" />
<package id="Microsoft.Data.SqlClient" version="1.1.3" targetFramework="net462" />
<package id="Microsoft.Data.SqlClient.SNI" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.EntityFrameworkCore" version="3.1.32" targetFramework="net462" />
<package id="Microsoft.EntityFrameworkCore.Abstractions" version="3.1.32" targetFramework="net462" />
<package id="Microsoft.EntityFrameworkCore.Analyzers" version="3.1.32" targetFramework="net462" />
<package id="Microsoft.EntityFrameworkCore.Relational" version="3.1.32" targetFramework="net462" />
<package id="Microsoft.EntityFrameworkCore.SqlServer" version="3.1.32" targetFramework="net462" />
<package id="Microsoft.Extensions.Caching.Abstractions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Caching.Memory" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Configuration" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Configuration.Abstractions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Configuration.Binder" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.DependencyInjection" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Localization.Abstractions" version="7.0.16" targetFramework="net462" />
<package id="Microsoft.Extensions.Logging" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Options" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Options.ConfigurationExtensions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Primitives" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Identity.Client" version="4.77.0" targetFramework="net462" /> <package id="Microsoft.Identity.Client" version="4.77.0" targetFramework="net462" />
<package id="Microsoft.IdentityModel.Abstractions" version="8.14.0" targetFramework="net462" /> <package id="Microsoft.IdentityModel.Abstractions" version="8.14.0" targetFramework="net462" />
<package id="Microsoft.IdentityModel.JsonWebTokens" version="7.5.1" targetFramework="net462" />
<package id="Microsoft.IdentityModel.Logging" version="7.5.1" targetFramework="net462" />
<package id="Microsoft.IdentityModel.Protocols" version="5.5.0" targetFramework="net462" />
<package id="Microsoft.IdentityModel.Protocols.OpenIdConnect" version="5.5.0" targetFramework="net462" />
<package id="Microsoft.IdentityModel.Tokens" version="7.5.1" targetFramework="net462" />
<package id="Microsoft.VisualBasic" version="10.3.0" targetFramework="net462" /> <package id="Microsoft.VisualBasic" version="10.3.0" targetFramework="net462" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net462" /> <package id="Newtonsoft.Json" version="13.0.3" targetFramework="net462" />
<package id="Newtonsoft.Json.Bson" version="1.0.2" targetFramework="net462" /> <package id="Newtonsoft.Json.Bson" version="1.0.2" targetFramework="net462" />
@@ -33,15 +62,24 @@
<package id="System.Buffers" version="4.6.0" targetFramework="net462" /> <package id="System.Buffers" version="4.6.0" targetFramework="net462" />
<package id="System.CodeDom" version="9.0.0" targetFramework="net462" /> <package id="System.CodeDom" version="9.0.0" targetFramework="net462" />
<package id="System.Collections.Immutable" version="9.0.0" targetFramework="net462" /> <package id="System.Collections.Immutable" version="9.0.0" targetFramework="net462" />
<package id="System.ComponentModel.Annotations" version="4.7.0" targetFramework="net462" />
<package id="System.Data.Common" version="4.3.0" targetFramework="net462" />
<package id="System.Data.Odbc" version="6.0.1" targetFramework="net462" /> <package id="System.Data.Odbc" version="6.0.1" targetFramework="net462" />
<package id="System.Diagnostics.DiagnosticSource" version="7.0.0" targetFramework="net462" />
<package id="System.DirectoryServices.AccountManagement" version="7.0.1" targetFramework="net462" />
<package id="System.Formats.Asn1" version="9.0.0" targetFramework="net462" /> <package id="System.Formats.Asn1" version="9.0.0" targetFramework="net462" />
<package id="System.IdentityModel.Tokens.Jwt" version="7.5.1" targetFramework="net462" />
<package id="System.IO.Packaging" version="9.0.0" targetFramework="net462" /> <package id="System.IO.Packaging" version="9.0.0" targetFramework="net462" />
<package id="System.IO.Pipelines" version="9.0.0" targetFramework="net462" /> <package id="System.IO.Pipelines" version="9.0.0" targetFramework="net462" />
<package id="System.Management" version="9.0.0" targetFramework="net462" /> <package id="System.Management" version="9.0.0" targetFramework="net462" />
<package id="System.Memory" version="4.6.0" targetFramework="net462" /> <package id="System.Memory" version="4.6.0" targetFramework="net462" />
<package id="System.Numerics.Vectors" version="4.6.0" targetFramework="net462" /> <package id="System.Numerics.Vectors" version="4.6.0" targetFramework="net462" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.0" targetFramework="net462" /> <package id="System.Runtime.CompilerServices.Unsafe" version="6.1.0" targetFramework="net462" />
<package id="System.Security.Cryptography.Algorithms" version="4.3.1" targetFramework="net462" />
<package id="System.Security.Cryptography.Cng" version="5.0.0" targetFramework="net462" />
<package id="System.Security.Cryptography.Pkcs" version="9.0.0" targetFramework="net462" /> <package id="System.Security.Cryptography.Pkcs" version="9.0.0" targetFramework="net462" />
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net462" />
<package id="System.Security.Principal.Windows" version="5.0.0" targetFramework="net462" />
<package id="System.Text.Encodings.Web" version="9.0.0" targetFramework="net462" /> <package id="System.Text.Encodings.Web" version="9.0.0" targetFramework="net462" />
<package id="System.Text.Json" version="9.0.0" targetFramework="net462" /> <package id="System.Text.Json" version="9.0.0" targetFramework="net462" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net462" /> <package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net462" />

View File

@@ -48,6 +48,9 @@
<OptionInfer>On</OptionInfer> <OptionInfer>On</OptionInfer>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="AutoMapper, Version=10.0.0.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
<HintPath>..\packages\AutoMapper.10.1.1\lib\net461\AutoMapper.dll</HintPath>
</Reference>
<Reference Include="BouncyCastle.Cryptography, Version=2.0.0.0, Culture=neutral, PublicKeyToken=072edcf4a5328938, processorArchitecture=MSIL"> <Reference Include="BouncyCastle.Cryptography, Version=2.0.0.0, Culture=neutral, PublicKeyToken=072edcf4a5328938, processorArchitecture=MSIL">
<HintPath>..\packages\BouncyCastle.Cryptography.2.5.0\lib\net461\BouncyCastle.Cryptography.dll</HintPath> <HintPath>..\packages\BouncyCastle.Cryptography.2.5.0\lib\net461\BouncyCastle.Cryptography.dll</HintPath>
</Reference> </Reference>
@@ -69,6 +72,13 @@
<Reference Include="DevExpress.XtraEditors.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" /> <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.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="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>
<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>
<Private>True</Private>
</Reference>
<Reference Include="DigitalData.Modules.Base, Version=1.3.8.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="DigitalData.Modules.Base, Version=1.3.8.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Modules.Base.1.3.8\lib\net462\DigitalData.Modules.Base.dll</HintPath> <HintPath>..\packages\DigitalData.Modules.Base.1.3.8\lib\net462\DigitalData.Modules.Base.dll</HintPath>
</Reference> </Reference>
@@ -172,9 +182,84 @@
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Bcl.HashCode, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.HashCode.1.1.1\lib\net461\Microsoft.Bcl.HashCode.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> <Reference Include="Microsoft.Data.SqlClient, Version=1.13.20136.2, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath> <HintPath>..\packages\Microsoft.Data.SqlClient.1.1.3\lib\net46\Microsoft.Data.SqlClient.dll</HintPath>
</Reference>
<Reference Include="Microsoft.EntityFrameworkCore, Version=3.1.32.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.EntityFrameworkCore.3.1.32\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll</HintPath>
</Reference>
<Reference Include="Microsoft.EntityFrameworkCore.Abstractions, Version=3.1.32.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.EntityFrameworkCore.Abstractions.3.1.32\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.EntityFrameworkCore.Relational, Version=3.1.32.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.EntityFrameworkCore.Relational.3.1.32\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Relational.dll</HintPath>
</Reference>
<Reference Include="Microsoft.EntityFrameworkCore.SqlServer, Version=3.1.32.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.EntityFrameworkCore.SqlServer.3.1.32\lib\netstandard2.0\Microsoft.EntityFrameworkCore.SqlServer.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Caching.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Caching.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.Caching.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Caching.Memory, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Caching.Memory.7.0.0\lib\net462\Microsoft.Extensions.Caching.Memory.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Configuration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Configuration.7.0.0\lib\net462\Microsoft.Extensions.Configuration.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Configuration.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Configuration.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.Configuration.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Configuration.Binder, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Configuration.Binder.7.0.0\lib\net462\Microsoft.Extensions.Configuration.Binder.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.DependencyInjection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.7.0.0\lib\net462\Microsoft.Extensions.DependencyInjection.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Localization.Abstractions, Version=7.0.16.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Localization.Abstractions.7.0.16\lib\net462\Microsoft.Extensions.Localization.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Logging, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.7.0.0\lib\net462\Microsoft.Extensions.Logging.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Options, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Options.7.0.0\lib\net462\Microsoft.Extensions.Options.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Options.ConfigurationExtensions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Options.ConfigurationExtensions.7.0.0\lib\net462\Microsoft.Extensions.Options.ConfigurationExtensions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Primitives, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Primitives.7.0.0\lib\net462\Microsoft.Extensions.Primitives.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Identity.Client, Version=3.0.8.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Identity.Client.3.0.8\lib\net45\Microsoft.Identity.Client.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Abstractions, Version=7.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Abstractions.7.5.1\lib\net462\Microsoft.IdentityModel.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.JsonWebTokens, Version=7.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.JsonWebTokens.7.5.1\lib\net462\Microsoft.IdentityModel.JsonWebTokens.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Logging, Version=7.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Logging.7.5.1\lib\net462\Microsoft.IdentityModel.Logging.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Protocols, Version=5.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Protocols.5.5.0\lib\net461\Microsoft.IdentityModel.Protocols.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect, Version=5.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.5.5.0\lib\net461\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Tokens, Version=7.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Tokens.7.5.1\lib\net462\Microsoft.IdentityModel.Tokens.dll</HintPath>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
@@ -216,13 +301,26 @@
<Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Configuration" /> <Reference Include="System.Configuration" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Data.Common, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Data.Common.4.3.0\lib\net451\System.Data.Common.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Data.Odbc, Version=6.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Data.Odbc, Version=6.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Data.Odbc.6.0.1\lib\net461\System.Data.Odbc.dll</HintPath> <HintPath>..\packages\System.Data.Odbc.6.0.1\lib\net461\System.Data.Odbc.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Diagnostics.DiagnosticSource, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.7.0.0\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.DirectoryServices.AccountManagement" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Drawing.Common, Version=4.0.0.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Drawing.Common, Version=4.0.0.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Drawing.Common.4.7.3\lib\net461\System.Drawing.Common.dll</HintPath> <HintPath>..\packages\System.Drawing.Common.4.7.3\lib\net461\System.Drawing.Common.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.IdentityModel" />
<Reference Include="System.IdentityModel.Tokens.Jwt, Version=7.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\System.IdentityModel.Tokens.Jwt.7.5.1\lib\net462\System.IdentityModel.Tokens.Jwt.dll</HintPath>
</Reference>
<Reference Include="System.IO.Compression" /> <Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Packaging, Version=8.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.IO.Packaging, Version=8.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Packaging.8.0.1\lib\net462\System.IO.Packaging.dll</HintPath> <HintPath>..\packages\System.IO.Packaging.8.0.1\lib\net462\System.IO.Packaging.dll</HintPath>
@@ -244,9 +342,25 @@
<Reference Include="System.Runtime.Remoting" /> <Reference Include="System.Runtime.Remoting" />
<Reference Include="System.Runtime.Serialization" /> <Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Security" /> <Reference Include="System.Security" />
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net461\System.Security.Cryptography.Algorithms.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Cng, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Cng.5.0.0\lib\net462\System.Security.Cryptography.Cng.dll</HintPath>
</Reference>
<Reference Include="System.Security.Cryptography.Pkcs, Version=8.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Security.Cryptography.Pkcs, Version=8.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Pkcs.8.0.1\lib\net462\System.Security.Cryptography.Pkcs.dll</HintPath> <HintPath>..\packages\System.Security.Cryptography.Pkcs.8.0.1\lib\net462\System.Security.Cryptography.Pkcs.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Principal.Windows, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll</HintPath>
</Reference>
<Reference Include="System.ServiceModel" /> <Reference Include="System.ServiceModel" />
<Reference Include="System.Text.Encodings.Web, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Text.Encodings.Web, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.Encodings.Web.8.0.0\lib\net462\System.Text.Encodings.Web.dll</HintPath> <HintPath>..\packages\System.Text.Encodings.Web.8.0.0\lib\net462\System.Text.Encodings.Web.dll</HintPath>
@@ -446,6 +560,14 @@
<Project>{4f32a98d-e6f0-4a09-bd97-1cf26107e837}</Project> <Project>{4f32a98d-e6f0-4a09-bd97-1cf26107e837}</Project>
<Name>EnvelopeGenerator.Domain</Name> <Name>EnvelopeGenerator.Domain</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj">
<Project>{63e32615-0eca-42dc-96e3-91037324b7c7}</Project>
<Name>EnvelopeGenerator.Infrastructure</Name>
</ProjectReference>
<ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj">
<Project>{211619f5-ae25-4ba5-a552-bacafe0632d3}</Project>
<Name>EnvelopeGenerator.PdfEditor</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
@@ -455,7 +577,9 @@
<Error Condition="!Exists('..\packages\EntityFramework.6.4.4\build\EntityFramework.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\EntityFramework.6.4.4\build\EntityFramework.props'))" /> <Error Condition="!Exists('..\packages\EntityFramework.6.4.4\build\EntityFramework.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\EntityFramework.6.4.4\build\EntityFramework.props'))" />
<Error Condition="!Exists('..\packages\EntityFramework.6.4.4\build\EntityFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\EntityFramework.6.4.4\build\EntityFramework.targets'))" /> <Error Condition="!Exists('..\packages\EntityFramework.6.4.4\build\EntityFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\EntityFramework.6.4.4\build\EntityFramework.targets'))" />
<Error Condition="!Exists('..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets'))" /> <Error Condition="!Exists('..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Data.SqlClient.SNI.1.1.0\build\net46\Microsoft.Data.SqlClient.SNI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Data.SqlClient.SNI.1.1.0\build\net46\Microsoft.Data.SqlClient.SNI.targets'))" />
</Target> </Target>
<Import Project="..\packages\EntityFramework.6.4.4\build\EntityFramework.targets" Condition="Exists('..\packages\EntityFramework.6.4.4\build\EntityFramework.targets')" /> <Import Project="..\packages\EntityFramework.6.4.4\build\EntityFramework.targets" Condition="Exists('..\packages\EntityFramework.6.4.4\build\EntityFramework.targets')" />
<Import Project="..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets" Condition="Exists('..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets')" /> <Import Project="..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets" Condition="Exists('..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets')" />
<Import Project="..\packages\Microsoft.Data.SqlClient.SNI.1.1.0\build\net46\Microsoft.Data.SqlClient.SNI.targets" Condition="Exists('..\packages\Microsoft.Data.SqlClient.SNI.1.1.0\build\net46\Microsoft.Data.SqlClient.SNI.targets')" />
</Project> </Project>

View File

@@ -11,6 +11,10 @@ Imports EnvelopeGenerator.Domain.Constants
Imports DevExpress.DataProcessing Imports DevExpress.DataProcessing
Imports System.Data.SqlClient Imports System.Data.SqlClient
Imports EnvelopeGenerator.Domain.Entities Imports EnvelopeGenerator.Domain.Entities
Imports DigitalData.Core.Abstraction.Application
Imports EnvelopeGenerator.Infrastructure
Imports Microsoft.EntityFrameworkCore
Imports DigitalData.Core.Abstractions
Namespace Jobs Namespace Jobs
Public Class FinalizeDocumentJob Public Class FinalizeDocumentJob
@@ -62,7 +66,31 @@ Namespace Jobs
LicenseManager.RegisterKEY(oGdPictureKey) LicenseManager.RegisterKEY(oGdPictureKey)
Logger.Debug("Loading Database..") Logger.Debug("Loading Database..")
Database = GetDatabase(pContext, LogConfig) Dim oConnectionString As String = pContext.MergedJobDataMap.Item(Value.DATABASE)
Database = New MSSQLServer(LogConfig, MSSQLServer.DecryptConnectionString(oConnectionString))
#Disable Warning BC40000 ' Type or member is obsolete
Factory.Shared _
.BehaveOnPostBuild(PostBuildBehavior.Ignore) _
.AddEnvelopeGeneratorInfrastructureServices(
Sub(opt)
opt.AddDbTriggerParams(
Sub(triggers)
triggers("Envelope") = New List(Of String) From {"TBSIG_ENVELOPE_AFT_INS"}
triggers("History") = New List(Of String) From {"TBSIG_ENVELOPE_HISTORY_AFT_INS"}
triggers("EmailOut") = New List(Of String) From {"TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD"}
triggers("EnvelopeReceiverReadOnly") = New List(Of String) From {"TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD"}
triggers("Receiver") = New List(Of String)() ' no tigger
triggers("EmailTemplate") = New List(Of String) From {"TBSIG_EMAIL_TEMPLATE_AFT_UPD"}
End Sub)
opt.AddDbContext(
Sub(options)
options.UseSqlServer(oConnectionString) _
.EnableSensitiveDataLogging() _
.EnableDetailedErrors()
End Sub)
End Sub)
#Enable Warning BC40000 ' Type or member is obsolete
Logger.Debug("Loading Models & Services") Logger.Debug("Loading Models & Services")
Dim oState = GetState() Dim oState = GetState()
@@ -378,7 +406,6 @@ Namespace Jobs
ParentFolderUID = pEnvelopeData.EnvelopeUUID ParentFolderUID = pEnvelopeData.EnvelopeUUID
End If End If
Logger.Info("ParentFolderUID: [{0}]", ParentFolderUID) Logger.Info("ParentFolderUID: [{0}]", ParentFolderUID)
Dim oInputDocumentBuffer As Byte() Dim oInputDocumentBuffer As Byte()
If Not IsNothing(pEnvelopeData.DocAsByte) Then If Not IsNothing(pEnvelopeData.DocAsByte) Then
@@ -391,7 +418,7 @@ Namespace Jobs
End Try End Try
End If End If
Return PDFBurner.BurnInstantJSONAnnotationsToPDF(oInputDocumentBuffer, oAnnotations) Return PDFBurner.BurnAnnotsToPDF(oInputDocumentBuffer, oAnnotations, pEnvelopeData.EnvelopeId)
End Function End Function
Private Function GetEnvelopeData(pEnvelopeId As Integer) As EnvelopeData Private Function GetEnvelopeData(pEnvelopeId As Integer) As EnvelopeData
@@ -439,13 +466,6 @@ Namespace Jobs
ReportModel = New ReportModel(pState) ReportModel = New ReportModel(pState)
End Sub End Sub
Private Function GetDatabase(pContext As IJobExecutionContext, pLogConfig As LogConfig) As MSSQLServer
Dim oConnectionString As String = pContext.MergedJobDataMap.Item(Value.DATABASE)
Dim Database = New MSSQLServer(pLogConfig, MSSQLServer.DecryptConnectionString(oConnectionString))
Return Database
End Function
Private Function GetState() As State Private Function GetState() As State
Return New State With { Return New State With {
.LogConfig = LogConfig, .LogConfig = LogConfig,

View File

@@ -1,10 +1,17 @@
Imports System.Drawing Imports System.Collections.Immutable
Imports System.Drawing
Imports System.IO Imports System.IO
Imports DevExpress.DataProcessing
Imports DigitalData.Core.Abstraction.Application.Repository
Imports DigitalData.Core.Abstractions
Imports DigitalData.Modules.Base Imports DigitalData.Modules.Base
Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Logging
Imports GdPicture14
Imports Newtonsoft.Json
Imports EnvelopeGenerator.CommonServices.Jobs.FinalizeDocument.FinalizeDocumentExceptions Imports EnvelopeGenerator.CommonServices.Jobs.FinalizeDocument.FinalizeDocumentExceptions
Imports EnvelopeGenerator.Domain.Entities
Imports EnvelopeGenerator.PdfEditor
Imports GdPicture14
Imports Microsoft.EntityFrameworkCore
Imports Newtonsoft.Json
Namespace Jobs.FinalizeDocument Namespace Jobs.FinalizeDocument
Public Class PDFBurner Public Class PDFBurner
@@ -13,9 +20,6 @@ Namespace Jobs.FinalizeDocument
Private ReadOnly Manager As AnnotationManager Private ReadOnly Manager As AnnotationManager
Private ReadOnly LicenseManager As LicenseManager Private ReadOnly LicenseManager As LicenseManager
Private Const ANNOTATION_TYPE_IMAGE = "pspdfkit/image"
Private Const ANNOTATION_TYPE_INK = "pspdfkit/ink"
Private Const ANNOTATION_TYPE_WIDGET = "pspdfkit/widget"
Private Property _pdfBurnerParams As PDFBurnerParams Private Property _pdfBurnerParams As PDFBurnerParams
Public Sub New(pLogConfig As LogConfig, pGDPictureLicenseKey As String, pdfBurnerParams As PDFBurnerParams) Public Sub New(pLogConfig As LogConfig, pGDPictureLicenseKey As String, pdfBurnerParams As PDFBurnerParams)
@@ -29,7 +33,94 @@ Namespace Jobs.FinalizeDocument
_pdfBurnerParams = pdfBurnerParams _pdfBurnerParams = pdfBurnerParams
End Sub End Sub
Public Function BurnInstantJSONAnnotationsToPDF(pSourceBuffer As Byte(), pInstantJSONList As List(Of String)) As Byte() #Region "Burn PDF"
Public Function BurnAnnotsToPDF(pSourceBuffer As Byte(), pInstantJSONList As List(Of String), envelopeId As Integer) As Byte()
'read the elements of envelope with their annotations
Using scope = Factory.Shared.ScopeFactory.CreateScope()
Dim sigRepo = scope.ServiceProvider.Repository(Of Signature)()
Dim elements = sigRepo _
.Where(Function(sig) sig.Document.EnvelopeId = envelopeId) _
.Include(Function(sig) sig.Annotations) _
.ToList()
Return If(elements.Any(),
BurnElementAnnotsToPDF(pSourceBuffer, elements),
BurnInstantJSONAnnotsToPDF(pSourceBuffer, pInstantJSONList))
End Using
End Function
Public Function BurnElementAnnotsToPDF(pSourceBuffer As Byte(), elements As List(Of Signature)) As Byte()
' Add background
Using doc As Pdf(Of MemoryStream, MemoryStream) = 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()
Dim oResult As GdPictureStatus
Using oSourceStream As New MemoryStream(pSourceBuffer)
' Open PDF
oResult = Manager.InitFromStream(oSourceStream)
If oResult <> GdPictureStatus.OK Then
Throw New BurnAnnotationException($"Could not open document for burning: [{oResult}]")
End If
' Imported from background (add to configuration)
Dim margin As Double = 0.2
Dim inchFactor As Double = 72
' Y offset of form fields
Dim keys = {"position", "city", "date"} ' add to configuration
Dim unitYOffsets = 0.2
Dim yOffsetsOfFF = keys.
Select(Function(k, i) New With {Key .Key = k, Key .Value = unitYOffsets * i + 1}).
ToDictionary(Function(x) x.Key, Function(x) x.Value)
'Add annotations
For Each element In elements
Dim frameX = (element.Left - 0.7 - margin)
Dim frame = element.Annotations.FirstOrDefault(Function(a) a.Name = "frame")
Dim frameY = element.Top - 0.5 - margin
Dim frameYShift = frame.Y - frameY * inchFactor
Dim frameXShift = frame.X - frameX * inchFactor
For Each annot In element.Annotations
Dim yOffsetofFF As Double = If(yOffsetsOfFF.TryGetValue(annot.Name, yOffsetofFF), yOffsetofFF, 0)
Dim y = frameY + yOffsetofFF
If annot.Type = AnnotationType.FormField Then
AddFormFieldValue(annot.X / inchFactor, y, annot.Width / inchFactor, annot.Height / inchFactor, element.Page, annot.Value)
ElseIf annot.Type = AnnotationType.Image Then
AddImageAnnotation(
annot.X / inchFactor,
If(annot.Name = "signature", (annot.Y - frameYShift) / inchFactor, y),
annot.Width / inchFactor,
annot.Height / inchFactor,
element.Page,
annot.Value
)
ElseIf annot.Type = AnnotationType.Ink Then
AddInkAnnotation(element.Page, annot.Value)
End If
Next
Next
'Save PDF
Using oNewStream As New MemoryStream()
oResult = Manager.SaveDocumentToPDF(oNewStream)
If oResult <> GdPictureStatus.OK Then
Throw New BurnAnnotationException($"Could not save document to stream: [{oResult}]")
End If
Manager.Close()
Return oNewStream.ToArray()
End Using
End Using
End Using
End Function
Public Function BurnInstantJSONAnnotsToPDF(pSourceBuffer As Byte(), pInstantJSONList As List(Of String)) As Byte()
Dim oResult As GdPictureStatus Dim oResult As GdPictureStatus
Using oSourceStream As New MemoryStream(pSourceBuffer) Using oSourceStream As New MemoryStream(pSourceBuffer)
' Open PDF ' Open PDF
@@ -40,11 +131,13 @@ Namespace Jobs.FinalizeDocument
' Add annotation to PDF ' Add annotation to PDF
For Each oJSON In pInstantJSONList For Each oJSON In pInstantJSONList
If AddInstantJSONAnnotationToPDF(oJSON) = False Then Try
AddInstantJSONAnnotationToPDF(oJSON)
Catch ex As Exception
Logger.Warn($"Error in AddInstantJSONAnnotationToPDF - oJson: ") Logger.Warn($"Error in AddInstantJSONAnnotationToPDF - oJson: ")
Logger.Warn(oJSON) Logger.Warn(oJSON)
Throw New BurnAnnotationException($"Adding Annotation failed") Throw New BurnAnnotationException($"Adding Annotation failed", ex)
End If End Try
Next Next
oResult = Manager.BurnAnnotationsToPage(RemoveInitialAnnots:=True, VectorMode:=True) oResult = Manager.BurnAnnotationsToPage(RemoveInitialAnnots:=True, VectorMode:=True)
If oResult <> GdPictureStatus.OK Then If oResult <> GdPictureStatus.OK Then
@@ -64,128 +157,125 @@ Namespace Jobs.FinalizeDocument
End Using End Using
End Using End Using
End Function End Function
#End Region
Private Function AddInstantJSONAnnotationToPDF(pInstantJSON As String) As Boolean #Region "Add Value"
Try Private Sub AddInstantJSONAnnotationToPDF(pInstantJSON As String)
Dim oAnnotationData = JsonConvert.DeserializeObject(Of AnnotationData)(pInstantJSON) Dim oAnnotationData = JsonConvert.DeserializeObject(Of AnnotationData)(pInstantJSON)
oAnnotationData.annotations.Reverse()
Dim sigAnnotType = oAnnotationData.annotations.ElementAt(1).type oAnnotationData.annotations.Reverse()
Dim yPosOfSigAnnot = oAnnotationData.annotations.ElementAt(2).bbox.ElementAt(1) - 71.84002685546875 + 7
Dim isSeal = True 'First element is signature seal
Dim formFieldIndex = 0 For Each oAnnotation In oAnnotationData.annotations
For Each oAnnotation In oAnnotationData.annotations Logger.Debug("Adding AnnotationID: " + oAnnotation.id)
Logger.Debug("Adding AnnotationID: " + oAnnotation.id)
Select Case oAnnotation.type Select Case oAnnotation.type
Case ANNOTATION_TYPE_IMAGE Case AnnotationType.Image
AddImageAnnotation(oAnnotation, oAnnotationData.attachments)
Exit Select
Case AnnotationType.Ink
AddInkAnnotation(oAnnotation)
Exit Select
Case AnnotationType.Widget
'Add form field values
Dim formFieldValue = oAnnotationData.formFieldValues.FirstOrDefault(Function(fv) fv.name = oAnnotation.id)
If formFieldValue IsNot Nothing AndAlso Not _pdfBurnerParams.IgnoredLabels.Contains(formFieldValue.value) Then
AddFormFieldValue(oAnnotation, formFieldValue)
End If
Exit Select
End Select
Next
If (isSeal) Then End Sub
oAnnotation.bbox.Item(1) = yPosOfSigAnnot
End If
AddImageAnnotation(oAnnotation, oAnnotationData.attachments) Private Sub AddImageAnnotation(x As Double, y As Double, width As Double, height As Double, page As Integer, base64 As String)
Manager.SelectPage(page)
Manager.AddEmbeddedImageAnnotFromBase64(base64, x, y, width, height)
End Sub
Case ANNOTATION_TYPE_INK Private Sub AddImageAnnotation(pAnnotation As Annotation, pAttachments As Dictionary(Of String, Attachment))
AddInkAnnotation(oAnnotation) Dim oAttachment = pAttachments.Where(Function(a) a.Key = pAnnotation.imageAttachmentId).
SingleOrDefault()
Case ANNOTATION_TYPE_WIDGET ' Convert pixels to Inches
'Add form field values Dim oBounds = pAnnotation.bbox.Select(AddressOf ToInches).ToList()
Dim formFieldValue = oAnnotationData.formFieldValues.FirstOrDefault(Function(fv) fv.name = oAnnotation.id)
If formFieldValue IsNot Nothing AndAlso Not _pdfBurnerParams.IgnoredLabels.Contains(formFieldValue.value) Then
AddFormFieldValue(oAnnotation, formFieldValue, formFieldIndex)
formFieldIndex += 1
End If
End Select
isSeal = False Dim oX = oBounds.Item(0)
Next Dim oY = oBounds.Item(1)
Dim oWidth = oBounds.Item(2)
Dim oHeight = oBounds.Item(3)
Return True Manager.SelectPage(pAnnotation.pageIndex + 1)
Catch ex As Exception Manager.AddEmbeddedImageAnnotFromBase64(oAttachment.Value.binary, oX, oY, oWidth, oHeight)
Logger.Warn("Could not create annotation from InstantJSON") End Sub
Logger.Error(ex)
Return False
End Try
End Function
Private Function AddImageAnnotation(pAnnotation As Annotation, pAttachments As Dictionary(Of String, Attachment), Optional yOffset As Double = 0) As Boolean Private Sub AddInkAnnotation(page As Integer, value As String)
Try
Dim oAttachment = pAttachments.Where(Function(a) a.Key = pAnnotation.imageAttachmentId).
SingleOrDefault()
' Convert pixels to Inches Dim ink = JsonConvert.DeserializeObject(Of Ink)(value)
Dim oBounds = pAnnotation.bbox.Select(AddressOf ToInches).ToList()
Dim oX = oBounds.Item(0) Dim oSegments = ink.lines.points
Dim oY = oBounds.Item(1) + yOffset Dim oColor = ColorTranslator.FromHtml(ink.strokeColor)
Dim oWidth = oBounds.Item(2) Manager.SelectPage(page)
Dim oHeight = oBounds.Item(3)
Manager.SelectPage(pAnnotation.pageIndex + 1) For Each oSegment As List(Of List(Of Single)) In oSegments
Manager.AddEmbeddedImageAnnotFromBase64(oAttachment.Value.binary, oX, oY, oWidth, oHeight) Dim oPoints = oSegment.
Select(AddressOf ToPointF).
ToArray()
Return True Manager.AddFreeHandAnnot(oColor, oPoints)
Catch ex As Exception Next
Logger.Warn("Could not add image annotation!") End Sub
Logger.Error(ex)
Return False Private Sub AddInkAnnotation(pAnnotation As Annotation)
End Try Dim oSegments = pAnnotation.lines.points
End Function Dim oColor = ColorTranslator.FromHtml(pAnnotation.strokeColor)
Manager.SelectPage(pAnnotation.pageIndex + 1)
Private Function AddInkAnnotation(pAnnotation As Annotation) As Boolean For Each oSegment As List(Of List(Of Single)) In oSegments
Try Dim oPoints = oSegment.
Dim oSegments = pAnnotation.lines.points Select(AddressOf ToPointF).
Dim oColor = ColorTranslator.FromHtml(pAnnotation.strokeColor) ToArray()
Manager.SelectPage(pAnnotation.pageIndex + 1)
For Each oSegment As List(Of List(Of Single)) In oSegments Manager.AddFreeHandAnnot(oColor, oPoints)
Dim oPoints = oSegment. Next
Select(AddressOf ToPointF). End Sub
ToArray()
Manager.AddFreeHandAnnot(oColor, oPoints) Private Sub AddFormFieldValue(x As Double, y As Double, width As Double, height As Double, page As Integer, value As String)
Next Manager.SelectPage(page)
Return True ' Add the text annotation
Catch ex As Exception Dim ant = Manager.AddTextAnnot(x, y, width, height, value)
Logger.Warn("Could not add image annotation!")
Logger.Error(ex)
Return False ' Set the font properties
End Try ant.FontName = _pdfBurnerParams.FontName
ant.FontSize = _pdfBurnerParams.FontSize
ant.FontStyle = _pdfBurnerParams.FontStyle
Manager.SaveAnnotationsToPage()
End Sub
End Function Private Sub AddFormFieldValue(pAnnotation As Annotation, formFieldValue As FormFieldValue)
Dim ffIndex As Integer = EGName.Index(pAnnotation.egName)
Private Function AddFormFieldValue(pAnnotation As Annotation, formFieldValue As FormFieldValue, index As Integer) As Boolean ' Convert pixels to Inches
Try Dim oBounds = pAnnotation.bbox.Select(AddressOf ToInches).ToList()
' Convert pixels to Inches
Dim oBounds = pAnnotation.bbox.Select(AddressOf ToInches).ToList()
Dim oX = oBounds.Item(0) Dim oX = oBounds.Item(0)
Dim oY = oBounds.Item(1) + _pdfBurnerParams.YOffset * index + _pdfBurnerParams.TopMargin Dim oY = oBounds.Item(1) + _pdfBurnerParams.YOffset * ffIndex + _pdfBurnerParams.TopMargin
Dim oWidth = oBounds.Item(2) Dim oWidth = oBounds.Item(2)
Dim oHeight = oBounds.Item(3) Dim oHeight = oBounds.Item(3)
Manager.SelectPage(pAnnotation.pageIndex + 1) Manager.SelectPage(pAnnotation.pageIndex + 1)
' Add the text annotation ' Add the text annotation
Dim ant = Manager.AddTextAnnot(oX, oY, oWidth, oHeight, formFieldValue.value) Dim ant = Manager.AddTextAnnot(oX, oY, oWidth, oHeight, formFieldValue.value)
' Set the font properties ' Set the font properties
ant.FontName = _pdfBurnerParams.FontName ant.FontName = _pdfBurnerParams.FontName
ant.FontSize = _pdfBurnerParams.FontSize ant.FontSize = _pdfBurnerParams.FontSize
ant.FontStyle = _pdfBurnerParams.FontStyle ant.FontStyle = _pdfBurnerParams.FontStyle
Manager.SaveAnnotationsToPage() Manager.SaveAnnotationsToPage()
Return True End Sub
Catch ex As Exception #End Region
Logger.Warn("Could not add image annotation!")
Logger.Error(ex)
Return False
End Try
End Function
#Region "Helpers"
Private Function ToPointF(pPoints As List(Of Single)) As PointF Private Function ToPointF(pPoints As List(Of Single)) As PointF
Dim oPoints = pPoints.Select(AddressOf ToInches).ToList() Dim oPoints = pPoints.Select(AddressOf ToInches).ToList()
Return New PointF(oPoints.Item(0), oPoints.Item(1)) Return New PointF(oPoints.Item(0), oPoints.Item(1))
@@ -198,24 +288,138 @@ Namespace Jobs.FinalizeDocument
Private Function ToInches(pValue As Single) As Single Private Function ToInches(pValue As Single) As Single
Return pValue / 72 Return pValue / 72
End Function End Function
#End Region
#Region "Model"
Friend Class AnnotationType
Public Const Image As String = "pspdfkit/image"
Public Const Ink As String = "pspdfkit/ink"
Public Const Widget As String = "pspdfkit/widget"
Public Const FormField As String = "pspdfkit/form-field-value"
End Class
Friend Class AnnotationData Friend Class AnnotationData
Public Property annotations As List(Of Annotation) Public Property annotations As List(Of Annotation)
Public ReadOnly Property AnnotationsByReceiver As IEnumerable(Of List(Of Annotation))
Get
Return annotations _
.Where(Function(annot) annot.hasStructuredID).ToList() _
.GroupBy(Function(a) a.receiverId) _
.Select(Function(g) g.ToList())
End Get
End Property
Public ReadOnly Property UnstructuredAnnotations As IEnumerable(Of List(Of Annotation))
Get
Return annotations _
.Where(Function(annot) Not annot.hasStructuredID).ToList() _
.GroupBy(Function(a) a.receiverId) _
.Select(Function(g) g.ToList())
End Get
End Property
Public Property attachments As Dictionary(Of String, Attachment) Public Property attachments As Dictionary(Of String, Attachment)
Public Property formFieldValues As List(Of FormFieldValue) Public Property formFieldValues As List(Of FormFieldValue)
End Class End Class
Friend Class Annotation Friend Class Annotation
Private _id As String = Nothing
Public envelopeId As Integer = Nothing
Public receiverId As Integer = Nothing
Public index As Integer = Nothing
Public egName As String = PDFBurner.EGName.NoName
Public hasStructuredID As Boolean = False
Public ReadOnly Property isLabel As Boolean
Get
If String.IsNullOrEmpty(egName) Then
Return False
End If
Dim parts As String() = egName.Split("_"c)
Return parts.Length > 1 AndAlso parts(1) = "label"
End Get
End Property
Public Property id As String Public Property id As String
Get
Return _id
End Get
Set(value As String)
_id = value
If String.IsNullOrWhiteSpace(_id) Then
Throw New BurnAnnotationException("The identifier of annotation is null or empty.")
End If
Dim parts As String() = value.Split("#"c)
If (parts.Length <> 4) Then
Return
'Throw New BurnAnnotationException($"The identifier of annotation has more or less than 4 sub-part. Id: {_id}")
End If
If Not Integer.TryParse(parts(0), envelopeId) Then
Throw New BurnAnnotationException($"The envelope ID of annotation is not integer. Id: {_id}")
End If
If Not Integer.TryParse(parts(1), receiverId) Then
Throw New BurnAnnotationException($"The receiver ID of annotation is not integer. Id: {_id}")
End If
If Not Integer.TryParse(parts(2), index) Then
Throw New BurnAnnotationException($"The index of annotation is not integer. Id: {_id}")
End If
egName = parts(3)
hasStructuredID = True
End Set
End Property
Public Property bbox As List(Of Double) Public Property bbox As List(Of Double)
Public Property type As String Public Property type As String
Public Property isSignature As Boolean Public Property isSignature As Boolean
Public Property imageAttachmentId As String Public Property imageAttachmentId As String
Public Property lines As Lines Public Property lines As Lines
Public Property pageIndex As Integer Public Property pageIndex As Integer
Public Property strokeColor As String Public Property strokeColor As String
End Class End Class
Friend Class Ink
Public Property lines As Lines
Public Property strokeColor As String
End Class
Public Class EGName
Public Shared ReadOnly NoName As String = Guid.NewGuid().ToString()
Public Shared ReadOnly Seal As String = "signature"
Public Shared ReadOnly Index As ImmutableDictionary(Of String, Integer) =
New Dictionary(Of String, Integer) From {
{NoName, 0},
{Seal, 0},
{"position", 1},
{"city", 2},
{"date", 3}
}.ToImmutableDictionary()
End Class
Friend Class Lines Friend Class Lines
Public Property points As List(Of List(Of List(Of Single))) Public Property points As List(Of List(Of List(Of Single)))
End Class End Class
@@ -229,5 +433,6 @@ Namespace Jobs.FinalizeDocument
Public Property name As String Public Property name As String
Public Property value As String Public Property value As String
End Class End Class
#End Region
End Class End Class
End Namespace End Namespace

View File

@@ -77,6 +77,70 @@
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.5" newVersion="8.0.0.5" /> <bindingRedirect oldVersion="0.0.0.0-8.0.0.5" newVersion="8.0.0.5" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Caching.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Caching.Memory" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Options" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Configuration.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Logging" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Tokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IdentityModel.Tokens.Jwt" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.JsonWebTokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
</configuration> </configuration>

View File

@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="AutoMapper" version="10.1.1" targetFramework="net462" />
<package id="BouncyCastle.Cryptography" version="2.5.0" 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.Abstractions" version="4.3.0" targetFramework="net462" />
<package id="DigitalData.Modules.Base" version="1.3.8" targetFramework="net462" /> <package id="DigitalData.Modules.Base" version="1.3.8" targetFramework="net462" />
<package id="DigitalData.Modules.Config" version="1.3.0" targetFramework="net462" /> <package id="DigitalData.Modules.Config" version="1.3.0" targetFramework="net462" />
<package id="DigitalData.Modules.Database" version="2.3.5.4" targetFramework="net462" /> <package id="DigitalData.Modules.Database" version="2.3.5.4" targetFramework="net462" />
@@ -15,8 +18,35 @@
<package id="GdPicture.runtimes.windows" version="14.3.3" targetFramework="net462" /> <package id="GdPicture.runtimes.windows" version="14.3.3" targetFramework="net462" />
<package id="Microsoft.AspNet.WebApi.Client" version="6.0.0" targetFramework="net462" /> <package id="Microsoft.AspNet.WebApi.Client" version="6.0.0" targetFramework="net462" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="8.0.0" targetFramework="net462" /> <package id="Microsoft.Bcl.AsyncInterfaces" version="8.0.0" targetFramework="net462" />
<package id="Microsoft.Bcl.HashCode" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.CSharp" version="4.7.0" targetFramework="net462" /> <package id="Microsoft.CSharp" version="4.7.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="2.1.1" targetFramework="net462" /> <package id="Microsoft.Data.SqlClient" version="1.1.3" targetFramework="net462" />
<package id="Microsoft.Data.SqlClient.SNI" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.EntityFrameworkCore" version="3.1.32" targetFramework="net462" />
<package id="Microsoft.EntityFrameworkCore.Abstractions" version="3.1.32" targetFramework="net462" />
<package id="Microsoft.EntityFrameworkCore.Analyzers" version="3.1.32" targetFramework="net462" />
<package id="Microsoft.EntityFrameworkCore.Relational" version="3.1.32" targetFramework="net462" />
<package id="Microsoft.EntityFrameworkCore.SqlServer" version="3.1.32" targetFramework="net462" />
<package id="Microsoft.Extensions.Caching.Abstractions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Caching.Memory" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Configuration" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Configuration.Abstractions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Configuration.Binder" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.DependencyInjection" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Localization.Abstractions" version="7.0.16" targetFramework="net462" />
<package id="Microsoft.Extensions.Logging" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Options" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Options.ConfigurationExtensions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Primitives" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Identity.Client" version="3.0.8" targetFramework="net462" />
<package id="Microsoft.IdentityModel.Abstractions" version="7.5.1" targetFramework="net462" />
<package id="Microsoft.IdentityModel.JsonWebTokens" version="7.5.1" targetFramework="net462" />
<package id="Microsoft.IdentityModel.Logging" version="7.5.1" targetFramework="net462" />
<package id="Microsoft.IdentityModel.Protocols" version="5.5.0" targetFramework="net462" />
<package id="Microsoft.IdentityModel.Protocols.OpenIdConnect" version="5.5.0" targetFramework="net462" />
<package id="Microsoft.IdentityModel.Tokens" version="7.5.1" targetFramework="net462" />
<package id="Microsoft.VisualBasic" version="10.3.0" targetFramework="net462" /> <package id="Microsoft.VisualBasic" version="10.3.0" targetFramework="net462" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net462" /> <package id="Newtonsoft.Json" version="13.0.3" targetFramework="net462" />
<package id="Newtonsoft.Json.Bson" version="1.0.2" targetFramework="net462" /> <package id="Newtonsoft.Json.Bson" version="1.0.2" targetFramework="net462" />
@@ -31,14 +61,22 @@
<package id="System.CodeDom" version="8.0.0" targetFramework="net462" /> <package id="System.CodeDom" version="8.0.0" targetFramework="net462" />
<package id="System.Collections.Immutable" version="8.0.0" targetFramework="net462" /> <package id="System.Collections.Immutable" version="8.0.0" targetFramework="net462" />
<package id="System.ComponentModel.Annotations" version="4.7.0" targetFramework="net462" /> <package id="System.ComponentModel.Annotations" version="4.7.0" targetFramework="net462" />
<package id="System.Data.Common" version="4.3.0" targetFramework="net462" />
<package id="System.Data.Odbc" version="6.0.1" targetFramework="net462" /> <package id="System.Data.Odbc" version="6.0.1" targetFramework="net462" />
<package id="System.Diagnostics.DiagnosticSource" version="7.0.0" targetFramework="net462" />
<package id="System.DirectoryServices.AccountManagement" version="7.0.1" targetFramework="net462" />
<package id="System.Drawing.Common" version="4.7.3" targetFramework="net462" /> <package id="System.Drawing.Common" version="4.7.3" targetFramework="net462" />
<package id="System.IdentityModel.Tokens.Jwt" version="7.5.1" targetFramework="net462" />
<package id="System.IO.Packaging" version="8.0.1" targetFramework="net462" /> <package id="System.IO.Packaging" version="8.0.1" targetFramework="net462" />
<package id="System.Management" version="8.0.0" targetFramework="net462" /> <package id="System.Management" version="8.0.0" targetFramework="net462" />
<package id="System.Memory" version="4.6.0" targetFramework="net462" /> <package id="System.Memory" version="4.6.0" targetFramework="net462" />
<package id="System.Numerics.Vectors" version="4.6.0" targetFramework="net462" /> <package id="System.Numerics.Vectors" version="4.6.0" targetFramework="net462" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.0" targetFramework="net462" /> <package id="System.Runtime.CompilerServices.Unsafe" version="6.1.0" targetFramework="net462" />
<package id="System.Security.Cryptography.Algorithms" version="4.3.1" targetFramework="net462" />
<package id="System.Security.Cryptography.Cng" version="5.0.0" targetFramework="net462" />
<package id="System.Security.Cryptography.Pkcs" version="8.0.1" targetFramework="net462" /> <package id="System.Security.Cryptography.Pkcs" version="8.0.1" targetFramework="net462" />
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net462" />
<package id="System.Security.Principal.Windows" version="5.0.0" targetFramework="net462" />
<package id="System.Text.Encodings.Web" version="8.0.0" targetFramework="net462" /> <package id="System.Text.Encodings.Web" version="8.0.0" targetFramework="net462" />
<package id="System.Text.Json" version="8.0.5" targetFramework="net462" /> <package id="System.Text.Json" version="8.0.5" targetFramework="net462" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net462" /> <package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net462" />

View File

@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using EnvelopeGenerator.Domain.Interfaces;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
#if NETFRAMEWORK #if NETFRAMEWORK
using System.Drawing; using System.Drawing;
@@ -14,7 +15,7 @@ namespace EnvelopeGenerator.Domain.Entities
#endif #endif
[Table("TBSIG_ENVELOPE_DOCUMENT", Schema = "dbo")] [Table("TBSIG_ENVELOPE_DOCUMENT", Schema = "dbo")]
public class Document public class Document : IHasEnvelope
{ {
public Document() public Document()
{ {
@@ -30,27 +31,50 @@ public class Document
[Required] [Required]
[Column("ENVELOPE_ID")] [Column("ENVELOPE_ID")]
public int EnvelopeId { get; set; } = 0; public int EnvelopeId { get; set; }
#if NETFRAMEWORK
= 0;
#endif
[Column("BYTE_DATA", TypeName = "varbinary(max)")] [Column("BYTE_DATA", TypeName = "varbinary(max)")]
public byte[] ByteData { get; set; } public byte[]
#if NET
?
#endif
ByteData { get; set; }
public List<Signature> Elements { get; set; } #region File
[Column("FILENAME", TypeName = "nvarchar(256)")]
public string Filename { get; set; }
// TODO: * Check the Form App and remove the default value [Column("FILEPATH", TypeName = "nvarchar(256)")]
[NotMapped]
public string Filepath { get; set; } public string Filepath { get; set; }
[Column("FILENAME_ORIGINAL", TypeName = "nvarchar(256)")]
public string
#if NET
?
#endif
FileNameOriginal { get; set; }
#endregion
public virtual List<Signature>
#if NET
?
#endif
Elements { get; set; }
[ForeignKey("EnvelopeId")]
public virtual Envelope
#if NET
?
#endif
Envelope { get; set; }
#if NETFRAMEWORK #if NETFRAMEWORK
[NotMapped]
public string FileNameOriginal { get; set; }
[NotMapped] [NotMapped]
public bool IsTempFile { get; set; } public bool IsTempFile { get; set; }
[NotMapped]
public string Filename { get; set; }
[NotMapped] [NotMapped]
public Bitmap Thumbnail { get; set; } public Bitmap Thumbnail { get; set; }

View File

@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using DigitalData.Core.Abstractions.Interfaces;
using EnvelopeGenerator.Domain.Interfaces; using EnvelopeGenerator.Domain.Interfaces;
#if NETFRAMEWORK #if NETFRAMEWORK
@@ -14,7 +15,7 @@ namespace EnvelopeGenerator.Domain.Entities
#endif #endif
[Table("TBSIG_DOCUMENT_STATUS", Schema = "dbo")] [Table("TBSIG_DOCUMENT_STATUS", Schema = "dbo")]
public class DocumentStatus : IHasEnvelope, IHasReceiver public class DocumentStatus : IHasEnvelope, IHasReceiver, IEntity
{ {
public DocumentStatus() public DocumentStatus()
{ {

View File

@@ -0,0 +1,92 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
#if NETFRAMEWORK
using System;
#endif
namespace EnvelopeGenerator.Domain.Entities
#if NET
;
#elif NETFRAMEWORK
{
#endif
[Table("TBSIG_DOCUMENT_RECEIVER_ELEMENT_ANNOTATION")]
public class ElementAnnotation
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("GUID", TypeName = "bigint")]
public long Id { get; set; }
[Required]
[Column("ELEMENT_ID", TypeName = "int")]
public int ElementId { get; set; }
[Required]
[Column("NAME", TypeName = "nvarchar(100)")]
[StringLength(100)]
public string Name { get; set; }
[Required]
[Column("VALUE", TypeName = "nvarchar(max)")]
public string Value { get; set; }
[Required]
[Column("TYPE", TypeName = "nvarchar(50)")]
public string Type { get; set; }
[Column("POSITION_X", TypeName = "float")]
public double
#if NET
?
#endif
X { get; set; }
[Column("POSITION_Y", TypeName = "float")]
public double
#if NET
?
#endif
Y { get; set; }
[Column("WIDTH", TypeName = "float")]
public double
#if NET
?
#endif
Width { get; set; }
[Column("HEIGHT", TypeName = "float")]
public double
#if NET
?
#endif
Height { get; set; }
[Required]
[Column("ADDED_WHEN", TypeName = "datetime")]
public DateTime AddedWhen { get; set; }
[Column("CHANGED_WHEN", TypeName = "datetime")]
public DateTime? ChangedWhen { get; set; }
[Column("CHANGED_WHO", TypeName = "nvarchar(100)")]
[StringLength(100)]
public string
#if NET
?
#endif
ChangedWho { get; set; }
[ForeignKey("ElementId")]
public virtual Signature
#if NET
?
#endif
Element { get; set; }
}
#if NETFRAMEWORK
}
#endif

View File

@@ -42,13 +42,21 @@ namespace EnvelopeGenerator.Domain.Entities
[Column("ADDED_WHEN")] [Column("ADDED_WHEN")]
[Required] [Required]
public DateTime AddedWhen { get; set; } public DateTime
#if NET
?
#endif
AddedWhen { get; set; }
[Column("CHANGED_WHO")] [Column("CHANGED_WHO")]
[StringLength(100)] [StringLength(100)]
public string ChangedWho { get; set; } public string
#if NET
?
#endif
ChangedWho { get; set; }
[Column("CHANGED_WHEN")] [Column("CHANGED_WHEN")]
public DateTime ChangedWhen { get; set; } public DateTime? ChangedWhen { get; set; }
} }
} }

View File

@@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using EnvelopeGenerator.Domain.Interfaces; using EnvelopeGenerator.Domain.Interfaces;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Constants;
using DigitalData.Core.Abstractions.Interfaces;
#if NETFRAMEWORK #if NETFRAMEWORK
@@ -17,7 +18,7 @@ namespace EnvelopeGenerator.Domain.Entities
#endif #endif
[Table("TBSIG_ENVELOPE_HISTORY", Schema = "dbo")] [Table("TBSIG_ENVELOPE_HISTORY", Schema = "dbo")]
public class History : IHasEnvelope, IHasReceiver public class History : IHasEnvelope, IHasReceiver, IEntity
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]

View File

@@ -1,8 +1,10 @@
using System.ComponentModel; using EnvelopeGenerator.Domain.Interfaces;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
#if NETFRAMEWORK #if NETFRAMEWORK
using System; using System;
using System.Collections.Generic;
#endif #endif
namespace EnvelopeGenerator.Domain.Entities namespace EnvelopeGenerator.Domain.Entities
@@ -13,7 +15,7 @@ namespace EnvelopeGenerator.Domain.Entities
#endif #endif
[Table("TBSIG_DOCUMENT_RECEIVER_ELEMENT", Schema = "dbo")] [Table("TBSIG_DOCUMENT_RECEIVER_ELEMENT", Schema = "dbo")]
public class Signature public class Signature : ISignature, IHasReceiver
{ {
public Signature() public Signature()
{ {
@@ -106,7 +108,17 @@ public class Signature
public virtual Document Document { get; set; } public virtual Document Document { get; set; }
[ForeignKey("ReceiverId")] [ForeignKey("ReceiverId")]
public virtual Receiver Receiver { get; set; } public virtual Receiver
#if NET
?
#endif
Receiver { get; set; }
public virtual IEnumerable<ElementAnnotation>
#if NET
?
#endif
Annotations { get; set; }
#if NETFRAMEWORK #if NETFRAMEWORK
[NotMapped] [NotMapped]

View File

@@ -37,7 +37,7 @@
<PackageReference Include="DigitalData.EmailProfilerDispatcher.Abstraction.Attributes" Version="1.0.0" /> <PackageReference Include="DigitalData.EmailProfilerDispatcher.Abstraction.Attributes" Version="1.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="UserManager.Domain" Version="3.2.3" /> <PackageReference Include="UserManager.Domain" Version="3.2.3" />
<PackageReference Include="DigitalData.Core.Abstractions" Version="4.1.1" /> <PackageReference Include="DigitalData.Core.Abstractions" Version="4.3.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -0,0 +1,11 @@
namespace EnvelopeGenerator.Domain.Interfaces
{
public interface ISignature
{
int Page { get; set; }
double X { get; set; }
double Y { get; set; }
}
}

View File

@@ -101,7 +101,7 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
@@ -121,20 +121,92 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Pkcs" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Security.Cryptography.Pkcs" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.1" newVersion="8.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.IO.Packaging" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.IO.Packaging" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.1" newVersion="8.0.0.1" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.5" newVersion="8.0.0.5" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Abstractions" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.22.0.0" newVersion="6.22.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.1.1.0" newVersion="2.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.1" newVersion="6.0.0.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Caching.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Caching.Memory" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Options" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Configuration.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Logging" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Tokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IdentityModel.Tokens.Jwt" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Identity.Client" publicKeyToken="0a613f4dd989e8ae" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.8.0" newVersion="3.0.8.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.JsonWebTokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
<entityFramework> <entityFramework>

View File

@@ -185,7 +185,19 @@ try
// Envelope generator serives // Envelope generator serives
#pragma warning disable CS0618 // Type or member is obsolete #pragma warning disable CS0618 // Type or member is obsolete
builder.Services builder.Services
.AddEnvelopeGeneratorInfrastructureServices(sqlExecutorConfigureOptions: executor => executor.ConnectionString = connStr) .AddEnvelopeGeneratorInfrastructureServices(opt =>
{
opt.AddDbTriggerParams(config);
opt.AddDbContext((provider, options) =>
{
var logger = provider.GetRequiredService<ILogger<EGDbContext>>();
options.UseSqlServer(connStr)
.LogTo(log => logger.LogInformation("{log}", log), Microsoft.Extensions.Logging.LogLevel.Trace)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
});
opt.AddSQLExecutor(executor => executor.ConnectionString = connStr);
})
.AddEnvelopeGeneratorServices(config); .AddEnvelopeGeneratorServices(config);
#pragma warning restore CS0618 // Type or member is obsolete #pragma warning restore CS0618 // Type or member is obsolete

View File

@@ -0,0 +1,18 @@
#if NETFRAMEWORK
using System.Collections.Generic;
#endif
namespace EnvelopeGenerator.Infrastructure
#if NET
;
#elif NETFRAMEWORK
{
#endif
public class DbTriggerParams : Dictionary<string, ICollection<string>>
{
}
#if NETFRAMEWORK
}
#endif

View File

@@ -1,157 +0,0 @@
using EnvelopeGenerator.Infrastructure.Repositories;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.EntityFrameworkCore;
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.Extensions.Configuration;
using EnvelopeGenerator.Infrastructure.Executor;
using Dapper;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using DigitalData.UserManager.Domain.Entities;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
namespace EnvelopeGenerator.Infrastructure;
public static class DIExtensions
{
/// <summary>
/// Registers the required repositories for the Envelope Generator service to the given <see cref="IServiceCollection"/>.
/// This method adds the repositories for various envelope-related entities, such as configuration, document receivers, envelopes, and users,
/// as scoped services to the dependency injection container. Optionally, it can also configure the <see cref="EGDbContext"/>
/// with the provided database context options if specified.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to which the services are added.</param>
/// <param name="dbContextOptions">An optional action to configure the <see cref="DbContextOptionsBuilder"/> for the <see cref="EGDbContext"/>.
/// If not provided, the <see cref="EGDbContext"/> will not be configured.</param>
/// <returns>The updated <see cref="IServiceCollection"/> with the added repository services.</returns>
/// <remarks>
/// This method ensures that the repositories are registered as scoped services, meaning that a new instance of each repository
/// 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<IServiceProvider, DbContextOptionsBuilder>? dbContextOptions = null,
IConfiguration? sqlExecutorConfiguration = null,
Action<SQLExecutorParams>? sqlExecutorConfigureOptions = null)
{
if(dbContextOptions is not null)
services.AddDbContext<EGDbContext>(dbContextOptions);
services.TryAddScoped<IConfigRepository, ConfigRepository>();
services.TryAddScoped<IDocumentReceiverElementRepository, DocumentReceiverElementRepository>();
services.TryAddScoped<IEnvelopeDocumentRepository, EnvelopeDocumentRepository>();
services.TryAddScoped<IDocumentReceiverElementRepository, DocumentReceiverElementRepository>();
services.TryAddScoped<IDocumentStatusRepository, DocumentStatusRepository>();
services.TryAddScoped<IEmailTemplateRepository, EmailTemplateRepository>();
services.TryAddScoped<IEnvelopeRepository, EnvelopeRepository>();
services.TryAddScoped<IEnvelopeHistoryRepository, EnvelopeHistoryRepository>();
services.TryAddScoped<IEnvelopeReceiverRepository, EnvelopeReceiverRepository>();
services.TryAddScoped<IEnvelopeTypeRepository, EnvelopeTypeRepository>();
services.TryAddScoped<IReceiverRepository, ReceiverRepository>();
services.TryAddScoped<IEnvelopeReceiverReadOnlyRepository, EnvelopeReceiverReadOnlyRepository>();
services.AddDbRepository(opt =>
{
// scan EnvelopeGenerator
opt.RegisterFromAssembly<EGDbContext>(typeof(Config).Assembly);
// scan UserManager
opt.RegisterFromAssembly<EGDbContext>(typeof(User).Assembly);
// scan EmailProfilerDispatcher
opt.RegisterFromAssembly<EGDbContext>(typeof(EmailOut).Assembly);
});
services.AddSQLExecutor<Envelope>();
services.AddSQLExecutor<Receiver>();
services.AddSQLExecutor<Document>();
services.AddSQLExecutor<Signature>();
services.AddSQLExecutor<DocumentStatus>();
SetDapperTypeMap<Envelope>();
SetDapperTypeMap<User>();
SetDapperTypeMap<Receiver>();
SetDapperTypeMap<Document>();
SetDapperTypeMap<Signature>();
SetDapperTypeMap<DocumentStatus>();
services.AddScoped<IEnvelopeExecutor, EnvelopeExecutor>();
services.AddScoped<IEnvelopeReceiverExecutor, EnvelopeReceiverExecutor>();
services.AddScoped<IDocumentExecutor, DocumentExecutor>();
if (sqlExecutorConfiguration is not null || sqlExecutorConfigureOptions is not null)
services.AddSQLExecutor(sqlExecutorConfiguration, sqlExecutorConfigureOptions);
return services;
}
public static IServiceCollection AddSQLExecutor(this IServiceCollection services, IConfiguration? configuration = null, Action<SQLExecutorParams>? configureOptions = null)
{
if(configuration is not null && configureOptions is not null)
throw new InvalidOperationException("Cannot use both 'configuration' and 'configureOptions'. Only one should be provided.");
if (configuration is not null)
services.Configure<SQLExecutorParams>(configuration);
if(configureOptions is not null)
services.Configure(configureOptions);
return services.AddSingleton<ISQLExecutor, SQLExecutor>();
}
private static void SetDapperTypeMap<TModel>()
{
Dapper.SqlMapper.SetTypeMap(typeof(TModel), new CustomPropertyTypeMap(
typeof(TModel),
(type, columnName) =>
{
return type.GetProperties().FirstOrDefault(prop =>
{
var attr = prop.GetCustomAttribute<ColumnAttribute>();
return attr != null && string.Equals(attr.Name, columnName, StringComparison.OrdinalIgnoreCase)
|| string.Equals(prop.Name, columnName, StringComparison.OrdinalIgnoreCase);
})!;
}
));
}
public static IServiceCollection AddSQLExecutor<T>(this IServiceCollection services, IConfiguration? configuration = null, Action<SQLExecutorParams>? configureOptions = null) where T : class
{
if (configuration is not null && configureOptions is not null)
throw new InvalidOperationException("Cannot use both 'configuration' and 'configureOptions'. Only one should be provided.");
if (configuration is not null)
services.Configure<SQLExecutorParams>(configuration);
services.AddScoped<ISQLExecutor<T>, SQLExecutor<T>>();
var interfaceType = typeof(ISQL<>);
var targetGenericType = interfaceType.MakeGenericType(typeof(T));
var implementations = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a =>
{
try { return a.GetTypes(); }
catch { return Array.Empty<Type>(); }
})
.Where(t =>
t is { IsClass: true, IsAbstract: false } &&
t.GetInterfaces().Any(i =>
i.IsGenericType &&
i.GetGenericTypeDefinition() == interfaceType &&
i.GenericTypeArguments[0] == typeof(T)
)
);
foreach (var impl in implementations)
{
services.AddSingleton(impl);
}
return services;
}
}

View File

@@ -0,0 +1,213 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.EntityFrameworkCore;
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.Extensions.Configuration;
using Dapper;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using DigitalData.UserManager.Domain.Entities;
#if NET
using CommandDotNet.Execution;
using EnvelopeGenerator.Infrastructure.Repositories;
using EnvelopeGenerator.Infrastructure.Executor;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
#elif NETFRAMEWORK
using System;
#endif
namespace EnvelopeGenerator.Infrastructure
{
public static class DependencyInjection
{
/// <summary>
/// Registers the required repositories for the Envelope Generator service to the given <see cref="IServiceCollection"/>.
/// This method adds the repositories for various envelope-related entities, such as configuration, document receivers, envelopes, and users,
/// as scoped services to the dependency injection container. Optionally, it can also configure the <see cref="EGDbContext"/>
/// with the provided database context options if specified.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to which the services are added.</param>
/// <param name="dbContextOptions">An optional action to configure the <see cref="DbContextOptionsBuilder"/> for the <see cref="EGDbContext"/>.
/// If not provided, the <see cref="EGDbContext"/> will not be configured.</param>
/// <returns>The updated <see cref="IServiceCollection"/> with the added repository services.</returns>
/// <remarks>
/// This method ensures that the repositories are registered as scoped services, meaning that a new instance of each repository
/// 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)
{
// configure custom options
options(new Config(services));
#if NET
services.TryAddScoped<IConfigRepository, ConfigRepository>();
services.TryAddScoped<IDocumentReceiverElementRepository, DocumentReceiverElementRepository>();
services.TryAddScoped<IEnvelopeDocumentRepository, EnvelopeDocumentRepository>();
services.TryAddScoped<IDocumentReceiverElementRepository, DocumentReceiverElementRepository>();
services.TryAddScoped<IDocumentStatusRepository, DocumentStatusRepository>();
services.TryAddScoped<IEmailTemplateRepository, EmailTemplateRepository>();
services.TryAddScoped<IEnvelopeRepository, EnvelopeRepository>();
services.TryAddScoped<IEnvelopeHistoryRepository, EnvelopeHistoryRepository>();
services.TryAddScoped<IEnvelopeReceiverRepository, EnvelopeReceiverRepository>();
services.TryAddScoped<IEnvelopeTypeRepository, EnvelopeTypeRepository>();
services.TryAddScoped<IReceiverRepository, ReceiverRepository>();
services.TryAddScoped<IEnvelopeReceiverReadOnlyRepository, EnvelopeReceiverReadOnlyRepository>();
#endif
services.AddDbRepository(opt =>
{
// scan EnvelopeGenerator
opt.RegisterFromAssembly<EGDbContext>(typeof(EnvelopeReceiver).Assembly);
// scan UserManager
opt.RegisterFromAssembly<EGDbContext>(typeof(User).Assembly);
#if NET
// scan EmailProfilerDispatcher
opt.RegisterFromAssembly<EGDbContext>(typeof(EmailOut).Assembly);
#endif
});
#if NET
services.AddSQLExecutor<Envelope>();
services.AddSQLExecutor<Receiver>();
services.AddSQLExecutor<Document>();
services.AddSQLExecutor<Signature>();
services.AddSQLExecutor<DocumentStatus>();
SetDapperTypeMap<Envelope>();
SetDapperTypeMap<User>();
SetDapperTypeMap<Receiver>();
SetDapperTypeMap<Document>();
SetDapperTypeMap<Signature>();
SetDapperTypeMap<DocumentStatus>();
services.AddScoped<IEnvelopeExecutor, EnvelopeExecutor>();
services.AddScoped<IEnvelopeReceiverExecutor, EnvelopeReceiverExecutor>();
services.AddScoped<IDocumentExecutor, DocumentExecutor>();
#endif
return services;
}
#if NET
public static IServiceCollection AddSQLExecutor(this IServiceCollection services, IConfiguration? configuration = null, Action<SQLExecutorParams>? configureOptions = null)
{
if (configuration is not null && configureOptions is not null)
throw new InvalidOperationException("Cannot use both 'configuration' and 'configureOptions'. Only one should be provided.");
if (configuration is not null)
services.Configure<SQLExecutorParams>(configuration);
if (configureOptions is not null)
services.Configure(configureOptions);
return services.AddSingleton<ISQLExecutor, SQLExecutor>();
}
private static void SetDapperTypeMap<TModel>()
{
Dapper.SqlMapper.SetTypeMap(typeof(TModel), new CustomPropertyTypeMap(
typeof(TModel),
(type, columnName) =>
{
return type.GetProperties().FirstOrDefault(prop =>
{
var attr = prop.GetCustomAttribute<ColumnAttribute>();
return attr != null && string.Equals(attr.Name, columnName, StringComparison.OrdinalIgnoreCase)
|| string.Equals(prop.Name, columnName, StringComparison.OrdinalIgnoreCase);
})!;
}
));
}
public static IServiceCollection AddSQLExecutor<T>(this IServiceCollection services, IConfiguration? configuration = null, Action<SQLExecutorParams>? configureOptions = null) where T : class
{
if (configuration is not null && configureOptions is not null)
throw new InvalidOperationException("Cannot use both 'configuration' and 'configureOptions'. Only one should be provided.");
if (configuration is not null)
services.Configure<SQLExecutorParams>(configuration);
services.AddScoped<ISQLExecutor<T>, SQLExecutor<T>>();
var interfaceType = typeof(ISQL<>);
var targetGenericType = interfaceType.MakeGenericType(typeof(T));
var implementations = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a =>
{
try { return a.GetTypes(); }
catch { return Array.Empty<Type>(); }
})
.Where(t =>
t is { IsClass: true, IsAbstract: false } &&
t.GetInterfaces().Any(i =>
i.IsGenericType &&
i.GetGenericTypeDefinition() == interfaceType &&
i.GenericTypeArguments[0] == typeof(T)
)
);
foreach (var impl in implementations)
{
services.AddSingleton(impl);
}
return services;
}
#endif
public class Config
{
private readonly IServiceCollection _services;
internal Config(IServiceCollection services)
{
_services = services;
}
public void AddDbTriggerParams(IConfiguration configuration)
{
_services.Configure<DbTriggerParams>(configuration.GetSection(nameof(DbTriggerParams)));
}
public void AddDbTriggerParams(Action<DbTriggerParams> configureOptions)
{
_services.Configure(configureOptions);
}
public void AddDbContext(Action<DbContextOptionsBuilder> dbContextOptions)
{
_services.AddDbContext<EGDbContext>(dbContextOptions);
}
public void AddDbContext(Action<IServiceProvider, DbContextOptionsBuilder> dbContextOptions)
{
_services.AddDbContext<EGDbContext>(dbContextOptions);
}
#if NET
public void AddSQLExecutor(IConfiguration configuration)
{
_services.Configure<SQLExecutorParams>(configuration);
_services.AddSingleton<ISQLExecutor, SQLExecutor>();
_services.AddSQLExecutor(configuration, null);
}
public void AddSQLExecutor(Action<SQLExecutorParams> options)
{
_services.Configure(options);
_services.AddSingleton<ISQLExecutor, SQLExecutor>();
_services.AddSQLExecutor(null, options);
}
#endif
}
}
}

View File

@@ -1,28 +1,43 @@
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure;
using DigitalData.UserManager.Infrastructure.Contracts;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Group = DigitalData.UserManager.Domain.Entities.Group; using Group = DigitalData.UserManager.Domain.Entities.Group;
using Module = DigitalData.UserManager.Domain.Entities.Module; using Module = DigitalData.UserManager.Domain.Entities.Module;
using DigitalData.EmailProfilerDispatcher;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using DigitalData.Core.Client;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using EnvelopeGenerator.Application.Common.Configurations; #if NET
using DigitalData.EmailProfilerDispatcher;
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
using DigitalData.UserManager.Infrastructure;
using DigitalData.UserManager.Infrastructure.Contracts;
using DigitalData.Core.Client;
#elif NETFRAMEWORK
using System.Linq;
#endif
namespace EnvelopeGenerator.Infrastructure; namespace EnvelopeGenerator.Infrastructure
#if NET
;
#elif NETFRAMEWORK
{
#endif
public class EGDbContext : EGDbContextBase public class EGDbContext : EGDbContextBase
{ {
public EGDbContext(DbContextOptions<EGDbContext> options, IOptions<DbTriggerParams> triggerParamOptions, ILogger<EGDbContext>? logger = null) : base(options, triggerParamOptions, logger) public EGDbContext(DbContextOptions<EGDbContext> options, IOptions<DbTriggerParams> triggerParamOptions, ILogger<EGDbContext>
#if NET
?
#endif
logger = null) : base(options, triggerParamOptions, logger)
{ {
} }
} }
//TODO: Adding EmailOut instead of EmailOut.Abst is not correct for the arch. Re-design EmailPut consedering this. IMailDbContext shoud move to Abstraction layer (hint: in this case using DBSet in abst. will be problem because entity framework will have to be added. //TODO: Adding EmailOut instead of EmailOut.Abst is not correct for the arch. Re-design EmailPut consedering this. IMailDbContext shoud move to Abstraction layer (hint: in this case using DBSet in abst. will be problem because entity framework will have to be added.
public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailDbContext public abstract class EGDbContextBase : DbContext
#if NET
, IUserManagerDbContext, IMailDbContext
#endif
{ {
public DbSet<Config> Configs { get; set; } public DbSet<Config> Configs { get; set; }
@@ -56,7 +71,9 @@ public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailD
public DbSet<UserRep> UserReps { get; set; } public DbSet<UserRep> UserReps { get; set; }
#if NET
public DbSet<EmailOut> EMailOuts { get; set; } public DbSet<EmailOut> EMailOuts { get; set; }
#endif
public DbSet<EnvelopeReceiverReadOnly> EnvelopeReceiverReadOnlys { get; set; } public DbSet<EnvelopeReceiverReadOnly> EnvelopeReceiverReadOnlys { get; set; }
@@ -64,33 +81,22 @@ public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailD
private readonly DbTriggerParams _triggers; private readonly DbTriggerParams _triggers;
private readonly ILogger? _logger; private readonly ILogger
#if NET
?
#endif
_logger;
public bool IsMigration { get; set; } = false; public bool IsMigration { get; set; } = false;
public EGDbContextBase(DbContextOptions options, IOptions<DbTriggerParams> triggerParamOptions, ILogger? logger = null) : base(options) public EGDbContextBase(DbContextOptions options, IOptions<DbTriggerParams> triggerParamOptions, ILogger
#if NET
?
#endif
logger = null) : base(options)
{ {
_triggers = triggerParamOptions.Value; _triggers = triggerParamOptions.Value;
_logger = logger; _logger = logger;
Configs = Set<Config>();
EnvelopeReceivers = Set<EnvelopeReceiver>();
Envelopes = Set<Envelope>();
DocumentReceiverElements = Set<Signature>();
DocumentStatus = Set<DocumentStatus>();
EnvelopeDocument = Set<Document>();
EnvelopeHistories = Set<History>();
EnvelopeTypes = Set<EnvelopeType>();
Receivers = Set<Receiver>();
GroupOfUsers = Set<GroupOfUser>();
Groups = Set<Group>();
ModuleOfUsers = Set<ModuleOfUser>();
Modules = Set<Module>();
Users = Set<User>();
UserReps = Set<UserRep>();
EMailOuts = Set<EmailOut>();
EnvelopeReceiverReadOnlys = Set<EnvelopeReceiverReadOnly>();
} }
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
@@ -119,7 +125,7 @@ public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailD
modelBuilder.Entity<Envelope>() modelBuilder.Entity<Envelope>()
.HasMany(e => e.Documents) .HasMany(e => e.Documents)
.WithOne() .WithOne(d => d.Envelope)
.HasForeignKey(ed => ed.EnvelopeId); .HasForeignKey(ed => ed.EnvelopeId);
modelBuilder.Entity<Envelope>() modelBuilder.Entity<Envelope>()
@@ -187,6 +193,13 @@ public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailD
.HasForeignKey(ds => ds.ReceiverId); .HasForeignKey(ds => ds.ReceiverId);
#endregion DocumentStatus #endregion DocumentStatus
#region Annotation
modelBuilder.Entity<Signature>()
.HasMany(signature => signature.Annotations)
.WithOne(annot => annot.Element)
.HasForeignKey(annot => annot.ElementId);
#endregion
#region Trigger #region Trigger
// Configure entities to handle database triggers // Configure entities to handle database triggers
void AddTrigger<T>() where T : class => _triggers void AddTrigger<T>() where T : class => _triggers
@@ -194,7 +207,9 @@ public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailD
.SelectMany(t => t.Value) .SelectMany(t => t.Value)
.ForEach(tName => .ForEach(tName =>
{ {
#if NET
modelBuilder.Entity<T>().ToTable(tb => tb.HasTrigger(tName)); modelBuilder.Entity<T>().ToTable(tb => tb.HasTrigger(tName));
#endif
_logger?.LogInformation("Trigger '{triggerName}' has been added to the '{entityName}' entity.", tName, typeof(T).Name); _logger?.LogInformation("Trigger '{triggerName}' has been added to the '{entityName}' entity.", tName, typeof(T).Name);
}); });
@@ -210,12 +225,20 @@ public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailD
AddTrigger<EnvelopeReceiverReadOnly>(); AddTrigger<EnvelopeReceiverReadOnly>();
AddTrigger<EnvelopeType>(); AddTrigger<EnvelopeType>();
AddTrigger<Receiver>(); AddTrigger<Receiver>();
#if NET
AddTrigger<EmailOut>(); AddTrigger<EmailOut>();
#endif
#endregion Trigger #endregion Trigger
//configure model builder for user manager tables //configure model builder for user manager tables
#if NET
modelBuilder.ConfigureUserManager(); modelBuilder.ConfigureUserManager();
#endif
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
} }
} }
#if NETFRAMEWORK
}
#endif

View File

@@ -1,4 +1,5 @@
using EnvelopeGenerator.Application.Common.Configurations; #if NET
using EnvelopeGenerator.Application.Common.Configurations;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design; using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@@ -22,10 +23,10 @@ namespace EnvelopeGenerator.Infrastructure
// create DbTriggerParams // create DbTriggerParams
var triggerLists = config.GetSection("DbTriggerParams").Get<Dictionary<string, List<string>>>(); var triggerLists = config.GetSection("DbTriggerParams").Get<Dictionary<string, List<string>>>();
var dbTriggerParams = new DbTriggerParams(); var dbTriggerParams = new DbTriggerParams();
if(triggerLists is not null) if (triggerLists is not null)
foreach (var triggerList in triggerLists) foreach (var triggerList in triggerLists)
{ {
if(triggerList.Value.Count == 0) if (triggerList.Value.Count == 0)
continue; // Skip empty trigger lists continue; // Skip empty trigger lists
var tableName = triggerList.Key; var tableName = triggerList.Key;
@@ -44,3 +45,4 @@ namespace EnvelopeGenerator.Infrastructure
} }
} }
} }
#endif

View File

@@ -0,0 +1,20 @@
#if NETFRAMEWORK
using System.Collections.Generic;
using System;
#endif
namespace EnvelopeGenerator.Infrastructure
{
public static class EnumerableExtensions
{
#if NETFRAMEWORK
public static void ForEach<T>(this IEnumerable<T> values, Action<T> action)
{
foreach (T value in values)
{
action(value);
}
}
#endif
}
}

View File

@@ -1,25 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks> <TargetFrameworks>net462;net7.0;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<!-- disable for net462 -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net462'">
<Nullable>disable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<!-- enable for net7 and more -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net7.0' Or '$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net9.0'">
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapper" Version="2.1.66" /> <PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.3.1" /> <PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.4.0" />
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.4.1" /> <PackageReference Include="DigitalData.Core.Infrastructure" Version="2.4.5" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
<PackageReference Include="QuestPDF" Version="2025.7.1" /> <PackageReference Include="QuestPDF" Version="2025.7.1" />
<PackageReference Include="UserManager" Version="1.1.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" /> <ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net462'">
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
<PackageReference Include="UserManager" Version="1.1.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.32" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.32" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.32">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.32" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.20" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.20" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.20" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.20" />

View File

@@ -1,4 +1,5 @@
using Dapper; #if NET
using Dapper;
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor; using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
using EnvelopeGenerator.Application.Common.SQL; using EnvelopeGenerator.Application.Common.SQL;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
@@ -27,3 +28,4 @@ public class DocumentExecutor : SQLExecutor, IDocumentExecutor
$"base64={base64}, envelope_uuid='{envelope_uuid}'."); $"base64={base64}, envelope_uuid='{envelope_uuid}'.");
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
using Dapper; #if NET
using Dapper;
using DigitalData.UserManager.Application.Contracts.Repositories; using DigitalData.UserManager.Application.Contracts.Repositories;
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor; using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
using EnvelopeGenerator.Application.Common.SQL; using EnvelopeGenerator.Application.Common.SQL;
@@ -37,3 +38,4 @@ public class EnvelopeExecutor : SQLExecutor, IEnvelopeExecutor
return envelope; return envelope;
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
using Dapper; #if NET
using Dapper;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories; using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor; using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
using EnvelopeGenerator.Application.Common.SQL; using EnvelopeGenerator.Application.Common.SQL;
@@ -9,7 +10,7 @@ using Microsoft.Extensions.Options;
namespace EnvelopeGenerator.Infrastructure.Executor; namespace EnvelopeGenerator.Infrastructure.Executor;
public class EnvelopeReceiverExecutor: SQLExecutor, IEnvelopeReceiverExecutor public class EnvelopeReceiverExecutor : SQLExecutor, IEnvelopeReceiverExecutor
{ {
private readonly IEnvelopeReceiverRepository _erRepository; private readonly IEnvelopeReceiverRepository _erRepository;
@@ -33,3 +34,4 @@ public class EnvelopeReceiverExecutor: SQLExecutor, IEnvelopeReceiverExecutor
return await _erRepository.ReadByIdAsync(envelopeId: er.EnvelopeId, receiverId: er.ReceiverId); return await _erRepository.ReadByIdAsync(envelopeId: er.EnvelopeId, receiverId: er.ReceiverId);
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor; #if NET
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Infrastructure.Executor; namespace EnvelopeGenerator.Infrastructure.Executor;
@@ -39,3 +40,4 @@ public sealed record Query<TEntity> : IQuery<TEntity>
public async Task<IEnumerable<TEntity>> ToListAsync() => await _query.ToListAsync(); public async Task<IEnumerable<TEntity>> ToListAsync() => await _query.ToListAsync();
} }
#endif

View File

@@ -1,4 +1,5 @@
namespace EnvelopeGenerator.Infrastructure.Executor; #if NET
namespace EnvelopeGenerator.Infrastructure.Executor;
public static class QueryExtension public static class QueryExtension
{ {
@@ -7,3 +8,4 @@ public static class QueryExtension
return new Query<TEntity>(queryable); return new Query<TEntity>(queryable);
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
using Dapper; #if NET
using Dapper;
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor; using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -31,3 +32,4 @@ public class SQLExecutor : ISQLExecutor
return Execute<TEntity>(sql.Raw, parameters, cancellation); return Execute<TEntity>(sql.Raw, parameters, cancellation);
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor; #if NET
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@@ -29,3 +30,5 @@ public sealed class SQLExecutor<T> : SQLExecutor, ISQLExecutor<T> where T : clas
return Execute(sql.Raw); return Execute(sql.Raw);
} }
} }
#endif

View File

@@ -1,6 +1,9 @@
namespace EnvelopeGenerator.Infrastructure.Executor; #if NET
namespace EnvelopeGenerator.Infrastructure.Executor;
public class SQLExecutorParams public class SQLExecutorParams
{ {
public string? ConnectionString { get; set; } public string? ConnectionString { get; set; }
} }
#endif

View File

@@ -1,4 +1,5 @@
using System; #if NET
using System;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable #nullable disable
@@ -809,3 +810,4 @@ namespace EnvelopeGenerator.Infrastructure.Migrations
} }
} }
} }
#endif

View File

@@ -1,20 +1,21 @@
using DigitalData.Core.Infrastructure; #if NET
using EnvelopeGenerator.Domain.Entities; using DigitalData.Core.Infrastructure;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories; using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
namespace EnvelopeGenerator.Infrastructure.Repositories; namespace EnvelopeGenerator.Infrastructure.Repositories;
[Obsolete("Use IRepository")] [Obsolete("Use IRepository")]
public class ConfigRepository : CRUDRepository<Config, int, EGDbContext>, IConfigRepository public class ConfigRepository : CRUDRepository<Domain.Entities.Config, int, EGDbContext>, IConfigRepository
{ {
public ConfigRepository(EGDbContext dbContext) : base(dbContext, dbContext.Configs) public ConfigRepository(EGDbContext dbContext) : base(dbContext, dbContext.Configs)
{ {
} }
public async Task<Config?> ReadFirstAsync() public async Task<Domain.Entities.Config?> ReadFirstAsync()
{ {
var configs = await _dbSet.ToListAsync(); var configs = await _dbSet.ToListAsync();
return configs.Count > 0 ? configs[0] : default; return configs.Count > 0 ? configs[0] : default;
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Infrastructure; #if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories; using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
@@ -11,3 +12,4 @@ public class DocumentReceiverElementRepository : CRUDRepository<Signature, int,
{ {
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Infrastructure; #if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories; using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
@@ -11,3 +12,4 @@ public class DocumentStatusRepository : CRUDRepository<DocumentStatus, int, EGDb
{ {
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Infrastructure; #if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
@@ -9,26 +10,27 @@ namespace EnvelopeGenerator.Infrastructure.Repositories;
[Obsolete("Use TempRepo")] [Obsolete("Use TempRepo")]
public class EmailTemplateRepository : CRUDRepository<EmailTemplate, int, EGDbContext>, IEmailTemplateRepository public class EmailTemplateRepository : CRUDRepository<EmailTemplate, int, EGDbContext>, IEmailTemplateRepository
{
private readonly IMemoryCache _cache;
public EmailTemplateRepository(EGDbContext dbContext, IMemoryCache cache) : base(dbContext, dbContext.EmailTemplate)
{ {
private readonly IMemoryCache _cache; _cache = cache;
public EmailTemplateRepository(EGDbContext dbContext, IMemoryCache cache) : base(dbContext, dbContext.EmailTemplate)
{
_cache = cache;
}
private readonly Guid key_guid = Guid.NewGuid();
/// <summary>
/// Retrieves an email template based on its name.
/// Utilizes in-memory caching to improve performance for the limited number of email templates available.
/// If the template is not found in the cache, it queries the database and stores the result in the cache.
/// </summary>
/// <param name="type">The type of the email template, which corresponds to its name.</param>
/// <returns>
/// A task that represents the asynchronous operation. The task result contains the email template
/// if found, otherwise null.
/// </returns>
public async Task<EmailTemplate?> ReadByNameAsync(EmailTemplateType type) => await _cache.GetOrCreateAsync($"{type}{key_guid}", async _
=> await _dbSet.Where(t => t.Name == type.ToString()).FirstOrDefaultAsync());
} }
private readonly Guid key_guid = Guid.NewGuid();
/// <summary>
/// Retrieves an email template based on its name.
/// Utilizes in-memory caching to improve performance for the limited number of email templates available.
/// If the template is not found in the cache, it queries the database and stores the result in the cache.
/// </summary>
/// <param name="type">The type of the email template, which corresponds to its name.</param>
/// <returns>
/// A task that represents the asynchronous operation. The task result contains the email template
/// if found, otherwise null.
/// </returns>
public async Task<EmailTemplate?> ReadByNameAsync(EmailTemplateType type) => await _cache.GetOrCreateAsync($"{type}{key_guid}", async _
=> await _dbSet.Where(t => t.Name == type.ToString()).FirstOrDefaultAsync());
}
#endif

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Infrastructure; #if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories; using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
@@ -11,3 +12,4 @@ public class EnvelopeDocumentRepository : CRUDRepository<Document, int, EGDbCont
{ {
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Infrastructure; #if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Constants;
@@ -48,3 +49,4 @@ public class EnvelopeHistoryRepository : CRUDRepository<History, long, EGDbConte
withSender: withSender, withSender: withSender,
withReceiver: withReceiver).ToListAsync(); withReceiver: withReceiver).ToListAsync();
} }
#endif

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Infrastructure; #if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories; using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
@@ -52,7 +53,7 @@ public class EnvelopeReceiverReadOnlyRepository : CRUDRepository<EnvelopeReceive
private async Task<IEnumerable<EnvelopeReceiverReadOnly>> IncludeEnvelope(params EnvelopeReceiverReadOnly[] erros) private async Task<IEnumerable<EnvelopeReceiverReadOnly>> IncludeEnvelope(params EnvelopeReceiverReadOnly[] erros)
{ {
foreach (var erro in erros) foreach (var erro in erros)
erro.Envelope = await _envRepo.ReadByIdAsync((int) erro.EnvelopeId); erro.Envelope = await _envRepo.ReadByIdAsync((int)erro.EnvelopeId);
return erros; return erros;
} }
@@ -68,3 +69,4 @@ public class EnvelopeReceiverReadOnlyRepository : CRUDRepository<EnvelopeReceive
return erros; return erros;
} }
} }
#endif

View File

@@ -1,7 +1,7 @@
using DigitalData.Core.Infrastructure; #if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Domain;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories; using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
@@ -65,3 +65,4 @@ public class EnvelopeRepository : CRUDRepository<Envelope, int, EGDbContext>, IE
return await query.ToListAsync(); return await query.ToListAsync();
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Infrastructure; #if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories; using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
@@ -11,3 +12,4 @@ public class EnvelopeTypeRepository : CRUDRepository<EnvelopeType, int, EGDbCont
{ {
} }
} }
#endif

View File

@@ -1,8 +1,8 @@
using DigitalData.Core.Infrastructure; #if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using DigitalData.Core.Exceptions; using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Domain;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories; using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
@@ -108,7 +108,7 @@ public class EnvelopeReceiverRepository : CRUDRepository<EnvelopeReceiver, (int
var query = _dbSet.AsNoTracking(); var query = _dbSet.AsNoTracking();
if(email is not null) if (email is not null)
query = query.Where(er => er.Receiver!.EmailAddress == email); query = query.Where(er => er.Receiver!.EmailAddress == email);
if (id is not null) if (id is not null)
@@ -120,3 +120,4 @@ public class EnvelopeReceiverRepository : CRUDRepository<EnvelopeReceiver, (int
return await query.OrderBy(er => er.EnvelopeId).LastOrDefaultAsync(); return await query.OrderBy(er => er.EnvelopeId).LastOrDefaultAsync();
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Infrastructure; #if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories; using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
@@ -16,10 +17,10 @@ public class ReceiverRepository : CRUDRepository<Receiver, int, EGDbContext>, IR
{ {
IQueryable<Receiver> query = _dbSet.AsNoTracking(); IQueryable<Receiver> query = _dbSet.AsNoTracking();
if(emailAddress is not null) if (emailAddress is not null)
query = query.Where(r => r.EmailAddress == emailAddress); query = query.Where(r => r.EmailAddress == emailAddress);
if(signature is not null) if (signature is not null)
query = query.Where(r => r.Signature == signature); query = query.Where(r => r.Signature == signature);
// envelope receivers are ignored (with '[JsonIgnore]' attribute). The reson to add them is to get the las used receiver name // envelope receivers are ignored (with '[JsonIgnore]' attribute). The reson to add them is to get the las used receiver name
@@ -35,3 +36,4 @@ public class ReceiverRepository : CRUDRepository<Receiver, int, EGDbContext>, IR
public async override Task<IEnumerable<Receiver>> ReadAllAsync() => await ReadBy().ToListAsync(); public async override Task<IEnumerable<Receiver>> ReadAllAsync() => await ReadBy().ToListAsync();
} }
#endif

View File

@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net462;net7.0;net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net462'">
<ImplicitUsings>disable</ImplicitUsings>
<LangVersion>7.3</LangVersion>
<Nullable>disable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' != 'net462'">
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="itext" Version="8.0.5" />
<PackageReference Include="itext.bouncy-castle-adapter" Version="8.0.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,17 @@
#if NETFRAMEWORK
using System.IO;
#endif
namespace EnvelopeGenerator.PdfEditor
{
public static class Extensions
{
/// <summary>
/// Writes the stream contents to a byte array, regardless of the System.IO.MemoryStream.Position property.
/// </summary>
/// <typeparam name="TPdf"></typeparam>
/// <param name="pdf"></param>
/// <returns>A new byte array</returns>
public static byte[] ExportAsBytes<TPdf>(this TPdf pdf) where TPdf : Pdf<MemoryStream, MemoryStream> => pdf.ExportStream().ToArray();
}
}

View File

@@ -0,0 +1,208 @@
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Canvas;
using EnvelopeGenerator.Domain.Interfaces;
using iText.Kernel.Colors;
using DigitalData.Core.Abstractions.Interfaces;
#if NETFRAMEWORK
using System;
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
#endif
namespace EnvelopeGenerator.PdfEditor
{
public static class Pdf
{
public static Pdf<MemoryStream, MemoryStream> FromMemory(byte[] documentBytes)
{
return new Pdf<MemoryStream, MemoryStream>(new MemoryStream(documentBytes), new MemoryStream());
}
public static Pdf<MemoryStream, MemoryStream> FromMemory(MemoryStream stream, MemoryStream
#if NET
?
#endif
outputStream = null)
{
return new Pdf<MemoryStream, MemoryStream>(stream, outputStream ?? new MemoryStream(), disposeInputStream: false, disposeOutputStream: outputStream is null);
}
}
public class Pdf<TInputStream, TOutputStream> : IDisposable, IAsyncDisposable
where TInputStream : Stream
where TOutputStream : Stream
{
private readonly PdfDocument _doc;
private readonly TInputStream _inputStream;
private readonly TOutputStream _outputStream;
private readonly PdfReader _reader;
private readonly PdfWriter _writer;
private readonly bool _disposeInputStream;
private readonly bool _disposeOutputStream;
public Pdf(TInputStream inputStream, TOutputStream outputStream, bool disposeInputStream = true, bool disposeOutputStream = true)
{
_inputStream = inputStream;
_outputStream = outputStream;
_reader = new PdfReader(inputStream);
_writer = new PdfWriter(outputStream);
_doc = new PdfDocument(_reader, _writer);
_disposeInputStream = disposeInputStream;
_disposeOutputStream = disposeOutputStream;
}
/// <summary>
/// Gets the output stream containing the edited PDF document.
/// </summary>
/// <remarks>
/// Accessing this property will close the underlying <see cref="PdfDocument"/> to ensure
/// all changes are flushed to the stream. After accessing this property, the PDF document
/// can no longer be modified using this <see cref="Pdf{TInputStream, TOutputStream}"/> instance.
/// </remarks>
/// <returns>
/// The <typeparamref name="TOutputStream"/> instance that contains the updated PDF bytes.
/// </returns>
public TOutputStream ExportStream()
{
_doc.Close();
return _outputStream;
}
#region Edit methods
public Pdf<TInputStream, TOutputStream> Document(Action<PdfDocument> edit)
{
edit(_doc);
return this;
}
public Pdf<TInputStream, TOutputStream> Page(int pageIndex, Action<PdfPage> design)
=> Document(doc =>
{
var page = doc.GetPage(pageIndex);
design(doc.GetPage(pageIndex));
});
public Pdf<TInputStream, TOutputStream> Page(Action<PdfPage> design)
{
for (int i = 1; i <= _doc.GetNumberOfPages(); i++)
Page(i, design);
return this;
}
public Pdf<TInputStream, TOutputStream> Design(int pageIndex, Action<PdfCanvas> design)
=> Document(doc =>
{
var page = doc.GetPage(pageIndex);
var canvas = new PdfCanvas(page);
design(canvas);
});
#endregion
public Pdf<TInputStream, TOutputStream> Background<TSignature>(IEnumerable<TSignature> signatures, double widthPx = 1.9500000000000002, double heightPx = 2.52)
where TSignature : ISignature
{
// once per page
Page(page =>
{
var canvas = new PdfCanvas(page);
canvas.ConcatMatrix(1, 0, 0, -1, 0, page.GetPageSize().GetHeight());
});
foreach (var signature in signatures)
Page(signature.Page, page =>
{
var canvas = new PdfCanvas(page);
double inchFactor = 72;
double magin = .2;
double x = (signature.X - .7 - magin) * inchFactor;
double y = (signature.Y - .5 - magin) * inchFactor;
double width = widthPx * inchFactor;
double height = heightPx * inchFactor;
double bottomLineLength = 2.5;
// draw background
canvas.SetFillColor(new DeviceRgb(222, 220, 215))
.Rectangle(x, y, width, height)
.Fill();
// draw bottom line
canvas.SetFillColor(new DeviceRgb(204, 202, 198))
.Rectangle(x, y + height - bottomLineLength, width, bottomLineLength)
.Fill();
});
return this;
}
#region Finalizer
private bool _disposed = false;
~Pdf()
{
// If Dispose is not called, clean up unmanaged resources
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
// Managed resources
_doc?.Close();
if (_disposeInputStream) _inputStream?.Dispose();
if(_disposeOutputStream) _outputStream?.Dispose();
}
else
{
// When called by the finalizer, clean up only unmanaged resources
// Unmanaged resources such as PdfDocument, PdfReader, and PdfWriter are already IDisposable; we close them here
try { _doc?.Close(); } catch { }
try { if(_disposeInputStream) _inputStream?.Dispose(); } catch { }
try { if (_disposeOutputStream) _outputStream?.Dispose(); } catch { }
}
_disposed = true;
}
public async ValueTask DisposeAsync()
{
if (_disposed)
return;
_doc?.Close();
if (_disposeInputStream)
{
if (_inputStream is IAsyncDisposable asyncInput)
await asyncInput.DisposeAsync();
else
_inputStream.Dispose();
}
if (_disposeOutputStream)
{
if (_outputStream is IAsyncDisposable asyncOutput)
await asyncOutput.DisposeAsync();
else
_outputStream?.Dispose();
}
_disposed = true;
GC.SuppressFinalize(this);
}
#endregion
}
}

View File

@@ -53,6 +53,70 @@
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.5" newVersion="8.0.0.5" /> <bindingRedirect oldVersion="0.0.0.0-8.0.0.5" newVersion="8.0.0.5" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Caching.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Caching.Memory" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Options" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Configuration.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Logging" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Tokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IdentityModel.Tokens.Jwt" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.JsonWebTokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.1.0" newVersion="7.5.1.0" />
</dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
<entityFramework> <entityFramework>

View File

@@ -17,6 +17,21 @@
<TargetFrameworkProfile /> <TargetFrameworkProfile />
<NuGetPackageImportStamp> <NuGetPackageImportStamp>
</NuGetPackageImportStamp> </NuGetPackageImportStamp>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@@ -54,6 +69,9 @@
<Reference Include="BouncyCastle.Cryptography, Version=2.0.0.0, Culture=neutral, PublicKeyToken=072edcf4a5328938, processorArchitecture=MSIL"> <Reference Include="BouncyCastle.Cryptography, Version=2.0.0.0, Culture=neutral, PublicKeyToken=072edcf4a5328938, processorArchitecture=MSIL">
<HintPath>..\packages\BouncyCastle.Cryptography.2.5.0\lib\net461\BouncyCastle.Cryptography.dll</HintPath> <HintPath>..\packages\BouncyCastle.Cryptography.2.5.0\lib\net461\BouncyCastle.Cryptography.dll</HintPath>
</Reference> </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>
</Reference>
<Reference Include="DigitalData.Modules.Base, Version=1.3.8.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="DigitalData.Modules.Base, Version=1.3.8.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\2_DLL Projekte\DDModules\Base\bin\Debug\DigitalData.Modules.Base.dll</HintPath> <HintPath>..\..\2_DLL Projekte\DDModules\Base\bin\Debug\DigitalData.Modules.Base.dll</HintPath>
@@ -160,8 +178,14 @@
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> <Reference Include="Microsoft.Extensions.DependencyInjection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath> <HintPath>..\packages\Microsoft.Extensions.DependencyInjection.7.0.0\lib\net462\Microsoft.Extensions.DependencyInjection.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
@@ -335,6 +359,18 @@
<Name>EnvelopeGenerator.Domain</Name> <Name>EnvelopeGenerator.Domain</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.6.2">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.6.2 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<Import Project="..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets" Condition="Exists('..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets')" /> <Import Project="..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets" Condition="Exists('..\packages\GdPicture.runtimes.windows.14.3.3\build\net462\GdPicture.runtimes.windows.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View File

@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<Config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ConnectionString>Server=sDD-VMP04-SQL17\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;TrustServerCertificate=True;</ConnectionString>
<Debug>true</Debug>
<IntervalInMin>1</IntervalInMin>
<IgnoredLabels>
<Label>Date</Label>
<Label>Datum</Label>
<Label>ZIP</Label>
<Label>PLZ</Label>
<Label>Place</Label>
<Label>Ort</Label>
<Label>Position</Label>
<Label>Stellung</Label>
</IgnoredLabels>
</Config>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="BouncyCastle.Cryptography" version="2.5.0" targetFramework="net48" /> <package id="BouncyCastle.Cryptography" version="2.5.0" targetFramework="net48" />
<package id="DigitalData.Core.Abstractions" version="4.3.0" targetFramework="net462" />
<package id="DocumentFormat.OpenXml" version="3.2.0" targetFramework="net48" /> <package id="DocumentFormat.OpenXml" version="3.2.0" targetFramework="net48" />
<package id="DocumentFormat.OpenXml.Framework" version="3.2.0" targetFramework="net48" /> <package id="DocumentFormat.OpenXml.Framework" version="3.2.0" targetFramework="net48" />
<package id="EntityFramework" version="6.4.4" targetFramework="net48" /> <package id="EntityFramework" version="6.4.4" targetFramework="net48" />
@@ -11,7 +12,9 @@
<package id="Microsoft.AspNet.WebApi.Client" version="6.0.0" targetFramework="net48" /> <package id="Microsoft.AspNet.WebApi.Client" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="8.0.0" targetFramework="net48" /> <package id="Microsoft.Bcl.AsyncInterfaces" version="8.0.0" targetFramework="net48" />
<package id="Microsoft.CSharp" version="4.7.0" targetFramework="net48" /> <package id="Microsoft.CSharp" version="4.7.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="2.1.1" targetFramework="net462" /> <package id="Microsoft.Extensions.DependencyInjection" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="7.0.0" targetFramework="net462" />
<package id="Microsoft.VisualBasic" version="10.3.0" targetFramework="net48" /> <package id="Microsoft.VisualBasic" version="10.3.0" targetFramework="net48" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" /> <package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
<package id="Newtonsoft.Json.Bson" version="1.0.2" targetFramework="net48" /> <package id="Newtonsoft.Json.Bson" version="1.0.2" targetFramework="net48" />

View File

@@ -20,8 +20,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Bogus" Version="35.6.3" /> <PackageReference Include="Bogus" Version="35.6.3" />
<PackageReference Include="coverlet.collector" Version="6.0.0" /> <PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.3.1" /> <PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.4.0" />
<PackageReference Include="DigitalData.Core.Abstractions" Version="4.1.1" /> <PackageReference Include="DigitalData.Core.Abstractions" Version="4.3.0" />
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" /> <PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" /> <PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.20" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.20" />

View File

@@ -4,6 +4,7 @@ using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Interfaces.Services; using EnvelopeGenerator.Application.Common.Interfaces.Services;
using EnvelopeGenerator.Application.Common.Notifications.DocSigned; using EnvelopeGenerator.Application.Common.Notifications.DocSigned;
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries; using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
using EnvelopeGenerator.Application.Histories.Queries;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Web.Extensions; using EnvelopeGenerator.Web.Extensions;
using MediatR; using MediatR;
@@ -11,7 +12,6 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Dynamic;
namespace EnvelopeGenerator.Web.Controllers; namespace EnvelopeGenerator.Web.Controllers;
@@ -44,7 +44,7 @@ public class AnnotationController : ControllerBase
[Authorize(Roles = ReceiverRole.FullyAuth)] [Authorize(Roles = ReceiverRole.FullyAuth)]
[HttpPost] [HttpPost]
public async Task<IActionResult> CreateOrUpdate([FromBody] ExpandoObject annotations, CancellationToken cancel = default) public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation psPdfKitAnnotation, CancellationToken cancel = default)
{ {
// get claims // get claims
var signature = User.GetAuthReceiverSignature(); var signature = User.GetAuthReceiverSignature();
@@ -58,14 +58,16 @@ public class AnnotationController : ControllerBase
// Again check if receiver has already signed // Again check if receiver has already signed
if (await _mediator.IsSignedAsync(uuid, signature, cancel)) if (await _mediator.IsSignedAsync(uuid, signature, cancel))
return Problem(statusCode: 403); return Problem(statusCode: 409);
else if (await _mediator.AnyHistoryAsync(uuid, new[] { EnvelopeStatus.EnvelopeRejected, EnvelopeStatus.DocumentRejected }, cancel))
return Problem(statusCode: 423);
var docSignedNotification = await _mediator var docSignedNotification = await _mediator
.ReadEnvelopeReceiverAsync(uuid, signature, cancel) .ReadEnvelopeReceiverAsync(uuid, signature, cancel)
.ToDocSignedNotification(annotations) .ToDocSignedNotification(psPdfKitAnnotation)
?? throw new NotFoundException("Envelope receiver is not found."); ?? throw new NotFoundException("Envelope receiver is not found.");
await _mediator.Publish(docSignedNotification, cancel); await _mediator.PublishSafely(docSignedNotification, cancel);
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

View File

@@ -0,0 +1,425 @@
using DigitalData.Core.Abstraction.Application.DTO;
using DigitalData.Core.API;
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Interfaces.Services;
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
using EnvelopeGenerator.Application.Resources;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.PdfEditor;
using EnvelopeGenerator.Web.Extensions;
using EnvelopeGenerator.Web.Models;
using MediatR;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json;
using OtpNet;
namespace EnvelopeGenerator.Web.Controllers;
[Route("Envelope")]
public class EnvelopeController : ViewControllerBase
{
[Obsolete("Use MediatR")]
private readonly IEnvelopeReceiverService _envRcvService;
[Obsolete("Use MediatR")]
private readonly IEnvelopeHistoryService _historyService;
private readonly IConfiguration _configuration;
[Obsolete("Use MediatR")]
private readonly IEnvelopeMailService _mailService;
[Obsolete("Use MediatR")]
private readonly IEnvelopeReceiverReadOnlyService _readOnlyService;
private readonly IAuthenticator _authenticator;
[Obsolete("Use MediatR")]
private readonly IReceiverService _rcvService;
private readonly IEnvelopeSmsHandler _envSmsHandler;
private readonly IMediator _mediator;
[Obsolete("Use MediatR")]
public EnvelopeController(ILogger<EnvelopeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration, Cultures cultures, IEnvelopeMailService envelopeMailService, IEnvelopeReceiverReadOnlyService readOnlyService, IAuthenticator authenticator, IReceiverService receiverService, IEnvelopeSmsHandler envelopeSmsService, IMediator mediator) : base(logger, cultures, localizer)
{
_envRcvService = envelopeReceiverService;
_historyService = historyService;
_configuration = configuration;
_mailService = envelopeMailService;
_readOnlyService = readOnlyService;
_authenticator = authenticator;
_rcvService = receiverService;
_envSmsHandler = envelopeSmsService;
_mediator = mediator;
}
[HttpGet("{envelopeReceiverId}")]
[Obsolete("Use MediatR")]
public async Task<IActionResult> Main([FromRoute] string envelopeReceiverId, CancellationToken cancel)
{
try
{
ViewData["EnvelopeKey"] = envelopeReceiverId;
if (!envelopeReceiverId.TryDecode(out var decoded))
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
return this.ViewDocumentNotFound();
}
#region Read Only
if (decoded.GetEncodeType() == EncodeType.EnvelopeReceiverReadOnly)
return await EnvelopeReceiverReadOnly(decoded.ParseReadOnlyId());
#endregion
var er = await _mediator.ReadEnvelopeReceiverAsync(envelopeReceiverId, cancel);
if (er is null)
return this.ViewEnvelopeNotFound();
#region Rejected or Signed
//check rejection
var rejRcvrs = await _historyService.ReadRejectingReceivers(er.Envelope!.Id);
if (rejRcvrs.Any())
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
ViewBag.IsExt = !rejRcvrs.Contains(er.Receiver); //external if the current user is not rejected
return View("EnvelopeRejected", er);
}
//check if it has already signed
if (await _historyService.IsSigned(envelopeId: er.Envelope!.Id, userReference: er.Receiver!.EmailAddress))
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return View("EnvelopeSigned");
}
#endregion
#region UseAccessCode
if (!er.Envelope!.UseAccessCode)
{
(string? uuid, string? signature) = decoded.ParseEnvelopeReceiverId();
var er_secret_res = await _envRcvService.ReadWithSecretByUuidSignatureAsync(uuid: uuid!, signature: signature!);
if (er_secret_res.IsFailed)
{
_logger.LogNotice(er_secret_res.Notices);
return this.ViewEnvelopeNotFound();
}
var er_secret = er_secret_res.Data;
await HttpContext.SignInEnvelopeAsync(er_secret, ReceiverRole.FullyAuth);
return await CreateShowEnvelopeView(er_secret);
}
#endregion UseAccessCode
#region Send Access Code
bool accessCodeAlreadyRequested = await _historyService.AccessCodeAlreadyRequested(envelopeId: er.Envelope!.Id, userReference: er.Receiver!.EmailAddress);
if (!accessCodeAlreadyRequested)
{
await _historyService.RecordAsync(er.EnvelopeId, er.Receiver.EmailAddress, EnvelopeStatus.AccessCodeRequested);
var mailRes = await _mailService.SendAccessCodeAsync(envelopeReceiverDto: er);
if (mailRes.IsFailed)
{
_logger.LogNotice(mailRes);
return this.ViewAccessCodeNotSent();
}
}
#endregion
return await CreateEnvelopeLockedView(er, cancel);
}
catch(Exception ex)
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception:ex, message: _localizer.UnexpectedError());
return this.ViewInnerServiceError();
}
}
[HttpPost("{envelopeReceiverId}")]
[Obsolete("Use MediatR")]
public async Task<IActionResult> LogInEnvelope([FromRoute] string envelopeReceiverId, [FromForm] Auth auth, CancellationToken cancel)
{
try
{
ViewData["EnvelopeKey"] = envelopeReceiverId;
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
if (uuid is null || signature is null)
{
_logger.LogEnvelopeError(uuid: uuid, signature: signature, message: _localizer.WrongEnvelopeReceiverId());
return Unauthorized();
}
_logger.LogInformation("Envelope UUID: [{uuid}]\nReceiver Signature: [{signature}]", uuid, signature);
var er_secret_res = await _envRcvService.ReadWithSecretByUuidSignatureAsync(uuid: uuid, signature: signature);
if (er_secret_res.IsFailed)
{
_logger.LogNotice(er_secret_res.Notices);
return this.ViewEnvelopeNotFound();
}
var er_secret = er_secret_res.Data;
//check rejection
var rejRcvrs = await _historyService.ReadRejectingReceivers(er_secret.Envelope!.Id);
if (rejRcvrs.Any())
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
ViewBag.IsExt = !rejRcvrs.Contains(er_secret.Receiver); //external if the current user is not rejected
return View("EnvelopeRejected", er_secret);
}
// show envelope if already logged in
if (User.IsInRole(ReceiverRole.FullyAuth))
return await CreateShowEnvelopeView(er_secret);
if (auth.HasMulti)
{
return Unauthorized();
}
else if (auth.HasAccessCode)
{
if (await HandleAccessCodeAsync(auth, er_secret, envelopeReceiverId) is IActionResult acView)
return acView;
}
else if (auth.HasSmsCode)
{
if (await HandleSmsAsync(auth, er_secret, envelopeReceiverId) is IActionResult smsView)
return smsView;
}
else if (auth.HasAuthenticatorCode)
{
if (await HandleAuthenticatorAsync(auth, er_secret, envelopeReceiverId) is IActionResult aView)
return aView;
}
else
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
return View("EnvelopeLocked")
.WithData("EnvelopeKey", envelopeReceiverId)
.WithData("TFAEnabled", er_secret.Envelope!.TFAEnabled)
.WithData("HasPhoneNumber", er_secret.HasPhoneNumber)
.WithData("SenderEmail", er_secret.Envelope.User!.Email)
.WithData("EnvelopeTitle", er_secret.Envelope.Title)
.WithData("ErrorMessage", _localizer.WrongEnvelopeReceiverId());
}
await HttpContext.SignInEnvelopeAsync(er_secret, ReceiverRole.FullyAuth);
return await CreateShowEnvelopeView(er_secret);
}
catch (Exception ex)
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception: ex);
return this.ViewInnerServiceError();
}
}
private async Task<IActionResult> CreateEnvelopeLockedView(EnvelopeReceiverDto er, CancellationToken cancel)
{
var uuidClaim = User.GetAuthEnvelopeUuid();
var signatureClaim = User.GetAuthReceiverSignature();
if (uuidClaim is not null
&& uuidClaim == er.Envelope?.Uuid
&& signatureClaim is not null
&& signatureClaim == er.Receiver?.Signature
&& User.IsInRole(ReceiverRole.FullyAuth))
{
await HttpContext.SignInEnvelopeAsync(er, ReceiverRole.FullyAuth);
//add PSPDFKit licence key
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
return await CreateShowEnvelopeView(er);
}
else
{
ViewData["TFAEnabled"] = er.Envelope!.TFAEnabled;
ViewData["HasPhoneNumber"] = er.HasPhoneNumber;
ViewData["SenderEmail"] = er.Envelope.User!.Email;
ViewData["EnvelopeTitle"] = er.Envelope.Title;
return View("EnvelopeLocked");
}
}
private async Task<IActionResult> CreateShowEnvelopeView(EnvelopeReceiverDto er)
{
if (er.Envelope!.Documents?.FirstOrDefault() is DocumentDto doc && doc.ByteData is not null)
{
using var pdf = Pdf.FromMemory(doc.ByteData).Background(doc.Elements!);
doc.ByteData = pdf.ExportAsBytes();
ViewData["DocumentBytes"] = doc.ByteData;
}
else
{
_logger.LogEnvelopeError(er.Envelope.Uuid, er.Receiver?.Signature, message: "No document byte-data was found in ENVELOPE_DOCUMENT table.");
return this.ViewDocumentNotFound();
}
await HttpContext.SignInEnvelopeAsync(er, ReceiverRole.FullyAuth);
//add PSPDFKit licence key
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
return View("ShowEnvelope", er);
}
#region TFA Views
[NonAction]
private async Task<IActionResult> TFAViewAsync(bool viaSms, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
{
if (viaSms)
{
var (smsRes, expiration) = await _envSmsHandler.SendTotpAsync(er_secret);
ViewData["EnvelopeKey"] = envelopeReceiverId;
ViewData["TFAEnabled"] = er_secret.Envelope!.TFAEnabled;
ViewData["HasPhoneNumber"] = er_secret.HasPhoneNumber;
ViewData["SenderEmail"] = er_secret.Envelope.User!.Email;
ViewData["EnvelopeTitle"] = er_secret.Envelope.Title;
if (smsRes?.Failed ?? false)
{
var res_json = JsonConvert.SerializeObject(smsRes);
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, message: $"An unexpected error occurred while sending an SMS code. Response: ${res_json}");
return this.ViewInnerServiceError();
}
return View("EnvelopeLocked").WithData("CodeType", "smsCode").WithData("SmsExpiration", expiration);
}
else
{
return View("EnvelopeLocked")
.WithData("CodeType", "authenticatorCode")
.WithData("TfaRegDeadline", er_secret.Receiver?.TfaRegDeadline);
}
}
[Obsolete("Use MediatR")]
[NonAction]
private async Task<IActionResult?> HandleAccessCodeAsync(Auth auth, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
{
//check the access code verification
if (er_secret.AccessCode != auth.AccessCode)
{
//EnvelopeStatusQuery.AccessCodeIncorrect
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, EnvelopeStatus.AccessCodeIncorrect);
Response.StatusCode = StatusCodes.Status401Unauthorized;
return View("EnvelopeLocked")
.WithData("EnvelopeKey", envelopeReceiverId)
.WithData("TFAEnabled", er_secret.Envelope!.TFAEnabled)
.WithData("HasPhoneNumber", er_secret.HasPhoneNumber)
.WithData("SenderEmail", er_secret.Envelope.User!.Email)
.WithData("EnvelopeTitle", er_secret.Envelope.Title)
.WithData("ErrorMessage", _localizer.WrongAccessCode());
}
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, EnvelopeStatus.AccessCodeCorrect);
//check if the user has phone is added
if (er_secret.Envelope!.TFAEnabled)
{
var rcv = er_secret.Receiver;
if (rcv.TotpSecretkey is null)
{
rcv.TotpSecretkey = _authenticator.GenerateTotpSecretKey();
await _rcvService.UpdateAsync(rcv);
}
await HttpContext.SignInEnvelopeAsync(er_secret, ReceiverRole.PreAuth);
return await TFAViewAsync(auth.UserSelectSMS, er_secret, envelopeReceiverId);
}
return null;
}
[NonAction]
private async Task<IActionResult?> HandleSmsAsync(Auth auth, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
{
if (er_secret.Receiver!.TotpSecretkey is null)
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
if (!User.IsInRole(ReceiverRole.PreAuth) || !_envSmsHandler.VerifyTotp(auth.SmsCode!, er_secret.Receiver.TotpSecretkey))
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
return await TFAViewAsync(viaSms: true, er_secret, envelopeReceiverId);
}
return null;
}
[NonAction]
private async Task<IActionResult?> HandleAuthenticatorAsync(Auth auth, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
{
if (er_secret.Receiver!.TotpSecretkey is null)
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
if (!User.IsInRole(ReceiverRole.PreAuth) || !_authenticator.VerifyTotp(auth.AuthenticatorCode!, er_secret.Receiver.TotpSecretkey, window: VerificationWindow.RfcSpecifiedNetworkDelay))
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
return await TFAViewAsync(viaSms: false, er_secret, envelopeReceiverId);
}
return null;
}
#endregion
[NonAction]
[Obsolete("Use MediatR")]
public async Task<IActionResult> EnvelopeReceiverReadOnly([FromRoute] long readOnlyId)
{
var erro_res = await _readOnlyService.ReadByIdAsync(readOnlyId);
if (erro_res.IsFailed)
{
_logger.LogNotice(erro_res.Notices);
return this.ViewInnerServiceError();
}
var erro = erro_res.Data;
if (DateTime.Now > erro.DateValid)
return View("EnvelopeExpired");
var erRes = await _envRcvService.ReadByUuidSignatureAsync(uuid: erro.Envelope!.Uuid, erro.Receiver!.Signature);
if (erRes.IsFailed)
{
_logger.LogNotice(erRes.Notices);
return this.ViewEnvelopeNotFound();
}
var er = erRes.Data;
var envelopeKey = (er.Envelope!.Uuid, er.Receiver!.Signature).ToEnvelopeKey();
//TODO: implement multi-threading to history process (Task)
var hist_res = await _historyService.RecordAsync((int)erro.EnvelopeId, erro.AddedWho, EnvelopeStatus.EnvelopeViewed);
if (hist_res.IsFailed)
{
_logger.LogError(
"Although the envelope was sent as read-only, the EnvelopeShared hisotry could not be saved. ReadOnly-Id: {readOnlyKey}\nEnvelope Receiver:\n{envelopeReceiver}",
readOnlyId, JsonConvert.SerializeObject(er));
_logger.LogNotice(hist_res.Notices);
}
if (er.Envelope.Documents?.FirstOrDefault() is DocumentDto doc && doc.ByteData is not null)
{
ViewData["DocumentBytes"] = doc.ByteData;
ViewData["EnvelopeKey"] = envelopeKey;
ViewData["IsReadOnly"] = true;
ViewData["ReadOnly"] = erro;
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
return View("ShowEnvelope", er);
}
else
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeKey, message: "No document byte-data was found in ENVELOPE_DOCUMENT table.");
return this.ViewDocumentNotFound();
}
}
}

View File

@@ -1,60 +1,22 @@
using DigitalData.Core.Abstraction.Application.DTO; using EnvelopeGenerator.Application.Resources;
using DigitalData.Core.API;
using DigitalData.Core.Client;
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
using EnvelopeGenerator.Application.Resources;
using EnvelopeGenerator.Web.Extensions; using EnvelopeGenerator.Web.Extensions;
using EnvelopeGenerator.Web.Models; using EnvelopeGenerator.Web.Models;
using MediatR;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Newtonsoft.Json;
using OtpNet;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Interfaces.Services;
namespace EnvelopeGenerator.Web.Controllers; namespace EnvelopeGenerator.Web.Controllers;
[Route("Envelope")] [Route("/")]
public class HomeController : ViewControllerBase public class HomeController : ViewControllerBase
{ {
[Obsolete("Use MediatR")]
private readonly IEnvelopeReceiverService _envRcvService;
[Obsolete("Use MediatR")]
private readonly IEnvelopeHistoryService _historyService;
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
[Obsolete("Use MediatR")]
private readonly IEnvelopeMailService _mailService;
[Obsolete("Use MediatR")]
private readonly IEnvelopeReceiverReadOnlyService _readOnlyService;
private readonly IAuthenticator _authenticator;
[Obsolete("Use MediatR")]
private readonly IReceiverService _rcvService;
private readonly IEnvelopeSmsHandler _envSmsHandler;
private readonly IMediator _mediator; public HomeController(IConfiguration configuration, ILogger<HomeController> logger, Cultures cultures, IStringLocalizer<Resource> localizer) : base(logger, cultures, localizer)
[Obsolete("Use MediatR")]
public HomeController(ILogger<HomeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration, Cultures cultures, IEnvelopeMailService envelopeMailService, IEnvelopeReceiverReadOnlyService readOnlyService, IAuthenticator authenticator, IReceiverService receiverService, IEnvelopeSmsHandler envelopeSmsService, IMediator mediator) : base(logger, cultures, localizer)
{ {
_envRcvService = envelopeReceiverService;
_historyService = historyService;
_configuration = configuration; _configuration = configuration;
_mailService = envelopeMailService;
_readOnlyService = readOnlyService;
_authenticator = authenticator;
_rcvService = receiverService;
_envSmsHandler = envelopeSmsService;
_mediator = mediator;
} }
[HttpGet("/")] [HttpGet]
public IActionResult Main() public IActionResult Main()
{ {
return View(new MainViewModel() return View(new MainViewModel()
@@ -63,367 +25,6 @@ public class HomeController : ViewControllerBase
}); });
} }
[HttpGet("{envelopeReceiverId}")]
[Obsolete("Use MediatR")]
public async Task<IActionResult> MainAsync([FromRoute] string envelopeReceiverId)
{
try
{
if (!envelopeReceiverId.TryDecode(out var decoded))
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
return this.ViewDocumentNotFound();
}
if(decoded.GetEncodeType() == EncodeType.EnvelopeReceiverReadOnly)
return await EnvelopeReceiverReadOnly(decoded.ParseReadOnlyId());
ViewData["EnvelopeKey"] = envelopeReceiverId;
var er = await _mediator.ReadEnvelopeReceiverAsync(envelopeReceiverId);
if (er is null)
return this.ViewEnvelopeNotFound();
bool accessCodeAlreadyRequested = await _historyService.AccessCodeAlreadyRequested(envelopeId: er.Envelope!.Id, userReference: er.Receiver!.EmailAddress);
if (!accessCodeAlreadyRequested)
{
await _historyService.RecordAsync(er.EnvelopeId, er.Receiver.EmailAddress, EnvelopeStatus.AccessCodeRequested);
var mailRes = await _mailService.SendAccessCodeAsync(envelopeReceiverDto: er);
if (mailRes.IsFailed)
{
_logger.LogNotice(mailRes);
return this.ViewAccessCodeNotSent();
}
}
return Redirect($"{envelopeReceiverId}/Locked");
}
catch(Exception ex)
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception:ex, message: _localizer.UnexpectedError());
return this.ViewInnerServiceError();
}
}
[HttpGet("{envelopeReceiverId}/Locked")]
[Obsolete("Use DigitalData.Core.Exceptions and .Middleware")]
public async Task<IActionResult> EnvelopeLocked([FromRoute] string envelopeReceiverId, CancellationToken cancel)
{
try
{
var er = await _mediator.ReadEnvelopeReceiverAsync(envelopeReceiverId, cancel);
if (er is null)
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
return this.ViewEnvelopeNotFound();
}
if (User.IsInRole(ReceiverRole.FullyAuth))
return await CreateShowEnvelopeView(envelopeReceiverId, er);
else
{
ViewData["EnvelopeKey"] = envelopeReceiverId;
ViewData["TFAEnabled"] = er.Envelope!.TFAEnabled;
ViewData["HasPhoneNumber"] = er.HasPhoneNumber;
ViewData["SenderEmail"] = er.Envelope.User!.Email;
ViewData["EnvelopeTitle"] = er.Envelope.Title;
return View();
}
}
catch(Exception ex)
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception: ex);
return this.ViewInnerServiceError();
}
}
[Obsolete("Use MediatR")]
private async Task<IActionResult> CreateShowEnvelopeView(string envelopeReceiverId, EnvelopeReceiverDto er)
{
try
{
ViewData["EnvelopeKey"] = envelopeReceiverId;
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
if (uuid is null || signature is null)
{
_logger.LogEnvelopeError(uuid: uuid, signature: signature, message: _localizer.WrongEnvelopeReceiverId());
return Unauthorized();
}
_logger.LogInformation("Envelope UUID: [{uuid}]\nReceiver Signature: [{signature}]", uuid, signature);
//check rejection
var rejRcvrs = await _historyService.ReadRejectingReceivers(er.Envelope!.Id);
if (rejRcvrs.Any())
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
ViewBag.IsExt = !rejRcvrs.Contains(er.Receiver); //external if the current user is not rejected
return View("EnvelopeRejected", er);
}
//check if it has already signed
if (await _historyService.IsSigned(envelopeId: er.Envelope!.Id, userReference: er.Receiver!.EmailAddress))
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return View("EnvelopeSigned");
}
if (er.Envelope.Documents?.FirstOrDefault() is DocumentDto doc && doc.ByteData is not null)
{
ViewData["DocumentBytes"] = doc.ByteData;
}
else
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, message: "No document byte-data was found in ENVELOPE_DOCUMENT table.");
return this.ViewDocumentNotFound();
}
await HttpContext.SignInEnvelopeAsync(er, ReceiverRole.FullyAuth);
//add PSPDFKit licence key
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
return View("ShowEnvelope", er);
}
catch (Exception ex)
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception: ex);
return this.ViewInnerServiceError();
}
}
#region TFA Views
[NonAction]
private async Task<IActionResult> TFAViewAsync(bool viaSms, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
{
if (viaSms)
{
var (smsRes, expiration) = await _envSmsHandler.SendTotpAsync(er_secret);
ViewData["EnvelopeKey"] = envelopeReceiverId;
ViewData["TFAEnabled"] = er_secret.Envelope!.TFAEnabled;
ViewData["HasPhoneNumber"] = er_secret.HasPhoneNumber;
ViewData["SenderEmail"] = er_secret.Envelope.User!.Email;
ViewData["EnvelopeTitle"] = er_secret.Envelope.Title;
if (smsRes?.Failed ?? false)
{
var res_json = JsonConvert.SerializeObject(smsRes);
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, message: $"An unexpected error occurred while sending an SMS code. Response: ${res_json}");
return this.ViewInnerServiceError();
}
return View("EnvelopeLocked").WithData("CodeType", "smsCode").WithData("SmsExpiration", expiration);
}
else
{
return View("EnvelopeLocked")
.WithData("CodeType", "authenticatorCode")
.WithData("TfaRegDeadline", er_secret.Receiver?.TfaRegDeadline);
}
}
[Obsolete("Use MediatR")]
[NonAction]
private async Task<IActionResult?> HandleAccessCodeAsync(Auth auth, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
{
//check the access code verification
if (er_secret.AccessCode != auth.AccessCode)
{
//EnvelopeStatusQuery.AccessCodeIncorrect
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, EnvelopeStatus.AccessCodeIncorrect);
Response.StatusCode = StatusCodes.Status401Unauthorized;
return View("EnvelopeLocked")
.WithData("EnvelopeKey", envelopeReceiverId)
.WithData("TFAEnabled", er_secret.Envelope!.TFAEnabled)
.WithData("HasPhoneNumber", er_secret.HasPhoneNumber)
.WithData("SenderEmail", er_secret.Envelope.User!.Email)
.WithData("EnvelopeTitle", er_secret.Envelope.Title)
.WithData("ErrorMessage", _localizer.WrongAccessCode());
}
await _historyService.RecordAsync(er_secret.EnvelopeId, er_secret.Receiver!.EmailAddress, EnvelopeStatus.AccessCodeCorrect);
//check if the user has phone is added
if (er_secret.Envelope!.TFAEnabled)
{
var rcv = er_secret.Receiver;
if (rcv.TotpSecretkey is null)
{
rcv.TotpSecretkey = _authenticator.GenerateTotpSecretKey();
await _rcvService.UpdateAsync(rcv);
}
await HttpContext.SignInEnvelopeAsync(er_secret, ReceiverRole.PreAuth);
return await TFAViewAsync(auth.UserSelectSMS, er_secret, envelopeReceiverId);
}
return null;
}
[NonAction]
private async Task<IActionResult?> HandleSmsAsync(Auth auth, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
{
if (er_secret.Receiver!.TotpSecretkey is null)
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
if (!User.IsInRole(ReceiverRole.PreAuth) || !_envSmsHandler.VerifyTotp(auth.SmsCode!, er_secret.Receiver.TotpSecretkey))
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
return await TFAViewAsync(viaSms: true, er_secret, envelopeReceiverId);
}
return null;
}
[NonAction]
private async Task<IActionResult?> HandleAuthenticatorAsync(Auth auth, EnvelopeReceiverSecretDto er_secret, string envelopeReceiverId)
{
if (er_secret.Receiver!.TotpSecretkey is null)
throw new InvalidOperationException($"TotpSecretkey of DTO cannot validate without TotpSecretkey. Dto: {JsonConvert.SerializeObject(er_secret)}");
if (!User.IsInRole(ReceiverRole.PreAuth) || !_authenticator.VerifyTotp(auth.AuthenticatorCode!, er_secret.Receiver.TotpSecretkey, window: VerificationWindow.RfcSpecifiedNetworkDelay))
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
ViewData["ErrorMessage"] = _localizer.WrongAccessCode();
return await TFAViewAsync(viaSms: false, er_secret, envelopeReceiverId);
}
return null;
}
#endregion
[HttpPost("{envelopeReceiverId}/Locked")]
[Obsolete("Use MediatR")]
public async Task<IActionResult> LogInEnvelope([FromRoute] string envelopeReceiverId, [FromForm] Auth auth)
{
try
{
ViewData["EnvelopeKey"] = envelopeReceiverId;
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
if (uuid is null || signature is null)
{
_logger.LogEnvelopeError(uuid: uuid, signature: signature, message: _localizer.WrongEnvelopeReceiverId());
return Unauthorized();
}
_logger.LogInformation("Envelope UUID: [{uuid}]\nReceiver Signature: [{signature}]", uuid, signature);
var er_secret_res = await _envRcvService.ReadWithSecretByUuidSignatureAsync(uuid: uuid, signature: signature);
if (er_secret_res.IsFailed)
{
_logger.LogNotice(er_secret_res.Notices);
return this.ViewEnvelopeNotFound();
}
var er_secret = er_secret_res.Data;
// show envelope if already logged in
if (User.IsInRole(ReceiverRole.FullyAuth))
return await CreateShowEnvelopeView(envelopeReceiverId, er_secret);
if (auth.HasMulti)
{
return Unauthorized();
}
else if (auth.HasAccessCode)
{
if (await HandleAccessCodeAsync(auth, er_secret, envelopeReceiverId) is IActionResult acView)
return acView;
}
else if (auth.HasSmsCode)
{
if (await HandleSmsAsync(auth, er_secret, envelopeReceiverId) is IActionResult smsView)
return smsView;
}
else if (auth.HasAuthenticatorCode)
{
if(await HandleAuthenticatorAsync(auth, er_secret, envelopeReceiverId) is IActionResult aView)
return aView;
}
else
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
return View("EnvelopeLocked")
.WithData("EnvelopeKey", envelopeReceiverId)
.WithData("TFAEnabled", er_secret.Envelope!.TFAEnabled)
.WithData("HasPhoneNumber", er_secret.HasPhoneNumber)
.WithData("SenderEmail", er_secret.Envelope.User!.Email)
.WithData("EnvelopeTitle", er_secret.Envelope.Title)
.WithData("ErrorMessage", _localizer.WrongEnvelopeReceiverId());
}
await HttpContext.SignInEnvelopeAsync(er_secret, ReceiverRole.FullyAuth);
return await CreateShowEnvelopeView(envelopeReceiverId, er_secret);
}
catch (Exception ex)
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception: ex);
return this.ViewInnerServiceError();
}
}
[NonAction]
[Obsolete("Use MediatR")]
public async Task<IActionResult> EnvelopeReceiverReadOnly([FromRoute] long readOnlyId)
{
var erro_res = await _readOnlyService.ReadByIdAsync(readOnlyId);
if (erro_res.IsFailed)
{
_logger.LogNotice(erro_res.Notices);
return this.ViewInnerServiceError();
}
var erro = erro_res.Data;
if (DateTime.Now > erro.DateValid)
return View("EnvelopeExpired");
return await _envRcvService.ReadByUuidSignatureAsync(uuid: erro.Envelope!.Uuid, erro.Receiver!.Signature).ThenAsync(
SuccessAsync: async er =>
{
var envelopeKey = (er.Envelope!.Uuid, er.Receiver!.Signature).ToEnvelopeKey();
//TODO: implement multi-threading to history process (Task)
var hist_res = await _historyService.RecordAsync((int)erro.EnvelopeId, erro.AddedWho, EnvelopeStatus.EnvelopeViewed);
if (hist_res.IsFailed)
{
_logger.LogError(
"Although the envelope was sent as read-only, the EnvelopeShared hisotry could not be saved. ReadOnly-Id: {readOnlyKey}\nEnvelope Receiver:\n{envelopeReceiver}",
readOnlyId, JsonConvert.SerializeObject(er));
_logger.LogNotice(hist_res.Notices);
}
if (er.Envelope.Documents?.FirstOrDefault() is DocumentDto doc && doc.ByteData is not null)
{
ViewData["DocumentBytes"] = doc.ByteData;
ViewData["EnvelopeKey"] = envelopeKey;
ViewData["IsReadOnly"] = true;
ViewData["ReadOnly"] = erro;
ViewData["PSPDFKitLicenseKey"] = _configuration["PSPDFKitLicenseKey"];
return View("ShowEnvelope", er);
}
else
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeKey, message: "No document byte-data was found in ENVELOPE_DOCUMENT table.");
return this.ViewDocumentNotFound();
}
},
Fail: (messages, notices) =>
{
_logger.LogNotice(notices);
return this.ViewEnvelopeNotFound();
});
}
[HttpGet("Error404")] [HttpGet("Error404")]
public IActionResult Error404() => this.ViewError404(); public IActionResult Error404() => this.ViewError404();
} }

View File

@@ -0,0 +1,35 @@
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Notifications.RemoveSignature;
using MediatR;
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Web.Controllers.Test;
[Route("api/[controller]")]
[ApiController]
public class TestAnnotationController : ControllerBase
{
private readonly IMediator _mediator;
public TestAnnotationController(IMediator mediator)
{
_mediator = mediator;
}
[HttpDelete("{envelopeKey}")]
public async Task<IActionResult> Delete([FromRoute] string envelopeKey)
{
if (envelopeKey.GetEnvelopeUuid() is not string uuid)
return BadRequest();
if (envelopeKey.GetReceiverSignature() is not string signature)
return BadRequest();
await _mediator.Publish(new RemoveSignatureNotification()
{
EnvelopeUuid = uuid,
ReceiverSignature = signature
});
return Ok();
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<PackageId>EnvelopeGenerator.Web</PackageId> <PackageId>EnvelopeGenerator.Web</PackageId>
@@ -12,9 +12,9 @@
<PackageTags>digital data envelope generator web</PackageTags> <PackageTags>digital data envelope generator web</PackageTags>
<Description>EnvelopeGenerator.Web is an ASP.NET MVC application developed to manage signing processes. It uses Entity Framework Core (EF Core) for database operations. The user interface for signing processes is developed with Razor View Engine (.cshtml files) and JavaScript under wwwroot, integrated with PSPDFKit. This integration allows users to view and sign documents seamlessly.</Description> <Description>EnvelopeGenerator.Web is an ASP.NET MVC application developed to manage signing processes. It uses Entity Framework Core (EF Core) for database operations. The user interface for signing processes is developed with Razor View Engine (.cshtml files) and JavaScript under wwwroot, integrated with PSPDFKit. This integration allows users to view and sign documents seamlessly.</Description>
<ApplicationIcon>Assets\icon.ico</ApplicationIcon> <ApplicationIcon>Assets\icon.ico</ApplicationIcon>
<Version>3.3.0</Version> <Version>3.7.0</Version>
<AssemblyVersion>3.3.0</AssemblyVersion> <AssemblyVersion>3.7.0</AssemblyVersion>
<FileVersion>3.3.0</FileVersion> <FileVersion>3.7.0</FileVersion>
<Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright> <Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright>
</PropertyGroup> </PropertyGroup>
@@ -2094,20 +2094,13 @@
<None Include="wwwroot\lib\bootstrap-icons\icons\zoom-out.svg" /> <None Include="wwwroot\lib\bootstrap-icons\icons\zoom-out.svg" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="BuildBundlerMinifier2022" Version="2.9.9" /> <PackageReference Include="BuildBundlerMinifier2022" Version="2.9.9" />
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" /> <PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" /> <PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" /> <PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
<PackageReference Include="HtmlSanitizer" Version="8.0.865" /> <PackageReference Include="HtmlSanitizer" Version="8.0.865" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.20" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.20" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="7.0.20" /> <PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="7.0.20" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.2.5" /> <PackageReference Include="NLog" Version="5.2.5" />
@@ -2126,12 +2119,84 @@
<PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" /> <PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" /> <PackageReference Include="BuildBundlerMinifier2022" Version="2.9.9" />
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" /> <PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
<PackageReference Include="HtmlSanitizer" Version="8.0.865" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="7.0.20" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.2.5" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.0" />
<PackageReference Include="Quartz" Version="3.8.0" />
<PackageReference Include="Quartz.AspNetCore" Version="3.8.0" />
<PackageReference Include="Quartz.Plugins" Version="3.8.0" />
<PackageReference Include="Quartz.Serialization.Json" Version="3.8.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.0.1" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.1" />
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="7.0.0" />
<PackageReference Include="System.DirectoryServices" Version="8.0.0" />
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="8.0.1" />
<PackageReference Include="System.DirectoryServices.Protocols" Version="8.0.1" />
<PackageReference Include="System.Drawing.Common" Version="8.0.16" />
<PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="BuildBundlerMinifier2022" Version="2.9.9" />
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
<PackageReference Include="HtmlSanitizer" Version="8.0.865" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="7.0.20" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.2.5" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.0" />
<PackageReference Include="Quartz" Version="3.8.0" />
<PackageReference Include="Quartz.AspNetCore" Version="3.8.0" />
<PackageReference Include="Quartz.Plugins" Version="3.8.0" />
<PackageReference Include="Quartz.Serialization.Json" Version="3.8.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.0.1" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="9.0.11" />
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="7.0.0" />
<PackageReference Include="System.DirectoryServices" Version="9.0.4" />
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="9.0.4" />
<PackageReference Include="System.DirectoryServices.Protocols" Version="9.0.4" />
<PackageReference Include="System.Drawing.Common" Version="9.0.5" />
<PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Logging.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.GSM.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Security.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.UI.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Database.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Mail.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.PSPDFKitLicense.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.json"> <Content Update="appsettings.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content> </Content>

View File

@@ -34,6 +34,13 @@ try
var config = builder.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));
var allowedOrigins = config.GetSection("AllowedOrigins").Get<string[]>() ?? var allowedOrigins = config.GetSection("AllowedOrigins").Get<string[]>() ??
throw new InvalidOperationException("AllowedOrigins section is missing in the configuration."); throw new InvalidOperationException("AllowedOrigins section is missing in the configuration.");
@@ -96,14 +103,19 @@ try
// Add envelope generator services // Add envelope generator services
#pragma warning disable CS0618 // Type or member is obsolete #pragma warning disable CS0618 // Type or member is obsolete
builder.Services.AddEnvelopeGeneratorInfrastructureServices((provider, options) => builder.Services.AddEnvelopeGeneratorInfrastructureServices(
{ opt =>
var logger = provider.GetRequiredService<ILogger<EGDbContext>>(); {
options.UseSqlServer(connStr) opt.AddDbTriggerParams(config);
.LogTo(log => logger.LogInformation("{log}", log), Microsoft.Extensions.Logging.LogLevel.Trace) opt.AddDbContext((provider, options) =>
.EnableSensitiveDataLogging() {
.EnableDetailedErrors(); var logger = provider.GetRequiredService<ILogger<EGDbContext>>();
}); options.UseSqlServer(connStr)
.LogTo(log => logger.LogInformation("{log}", log), Microsoft.Extensions.Logging.LogLevel.Trace)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
});
});
#pragma warning restore CS0618 // Type or member is obsolete #pragma warning restore CS0618 // Type or member is obsolete
#pragma warning disable CS0618 // Type or member is obsolete #pragma warning disable CS0618 // Type or member is obsolete
@@ -136,7 +148,7 @@ try
{ {
// Dynamically calculate the redirection path, for example: // Dynamically calculate the redirection path, for example:
var envelopeReceiverId = context.HttpContext.Request.RouteValues["envelopeReceiverId"]; var envelopeReceiverId = context.HttpContext.Request.RouteValues["envelopeReceiverId"];
context.RedirectUri = $"/EnvelopeKey/{envelopeReceiverId}/Locked"; context.RedirectUri = $"/EnvelopeKey/{envelopeReceiverId}";
context.Response.Redirect(context.RedirectUri); context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask; return Task.CompletedTask;
@@ -145,7 +157,7 @@ try
{ {
// Apply a similar redirection logic for logout // Apply a similar redirection logic for logout
var envelopeReceiverId = context.HttpContext.Request.RouteValues["envelopeReceiverId"]; var envelopeReceiverId = context.HttpContext.Request.RouteValues["envelopeReceiverId"];
context.RedirectUri = $"/EnvelopeKey/{envelopeReceiverId}/Success"; context.RedirectUri = $"/EnvelopeKey/{envelopeReceiverId}";
context.Response.Redirect(context.RedirectUri); context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask; return Task.CompletedTask;

View File

@@ -11,12 +11,10 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish> <LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data> <ExcludeApp_Data>false</ExcludeApp_Data>
<ProjectGuid>5e0e17c0-ff5a-4246-bf87-1add85376a27</ProjectGuid> <ProjectGuid>5e0e17c0-ff5a-4246-bf87-1add85376a27</ProjectGuid>
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\signFLOW\Web\net9\win64\$(Version)\EnvelopeGenerator.Web.zip</DesktopBuildPackageLocation> <DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\signFLOW\Web\net8\$(Version)\EnvelopeGenerator.Web.zip</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile> <PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath>EnvelopeGenerator</DeployIisAppPath> <DeployIisAppPath>EnvelopeGenerator</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId> <_TargetId>IISWebDeployPackage</_TargetId>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -96,6 +96,8 @@
</details> </details>
</section> </section>
</div> </div>
@if (smsExpiration is not null)
{
<script nonce="@nonce"> <script nonce="@nonce">
var expiration = new Date(@Html.Raw(JsonConvert.SerializeObject(smsExpiration))); var expiration = new Date(@Html.Raw(JsonConvert.SerializeObject(smsExpiration)));
@@ -122,3 +124,4 @@
element.textContent = remainingTime; element.textContent = remainingTime;
}, 1000); }, 1000);
</script> </script>
}

View File

@@ -79,7 +79,7 @@
} }
else else
{ {
<h6>@($"{@envelope?.Message}")</h6> <div class="markdown">@(envelope?.Message)</div>
} }
<p> <p>
<small class="text-body-secondary"> <small class="text-body-secondary">

View File

@@ -20,9 +20,8 @@
<link rel="stylesheet" href="~/css/card.min.css" asp-append-version="true" /> <link rel="stylesheet" href="~/css/card.min.css" asp-append-version="true" />
<link rel="stylesheet" href="~/EnvelopeGenerator.Web.styles.css" asp-append-version="true" /> <link rel="stylesheet" href="~/EnvelopeGenerator.Web.styles.css" asp-append-version="true" />
<link rel="stylesheet" href="~/lib/flag-icons-main/css/flag-icons.min.css" asp-append-version="true" /> <link rel="stylesheet" href="~/lib/flag-icons-main/css/flag-icons.min.css" asp-append-version="true" />
<link rel="stylesheet" href="~/css/google-font.css" asp-append-version="true" />
<link rel="stylesheet" href="~/lib/alertifyjs/css/alertify.min.css" /> <link rel="stylesheet" href="~/lib/alertifyjs/css/alertify.min.css" />
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" />
<link rel="stylesheet" href="~/lib/bootstrap-icons/font/bootstrap-icons.min.css"> <link rel="stylesheet" href="~/lib/bootstrap-icons/font/bootstrap-icons.min.css">
</head> </head>
<body> <body>
@@ -50,13 +49,14 @@
<script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script> <script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="~/lib/sweetalert2/sweetalert2.min.js"></script> <script src="~/lib/sweetalert2/sweetalert2.min.js"></script>
<script src="~/lib/alertifyjs/alertify.min.js"></script> <script src="~/lib/alertifyjs/alertify.min.js"></script>
<script src="~/lib/marked/marked.umd.min.js"></script>
<script src="~/js/lazy.min.js" asp-append-version="true"></script>
<script src="~/js/ui.min.js" asp-append-version="true"></script> <script src="~/js/ui.min.js" asp-append-version="true"></script>
<script src="~/js/annotation.js" asp-append-version="true"></script> <script src="~/js/annotation.js" asp-append-version="true"></script>
<script src="~/js/network.min.js" asp-append-version="true"></script>
<script src="~/js/app.min.js" asp-append-version="true"></script> <script src="~/js/app.min.js" asp-append-version="true"></script>
<script src="~/lib/pspdfkit/dist-2024.3.2/pspdfkit.js"></script> <script src="~/lib/pspdfkit/dist-2024.3.2/pspdfkit.js"></script>
<script src="~/js/util.min.js" asp-append-version="true"></script> <script src="~/js/util.min.js" asp-append-version="true"></script>
<script src="~/js/api-service.min.js" asp-append-version="true"></script> <script src="~/js/envelope-api.min.js" asp-append-version="true"></script>
<script src="~/lib/typed.js@2.1.0/dist/typed.umd.js"></script> <script src="~/lib/typed.js@2.1.0/dist/typed.umd.js"></script>
@await RenderSectionAsync("Scripts", required: false) @await RenderSectionAsync("Scripts", required: false)
@{ @{
@@ -96,5 +96,6 @@
</div> </div>
<a href="/privacy-policy.@(_localizer.Culture()).html" target="_blank">@_localizer.Privacy()</a> <a href="/privacy-policy.@(_localizer.Culture()).html" target="_blank">@_localizer.Privacy()</a>
</footer> </footer>
<script src="~/js/markdown.min.js" asp-append-version="true"></script>
</body> </body>
</html> </html>

View File

@@ -0,0 +1,15 @@
{
"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" ]
}
}

View File

@@ -0,0 +1,10 @@
{
"GtxMessagingParams": {
"Uri": "https://rest.gtx-messaging.net",
"Path": "smsc/sendsms/f566f7e5-bdf2-4a9a-bf52-ed88215a432e/json",
"Headers": {},
"QueryParams": {
"from": "signFlow"
}
}
}

View File

@@ -0,0 +1,61 @@
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Hosting.Diagnostics": "Warning"
}
},
"NLog": {
"throwConfigExceptions": true,
"variables": {
"logDirectory": "E:\\LogFiles\\Digital Data\\signFlow",
"logFileNamePrefix": "${shortdate}-ECM.EnvelopeGenerator.Web"
},
"targets": {
"infoLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log",
"maxArchiveDays": 30
},
"warningLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Warning.log",
"maxArchiveDays": 30
},
"errorLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log",
"maxArchiveDays": 30
},
"criticalLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log",
"maxArchiveDays": 30
}
},
"rules": [
{
"logger": "*",
"level": "Info",
"writeTo": "infoLogs"
},
{
"logger": "*",
"level": "Warn",
"writeTo": "warningLogs"
},
{
"logger": "*",
"level": "Error",
"writeTo": "errorLogs"
},
{
"logger": "*",
"level": "Fatal",
"writeTo": "criticalLogs"
}
]
}
}

View File

@@ -0,0 +1,15 @@
{
"DispatcherParams": {
"SendingProfile": 1,
"AddedWho": "DDEnvelopGenerator",
"ReminderTypeId": 202377,
"EmailAttmt1": null
},
"MailParams": {
"Placeholders": {
"[NAME_PORTAL]": "signFlow",
"[SIGNATURE_TYPE]": "signieren",
"[REASON]": ""
}
}
}

View File

@@ -0,0 +1,3 @@
{
"PSPDFKitLicenseKey": "SXCtGGY9XA-31OGUXQK-r7c6AkdLGPm2ljuyDr1qu0kkhLvydg-Do-fxpNUF4Rq3fS_xAnZRNFRHbXpE6sQ2BMcCSVTcXVJO6tPviexjpiT-HnrDEySlUERJnnvh-tmeOWprxS6BySPnSILkmaVQtUfOIUS-cUbvvEYHTvQBKbSF8di4XHQFyfv49ihr51axm3NVV3AXwh2EiKL5C5XdqBZ4sQ4O7vXBjM2zvxdPxlxdcNYmiU83uAzw7B83O_jubPzya4CdUHh_YH7Nlp2gP56MeG1Sw2JhMtfG3Rj14Sg4ctaeL9p6AEWca5dDjJ2li5tFIV2fQSsw6A_cowLu0gtMm5i8IfJXeIcQbMC2-0wGv1oe9hZYJvFMdzhTM_FiejM0agemxt3lJyzuyP8zbBSOgp7Si6A85krLWPZptyZBTG7pp7IHboUHfPMxCXqi-zMsqewOJtQBE2mjntU-lPryKnssOpMPfswwQX7QSkJYV5EMqNmEhQX6mEkp2wcqFzMC7bJQew1aO4pOpvChUaMvb1vgRek0HxLag0nwQYX2YrYGh7F_xXJs-8HNwJe8H0-eW4x4faayCgM5rB5772CCCsD9ThZcvXFrjNHHLGJ8WuBUFm6LArvSfFQdii_7j-_sqHMpeKZt26NFgivj1A=="
}

View File

@@ -0,0 +1,17 @@
{
"Content-Security-Policy": [ // The first format parameter {0} will be replaced by the nonce value.
"default-src 'self'",
"script-src 'self' 'nonce-{0}' 'unsafe-eval'",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com:*",
"img-src 'self' data: https: blob:",
"font-src 'self' https://fonts.gstatic.com:*",
"connect-src 'self' https://nominatim.openstreetmap.org:* http://localhost:* https://localhost:* ws://localhost:* wss://localhost:* blob:",
"frame-src 'self'",
"media-src 'self'",
"object-src 'self'"
],
"AllowedOrigins": [ "https://localhost:7202", "https://digitale.unterschrift.wisag.de/" ],
"TFARegParams": {
"TimeLimit": "90.00:00:00"
}
}

View File

@@ -0,0 +1,121 @@
{
"AnnotationParams": {
"Background": {
"Margin": 0.20,
"BackgroundColor": {
"R": 222,
"G": 220,
"B": 215
},
"BorderColor": {
"R": 204,
"G": 202,
"B": 198
},
"BorderStyle": "underline",
"BorderWidth": 4
},
"DefaultAnnotation": {
"Width": 1,
"Height": 0.5,
"MarginTop": 1
},
"Annotations": [
{
"Name": "Signature",
"MarginTop": 0
},
{
"Name": "PositionLabel",
"VerBoundAnnotName": "Signature",
"WidthRatio": 1.2,
"HeightRatio": 0.5,
"MarginTopRatio": 0.22
},
{
"Name": "Position",
"VerBoundAnnotName": "PositionLabel",
"WidthRatio": 1.2,
"HeightRatio": 0.5,
"MarginTopRatio": -0.05
},
{
"Name": "CityLabel",
"VerBoundAnnotName": "Position",
"WidthRatio": 1.2,
"HeightRatio": 0.5,
"MarginTopRatio": 0.05
},
{
"Name": "City",
"VerBoundAnnotName": "CityLabel",
"WidthRatio": 1.2,
"HeightRatio": 0.5,
"MarginTopRatio": -0.05
},
{
"Name": "DateLabel",
"VerBoundAnnotName": "City",
"WidthRatio": 1.55,
"HeightRatio": 0.5,
"MarginTopRatio": 0.05
},
{
"Name": "Date",
"VerBoundAnnotName": "DateLabel",
"WidthRatio": 1.55,
"HeightRatio": 0.5,
"MarginTopRatio": -0.1
}
]
},
"ContactLink": {
"Label": "Kontakt",
"Href": "https://digitaldata.works/",
"HrefLang": "de",
"Target": "_blank",
"Title": "Digital Data GmbH"
},
/* Resx naming format is -> Resource.language.resx (eg: Resource.de_DE.resx).
To add a new language, first you should write the required resx file.
first is the default culture name. */
"Cultures": [
{
"Language": "de-DE",
"FIClass": "fi-de"
},
{
"Language": "en-US",
"FIClass": "fi-us"
}
],
"DisableMultiLanguage": false,
"Regexes": [
{
"Pattern": "/^\\p{L}+(?:([\\ \\-\\']|(\\.\\ ))\\p{L}+)*$/u",
"Name": "City",
"Platforms": [ ".NET" ]
},
{
"Pattern": "/^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$/",
"Name": "City",
"Platforms": [ "javascript" ]
}
],
"CustomImages": {
"App": {
"Src": "/img/DD_signFLOW_LOGO.png",
"Classes": {
"Main": "signFlow-logo"
}
},
"Company": {
"Src": "/img/digital_data.svg",
"Classes": {
"Show": "dd-show-logo",
"Locked": "dd-locked-logo"
}
}
},
"MainPageTitle": null
}

View File

@@ -1,234 +1,6 @@
{ {
"DiPMode": false, //Please be careful when enabling Development in Production (DiP) mode. It allows Swagger and test controllers to be enabled in a production environment. "DiPMode": false, //Please be careful when enabling Development in Production (DiP) mode. It allows Swagger and test controllers to be enabled in a production environment.
"EnableSwagger": true, "EnableSwagger": true,
"UseDbMigration": false,
"EnableTestControllers": true, "EnableTestControllers": true,
"DetailedErrors": true, "DetailedErrors": true
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Hosting.Diagnostics": "Warning"
}
},
"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;"
},
"PSPDFKitLicenseKey": "SXCtGGY9XA-31OGUXQK-r7c6AkdLGPm2ljuyDr1qu0kkhLvydg-Do-fxpNUF4Rq3fS_xAnZRNFRHbXpE6sQ2BMcCSVTcXVJO6tPviexjpiT-HnrDEySlUERJnnvh-tmeOWprxS6BySPnSILkmaVQtUfOIUS-cUbvvEYHTvQBKbSF8di4XHQFyfv49ihr51axm3NVV3AXwh2EiKL5C5XdqBZ4sQ4O7vXBjM2zvxdPxlxdcNYmiU83uAzw7B83O_jubPzya4CdUHh_YH7Nlp2gP56MeG1Sw2JhMtfG3Rj14Sg4ctaeL9p6AEWca5dDjJ2li5tFIV2fQSsw6A_cowLu0gtMm5i8IfJXeIcQbMC2-0wGv1oe9hZYJvFMdzhTM_FiejM0agemxt3lJyzuyP8zbBSOgp7Si6A85krLWPZptyZBTG7pp7IHboUHfPMxCXqi-zMsqewOJtQBE2mjntU-lPryKnssOpMPfswwQX7QSkJYV5EMqNmEhQX6mEkp2wcqFzMC7bJQew1aO4pOpvChUaMvb1vgRek0HxLag0nwQYX2YrYGh7F_xXJs-8HNwJe8H0-eW4x4faayCgM5rB5772CCCsD9ThZcvXFrjNHHLGJ8WuBUFm6LArvSfFQdii_7j-_sqHMpeKZt26NFgivj1A==",
"Content-Security-Policy": [ // The first format parameter {0} will be replaced by the nonce value.
"default-src 'self'",
"script-src 'self' 'nonce-{0}' 'unsafe-eval'",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com:*",
"img-src 'self' data: https: blob:",
"font-src 'self' https://fonts.gstatic.com:*",
"connect-src 'self' https://nominatim.openstreetmap.org:* http://localhost:* https://localhost:* ws://localhost:* wss://localhost:* blob:",
"frame-src 'self'",
"media-src 'self'",
"object-src 'self'"
],
"AllowedOrigins": [ "https://localhost:7202", "https://digitale.unterschrift.wisag.de/" ],
"NLog": {
"throwConfigExceptions": true,
"variables": {
"logDirectory": "E:\\LogFiles\\Digital Data\\signFlow",
"logFileNamePrefix": "${shortdate}-ECM.EnvelopeGenerator.Web"
},
"targets": {
"infoLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log",
"maxArchiveDays": 30
},
"warningLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Warning.log",
"maxArchiveDays": 30
},
"errorLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log",
"maxArchiveDays": 30
},
"criticalLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log",
"maxArchiveDays": 30
}
},
"rules": [
{
"logger": "*",
"level": "Info",
"writeTo": "infoLogs"
},
{
"logger": "*",
"level": "Warn",
"writeTo": "warningLogs"
},
{
"logger": "*",
"level": "Error",
"writeTo": "errorLogs"
},
{
"logger": "*",
"level": "Fatal",
"writeTo": "criticalLogs"
}
]
},
"ContactLink": {
"Label": "Kontakt",
"Href": "https://digitaldata.works/",
"HrefLang": "de",
"Target": "_blank",
"Title": "Digital Data GmbH"
},
/* Resx naming format is -> Resource.language.resx (eg: Resource.de_DE.resx).
To add a new language, first you should write the required resx file.
first is the default culture name. */
"Cultures": [
{
"Language": "de-DE",
"FIClass": "fi-de"
},
{
"Language": "en-US",
"FIClass": "fi-us"
}
],
"DisableMultiLanguage": false,
"Regexes": [
{
"Pattern": "/^\\p{L}+(?:([\\ \\-\\']|(\\.\\ ))\\p{L}+)*$/u",
"Name": "City",
"Platforms": [ ".NET" ]
},
{
"Pattern": "/^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$/",
"Name": "City",
"Platforms": [ "javascript" ]
}
],
"CustomImages": {
"App": {
"Src": "/img/DD_signFLOW_LOGO.png",
"Classes": {
"Main": "signFlow-logo"
}
},
"Company": {
"Src": "/img/digital_data.svg",
"Classes": {
"Show": "dd-show-logo",
"Locked": "dd-locked-logo"
}
}
},
"DispatcherParams": {
"SendingProfile": 1,
"AddedWho": "DDEnvelopGenerator",
"ReminderTypeId": 202377,
"EmailAttmt1": null
},
"MailParams": {
"Placeholders": {
"[NAME_PORTAL]": "signFlow",
"[SIGNATURE_TYPE]": "signieren",
"[REASON]": ""
}
},
"GtxMessagingParams": {
"Uri": "https://rest.gtx-messaging.net",
"Path": "smsc/sendsms/f566f7e5-bdf2-4a9a-bf52-ed88215a432e/json",
"Headers": {},
"QueryParams": {
"from": "signFlow"
}
},
"TFARegParams": {
"TimeLimit": "90.00:00:00"
},
"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" ]
},
"MainPageTitle": null,
"AnnotationParams": {
"Background": {
"Margin": 0.20,
"BackgroundColor": {
"R": 222,
"G": 220,
"B": 215
},
"BorderColor": {
"R": 204,
"G": 202,
"B": 198
},
"BorderStyle": "underline",
"BorderWidth": 4
},
"DefaultAnnotation": {
"Width": 1,
"Height": 0.5,
"MarginTop": 1
},
"Annotations": [
{
"Name": "Signature",
"MarginTop": 0
},
{
"Name": "PositionLabel",
"VerBoundAnnotName": "Signature",
"WidthRatio": 1.2,
"HeightRatio": 0.5,
"MarginTopRatio": 0.22
},
{
"Name": "Position",
"VerBoundAnnotName": "PositionLabel",
"WidthRatio": 1.2,
"HeightRatio": 0.5,
"MarginTopRatio": -0.05
},
{
"Name": "CityLabel",
"VerBoundAnnotName": "Position",
"WidthRatio": 1.2,
"HeightRatio": 0.5,
"MarginTopRatio": 0.05
},
{
"Name": "City",
"VerBoundAnnotName": "CityLabel",
"WidthRatio": 1.2,
"HeightRatio": 0.5,
"MarginTopRatio": -0.05
},
{
"Name": "DateLabel",
"VerBoundAnnotName": "City",
"WidthRatio": 1.55,
"HeightRatio": 0.5,
"MarginTopRatio": 0.05
},
{
"Name": "Date",
"VerBoundAnnotName": "DateLabel",
"WidthRatio": 1.55,
"HeightRatio": 0.5,
"MarginTopRatio": -0.1
}
]
}
} }

View File

@@ -6,9 +6,15 @@
] ]
}, },
{ {
"outputFileName": "wwwroot/js/api-service.min.js", "outputFileName": "wwwroot/js/lazy.min.js",
"inputFiles": [ "inputFiles": [
"wwwroot/js/api-service.js" "wwwroot/js/lazy.js"
]
},
{
"outputFileName": "wwwroot/js/envelope-api.min.js",
"inputFiles": [
"wwwroot/js/envelope-api.js"
] ]
}, },
{ {
@@ -23,12 +29,6 @@
"wwwroot/js/event-binder.js" "wwwroot/js/event-binder.js"
] ]
}, },
{
"outputFileName": "wwwroot/js/network.min.js",
"inputFiles": [
"wwwroot/js/network.js"
]
},
{ {
"outputFileName": "wwwroot/js/ui.min.js", "outputFileName": "wwwroot/js/ui.min.js",
"inputFiles": [ "inputFiles": [
@@ -41,6 +41,12 @@
"wwwroot/js/util.js" "wwwroot/js/util.js"
] ]
}, },
{
"outputFileName": "wwwroot/js/markdown.min.js",
"inputFiles": [
"wwwroot/js/markdown.js"
]
},
{ {
"outputFileName": "wwwroot/css/error-space.min.css", "outputFileName": "wwwroot/css/error-space.min.css",
"inputFiles": [ "inputFiles": [

View File

@@ -0,0 +1,23 @@
/* fallback */
@font-face {
font-family: 'Material Symbols Outlined';
font-style: normal;
font-weight: 400;
src: url('/css/fonts/kJF1BvYX7BgnkSrUwT8OhrdQw4oELdPIeeII9v6oDMzByHX9rA6RzaxHMPdY43zj-jCxv3fzvRNU22ZXGJpEpjC_1v-p_4MrImHCIJIZrDCvHOej.woff2') format('woff2');
}
.material-symbols-outlined {
font-family: 'Material Symbols Outlined';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-webkit-font-feature-settings: 'liga';
-webkit-font-smoothing: antialiased;
}

View File

@@ -1,41 +1,16 @@
function generateId(envelopeId, receiverId, elementId, annotationType) {
async function createAnnotations(document, instance) { return `${envelopeId}#${receiverId}#${elementId}#${annotationType}`;
}
async function createAnnotations(document, envelopeId, receiverId) {
const signatures = []; const signatures = [];
for(var element of document.elements) { for (let element of document.elements) {
const annotParams = await getAnnotationParams(element.left, element.top); const annotParams = await getAnnotationParams(element.left, element.top);
const page = element.page - 1 const page = element.page - 1
//background //#region signatures
if(annotParams.background){ const id = generateId(envelopeId, receiverId, element.id, 'signature');
let background = annotParams.background;
const id_background = PSPDFKit.generateInstantId();
const annotation_background = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_background,
pageIndex: page,
formFieldName: id_background,
backgroundColor: background?.backgroundColor ? new PSPDFKit.Color(background.backgroundColor) : null,
blendMode: 'normal',
boundingBox: new PSPDFKit.Geometry.Rect(background),
fontSize: 8,
borderStyle: background.borderStyle,
borderWidth: background.borderWidth,
borderColor: background?.borderColor ? new PSPDFKit.Color(background.borderColor) : null
});
const formFieldBackground = new PSPDFKit.FormFields.ButtonFormField({
name: id_background,
annotationIds: PSPDFKit.Immutable.List([annotation_background.id]),
value: "",
readOnly: false
});
signatures.push(annotation_background)
signatures.push(formFieldBackground)
}
//signatures
const id = PSPDFKit.generateInstantId()
const annotation = new PSPDFKit.Annotations.WidgetAnnotation({ const annotation = new PSPDFKit.Annotations.WidgetAnnotation({
id: id, id: id,
pageIndex: page, pageIndex: page,
@@ -49,9 +24,10 @@ async function createAnnotations(document, instance) {
name: id, name: id,
annotationIds: PSPDFKit.Immutable.List([annotation.id]), annotationIds: PSPDFKit.Immutable.List([annotation.id]),
}) })
//#endregion
//position //#region position
const id_position = PSPDFKit.generateInstantId() const id_position = generateId(envelopeId, receiverId, element.id, 'position');
const annotation_position = new PSPDFKit.Annotations.WidgetAnnotation({ const annotation_position = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_position, id: id_position,
pageIndex: page, pageIndex: page,
@@ -68,9 +44,10 @@ async function createAnnotations(document, instance) {
value: "", value: "",
readOnly: false readOnly: false
}) })
//#endregion
//city //#region city
const id_city = PSPDFKit.generateInstantId() const id_city = generateId(envelopeId, receiverId, element.id, 'city');
const annotation_city = new PSPDFKit.Annotations.WidgetAnnotation({ const annotation_city = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_city, id: id_city,
pageIndex: page, pageIndex: page,
@@ -87,9 +64,10 @@ async function createAnnotations(document, instance) {
value: "", value: "",
readOnly: false readOnly: false
}) })
//#endregion
//date //#region date
const id_date = PSPDFKit.generateInstantId() const id_date = generateId(envelopeId, receiverId, element.id, 'date');
const annotation_date = new PSPDFKit.Annotations.WidgetAnnotation({ const annotation_date = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_date, id: id_date,
pageIndex: page, pageIndex: page,
@@ -110,17 +88,14 @@ async function createAnnotations(document, instance) {
value: detailedCurrentDate(), value: detailedCurrentDate(),
readOnly: true readOnly: true
}) })
//#endregion
this.markFieldAsRequired(formFieldCity); this.markFieldAsRequired(formFieldCity);
this.markFieldAsCity(formFieldCity); this.markFieldAsCity(formFieldCity);
/** //#region date label
* Date, post code and place label part const id_date_label = generateId(envelopeId, receiverId, element.id, 'date_label');
*/
//date label
const id_date_label = PSPDFKit.generateInstantId()
const annotation_date_label = new PSPDFKit.Annotations.WidgetAnnotation({ const annotation_date_label = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_date_label, id: id_date_label,
pageIndex: page, pageIndex: page,
@@ -140,9 +115,10 @@ async function createAnnotations(document, instance) {
value: "Date", value: "Date",
readOnly: true readOnly: true
}) })
//#endregion
//city label //#region city label
const id_city_label = PSPDFKit.generateInstantId() const id_city_label = generateId(envelopeId, receiverId, element.id, 'city_label');
const annotation_city_label = new PSPDFKit.Annotations.WidgetAnnotation({ const annotation_city_label = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_city_label, id: id_city_label,
pageIndex: page, pageIndex: page,
@@ -162,9 +138,10 @@ async function createAnnotations(document, instance) {
readOnly: true, readOnly: true,
color: PSPDFKit.Color.BLACK color: PSPDFKit.Color.BLACK
}) })
//#endregion
//position label //#region position label
const id_position_label = PSPDFKit.generateInstantId() const id_position_label = generateId(envelopeId, receiverId, element.id, 'position_label');
const annotation_position_label = new PSPDFKit.Annotations.WidgetAnnotation({ const annotation_position_label = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_position_label, id: id_position_label,
pageIndex: page, pageIndex: page,
@@ -183,6 +160,7 @@ async function createAnnotations(document, instance) {
value: "Position", value: "Position",
readOnly: true readOnly: true
}) })
//#endregion
signatures.push(annotation) signatures.push(annotation)
signatures.push(formField) signatures.push(formField)
@@ -203,7 +181,7 @@ async function createAnnotations(document, instance) {
signatures.push(formFieldPositionLabel) signatures.push(formFieldPositionLabel)
} }
await instance.create(signatures); return signatures;
} }
async function getAnnotations(instance) { async function getAnnotations(instance) {
@@ -239,8 +217,9 @@ function isSignature(annotation) {
return !!annotation.isSignature || annotation.description == 'FRAME' return !!annotation.isSignature || annotation.description == 'FRAME'
} }
function createImageAnnotation(boundingBox, pageIndex, imageAttachmentId) { function createImageAnnotation(boundingBox, pageIndex, imageAttachmentId, id) {
const frameAnnotation = new PSPDFKit.Annotations.ImageAnnotation({ const frameAnnotation = new PSPDFKit.Annotations.ImageAnnotation({
id: id,
pageIndex: pageIndex, pageIndex: pageIndex,
isSignature: false, isSignature: false,
readOnly: true, readOnly: true,
@@ -329,3 +308,80 @@ function markFieldAsCity(formField) {
function isCityField(formField) { function isCityField(formField) {
return cityFieldNames.includes(formField.name) return cityFieldNames.includes(formField.name)
} }
function fixBase64(escapedBase64) {
return escapedBase64
.replace(/\\u002B/g, "+")
.replace(/\\u002F/g, "/")
.replace(/\\u003D/g, "=");
}
function mapSignature(iJSON) {
return [
// formFields
...iJSON.formFieldValues.filter(field => !field.name.includes("label")).map((field) => {
const nameParts = field.name.split('#');
const [x, y, width, height] = iJSON.annotations.find(iAnnot => iAnnot.id === field.name).bbox;
return {
elementId: Number(nameParts[2]),
name: nameParts[3],
value: field.value,
type: field.type,
x: x,
y: y,
width: width,
height: height
};
}),
// frames
...iJSON.annotations.filter(annot => annot.description === 'FRAME').map((annot) => {
const preElement = findNearest(annot, e => e.bbox[0], e => e.bbox[1], iJSON.annotations.filter(
field => field.id.includes("signature") && field.pageIndex === annot.pageIndex
));
const idPartsOfPre = preElement.id.split('#');
const [x, y, width, height] = annot.bbox;
return {
elementId: Number(idPartsOfPre[2]),
name: 'frame',
value: fixBase64(iJSON.attachments[annot.imageAttachmentId]?.binary),
type: annot.type,
x: x,
y: y,
width: width,
height: height
};
}),
// signatures
...iJSON.annotations.filter(annot => annot.isSignature).map(annot => {
const preElement = findNearest(annot, e => e.bbox[0], e => e.bbox[1], iJSON.annotations.filter(
field => field.id.includes("signature") && field.pageIndex === annot.pageIndex
));
const idPartsOfPre = preElement.id.split('#');
let value;
if (annot.imageAttachmentId)
value = iJSON.attachments[annot.imageAttachmentId]?.binary;
else if (annot.lines && annot.strokeColor)
value = JSON.stringify({
lines: annot.lines,
strokeColor: annot.strokeColor
});
else
throw new Error("Signature mapping failed: The data structure from the third-party library is incompatible or missing required fields.");
const [x, y, width, height] = annot.bbox;
return {
elementId: Number(idPartsOfPre[2]),
name: 'signature',
value,
type: annot.type,
x: x,
y: y,
width: width,
height: height
};
})
];
}

Some files were not shown because too many files have changed in this diff Show More