Compare commits

...

231 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
6327fef2e0 chore(Web): bump to 3.3.0 2025-09-17 17:48:58 +02:00
d0bfe795d7 refactor(HomeController): Ändern Sie den Routenparameter „EnvelopeReceiverReadOnly” von einem String-Schlüssel zu einer langen ID
- Aktualisieren Sie „MainAsync”, um mit einer geparsten schreibgeschützten ID anstelle eines String-Schlüssels zu „EnvelopeReceiverReadOnly” umzuleiten
- Ändern Sie die Aktionssignatur „EnvelopeReceiverReadOnly”, um „long readOnlyId” anstelle von „[FromRoute] string readOnlyKey” zu akzeptieren
- Inline-Schlüssel-Decodierungs-/Validierungslogik entfernt und Decodierung an `MainAsync` delegiert
- Protokollmeldungen angepasst, um `ReadOnly-Id` anstelle von `ReadOnly-key` widerzuspiegeln
2025-09-17 13:59:31 +02:00
10b1de4cf0 remove envelopeRejected-endpoint 2025-09-17 13:17:25 +02:00
cf3535b4de refactor(HomeController): remove envelopeSigned endpoint 2025-09-17 13:07:06 +02:00
118 changed files with 6112 additions and 3950 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>
public bool? UseAccessCode { get; set; }
public bool UseAccessCode { get; set; } = true;
/// <summary>
///

View File

@@ -35,6 +35,7 @@ public class MappingProfile : Profile
CreateMap<EnvelopeType, EnvelopeTypeDto>();
CreateMap<Domain.Entities.Receiver, ReceiverDto>();
CreateMap<Domain.Entities.EnvelopeReceiverReadOnly, EnvelopeReceiverReadOnlyDto>();
CreateMap<ElementAnnotation, AnnotationDto>();
// DTO to Entity mappings
CreateMap<ConfigDto, Config>();
@@ -50,6 +51,8 @@ public class MappingProfile : Profile
CreateMap<ReceiverDto, Domain.Entities.Receiver>().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore());
CreateMap<EnvelopeReceiverReadOnlyCreateDto, Domain.Entities.EnvelopeReceiverReadOnly>();
CreateMap<EnvelopeReceiverReadOnlyUpdateDto, Domain.Entities.EnvelopeReceiverReadOnly>();
CreateMap<AnnotationCreateDto, ElementAnnotation>()
.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.UtcNow));
// Messaging mappings
// 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;
@@ -6,7 +7,7 @@ namespace EnvelopeGenerator.Application.Common.Dto;
/// Data Transfer Object representing a positioned element assigned to a document receiver.
/// </summary>
[ApiExplorerSettings(IgnoreApi = true)]
public class SignatureDto
public class SignatureDto : ISignature
{
/// <summary>
/// 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.Notifications.RemoveSignature;
using EnvelopeGenerator.Domain.Constants;
using MediatR;
using Newtonsoft.Json;
using System.Dynamic;
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>
@@ -16,7 +24,7 @@ public record DocSignedNotification(EnvelopeReceiverDto Original) : EnvelopeRece
/// <summary>
///
/// </summary>
public required ExpandoObject Annotations { get; init; }
public PsPdfKitAnnotation PsPdfKitAnnotation { get; init; } = null!;
/// <summary>
///
@@ -40,17 +48,41 @@ public static class DocSignedNotificationExtensions
/// Converts an <see cref="EnvelopeReceiverDto"/> to a <see cref="DocSignedNotification"/>.
/// </summary>
/// <param name="dto">The DTO to convert.</param>
/// <param name="annotations"></param>
/// <param name="psPdfKitAnnotation"></param>
/// <returns>A new <see cref="DocSignedNotification"/> instance.</returns>
public static DocSignedNotification ToDocSignedNotification(this EnvelopeReceiverDto dto, ExpandoObject annotations)
=> new(dto) { Annotations = annotations };
public static DocSignedNotification ToDocSignedNotification(this EnvelopeReceiverDto dto, PsPdfKitAnnotation psPdfKitAnnotation)
=> new(dto) { PsPdfKitAnnotation = psPdfKitAnnotation };
/// <summary>
/// Asynchronously converts a <see cref="Task{EnvelopeReceiverDto}"/> to a <see cref="DocSignedNotification"/>.
///
/// </summary>
/// <param name="dtoTask">The task that returns the DTO to convert.</param>
/// <param name="annotations"></param>
/// <returns>A task that represents the asynchronous conversion operation.</returns>
public static async Task<DocSignedNotification?> ToDocSignedNotification(this Task<EnvelopeReceiverDto?> dtoTask, ExpandoObject annotations)
=> await dtoTask is EnvelopeReceiverDto dto ? new(dto) { Annotations = annotations } : null;
/// <param name="dtoTask"></param>
/// <param name="psPdfKitAnnotation"></param>
/// <returns></returns>
public static async Task<DocSignedNotification?> ToDocSignedNotification(this Task<EnvelopeReceiverDto?> dtoTask, PsPdfKitAnnotation psPdfKitAnnotation)
=> 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 EnvelopeGenerator.Domain.Constants;
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using System.Text.Json;
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
@@ -10,15 +9,18 @@ namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
/// </summary>
public class AnnotationHandler : INotificationHandler<DocSignedNotification>
{
private readonly ISender _sender;
/// <summary>
///
/// </summary>
private readonly IRepository<ElementAnnotation> _repo;
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
public AnnotationHandler(ISender sender)
/// <param name="repository"></param>
public AnnotationHandler(IRepository<ElementAnnotation> repository)
{
_sender = sender;
_repo = repository;
}
/// <summary>
@@ -27,13 +29,6 @@ public class AnnotationHandler : INotificationHandler<DocSignedNotification>
/// <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.Annotations, Format.Json.ForAnnotations)
}, cancel);
}
public Task Handle(DocSignedNotification notification, CancellationToken cancel)
=> _repo.CreateAsync(notification.PsPdfKitAnnotation.Structured, 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.Domain.Constants;
using MediatR;
using Newtonsoft.Json;
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<AuthenticatorParams>(config.GetSection(nameof(AuthenticatorParams)));
services.Configure<TotpSmsParams>(config.GetSection(nameof(TotpSmsParams)));
services.Configure<DbTriggerParams>(config.GetSection(nameof(DbTriggerParams)));
services.AddHttpClientService<GtxMessagingParams>(config.GetSection(nameof(GtxMessagingParams)));
services.TryAddSingleton<ISmsSender, GTXSmsSender>();

View File

@@ -14,7 +14,7 @@
<ItemGroup>
<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.Client" Version="2.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 EnvelopeGenerator.Domain.Constants;
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Application.Common.Dto.History;
using MediatR;
using System.ComponentModel.DataAnnotations;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Application.Histories.Queries;
@@ -9,21 +12,81 @@ namespace EnvelopeGenerator.Application.Histories.Queries;
/// <summary>
/// Repräsentiert eine Abfrage für die Verlaufshistorie eines Umschlags.
/// </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>
/// Abfrage zur Steuerung, ob nur der aktuelle Include oder der gesamte Datensatz zurückgegeben wird.
/// </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>
</PropertyGroup>
<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">
<HintPath>..\packages\BouncyCastle.Cryptography.2.5.0\lib\net461\BouncyCastle.Cryptography.dll</HintPath>
</Reference>
@@ -67,6 +70,12 @@
<Reference Include="DigitalData.Controls.DocumentViewer, Version=1.9.8.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Controls.DocumentViewer.1.9.8\lib\net462\DigitalData.Controls.DocumentViewer.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.Abstraction.Application, Version=1.4.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Core.Abstraction.Application.1.4.0\lib\net462\DigitalData.Core.Abstraction.Application.dll</HintPath>
</Reference>
<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">
<HintPath>..\packages\DigitalData.Modules.Base.1.3.8\lib\net462\DigitalData.Modules.Base.dll</HintPath>
</Reference>
@@ -176,12 +185,84 @@
<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>
</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">
<HintPath>..\packages\Microsoft.Identity.Client.4.77.0\lib\net462\Microsoft.Identity.Client.dll</HintPath>
</Reference>
<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>
</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">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
@@ -216,19 +297,31 @@
<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>
</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.Configuration" />
<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">
<HintPath>..\packages\System.Data.Odbc.6.0.1\lib\net461\System.Data.Odbc.dll</HintPath>
</Reference>
<Reference Include="System.Diagnostics.DiagnosticSource, Version=6.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.6.0.1\lib\net461\System.Diagnostics.DiagnosticSource.dll</HintPath>
<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.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>
</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.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>
@@ -252,9 +345,25 @@
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<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">
<HintPath>..\packages\System.Security.Cryptography.Pkcs.9.0.0\lib\net462\System.Security.Cryptography.Pkcs.dll</HintPath>
</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">
<HintPath>..\packages\System.Text.Encodings.Web.9.0.0\lib\net462\System.Text.Encodings.Web.dll</HintPath>
</Reference>
@@ -361,6 +470,10 @@
<Project>{4F32A98D-E6F0-4A09-BD97-1CF26107E837}</Project>
<Name>EnvelopeGenerator.Domain</Name>
</ProjectReference>
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj">
<Project>{63e32615-0eca-42dc-96e3-91037324b7c7}</Project>
<Name>EnvelopeGenerator.Infrastructure</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<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\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\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>
<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.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -107,6 +107,78 @@
<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" />
</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>
</runtime>
<entityFramework>

View File

@@ -1,10 +1,14 @@
Imports DigitalData.Modules.Database
Imports System.IO
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Imports EnvelopeGenerator.CommonServices.Jobs
Imports EnvelopeGenerator.CommonServices.Jobs.FinalizeDocument
Imports GdPicture14
Imports Newtonsoft.Json.Linq
Imports EnvelopeGenerator.CommonServices.Jobs
Imports System.IO
Imports EnvelopeGenerator.CommonServices.Jobs.FinalizeDocument
Imports EnvelopeGenerator.Infrastructure
Imports Microsoft.EntityFrameworkCore
Imports System.Text
Imports DigitalData.Core.Abstractions
Public Class frmFinalizePDF
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
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()
Manager = New AnnotationManager()
@@ -72,8 +101,9 @@ Public Class frmFinalizePDF
Select(Function(r As DataRow) r.Item("VALUE").ToString()).
ToList()
Dim oBuffer As Byte() = ReadEnvelope(CInt(txtEnvelope.Text))
Dim oNewBuffer = PDFBurner.BurnInstantJSONAnnotationsToPDF(oBuffer, oJsonList)
Dim envelopeId As Integer = CInt(txtEnvelope.Text)
Dim oBuffer As Byte() = ReadEnvelope(envelopeId)
Dim oNewBuffer = PDFBurner.BurnAnnotsToPDF(oBuffer, oJsonList, envelopeId)
Dim desktopPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
Dim oNewPath = Path.Combine(desktopPath, $"E{txtEnvelope.Text}R{txtReceiver.Text}.burned.pdf")
@@ -81,7 +111,15 @@ Public Class frmFinalizePDF
Process.Start(oNewPath)
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 Sub

View File

@@ -1,7 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoMapper" version="10.1.1" targetFramework="net462" />
<package id="BouncyCastle.Cryptography" version="2.5.0" targetFramework="net462" />
<package id="DigitalData.Controls.DocumentViewer" version="1.9.8" targetFramework="net462" />
<package id="DigitalData.Core.Abstraction.Application" version="1.4.0" targetFramework="net462" />
<package id="DigitalData.Core.Abstractions" version="4.3.0" targetFramework="net462" />
<package id="DigitalData.Modules.Base" version="1.3.8" targetFramework="net462" />
<package id="DigitalData.Modules.Config" version="1.3.0" targetFramework="net462" />
<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.Bcl.AsyncInterfaces" 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.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.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="Newtonsoft.Json" version="13.0.3" 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.CodeDom" 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.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.IdentityModel.Tokens.Jwt" version="7.5.1" 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.Management" version="9.0.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.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.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.Json" version="9.0.0" targetFramework="net462" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net462" />

View File

@@ -48,6 +48,9 @@
<OptionInfer>On</OptionInfer>
</PropertyGroup>
<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">
<HintPath>..\packages\BouncyCastle.Cryptography.2.5.0\lib\net461\BouncyCastle.Cryptography.dll</HintPath>
</Reference>
@@ -69,6 +72,13 @@
<Reference Include="DevExpress.XtraEditors.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
<Reference Include="DevExpress.XtraGauges.v21.2.Core, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a, processorArchitecture=MSIL" />
<Reference Include="DevExpress.XtraReports.v21.2, Version=21.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a, processorArchitecture=MSIL" />
<Reference Include="DigitalData.Core.Abstraction.Application, Version=1.4.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DigitalData.Core.Abstraction.Application.1.4.0\lib\net462\DigitalData.Core.Abstraction.Application.dll</HintPath>
</Reference>
<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">
<HintPath>..\packages\DigitalData.Modules.Base.1.3.8\lib\net462\DigitalData.Modules.Base.dll</HintPath>
</Reference>
@@ -172,9 +182,84 @@
<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>
</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.Extensions.Logging.Abstractions, Version=2.1.1.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>
<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=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 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>
@@ -216,13 +301,26 @@
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Configuration" />
<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">
<HintPath>..\packages\System.Data.Odbc.6.0.1\lib\net461\System.Data.Odbc.dll</HintPath>
</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.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>
</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.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>
@@ -244,9 +342,25 @@
<Reference Include="System.Runtime.Remoting" />
<Reference Include="System.Runtime.Serialization" />
<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">
<HintPath>..\packages\System.Security.Cryptography.Pkcs.8.0.1\lib\net462\System.Security.Cryptography.Pkcs.dll</HintPath>
</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.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>
@@ -446,6 +560,14 @@
<Project>{4f32a98d-e6f0-4a09-bd97-1cf26107e837}</Project>
<Name>EnvelopeGenerator.Domain</Name>
</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>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<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.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\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>
<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\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>

View File

@@ -11,6 +11,10 @@ Imports EnvelopeGenerator.Domain.Constants
Imports DevExpress.DataProcessing
Imports System.Data.SqlClient
Imports EnvelopeGenerator.Domain.Entities
Imports DigitalData.Core.Abstraction.Application
Imports EnvelopeGenerator.Infrastructure
Imports Microsoft.EntityFrameworkCore
Imports DigitalData.Core.Abstractions
Namespace Jobs
Public Class FinalizeDocumentJob
@@ -62,7 +66,31 @@ Namespace Jobs
LicenseManager.RegisterKEY(oGdPictureKey)
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")
Dim oState = GetState()
@@ -378,7 +406,6 @@ Namespace Jobs
ParentFolderUID = pEnvelopeData.EnvelopeUUID
End If
Logger.Info("ParentFolderUID: [{0}]", ParentFolderUID)
Dim oInputDocumentBuffer As Byte()
If Not IsNothing(pEnvelopeData.DocAsByte) Then
@@ -391,7 +418,7 @@ Namespace Jobs
End Try
End If
Return PDFBurner.BurnInstantJSONAnnotationsToPDF(oInputDocumentBuffer, oAnnotations)
Return PDFBurner.BurnAnnotsToPDF(oInputDocumentBuffer, oAnnotations, pEnvelopeData.EnvelopeId)
End Function
Private Function GetEnvelopeData(pEnvelopeId As Integer) As EnvelopeData
@@ -439,13 +466,6 @@ Namespace Jobs
ReportModel = New ReportModel(pState)
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
Return New State With {
.LogConfig = LogConfig,

View File

@@ -1,10 +1,17 @@
Imports System.Drawing
Imports System.Collections.Immutable
Imports System.Drawing
Imports System.IO
Imports DevExpress.DataProcessing
Imports DigitalData.Core.Abstraction.Application.Repository
Imports DigitalData.Core.Abstractions
Imports DigitalData.Modules.Base
Imports DigitalData.Modules.Logging
Imports GdPicture14
Imports Newtonsoft.Json
Imports EnvelopeGenerator.CommonServices.Jobs.FinalizeDocument.FinalizeDocumentExceptions
Imports EnvelopeGenerator.Domain.Entities
Imports EnvelopeGenerator.PdfEditor
Imports GdPicture14
Imports Microsoft.EntityFrameworkCore
Imports Newtonsoft.Json
Namespace Jobs.FinalizeDocument
Public Class PDFBurner
@@ -13,9 +20,6 @@ Namespace Jobs.FinalizeDocument
Private ReadOnly Manager As AnnotationManager
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
Public Sub New(pLogConfig As LogConfig, pGDPictureLicenseKey As String, pdfBurnerParams As PDFBurnerParams)
@@ -29,7 +33,94 @@ Namespace Jobs.FinalizeDocument
_pdfBurnerParams = pdfBurnerParams
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
Using oSourceStream As New MemoryStream(pSourceBuffer)
' Open PDF
@@ -40,11 +131,13 @@ Namespace Jobs.FinalizeDocument
' Add annotation to PDF
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(oJSON)
Throw New BurnAnnotationException($"Adding Annotation failed")
End If
Throw New BurnAnnotationException($"Adding Annotation failed", ex)
End Try
Next
oResult = Manager.BurnAnnotationsToPage(RemoveInitialAnnots:=True, VectorMode:=True)
If oResult <> GdPictureStatus.OK Then
@@ -64,128 +157,125 @@ Namespace Jobs.FinalizeDocument
End Using
End Using
End Function
#End Region
Private Function AddInstantJSONAnnotationToPDF(pInstantJSON As String) As Boolean
Try
Dim oAnnotationData = JsonConvert.DeserializeObject(Of AnnotationData)(pInstantJSON)
oAnnotationData.annotations.Reverse()
#Region "Add Value"
Private Sub AddInstantJSONAnnotationToPDF(pInstantJSON As String)
Dim oAnnotationData = JsonConvert.DeserializeObject(Of AnnotationData)(pInstantJSON)
Dim sigAnnotType = oAnnotationData.annotations.ElementAt(1).type
Dim yPosOfSigAnnot = oAnnotationData.annotations.ElementAt(2).bbox.ElementAt(1) - 71.84002685546875 + 7
Dim isSeal = True 'First element is signature seal
oAnnotationData.annotations.Reverse()
Dim formFieldIndex = 0
For Each oAnnotation In oAnnotationData.annotations
Logger.Debug("Adding AnnotationID: " + oAnnotation.id)
For Each oAnnotation In oAnnotationData.annotations
Logger.Debug("Adding AnnotationID: " + oAnnotation.id)
Select Case oAnnotation.type
Case ANNOTATION_TYPE_IMAGE
Select Case oAnnotation.type
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
oAnnotation.bbox.Item(1) = yPosOfSigAnnot
End If
End Sub
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
AddInkAnnotation(oAnnotation)
Private Sub AddImageAnnotation(pAnnotation As Annotation, pAttachments As Dictionary(Of String, Attachment))
Dim oAttachment = pAttachments.Where(Function(a) a.Key = pAnnotation.imageAttachmentId).
SingleOrDefault()
Case ANNOTATION_TYPE_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, formFieldIndex)
formFieldIndex += 1
End If
End Select
' Convert pixels to Inches
Dim oBounds = pAnnotation.bbox.Select(AddressOf ToInches).ToList()
isSeal = False
Next
Dim oX = oBounds.Item(0)
Dim oY = oBounds.Item(1)
Dim oWidth = oBounds.Item(2)
Dim oHeight = oBounds.Item(3)
Return True
Catch ex As Exception
Logger.Warn("Could not create annotation from InstantJSON")
Logger.Error(ex)
Return False
End Try
End Function
Manager.SelectPage(pAnnotation.pageIndex + 1)
Manager.AddEmbeddedImageAnnotFromBase64(oAttachment.Value.binary, oX, oY, oWidth, oHeight)
End Sub
Private Function AddImageAnnotation(pAnnotation As Annotation, pAttachments As Dictionary(Of String, Attachment), Optional yOffset As Double = 0) As Boolean
Try
Dim oAttachment = pAttachments.Where(Function(a) a.Key = pAnnotation.imageAttachmentId).
SingleOrDefault()
Private Sub AddInkAnnotation(page As Integer, value As String)
' Convert pixels to Inches
Dim oBounds = pAnnotation.bbox.Select(AddressOf ToInches).ToList()
Dim ink = JsonConvert.DeserializeObject(Of Ink)(value)
Dim oX = oBounds.Item(0)
Dim oY = oBounds.Item(1) + yOffset
Dim oWidth = oBounds.Item(2)
Dim oHeight = oBounds.Item(3)
Dim oSegments = ink.lines.points
Dim oColor = ColorTranslator.FromHtml(ink.strokeColor)
Manager.SelectPage(page)
Manager.SelectPage(pAnnotation.pageIndex + 1)
Manager.AddEmbeddedImageAnnotFromBase64(oAttachment.Value.binary, oX, oY, oWidth, oHeight)
For Each oSegment As List(Of List(Of Single)) In oSegments
Dim oPoints = oSegment.
Select(AddressOf ToPointF).
ToArray()
Return True
Catch ex As Exception
Logger.Warn("Could not add image annotation!")
Logger.Error(ex)
Manager.AddFreeHandAnnot(oColor, oPoints)
Next
End Sub
Return False
End Try
End Function
Private Sub AddInkAnnotation(pAnnotation As Annotation)
Dim oSegments = pAnnotation.lines.points
Dim oColor = ColorTranslator.FromHtml(pAnnotation.strokeColor)
Manager.SelectPage(pAnnotation.pageIndex + 1)
Private Function AddInkAnnotation(pAnnotation As Annotation) As Boolean
Try
Dim oSegments = pAnnotation.lines.points
Dim oColor = ColorTranslator.FromHtml(pAnnotation.strokeColor)
Manager.SelectPage(pAnnotation.pageIndex + 1)
For Each oSegment As List(Of List(Of Single)) In oSegments
Dim oPoints = oSegment.
Select(AddressOf ToPointF).
ToArray()
For Each oSegment As List(Of List(Of Single)) In oSegments
Dim oPoints = oSegment.
Select(AddressOf ToPointF).
ToArray()
Manager.AddFreeHandAnnot(oColor, oPoints)
Next
End Sub
Manager.AddFreeHandAnnot(oColor, oPoints)
Next
Private Sub AddFormFieldValue(x As Double, y As Double, width As Double, height As Double, page As Integer, value As String)
Manager.SelectPage(page)
Return True
Catch ex As Exception
Logger.Warn("Could not add image annotation!")
Logger.Error(ex)
' Add the text annotation
Dim ant = Manager.AddTextAnnot(x, y, width, height, value)
Return False
End Try
' Set the font properties
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
Try
' Convert pixels to Inches
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 oY = oBounds.Item(1) + _pdfBurnerParams.YOffset * index + _pdfBurnerParams.TopMargin
Dim oWidth = oBounds.Item(2)
Dim oHeight = oBounds.Item(3)
Dim oX = oBounds.Item(0)
Dim oY = oBounds.Item(1) + _pdfBurnerParams.YOffset * ffIndex + _pdfBurnerParams.TopMargin
Dim oWidth = oBounds.Item(2)
Dim oHeight = oBounds.Item(3)
Manager.SelectPage(pAnnotation.pageIndex + 1)
' Add the text annotation
Dim ant = Manager.AddTextAnnot(oX, oY, oWidth, oHeight, formFieldValue.value)
Manager.SelectPage(pAnnotation.pageIndex + 1)
' Add the text annotation
Dim ant = Manager.AddTextAnnot(oX, oY, oWidth, oHeight, formFieldValue.value)
' Set the font properties
ant.FontName = _pdfBurnerParams.FontName
ant.FontSize = _pdfBurnerParams.FontSize
ant.FontStyle = _pdfBurnerParams.FontStyle
Manager.SaveAnnotationsToPage()
Return True
Catch ex As Exception
Logger.Warn("Could not add image annotation!")
Logger.Error(ex)
Return False
End Try
End Function
' Set the font properties
ant.FontName = _pdfBurnerParams.FontName
ant.FontSize = _pdfBurnerParams.FontSize
ant.FontStyle = _pdfBurnerParams.FontStyle
Manager.SaveAnnotationsToPage()
End Sub
#End Region
#Region "Helpers"
Private Function ToPointF(pPoints As List(Of Single)) As PointF
Dim oPoints = pPoints.Select(AddressOf ToInches).ToList()
Return New PointF(oPoints.Item(0), oPoints.Item(1))
@@ -198,24 +288,138 @@ Namespace Jobs.FinalizeDocument
Private Function ToInches(pValue As Single) As Single
Return pValue / 72
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
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 formFieldValues As List(Of FormFieldValue)
End Class
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
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 type As String
Public Property isSignature As Boolean
Public Property imageAttachmentId As String
Public Property lines As Lines
Public Property pageIndex As Integer
Public Property strokeColor As String
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
Public Property points As List(Of List(Of List(Of Single)))
End Class
@@ -229,5 +433,6 @@ Namespace Jobs.FinalizeDocument
Public Property name As String
Public Property value As String
End Class
#End Region
End Class
End Namespace
End Namespace

View File

@@ -77,6 +77,70 @@
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.5" newVersion="8.0.0.5" />
</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>
</runtime>
</configuration>

View File

@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoMapper" version="10.1.1" targetFramework="net462" />
<package id="BouncyCastle.Cryptography" version="2.5.0" targetFramework="net462" />
<package id="DigitalData.Core.Abstraction.Application" version="1.4.0" targetFramework="net462" />
<package id="DigitalData.Core.Abstractions" version="4.3.0" targetFramework="net462" />
<package id="DigitalData.Modules.Base" version="1.3.8" targetFramework="net462" />
<package id="DigitalData.Modules.Config" version="1.3.0" targetFramework="net462" />
<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="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.HashCode" version="1.1.1" 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="Newtonsoft.Json" version="13.0.3" 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.Collections.Immutable" version="8.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.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.IdentityModel.Tokens.Jwt" version="7.5.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.Memory" 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.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.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.Json" version="8.0.5" 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;
#if NETFRAMEWORK
using System.Drawing;
@@ -14,7 +15,7 @@ namespace EnvelopeGenerator.Domain.Entities
#endif
[Table("TBSIG_ENVELOPE_DOCUMENT", Schema = "dbo")]
public class Document
public class Document : IHasEnvelope
{
public Document()
{
@@ -30,27 +31,50 @@ public class Document
[Required]
[Column("ENVELOPE_ID")]
public int EnvelopeId { get; set; } = 0;
public int EnvelopeId { get; set; }
#if NETFRAMEWORK
= 0;
#endif
[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
[NotMapped]
[Column("FILEPATH", TypeName = "nvarchar(256)")]
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
[NotMapped]
public string FileNameOriginal { get; set; }
[NotMapped]
public bool IsTempFile { get; set; }
[NotMapped]
public string Filename { get; set; }
[NotMapped]
public Bitmap Thumbnail { get; set; }

View File

@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using DigitalData.Core.Abstractions.Interfaces;
using EnvelopeGenerator.Domain.Interfaces;
#if NETFRAMEWORK
@@ -14,7 +15,7 @@ namespace EnvelopeGenerator.Domain.Entities
#endif
[Table("TBSIG_DOCUMENT_STATUS", Schema = "dbo")]
public class DocumentStatus : IHasEnvelope, IHasReceiver
public class DocumentStatus : IHasEnvelope, IHasReceiver, IEntity
{
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")]
[Required]
public DateTime AddedWhen { get; set; }
public DateTime
#if NET
?
#endif
AddedWhen { get; set; }
[Column("CHANGED_WHO")]
[StringLength(100)]
public string ChangedWho { get; set; }
public string
#if NET
?
#endif
ChangedWho { get; set; }
[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 EnvelopeGenerator.Domain.Interfaces;
using EnvelopeGenerator.Domain.Constants;
using DigitalData.Core.Abstractions.Interfaces;
#if NETFRAMEWORK
@@ -17,7 +18,7 @@ namespace EnvelopeGenerator.Domain.Entities
#endif
[Table("TBSIG_ENVELOPE_HISTORY", Schema = "dbo")]
public class History : IHasEnvelope, IHasReceiver
public class History : IHasEnvelope, IHasReceiver, IEntity
{
[Key]
[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.Schema;
#if NETFRAMEWORK
using System;
using System.Collections.Generic;
#endif
namespace EnvelopeGenerator.Domain.Entities
@@ -13,7 +15,7 @@ namespace EnvelopeGenerator.Domain.Entities
#endif
[Table("TBSIG_DOCUMENT_RECEIVER_ELEMENT", Schema = "dbo")]
public class Signature
public class Signature : ISignature, IHasReceiver
{
public Signature()
{
@@ -106,7 +108,17 @@ public class Signature
public virtual Document Document { get; set; }
[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
[NotMapped]

View File

@@ -37,7 +37,7 @@
<PackageReference Include="DigitalData.EmailProfilerDispatcher.Abstraction.Attributes" Version="1.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.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>

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>
<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>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
@@ -121,20 +121,92 @@
</dependentAssembly>
<dependentAssembly>
<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>
<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>
<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>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</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>
</runtime>
<entityFramework>

View File

@@ -185,7 +185,19 @@ try
// Envelope generator serives
#pragma warning disable CS0618 // Type or member is obsolete
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);
#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.Infrastructure;
using DigitalData.UserManager.Infrastructure.Contracts;
using DigitalData.UserManager.Domain.Entities;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Group = DigitalData.UserManager.Domain.Entities.Group;
using Module = DigitalData.UserManager.Domain.Entities.Module;
using DigitalData.EmailProfilerDispatcher;
using Microsoft.Extensions.Options;
using DigitalData.Core.Client;
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 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.
public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailDbContext
public abstract class EGDbContextBase : DbContext
#if NET
, IUserManagerDbContext, IMailDbContext
#endif
{
public DbSet<Config> Configs { get; set; }
@@ -56,7 +71,9 @@ public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailD
public DbSet<UserRep> UserReps { get; set; }
#if NET
public DbSet<EmailOut> EMailOuts { get; set; }
#endif
public DbSet<EnvelopeReceiverReadOnly> EnvelopeReceiverReadOnlys { get; set; }
@@ -64,33 +81,22 @@ public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailD
private readonly DbTriggerParams _triggers;
private readonly ILogger? _logger;
private readonly ILogger
#if NET
?
#endif
_logger;
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;
_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)
@@ -119,7 +125,7 @@ public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailD
modelBuilder.Entity<Envelope>()
.HasMany(e => e.Documents)
.WithOne()
.WithOne(d => d.Envelope)
.HasForeignKey(ed => ed.EnvelopeId);
modelBuilder.Entity<Envelope>()
@@ -187,6 +193,13 @@ public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailD
.HasForeignKey(ds => ds.ReceiverId);
#endregion DocumentStatus
#region Annotation
modelBuilder.Entity<Signature>()
.HasMany(signature => signature.Annotations)
.WithOne(annot => annot.Element)
.HasForeignKey(annot => annot.ElementId);
#endregion
#region Trigger
// Configure entities to handle database triggers
void AddTrigger<T>() where T : class => _triggers
@@ -194,7 +207,9 @@ public abstract class EGDbContextBase : DbContext, IUserManagerDbContext, IMailD
.SelectMany(t => t.Value)
.ForEach(tName =>
{
#if NET
modelBuilder.Entity<T>().ToTable(tb => tb.HasTrigger(tName));
#endif
_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<EnvelopeType>();
AddTrigger<Receiver>();
#if NET
AddTrigger<EmailOut>();
#endif
#endregion Trigger
//configure model builder for user manager tables
#if NET
modelBuilder.ConfigureUserManager();
#endif
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.Design;
using Microsoft.Extensions.Configuration;
@@ -22,10 +23,10 @@ namespace EnvelopeGenerator.Infrastructure
// create DbTriggerParams
var triggerLists = config.GetSection("DbTriggerParams").Get<Dictionary<string, List<string>>>();
var dbTriggerParams = new DbTriggerParams();
if(triggerLists is not null)
if (triggerLists is not null)
foreach (var triggerList in triggerLists)
{
if(triggerList.Value.Count == 0)
if (triggerList.Value.Count == 0)
continue; // Skip empty trigger lists
var tableName = triggerList.Key;
@@ -43,4 +44,5 @@ namespace EnvelopeGenerator.Infrastructure
return dbContext;
}
}
}
}
#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,24 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net462;net7.0;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</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>
<PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.3.1" />
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.4.1" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.4.0" />
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.4.5" />
<PackageReference Include="QuestPDF" Version="2025.7.1" />
<PackageReference Include="UserManager" Version="1.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
</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'">
<PackageReference Include="Microsoft.EntityFrameworkCore" 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.SQL;
using EnvelopeGenerator.Domain.Entities;
@@ -27,3 +28,4 @@ public class DocumentExecutor : SQLExecutor, IDocumentExecutor
$"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 EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
using EnvelopeGenerator.Application.Common.SQL;
@@ -36,4 +37,5 @@ public class EnvelopeExecutor : SQLExecutor, IEnvelopeExecutor
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.SQLExecutor;
using EnvelopeGenerator.Application.Common.SQL;
@@ -9,7 +10,7 @@ using Microsoft.Extensions.Options;
namespace EnvelopeGenerator.Infrastructure.Executor;
public class EnvelopeReceiverExecutor: SQLExecutor, IEnvelopeReceiverExecutor
public class EnvelopeReceiverExecutor : SQLExecutor, IEnvelopeReceiverExecutor
{
private readonly IEnvelopeReceiverRepository _erRepository;
@@ -33,3 +34,4 @@ public class EnvelopeReceiverExecutor: SQLExecutor, IEnvelopeReceiverExecutor
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;
namespace EnvelopeGenerator.Infrastructure.Executor;
@@ -38,4 +39,5 @@ public sealed record Query<TEntity> : IQuery<TEntity>
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
{
@@ -6,4 +7,5 @@ public static class QueryExtension
{
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 Microsoft.Data.SqlClient;
using Microsoft.Extensions.DependencyInjection;
@@ -30,4 +31,5 @@ public class SQLExecutor : ISQLExecutor
var sql = Provider.GetRequiredService<TSQL>();
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.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@@ -29,3 +30,5 @@ public sealed class SQLExecutor<T> : SQLExecutor, ISQLExecutor<T> where T : clas
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 string? ConnectionString { get; set; }
}
#endif

View File

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

View File

@@ -1,20 +1,21 @@
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities;
#if NET
using DigitalData.Core.Infrastructure;
using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
namespace EnvelopeGenerator.Infrastructure.Repositories;
[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 async Task<Config?> ReadFirstAsync()
public async Task<Domain.Entities.Config?> ReadFirstAsync()
{
var configs = await _dbSet.ToListAsync();
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.Application.Common.Interfaces.Repositories;
@@ -10,4 +11,5 @@ public class DocumentReceiverElementRepository : CRUDRepository<Signature, int,
public DocumentReceiverElementRepository(EGDbContext dbContext) : base(dbContext, dbContext.DocumentReceiverElements)
{
}
}
}
#endif

View File

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

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Infrastructure;
#if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
@@ -9,26 +10,27 @@ namespace EnvelopeGenerator.Infrastructure.Repositories;
[Obsolete("Use TempRepo")]
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();
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 _
/// <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.Application.Common.Interfaces.Repositories;
@@ -10,4 +11,5 @@ public class EnvelopeDocumentRepository : CRUDRepository<Document, int, EGDbCont
public EnvelopeDocumentRepository(EGDbContext dbContext) : base(dbContext, dbContext.EnvelopeDocument)
{
}
}
}
#endif

View File

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

View File

@@ -1,7 +1,7 @@
using DigitalData.Core.Infrastructure;
#if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Domain;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
@@ -64,4 +64,5 @@ public class EnvelopeRepository : CRUDRepository<Envelope, int, EGDbContext>, IE
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.Application.Common.Interfaces.Repositories;
@@ -10,4 +11,5 @@ public class EnvelopeTypeRepository : CRUDRepository<EnvelopeType, int, EGDbCont
public EnvelopeTypeRepository(EGDbContext dbContext) : base(dbContext, dbContext.EnvelopeTypes)
{
}
}
}
#endif

View File

@@ -1,8 +1,8 @@
using DigitalData.Core.Infrastructure;
#if NET
using DigitalData.Core.Infrastructure;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Domain;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
@@ -59,7 +59,7 @@ public class EnvelopeReceiverRepository : CRUDRepository<EnvelopeReceiver, (int
private IQueryable<EnvelopeReceiver> ReadById(int envelopeId, int receiverId, bool withEnvelope = true, bool withReceiver = true, bool readOnly = true)
{
var query = readOnly ? _dbSet.AsNoTracking() : _dbSet;
if (withEnvelope)
query = query
.Include(er => er.Envelope).ThenInclude(e => e!.Documents!).ThenInclude(d => d.Elements!.Where(e => e.Receiver!.Id == receiverId))
@@ -108,7 +108,7 @@ public class EnvelopeReceiverRepository : CRUDRepository<EnvelopeReceiver, (int
var query = _dbSet.AsNoTracking();
if(email is not null)
if (email is not null)
query = query.Where(er => er.Receiver!.EmailAddress == email);
if (id is not null)
@@ -118,5 +118,6 @@ public class EnvelopeReceiverRepository : CRUDRepository<EnvelopeReceiver, (int
query = query.Where(er => er.Receiver!.Signature == signature);
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 Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
@@ -16,10 +17,10 @@ public class ReceiverRepository : CRUDRepository<Receiver, int, EGDbContext>, IR
{
IQueryable<Receiver> query = _dbSet.AsNoTracking();
if(emailAddress is not null)
if (emailAddress is not null)
query = query.Where(r => r.EmailAddress == emailAddress);
if(signature is not null)
if (signature is not null)
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
@@ -34,4 +35,5 @@ public class ReceiverRepository : CRUDRepository<Receiver, int, EGDbContext>, IR
public async Task<Receiver?> ReadByAsync(string? emailAddress = null, string? signature = null) => await ReadBy(emailAddress, signature).FirstOrDefaultAsync();
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" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.5" newVersion="8.0.0.5" />
</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>
</runtime>
<entityFramework>

View File

@@ -17,6 +17,21 @@
<TargetFrameworkProfile />
<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 Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -54,6 +69,9 @@
<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>
</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">
<SpecificVersion>False</SpecificVersion>
<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>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=2.1.1.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>
<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.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="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>
@@ -335,6 +359,18 @@
<Name>EnvelopeGenerator.Domain</Name>
</ProjectReference>
</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="..\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">

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"?>
<packages>
<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.Framework" version="3.2.0" 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.Bcl.AsyncInterfaces" version="8.0.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="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
<package id="Newtonsoft.Json.Bson" version="1.0.2" targetFramework="net48" />

View File

@@ -20,8 +20,8 @@
<ItemGroup>
<PackageReference Include="Bogus" Version="35.6.3" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.3.1" />
<PackageReference Include="DigitalData.Core.Abstractions" Version="4.1.1" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.4.0" />
<PackageReference Include="DigitalData.Core.Abstractions" Version="4.3.0" />
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
<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.Notifications.DocSigned;
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
using EnvelopeGenerator.Application.Histories.Queries;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Web.Extensions;
using MediatR;
@@ -11,7 +12,6 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Dynamic;
namespace EnvelopeGenerator.Web.Controllers;
@@ -44,7 +44,7 @@ public class AnnotationController : ControllerBase
[Authorize(Roles = ReceiverRole.FullyAuth)]
[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
var signature = User.GetAuthReceiverSignature();
@@ -58,20 +58,22 @@ public class AnnotationController : ControllerBase
// Again check if receiver has already signed
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
.ReadEnvelopeReceiverAsync(uuid, signature, cancel)
.ToDocSignedNotification(annotations)
.ToDocSignedNotification(psPdfKitAnnotation)
?? throw new NotFoundException("Envelope receiver is not found.");
await _mediator.Publish(docSignedNotification, cancel);
await _mediator.PublishSafely(docSignedNotification, cancel);
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Ok();
}
[Authorize(Roles = ReceiverRole.FullyAuth)]
[HttpPost("reject")]
[Obsolete("Use DigitalData.Core.Exceptions and .Middleware")]

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 DigitalData.Core.API;
using DigitalData.Core.Client;
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
using EnvelopeGenerator.Application.Resources;
using EnvelopeGenerator.Application.Resources;
using EnvelopeGenerator.Web.Extensions;
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.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;
[Route("Envelope")]
[Route("/")]
public class HomeController : 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 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)
public HomeController(IConfiguration configuration, ILogger<HomeController> logger, Cultures cultures, IStringLocalizer<Resource> localizer) : base(logger, cultures, localizer)
{
_envRcvService = envelopeReceiverService;
_historyService = historyService;
_configuration = configuration;
_mailService = envelopeMailService;
_readOnlyService = readOnlyService;
_authenticator = authenticator;
_rcvService = receiverService;
_envSmsHandler = envelopeSmsService;
_mediator = mediator;
}
[HttpGet("/")]
[HttpGet]
public IActionResult Main()
{
return View(new MainViewModel()
@@ -63,442 +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 Redirect($"{envelopeReceiverId}/ReadOnly");
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())
{
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))
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();
}
}
[Authorize(Roles = ReceiverRole.FullyAuth)]
[HttpGet("{envelopeReceiverId}/Success")]
[Obsolete("Use DigitalData.Core.Exceptions and .Middleware")]
public async Task<IActionResult> EnvelopeSigned([FromRoute] string envelopeReceiverId, CancellationToken cancel)
{
try
{
return await _envRcvService.IsExisting(envelopeReceiverId: envelopeReceiverId).ThenAsync(
SuccessAsync: (Func<bool, Task<IActionResult>>)(async isExisting =>
{
if(!isExisting)
return this.ViewEnvelopeNotFound();
var signed = await _mediator.IsSignedAsync(envelopeReceiverId, cancel);
if (signed)
return base.Redirect($"/Envelope/{envelopeReceiverId}/Locked");
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
ViewData["EnvelopeKey"] = envelopeReceiverId;
return base.View();
}),
Fail: IActionResult (messages, notices) =>
{
_logger.LogNotice(notices);
return this.ViewEnvelopeNotFound();
});
}
catch (Exception ex)
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception: ex);
return this.ViewInnerServiceError();
}
}
[Authorize(Roles = ReceiverRole.FullyAuth)]
[HttpGet("{envelopeReceiverId}/Rejected")]
[Obsolete("Use MediatR")]
public async Task<IActionResult> EnvelopeRejected([FromRoute] string envelopeReceiverId)
{
try
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return await _envRcvService.ReadByEnvelopeReceiverIdAsync(envelopeReceiverId).ThenAsync(
SuccessAsync: async (er) =>
{
return await _historyService.IsRejected(envelopeId: er.EnvelopeId)
? View(er)
: Redirect($"/Envelope/{envelopeReceiverId}/Locked");
},
Fail: IActionResult (messages, notices) =>
{
_logger.LogNotice(notices);
return this.ViewEnvelopeNotFound();
});
}
catch (Exception ex)
{
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception: ex);
return this.ViewInnerServiceError();
}
}
[HttpGet("{readOnlyKey}/ReadOnly")]
[Obsolete("Use MediatR")]
public async Task<IActionResult> EnvelopeReceiverReadOnly([FromRoute] string readOnlyKey)
{
try
{
// check if the readOnlyId is valid
if (!readOnlyKey.TryDecode(out var decodedKeys) || decodedKeys.GetEncodeType() != EncodeType.EnvelopeReceiverReadOnly)
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
return this.ViewDocumentNotFound();
}
var readOnlyId = decodedKeys.ParseReadOnlyId();
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-key: {readOnlyKey}\nEnvelope Receiver:\n{envelopeReceiver}",
readOnlyKey, 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();
});
}
catch (Exception ex)
{
_logger.LogError(ex, "An unexpected error occurred while displaying a read-only envelope. Read-only key is {readOnlyKey}. {message}", readOnlyKey, ex.Message);
return this.ViewInnerServiceError();
}
}
[HttpGet("Error404")]
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">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<PackageId>EnvelopeGenerator.Web</PackageId>
@@ -12,9 +12,9 @@
<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>
<ApplicationIcon>Assets\icon.ico</ApplicationIcon>
<Version>3.2.3</Version>
<AssemblyVersion>3.2.3</AssemblyVersion>
<FileVersion>3.2.3</FileVersion>
<Version>3.7.0</Version>
<AssemblyVersion>3.7.0</AssemblyVersion>
<FileVersion>3.7.0</FileVersion>
<Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright>
</PropertyGroup>
@@ -2094,20 +2094,13 @@
<None Include="wwwroot\lib\bootstrap-icons\icons\zoom-out.svg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<ItemGroup Condition="'$(TargetFramework)' == 'net7.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.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="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.2.5" />
@@ -2126,12 +2119,84 @@
<PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
<ItemGroup Condition="'$(TargetFramework)' == 'net8.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="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>
<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">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>

View File

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

View File

@@ -11,12 +11,10 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data>
<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>
<DeployIisAppPath>EnvelopeGenerator</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId>
<TargetFramework>net9.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -96,6 +96,8 @@
</details>
</section>
</div>
@if (smsExpiration is not null)
{
<script nonce="@nonce">
var expiration = new Date(@Html.Raw(JsonConvert.SerializeObject(smsExpiration)));
@@ -121,4 +123,5 @@
var remainingTime = `${formattedMinutes}:${formattedSeconds}`;
element.textContent = remainingTime;
}, 1000);
</script>
</script>
}

View File

@@ -79,7 +79,7 @@
}
else
{
<h6>@($"{@envelope?.Message}")</h6>
<div class="markdown">@(envelope?.Message)</div>
}
<p>
<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="~/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="~/css/google-font.css" asp-append-version="true" />
<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">
</head>
<body>
@@ -50,13 +49,14 @@
<script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="~/lib/sweetalert2/sweetalert2.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/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="~/lib/pspdfkit/dist-2024.3.2/pspdfkit.js"></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>
@await RenderSectionAsync("Scripts", required: false)
@{
@@ -96,5 +96,6 @@
</div>
<a href="/privacy-policy.@(_localizer.Culture()).html" target="_blank">@_localizer.Privacy()</a>
</footer>
<script src="~/js/markdown.min.js" asp-append-version="true"></script>
</body>
</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.
"EnableSwagger": true,
"UseDbMigration": false,
"EnableTestControllers": 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
}
]
}
}
"DetailedErrors": true
}

View File

@@ -6,9 +6,15 @@
]
},
{
"outputFileName": "wwwroot/js/api-service.min.js",
"outputFileName": "wwwroot/js/lazy.min.js",
"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"
]
},
{
"outputFileName": "wwwroot/js/network.min.js",
"inputFiles": [
"wwwroot/js/network.js"
]
},
{
"outputFileName": "wwwroot/js/ui.min.js",
"inputFiles": [
@@ -41,6 +41,12 @@
"wwwroot/js/util.js"
]
},
{
"outputFileName": "wwwroot/js/markdown.min.js",
"inputFiles": [
"wwwroot/js/markdown.js"
]
},
{
"outputFileName": "wwwroot/css/error-space.min.css",
"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 @@
async function createAnnotations(document, instance) {
function generateId(envelopeId, receiverId, elementId, annotationType) {
return `${envelopeId}#${receiverId}#${elementId}#${annotationType}`;
}
async function createAnnotations(document, envelopeId, receiverId) {
const signatures = [];
for(var element of document.elements) {
for (let element of document.elements) {
const annotParams = await getAnnotationParams(element.left, element.top);
const page = element.page - 1
//background
if(annotParams.background){
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()
//#region signatures
const id = generateId(envelopeId, receiverId, element.id, 'signature');
const annotation = new PSPDFKit.Annotations.WidgetAnnotation({
id: id,
pageIndex: page,
@@ -49,9 +24,10 @@ async function createAnnotations(document, instance) {
name: id,
annotationIds: PSPDFKit.Immutable.List([annotation.id]),
})
//#endregion
//position
const id_position = PSPDFKit.generateInstantId()
//#region position
const id_position = generateId(envelopeId, receiverId, element.id, 'position');
const annotation_position = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_position,
pageIndex: page,
@@ -68,9 +44,10 @@ async function createAnnotations(document, instance) {
value: "",
readOnly: false
})
//#endregion
//city
const id_city = PSPDFKit.generateInstantId()
//#region city
const id_city = generateId(envelopeId, receiverId, element.id, 'city');
const annotation_city = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_city,
pageIndex: page,
@@ -87,9 +64,10 @@ async function createAnnotations(document, instance) {
value: "",
readOnly: false
})
//#endregion
//date
const id_date = PSPDFKit.generateInstantId()
//#region date
const id_date = generateId(envelopeId, receiverId, element.id, 'date');
const annotation_date = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_date,
pageIndex: page,
@@ -110,17 +88,14 @@ async function createAnnotations(document, instance) {
value: detailedCurrentDate(),
readOnly: true
})
//#endregion
this.markFieldAsRequired(formFieldCity);
this.markFieldAsCity(formFieldCity);
/**
* Date, post code and place label part
*/
//date label
const id_date_label = PSPDFKit.generateInstantId()
//#region date label
const id_date_label = generateId(envelopeId, receiverId, element.id, 'date_label');
const annotation_date_label = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_date_label,
pageIndex: page,
@@ -140,9 +115,10 @@ async function createAnnotations(document, instance) {
value: "Date",
readOnly: true
})
//#endregion
//city label
const id_city_label = PSPDFKit.generateInstantId()
//#region city label
const id_city_label = generateId(envelopeId, receiverId, element.id, 'city_label');
const annotation_city_label = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_city_label,
pageIndex: page,
@@ -162,9 +138,10 @@ async function createAnnotations(document, instance) {
readOnly: true,
color: PSPDFKit.Color.BLACK
})
//#endregion
//position label
const id_position_label = PSPDFKit.generateInstantId()
//#region position label
const id_position_label = generateId(envelopeId, receiverId, element.id, 'position_label');
const annotation_position_label = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_position_label,
pageIndex: page,
@@ -183,6 +160,7 @@ async function createAnnotations(document, instance) {
value: "Position",
readOnly: true
})
//#endregion
signatures.push(annotation)
signatures.push(formField)
@@ -203,7 +181,7 @@ async function createAnnotations(document, instance) {
signatures.push(formFieldPositionLabel)
}
await instance.create(signatures);
return signatures;
}
async function getAnnotations(instance) {
@@ -239,8 +217,9 @@ function isSignature(annotation) {
return !!annotation.isSignature || annotation.description == 'FRAME'
}
function createImageAnnotation(boundingBox, pageIndex, imageAttachmentId) {
function createImageAnnotation(boundingBox, pageIndex, imageAttachmentId, id) {
const frameAnnotation = new PSPDFKit.Annotations.ImageAnnotation({
id: id,
pageIndex: pageIndex,
isSignature: false,
readOnly: true,
@@ -328,4 +307,81 @@ function markFieldAsCity(formField) {
function isCityField(formField) {
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