Compare commits
367 Commits
refactor/a
...
bugfix/dev
| Author | SHA1 | Date | |
|---|---|---|---|
| a3b33637fd | |||
| bc3134a033 | |||
| f106255c6b | |||
| cb103dcb69 | |||
| 8c1dd9c40d | |||
| ee358ffaab | |||
| 0780dbdd94 | |||
| d722742fe8 | |||
| 8c42105f58 | |||
| 88b196ed6d | |||
| c99511de29 | |||
| 7d0c5a0ee5 | |||
| 7001d7351f | |||
| cf16312394 | |||
| ecb7f45f14 | |||
| 0ba5578d94 | |||
| e093471a24 | |||
| b16ae70762 | |||
| c3e8f09291 | |||
| a9fb82bbea | |||
| 895fd5c509 | |||
| 3b4278d7e0 | |||
| 6d1fb05e10 | |||
| 26da78fa22 | |||
| 9eee2b523d | |||
| 9dc2b9adef | |||
| cc3c5ec9f0 | |||
| 2766d963af | |||
| 8fd9928524 | |||
| fb3ee14f8f | |||
| e3929a99e3 | |||
| b6d86aa3eb | |||
| 4171a3138b | |||
| e98e18cfe0 | |||
| 14aff03de4 | |||
| d828a5bfe2 | |||
| a6e174e7c1 | |||
| fc7aa83513 | |||
| 90661cb856 | |||
| 04e30b0d79 | |||
| d0a50f63db | |||
| 9d20ba1987 | |||
| 66f7b6f5e1 | |||
| a49dd0ff81 | |||
| e420e8d47a | |||
| d7f86adffe | |||
| cfb5c15fda | |||
| a0ed3e2fe4 | |||
| f5505190e9 | |||
| 1bfdbac8ff | |||
| 4a29511491 | |||
| 1089304bf1 | |||
| 9b606a0d3b | |||
| cb6dea319b | |||
| d59aa6157d | |||
| 1569647b60 | |||
| 3bb2a013ab | |||
| 215b755f92 | |||
| 3a94733047 | |||
| 7793d3cbb9 | |||
| 9174065365 | |||
| 19824afc1c | |||
| c7fe3f0b9c | |||
| a98024063a | |||
| e0cab3f965 | |||
| e5347b063d | |||
| ecd695ad37 | |||
| 5a8809ffc1 | |||
| b832637a6a | |||
| fceb266630 | |||
| 87bdef9d5e | |||
| 2fb32fb982 | |||
| 63d050244c | |||
| 126a4acb12 | |||
| 082cb322ef | |||
| cd077aa1bd | |||
| 49ac35153e | |||
| 91a563d995 | |||
| 308cdd03f2 | |||
| f35068e368 | |||
| e490805025 | |||
| 08550f87e6 | |||
| a22ec7a7d3 | |||
| f4681f85e7 | |||
| 0ed4a44df0 | |||
| b8926f25c4 | |||
| 5b220932d3 | |||
| 50c02314ef | |||
| 223bb88f54 | |||
| 38f4da00da | |||
| 120485ee8d | |||
| ee3a142af0 | |||
| 17e2de7f45 | |||
| de60bd239d | |||
| 52e5fce7fd | |||
| e319d4e833 | |||
| 9aa01f8e9a | |||
| 9535c7dd6b | |||
| 63b47ddbf2 | |||
| f6c7918fc3 | |||
| 0aeeacc291 | |||
| 4fdbbc832f | |||
| dbe1ad3b53 | |||
| 0b15496adb | |||
| 6d9b4d98ae | |||
| 334fc35b26 | |||
| 28b8bebe61 | |||
| 656fc97e74 | |||
| 6da68cdc86 | |||
| 5bed9c932f | |||
| 7a7fc2f903 | |||
| 2cea284a9d | |||
| c76ddb7123 | |||
| 80690d3d54 | |||
| 465986b527 | |||
| 09ff237ecc | |||
| 3f52858fe9 | |||
| ce43ace3c2 | |||
| 9523766678 | |||
| 382aafc186 | |||
| 45bb982414 | |||
| 3123102244 | |||
| 89fb6f1452 | |||
| 2f73e4f6da | |||
| b888c85937 | |||
| db70bbcebf | |||
| 6d6e14fcb7 | |||
| e6f12f0c68 | |||
| 7e2631cb21 | |||
| 34f145305c | |||
| a3b104cd78 | |||
| 53004504bd | |||
| cdc53c0bf7 | |||
| 2f1777af4a | |||
| dec2b81afe | |||
| 11a5012ab7 | |||
| b9efc75d4f | |||
| 8dc561cb8f | |||
| 76ce8a44b3 | |||
| e52972ee9b | |||
| 17ee715b46 | |||
| 6d8cecc20b | |||
| d32050ce03 | |||
| fc267e1eb4 | |||
| 86b821739a | |||
| 0f5acb7cf5 | |||
| c4ef195e20 | |||
| 0faf1fba7e | |||
| 139b92ed8c | |||
| ca3b74f939 | |||
| a6014ae88c | |||
| 4b5cdbfccd | |||
| 64068c9c29 | |||
| b913d5a88a | |||
| 51ea93200e | |||
| 9fa8ef29d8 | |||
| fb02a1a359 | |||
| bd6ff4e67e | |||
| c6d5656fce | |||
| 0282c8e5d3 | |||
| 6024f5c040 | |||
| d9ab6b3eff | |||
| c26ad9e1c2 | |||
| 76945c9051 | |||
| 34b620e749 | |||
| f7aaeccf58 | |||
| 41f3df4c71 | |||
| 3539907054 | |||
| f1ebd47c77 | |||
| 6b051155c4 | |||
| e3bc439444 | |||
| f4b78bce36 | |||
| d16a5020cb | |||
| 634043ebd9 | |||
| 1bf1c37296 | |||
| ea6f3e61be | |||
| 0c446bba56 | |||
| e74c777687 | |||
| d678111f25 | |||
| 469e335fc3 | |||
| 8d9dbbea19 | |||
| d1088798e5 | |||
| b4353cd9ff | |||
| 0ea7386cb6 | |||
| 161ec6491d | |||
| 6d254281f8 | |||
| 5dc32a02a9 | |||
| ba468c3f99 | |||
| bca0b09cf4 | |||
| a6ddc72df3 | |||
| 759a4e6b75 | |||
| cc68f76180 | |||
| 6a03308dc1 | |||
| 4c33b1020a | |||
| 5237c91100 | |||
| a6f699687c | |||
| 9cb29a0f1c | |||
| 26e6aea6f7 | |||
| 122942a5ff | |||
| 360a762fb9 | |||
| 683f1eaf13 | |||
| d657f3825c | |||
| 638c6f3291 | |||
| 1bdceb8b42 | |||
| 164dfacab3 | |||
| d8781a4b41 | |||
| ee442d35b5 | |||
| c9264dc8de | |||
| 6672b902b0 | |||
| 614a275740 | |||
| a668dfa3eb | |||
| 21c6b65318 | |||
| 759b60889e | |||
| 5964ebc088 | |||
| dcb3e5d45d | |||
| fb9bc95e5f | |||
| d97268c18c | |||
| d9d731ab59 | |||
| 1c7ca765cb | |||
| de8d363c27 | |||
| 390f2d2cae | |||
| 697f85f805 | |||
| d97172b9cf | |||
| 72cbccab8c | |||
| b708343db0 | |||
| b416823f38 | |||
| 5bdc552492 | |||
| cdf34a262b | |||
| 4c84b28034 | |||
| b70f902190 | |||
| 094c87eb88 | |||
| 9b2539e378 | |||
| 0b73a90b15 | |||
| 76cfe4dc46 | |||
| c1a10cc0fa | |||
| b6b5ca52f2 | |||
| 5279731281 | |||
| 27ed3689f2 | |||
| d4f23e0e82 | |||
| 618f899440 | |||
| 2eb258d236 | |||
| 28df3f4ec1 | |||
| 3e37dc1eff | |||
| 5fd8637913 | |||
| 31db160fba | |||
| b6e63841cd | |||
| f051896296 | |||
| 92b93e862e | |||
| 8876f5c286 | |||
| e93c7e8bc1 | |||
| 16493b4594 | |||
| 938504b2d1 | |||
| 3eb718f6ac | |||
| 99781aeb8a | |||
| ffcd41f4dc | |||
| a7ed9be1de | |||
| 32fbf782fa | |||
| bfae72529c | |||
| 33c52bcef8 | |||
| 40c5e1d044 | |||
| 67e6f288eb | |||
| 823bafeeb9 | |||
| 750b9f1b57 | |||
| 0a4daccc0f | |||
| bc4905d2f4 | |||
| 533d646211 | |||
| 7c737ee6ad | |||
| 7aa08cf8e9 | |||
| 4144d2abde | |||
| 2a8fed166b | |||
| 60f01565da | |||
| 8a796a2eec | |||
| 83957d28e9 | |||
| fe3f1347d5 | |||
| 0a22e4e5cc | |||
| 1e35e0447f | |||
| 0ca487d5bd | |||
| 7828ed237d | |||
| b3eafcbd0b | |||
| affdc44f91 | |||
| 8adc8683b8 | |||
| 4d91b0a4fb | |||
| e62cdc9d9d | |||
| 12a0974efe | |||
| 367850fee5 | |||
| 09cc639466 | |||
| c3730d109b | |||
| f510cfb5ad | |||
|
|
45377ea61c | ||
|
|
b5748550d1 | ||
| 64c018b92e | |||
| 176672d7eb | |||
| 05d54e87c3 | |||
| 06c2a07fbc | |||
| 7cb1546934 | |||
| 60db762bcc | |||
| 5e840db04c | |||
| e724a74f9c | |||
| 48b7afcdc1 | |||
| 717da90c01 | |||
| 8054bb377d | |||
| 200258ff73 | |||
| fa73d939b5 | |||
| ca9e25abcb | |||
| 82831991b0 | |||
|
|
260e8d53ba | ||
|
|
0fd174ee0c | ||
|
|
ab4cd7c254 | ||
| 1f5468b1ac | |||
| b20aafe7a5 | |||
| 466d0a3a7a | |||
| 7281cb47c3 | |||
| eb5db3d6be | |||
| 8a534b84d0 | |||
| c523153654 | |||
| 82c85643c8 | |||
| 69892d566c | |||
| 2f41348c59 | |||
| 0d56ac7448 | |||
| 18a563ecd1 | |||
| 73df248d15 | |||
| 7c7674c822 | |||
| 65f606f573 | |||
| 0341505f8d | |||
| d4eee1718e | |||
| 9b042d8f45 | |||
|
|
ad0c847172 | ||
| f6d57b1e38 | |||
| b64d2b7478 | |||
|
|
f8c7f60cf9 | ||
| 44edef8ba1 | |||
| 647c5d2353 | |||
| 4ce1d2a370 | |||
| bcc53bf9f1 | |||
| f1e38e3bd3 | |||
| e095860b17 | |||
| 9cfc74aa88 | |||
| 7cd6ca3a5f | |||
| 9b660cb25a | |||
| 3d43d1896d | |||
| bcc17f6def | |||
| 8e3c334fa3 | |||
| 08299451bb | |||
| 59d6d25bdd | |||
| 9a516ab3c9 | |||
| 8fed342dc5 | |||
| f44643aa3e | |||
| 86c99596c4 | |||
| 2d0c08b2ce | |||
| e0aa963184 | |||
| afa3694cd7 | |||
| 48f4ea0c50 | |||
| 74c4ddda83 | |||
| f9b1e583df | |||
| 7c8e0d8481 | |||
| 6d2ec4cc0b | |||
| 898097cdb5 | |||
| 689a1b355a | |||
| 3b3330bd54 | |||
| 511fad3950 | |||
| f5f137396e | |||
| 0d78e9b8f5 | |||
| 8258d9f43f | |||
| 01f3335238 | |||
| 1d0c758e00 | |||
| 711f7d12e8 | |||
| 43d89699a9 |
424
COPILOT_CONTEXT.md
Normal file
424
COPILOT_CONTEXT.md
Normal file
@@ -0,0 +1,424 @@
|
|||||||
|
# EnvelopeGenerator — AI Context Reference
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Digital document signing system with **unified Blazor WASM frontend** for both Senders and Receivers. Senders create envelopes and place signature fields. Receivers view PDFs, sign documents, export stamped PDFs.
|
||||||
|
|
||||||
|
**Primary Libraries:** DevExpress + PDF.js (PSPDFKit removed)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment Architecture
|
||||||
|
|
||||||
|
**Two Presentation Projects (Both Required):**
|
||||||
|
|
||||||
|
1. **EnvelopeGenerator.API** (ASP.NET Core Web API)
|
||||||
|
- Runs independently (development & production)
|
||||||
|
- **YARP Reverse Proxy** configured via `yarp.json`
|
||||||
|
- Proxies requests to:
|
||||||
|
- `EnvelopeGenerator.ReceiverUI` (Blazor WASM)
|
||||||
|
- External Auth.API service
|
||||||
|
- Serves as single entry point for all requests
|
||||||
|
|
||||||
|
2. **EnvelopeGenerator.ReceiverUI** (Blazor WebAssembly)
|
||||||
|
- Runs on separate host/port
|
||||||
|
- Accessed **only through API proxy** (not directly)
|
||||||
|
- Serves static files (HTML, JS, CSS, WASM)
|
||||||
|
|
||||||
|
**Request Flow:**
|
||||||
|
```
|
||||||
|
Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
|
||||||
|
? Auth.API:9090 (External Auth Service)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Configuration:** `EnvelopeGenerator.API/yarp.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ReceiverUI Route Structure
|
||||||
|
|
||||||
|
### Root Route
|
||||||
|
| Route | File | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `/` | `Index.razor` | Application entry point (landing page). |
|
||||||
|
|
||||||
|
### Sender Routes
|
||||||
|
| Route | File | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `/sender/login` | `LoginSenderPage.razor` | Username/password authentication |
|
||||||
|
| `/sender` | `EnvelopeSenderPage.razor` | Sender dashboard (envelope list) |
|
||||||
|
|
||||||
|
### Receiver Routes
|
||||||
|
| Route | File | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `/envelope/login/{EnvelopeKey}` | `LoginReceiverPage.razor` | Access code authentication for specific envelope |
|
||||||
|
| `/envelope/{EnvelopeKey}` | `EnvelopeReceiverPage.razor` | View & sign envelope (PDF.js viewer) |
|
||||||
|
|
||||||
|
**Multi-Envelope Support:** Receivers can login to multiple envelopes simultaneously (per-envelope cookie authentication).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture Evolution
|
||||||
|
|
||||||
|
### Old Architecture (Deprecated)
|
||||||
|
- **Sender UI:** `EnvelopeGenerator.Web` (Razor Pages + PSPDFKit)
|
||||||
|
- **Receiver UI:** `EnvelopeGenerator.ReceiverUI` (Blazor WASM + PDF.js)
|
||||||
|
- **Backend:** `EnvelopeGenerator.API`
|
||||||
|
|
||||||
|
### Current Architecture
|
||||||
|
- **Unified Frontend:** `EnvelopeGenerator.ReceiverUI` (Blazor WASM) — **Both Senders & Receivers**
|
||||||
|
- **Backend:** `EnvelopeGenerator.API` — **Both Senders & Receivers**
|
||||||
|
- **Libraries:** DevExpress + PDF.js
|
||||||
|
- **PSPDFKit:** **REMOVED**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Solution Structure
|
||||||
|
|
||||||
|
| Project | Target | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `EnvelopeGenerator.API` | net8.0 | ASP.NET Core Web API. Backend for **both Senders & Receivers**. Auth, PDF serving, signature endpoints. |
|
||||||
|
| `EnvelopeGenerator.ReceiverUI` | net8.0 WASM | **Unified Blazor WebAssembly Frontend**. UI for **both Senders & Receivers**. YARP proxy to API. |
|
||||||
|
| `EnvelopeGenerator.Web` | net7/8/9 | **DEPRECATED.** Legacy Razor Pages (Sender UI). No longer used. |
|
||||||
|
| `EnvelopeGenerator.Application` | multi | MediatR CQRS handlers. Business logic. |
|
||||||
|
| `EnvelopeGenerator.Domain` | multi | Domain models, constants, interfaces. |
|
||||||
|
| `EnvelopeGenerator.Infrastructure` | multi | EF Core repos, DB context. |
|
||||||
|
| `EnvelopeGenerator.PdfEditor` | multi | iText7 utilities (NOT used in ReceiverUI). |
|
||||||
|
| `EnvelopeGenerator.DependencyInjection` | multi | DI registration helpers. |
|
||||||
|
| **VB.NET projects** (Service/Form/BBTests) | net462 | **Legacy. Do NOT touch.** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Files & Routes
|
||||||
|
|
||||||
|
| File | Route/Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `ReceiverUI/Pages/Index.razor` | `/` — Application entry point (landing page). |
|
||||||
|
| `ReceiverUI/Pages/EnvelopeSenderPage.razor` | `/sender` — Sender dashboard (envelope list). |
|
||||||
|
| `ReceiverUI/Pages/EnvelopeReceiverPage.razor` | `/envelope/{key}` — Receiver PDF viewer & signing. |
|
||||||
|
| `ReceiverUI/Pages/LoginSenderPage.razor` | `/sender/login` — Sender username/password auth. |
|
||||||
|
| `ReceiverUI/Pages/LoginReceiverPage.razor` | `/envelope/login/{EnvelopeKey}` — Receiver access code auth. |
|
||||||
|
| `ReceiverUI/wwwroot/js/pdf-viewer.js` | PDF.js wrapper (zoom, pagination, thumbnails). |
|
||||||
|
| `ReceiverUI/wwwroot/js/receiver-signature.js` | Signature pad (draw/type/image). |
|
||||||
|
| `ReceiverUI/wwwroot/css/envelope-viewer.css` | EnvelopeViewer styles. |
|
||||||
|
| `ReceiverUI/Services/AuthService.cs` | Receiver + Sender authentication. |
|
||||||
|
| `ReceiverUI/Services/SignatureCacheService.cs` | Signature caching (Redis/SQL). |
|
||||||
|
| `API/Controllers/CacheController.cs` | Signature cache endpoints. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Coordinate System — CRITICAL
|
||||||
|
|
||||||
|
**Database Format:** INCHES (GdPicture14 native)
|
||||||
|
**Origin:** Top-left corner
|
||||||
|
**Axes:** X right, Y down
|
||||||
|
|
||||||
|
### Conversion Formulas
|
||||||
|
|
||||||
|
| From INCHES to | Formula | Example |
|
||||||
|
|---|---|---|
|
||||||
|
| **DevExpress DX** | `x_DX = x_inches * 100` | 1.5" ? 150 DX |
|
||||||
|
| **PDF Points** | `x_pt = x_inches * 72` | 1.5" ? 108 pt |
|
||||||
|
| **PDF.js Pixels** | Normalize ? scale | `(x_inches / pageWidth) * canvasWidth * scale` |
|
||||||
|
|
||||||
|
**A4 Dimensions:**
|
||||||
|
- Width: 8.27" = 595pt = 827 DX
|
||||||
|
- Height: 11.69" = 842pt = 1169 DX
|
||||||
|
|
||||||
|
### Unit Systems
|
||||||
|
|
||||||
|
| System | Unit | Origin | Y-Axis |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **Database (GdPicture14)** | Inches | Top-left | Down |
|
||||||
|
| PDF.js | Pixels | Top-left | Down |
|
||||||
|
| iText7 PDF | Points (1/72") | **Bottom-left** | **Up** (flip required) |
|
||||||
|
| ~~PSPDFKit~~ | ~~Points~~ | ~~Top-left~~ | **REMOVED** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## EnvelopeReceiver — PDF.js Viewer & Signing
|
||||||
|
|
||||||
|
**Route:** `/envelope/{EnvelopeKey}`
|
||||||
|
**Tech:** PDF.js 3.11.174 + Blazor WASM + configurable quality
|
||||||
|
**File:** `ReceiverUI/Pages/EnvelopeReceiverPage.razor`
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
1. HiDPI/Retina support (4x quality)
|
||||||
|
2. Configurable quality (`appsettings.json`)
|
||||||
|
3. Unlimited zoom (50%-300%)
|
||||||
|
4. Ctrl+Wheel global zoom
|
||||||
|
5. Resizable thumbnail sidebar (150-400px, localStorage)
|
||||||
|
6. Responsive (desktop/mobile)
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
**File:** `ReceiverUI/wwwroot/appsettings.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"PdfViewer": {
|
||||||
|
"ThumbnailBaseScale": 0.75,
|
||||||
|
"ThumbnailEnableHiDPI": true,
|
||||||
|
"MainCanvasEnableHiDPI": true,
|
||||||
|
"ZoomStepPercentage": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### JavaScript API
|
||||||
|
**File:** `ReceiverUI/wwwroot/js/pdf-viewer.js`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
window.pdfViewer = {
|
||||||
|
initialize(canvasId, pdfDataUrl, dotNetRef),
|
||||||
|
renderPage(num),
|
||||||
|
renderSignatureButtons(signatures, pageNum, dotNetRef),
|
||||||
|
applySignature(signatureId, dataUrl, fullName, position, place),
|
||||||
|
zoomIn(), zoomOut(), dispose()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Signature Workflow — EnvelopeReceiver
|
||||||
|
|
||||||
|
**IMPORTANT:** iText7 NOT used (GPL license issue). Client-side overlay system only.
|
||||||
|
|
||||||
|
### Workflow Steps
|
||||||
|
|
||||||
|
1. **Page Load:**
|
||||||
|
- Check `SignatureCacheService` for cached signature
|
||||||
|
- If cached ? skip popup, load signature
|
||||||
|
- If not ? show automatic popup (mandatory)
|
||||||
|
|
||||||
|
2. **Signature Popup (DxPopup):**
|
||||||
|
- **Cannot close** (no X, no ESC, no outside-click)
|
||||||
|
- **3 Tabs:** Draw (canvas) / Text (font select) / Image (upload)
|
||||||
|
- **Required:** Full name, Place
|
||||||
|
- **Optional:** Position
|
||||||
|
- **Save ?** Store in `_capturedSignature`, cache via API
|
||||||
|
|
||||||
|
3. **Signature Buttons:**
|
||||||
|
- Render purple "Unterschreiben" buttons at signature field positions
|
||||||
|
- Coordinates: INCHES ? POINTS ? Pixels (scaled)
|
||||||
|
- File: `pdf-viewer.js` ? `renderSignatureButtons()`
|
||||||
|
|
||||||
|
4. **Apply Signature (Click "Unterschreiben"):**
|
||||||
|
- JS: Remove button, create HTML overlay
|
||||||
|
- Format: Image + separator + text (Name, Position, Place, Date)
|
||||||
|
- **NOT stamped on PDF bytes** (visual overlay only)
|
||||||
|
|
||||||
|
5. **Re-rendering:**
|
||||||
|
- Zoom/Page change ? recalculate button positions
|
||||||
|
- Session state: `_capturedSignature` (lost on refresh)
|
||||||
|
|
||||||
|
### Data Model
|
||||||
|
**File:** `ReceiverUI/Models/SignatureCaptureDto.cs`
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public sealed record SignatureCaptureDto {
|
||||||
|
public required string DataUrl { get; init; } // base64 PNG
|
||||||
|
public required string FullName { get; init; }
|
||||||
|
public string Position { get; init; } = ""; // Optional
|
||||||
|
public required string Place { get; init; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Signature Caching
|
||||||
|
|
||||||
|
**Purpose:** Persist signature across page refreshes (distributed cache: Redis/SQL)
|
||||||
|
|
||||||
|
### API Endpoints
|
||||||
|
**Controller:** `API/Controllers/CacheController.cs`
|
||||||
|
|
||||||
|
- `POST /api/Cache/SignatureCapture/{envelopeKey}` — Save
|
||||||
|
- `GET /api/Cache/SignatureCapture/{envelopeKey}` — Load
|
||||||
|
- `DELETE /api/Cache/SignatureCapture/{envelopeKey}` — Delete
|
||||||
|
|
||||||
|
**Cache Key Format:**
|
||||||
|
```
|
||||||
|
signature:91751687-8ae6-4777-bf5f-b8846085e62e:{envelopeKey}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Configuration:** `appsettings.json`
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Cache": {
|
||||||
|
"SignatureCacheExpiration": null // or "02:00:00" for 2h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service
|
||||||
|
**File:** `ReceiverUI/Services/SignatureCacheService.cs`
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class SignatureCacheService {
|
||||||
|
Task SaveSignatureAsync(string envelopeKey, SignatureCaptureDto signature);
|
||||||
|
Task<SignatureCaptureDto?> GetSignatureAsync(string envelopeKey);
|
||||||
|
Task DeleteSignatureAsync(string envelopeKey);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error Handling:** Fire-and-forget saves, graceful degradation on load failure.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sender Login
|
||||||
|
|
||||||
|
**Route:** `/sender/login`
|
||||||
|
**File:** `ReceiverUI/Pages/LoginSenderPage.razor`
|
||||||
|
**Tech:** Bootstrap 5 + DevExpress Blazing Berry theme
|
||||||
|
|
||||||
|
### AuthService Extension
|
||||||
|
**File:** `ReceiverUI/Services/AuthService.cs`
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public enum SenderLoginResult { Success, InvalidCredentials, Error }
|
||||||
|
|
||||||
|
public async Task<SenderLoginResult> LoginSenderAsync(string username, string password) {
|
||||||
|
var response = await http.PostAsJsonAsync(
|
||||||
|
$"{_api.BaseUrl}/api/auth?cookie=true",
|
||||||
|
new { username, password });
|
||||||
|
|
||||||
|
return response.StatusCode switch {
|
||||||
|
HttpStatusCode.OK => SenderLoginResult.Success,
|
||||||
|
HttpStatusCode.Unauthorized => SenderLoginResult.InvalidCredentials,
|
||||||
|
_ => SenderLoginResult.Error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Integration
|
||||||
|
**Endpoint:** `POST /api/auth?cookie=true`
|
||||||
|
|
||||||
|
**Request:**
|
||||||
|
```json
|
||||||
|
{ "username": "TekH", "password": "***" }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
- `200 OK` ? Cookie set, redirect to `/sender`
|
||||||
|
- `401 Unauthorized` ? Show error: "Ungültige Anmeldedaten"
|
||||||
|
- Other ? Show error: "Serverfehler"
|
||||||
|
|
||||||
|
**Cookie:** HTTP-only, Secure (HTTPS), SameSite=Strict
|
||||||
|
|
||||||
|
### UI Flow
|
||||||
|
1. User enters username + password
|
||||||
|
2. Click "Anmelden" or press Enter
|
||||||
|
3. Call `AuthService.LoginSenderAsync()`
|
||||||
|
4. Success ? `Navigation.NavigateTo("/sender", forceLoad: true)`
|
||||||
|
5. Error ? Display alert
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Receiver Login
|
||||||
|
|
||||||
|
**Route:** `/envelope/login/{EnvelopeKey}`
|
||||||
|
**File:** `ReceiverUI/Pages/LoginReceiverPage.razor`
|
||||||
|
|
||||||
|
**Multi-Envelope Support:** Cookies are stored per-envelope (e.g., `AuthTokenSignFLOWReceiver.{envelopeKey}`), allowing simultaneous authentication for multiple envelopes in the same browser session.
|
||||||
|
|
||||||
|
### AuthService Method
|
||||||
|
```csharp
|
||||||
|
public enum EnvelopeLoginResult { Success, InvalidCode, NotFound, Error }
|
||||||
|
|
||||||
|
public async Task<EnvelopeLoginResult> LoginEnvelopeReceiverAsync(string key, string accessCode) {
|
||||||
|
var form = new MultipartFormDataContent();
|
||||||
|
form.Add(new StringContent(accessCode), "AccessCode");
|
||||||
|
|
||||||
|
var response = await http.PostAsync(
|
||||||
|
$"{_api.BaseUrl}/api/Auth/envelope-receiver/{Uri.EscapeDataString(key)}", form);
|
||||||
|
|
||||||
|
return response.StatusCode switch {
|
||||||
|
HttpStatusCode.OK => EnvelopeLoginResult.Success,
|
||||||
|
HttpStatusCode.Unauthorized => EnvelopeLoginResult.InvalidCode,
|
||||||
|
HttpStatusCode.NotFound => EnvelopeLoginResult.NotFound,
|
||||||
|
_ => EnvelopeLoginResult.Error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Success:** Redirect to `/envelope/{key}`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## NuGet Packages (ReceiverUI)
|
||||||
|
|
||||||
|
| Package | Version | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `DevExpress.Blazor.*` | 25.2.3 | UI components (grids, popups, etc.) |
|
||||||
|
| `SkiaSharp.*` | 3.119.1 | WASM rendering |
|
||||||
|
| ~~`itext`~~ | ~~8.0.5~~ | **NOT USED** (GPL license) |
|
||||||
|
|
||||||
|
**External CDN:**
|
||||||
|
- PDF.js 3.11.174: `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mistakes History — Do NOT Repeat
|
||||||
|
|
||||||
|
| Mistake | Why Wrong |
|
||||||
|
|---|---|
|
||||||
|
| Using iText7 in EnvelopeReceiver | GPL license issue. Use overlay system instead. |
|
||||||
|
| Using PSPDFKit | Removed from architecture. Use PDF.js + DevExpress. |
|
||||||
|
| Hardcoded quality values in PDF.js | Use `appsettings.json` for configurability. |
|
||||||
|
| Complex toolbar layouts | User wants simplicity. Keep horizontal layout. |
|
||||||
|
| Over-designed UI (gradients/badges) | User prefers simple text labels. |
|
||||||
|
| Ignoring "revert" instructions | Revert HTML structure, not just CSS. |
|
||||||
|
| `BottomMarginBand` for signatures | Repeats on every page. Use DetailBand. |
|
||||||
|
| `imageY = (page-1) * 1169 + ann.Y` | Inflates DetailBand. Calculate per-page. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Development Notes
|
||||||
|
|
||||||
|
### Deprecated Projects
|
||||||
|
**DO NOT USE:**
|
||||||
|
- `EnvelopeGenerator.Web` (Razor Pages) — Replaced by unified ReceiverUI
|
||||||
|
- PSPDFKit — Removed, use PDF.js + DevExpress instead
|
||||||
|
|
||||||
|
### Legacy Projects (VB.NET)
|
||||||
|
**DO NOT TOUCH:** `EnvelopeGenerator.Service`, `EnvelopeGenerator.Form`, `EnvelopeGenerator.BBTests`
|
||||||
|
|
||||||
|
### Signature Coordinate Evidence
|
||||||
|
**File:** `EnvelopeGenerator.Form/frmFieldEditor.vb` (VB.NET)
|
||||||
|
|
||||||
|
```vb
|
||||||
|
Private Const SIGNATURE_WIDTH As Single = 1.77 ' inches
|
||||||
|
Private Const SIGNATURE_HEIGHT As Single = 1.96 ' inches
|
||||||
|
|
||||||
|
Sub LoadAnnotation(pElement As Signature, ...)
|
||||||
|
oAnnotation.Left = CSng(pElement.X) ' Direct INCHES assignment
|
||||||
|
oAnnotation.Top = CSng(pElement.Y)
|
||||||
|
End Sub
|
||||||
|
```
|
||||||
|
|
||||||
|
Proves database uses INCHES natively.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### When working with coordinates:
|
||||||
|
1. **Database ? UI:** INCHES × 72 = PDF Points
|
||||||
|
2. **UI ? Display:** Points × scale = Pixels
|
||||||
|
3. **iText7 stamping:** Flip Y-axis (top-down ? bottom-up)
|
||||||
|
|
||||||
|
### When adding features:
|
||||||
|
1. Check `Mistakes History` first
|
||||||
|
2. Prefer simplicity over complexity
|
||||||
|
3. Use `appsettings.json` for configuration
|
||||||
|
4. Keep consistent with existing design (Bootstrap 5 + Blazing Berry)
|
||||||
|
5. **Unified frontend:** ReceiverUI serves both Senders and Receivers
|
||||||
|
|
||||||
|
### When debugging:
|
||||||
|
1. **Coordinates:** Always check unit system (inches/points/pixels)
|
||||||
|
2. **Authentication:** Check cookie name/domain/SameSite
|
||||||
|
3. **Cache:** Check Redis/SQL connection + key format
|
||||||
|
4. **Frontend confusion:** Only use ReceiverUI (Web is deprecated)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated:** Session 19 (Razor file naming convention + Index route proxy)
|
||||||
17
EnvelopeGenerator.API/AuthScheme.cs
Normal file
17
EnvelopeGenerator.API/AuthScheme.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace EnvelopeGenerator.API;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class AuthScheme
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Scheme name used for per-envelope receiver JWT authentication.
|
||||||
|
/// </summary>
|
||||||
|
public const string Receiver = "EnvelopeGenerator.API.ReceiverJWT";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scheme name used for per-envelope sender JWT authentication.
|
||||||
|
/// </summary>
|
||||||
|
public const string Sender = "EnvelopeGenerator.API.SenderJWT";
|
||||||
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.DTO;
|
using DigitalData.Core.Abstraction.Application.DTO;
|
||||||
using DigitalData.Core.Exceptions;
|
using DigitalData.Core.Exceptions;
|
||||||
|
using EnvelopeGenerator.API.Extensions;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||||
using EnvelopeGenerator.Application.Common.Notifications.DocSigned;
|
using EnvelopeGenerator.Application.Common.Notifications.DocSigned;
|
||||||
|
using EnvelopeGenerator.Application.Common.Notifications.RemoveSignature;
|
||||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
||||||
using EnvelopeGenerator.Application.Histories.Queries;
|
using EnvelopeGenerator.Application.Histories.Queries;
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using EnvelopeGenerator.API.Extensions;
|
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
@@ -59,8 +61,8 @@ public class AnnotationController : ControllerBase
|
|||||||
[Obsolete("PSPDF Kit will no longer be used.")]
|
[Obsolete("PSPDF Kit will no longer be used.")]
|
||||||
public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation? psPdfKitAnnotation = null, CancellationToken cancel = default)
|
public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation? psPdfKitAnnotation = null, CancellationToken cancel = default)
|
||||||
{
|
{
|
||||||
var signature = User.GetReceiverSignatureOfReceiver();
|
var signature = User.ReceiverSignature();
|
||||||
var uuid = User.GetEnvelopeUuidOfReceiver();
|
var uuid = User.EnvelopeUuid();
|
||||||
|
|
||||||
var envelopeReceiver = await _mediator.ReadEnvelopeReceiverAsync(uuid, signature, cancel).ThrowIfNull(Exceptions.NotFound);
|
var envelopeReceiver = await _mediator.ReadEnvelopeReceiverAsync(uuid, signature, cancel).ThrowIfNull(Exceptions.NotFound);
|
||||||
|
|
||||||
@@ -72,12 +74,24 @@ public class AnnotationController : ControllerBase
|
|||||||
else if (await _mediator.AnyHistoryAsync(uuid, new[] { EnvelopeStatus.EnvelopeRejected, EnvelopeStatus.DocumentRejected }, cancel))
|
else if (await _mediator.AnyHistoryAsync(uuid, new[] { EnvelopeStatus.EnvelopeRejected, EnvelopeStatus.DocumentRejected }, cancel))
|
||||||
return Problem(statusCode: StatusCodes.Status423Locked);
|
return Problem(statusCode: StatusCodes.Status423Locked);
|
||||||
|
|
||||||
var docSignedNotification = await _mediator
|
var envelopeReceiverDto = await _mediator.ReadEnvelopeReceiverAsync(uuid, signature, cancel);
|
||||||
.ReadEnvelopeReceiverAsync(uuid, signature, cancel)
|
var docSignedNotification = envelopeReceiverDto is not null
|
||||||
.ToDocSignedNotification(psPdfKitAnnotation)
|
? new DocSignedNotification { EnvelopeReceiver = envelopeReceiverDto, PsPdfKitAnnotation = psPdfKitAnnotation }
|
||||||
?? throw new NotFoundException("Envelope receiver is not found.");
|
: throw new NotFoundException("Envelope receiver is not found.");
|
||||||
|
|
||||||
await _mediator.PublishSafely(docSignedNotification, cancel);
|
try
|
||||||
|
{
|
||||||
|
await _mediator.Publish(docSignedNotification, cancel);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
await _mediator.Publish(new RemoveSignatureNotification()
|
||||||
|
{
|
||||||
|
EnvelopeId = docSignedNotification.EnvelopeReceiver.EnvelopeId,
|
||||||
|
ReceiverId = docSignedNotification.EnvelopeReceiver.ReceiverId
|
||||||
|
}, cancel);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
@@ -92,9 +106,9 @@ public class AnnotationController : ControllerBase
|
|||||||
[Obsolete("Use MediatR")]
|
[Obsolete("Use MediatR")]
|
||||||
public async Task<IActionResult> Reject([FromBody] string? reason = null)
|
public async Task<IActionResult> Reject([FromBody] string? reason = null)
|
||||||
{
|
{
|
||||||
var signature = User.GetReceiverSignatureOfReceiver();
|
var signature = User.ReceiverSignature();
|
||||||
var uuid = User.GetEnvelopeUuidOfReceiver();
|
var uuid = User.EnvelopeUuid();
|
||||||
var mail = User.GetReceiverMailOfReceiver();
|
var mail = User.ReceiverMail();
|
||||||
|
|
||||||
var envRcvRes = await _envelopeReceiverService.ReadByUuidSignatureAsync(uuid: uuid, signature: signature);
|
var envRcvRes = await _envelopeReceiverService.ReadByUuidSignatureAsync(uuid: uuid, signature: signature);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using DigitalData.Auth.Claims;
|
||||||
using EnvelopeGenerator.API.Controllers.Interfaces;
|
using EnvelopeGenerator.API.Controllers.Interfaces;
|
||||||
using EnvelopeGenerator.API.Models;
|
using EnvelopeGenerator.API.Models;
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
@@ -73,4 +74,44 @@ public partial class AuthController(IOptions<AuthTokenKeys> authTokenKeyOptions,
|
|||||||
=> role is not null && !User.IsInRole(role)
|
=> role is not null && !User.IsInRole(role)
|
||||||
? Unauthorized()
|
? Unauthorized()
|
||||||
: Ok();
|
: Ok();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the caller holds a valid per-envelope receiver token for the given envelope key.
|
||||||
|
/// The request must carry a cookie named <c>AuthTokenSignFLOWReceiver.{envelopeKey}</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="envelopeKey">The unique envelope key extracted from the route.</param>
|
||||||
|
/// <response code="200">Valid per-envelope token found.</response>
|
||||||
|
/// <response code="401">Token is missing, expired or invalid.</response>
|
||||||
|
[ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||||
|
[Authorize(Policy = AuthPolicy.Receiver)]
|
||||||
|
[HttpGet("check/envelope/{envelopeKey}")]
|
||||||
|
public IActionResult CheckEnvelopeReceiver([FromRoute] string envelopeKey) => Ok();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the per-envelope receiver cookie for the given envelope key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="envelopeKey">The unique envelope key whose cookie should be deleted.</param>
|
||||||
|
/// <response code="200">Cookie successfully deleted.</response>
|
||||||
|
[ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
|
||||||
|
[HttpPost("logout/envelope/{envelopeKey}")]
|
||||||
|
public IActionResult LogoutEnvelopeReceiver([FromRoute] string envelopeKey)
|
||||||
|
{
|
||||||
|
var cookieName = CookieNames.GetEnvelopeReceiverCookieName(authTokenKeys.Cookie, envelopeKey);
|
||||||
|
Response.Cookies.Delete(cookieName);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all per-envelope receiver cookies from the current request.
|
||||||
|
/// </summary>
|
||||||
|
/// <response code="200">All envelope receiver cookies successfully deleted.</response>
|
||||||
|
[ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
|
||||||
|
[HttpPost("logout/envelope")]
|
||||||
|
public IActionResult LogoutAllEnvelopeReceivers()
|
||||||
|
{
|
||||||
|
foreach (var cookieName in Request.Cookies.Keys.Where(k => CookieNames.IsEnvelopeReceiverCookie(k, authTokenKeys.Cookie)))
|
||||||
|
Response.Cookies.Delete(cookieName);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
84
EnvelopeGenerator.API/Controllers/CacheController.cs
Normal file
84
EnvelopeGenerator.API/Controllers/CacheController.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Text.Json;
|
||||||
|
using EnvelopeGenerator.API.Options;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
using EnvelopeGenerator.API.Extensions;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages cached data for receivers using distributed cache.
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[Authorize(Policy = AuthPolicy.Receiver)]
|
||||||
|
public class CacheController(
|
||||||
|
IDistributedCache cache,
|
||||||
|
IOptions<CacheOptions> cacheOptions) : ControllerBase
|
||||||
|
{
|
||||||
|
private const string SignatureCacheKeyPrefix = "envelope-generator.receiver-ui.signature:";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores a receiver's signature in cache for the specified envelope.
|
||||||
|
/// </summary>
|
||||||
|
[Authorize(Policy = AuthPolicy.Receiver)]
|
||||||
|
[HttpPost("SignatureCapture/{envelopeKey}")]
|
||||||
|
public async Task<IActionResult> SaveSignature(
|
||||||
|
[FromRoute] string envelopeKey,
|
||||||
|
[FromBody] SignatureCacheRequest request,
|
||||||
|
CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var cacheKey = $"{SignatureCacheKeyPrefix}{User.ReceiverSignature()}";
|
||||||
|
var json = JsonSerializer.Serialize(request);
|
||||||
|
|
||||||
|
var options = cacheOptions.Value.SignatureCacheExpiration.HasValue
|
||||||
|
? new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = cacheOptions.Value.SignatureCacheExpiration.Value }
|
||||||
|
: null;
|
||||||
|
|
||||||
|
await cache.SetStringAsync(cacheKey, json, options ?? new DistributedCacheEntryOptions(), cancel);
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a cached signature for the specified envelope.
|
||||||
|
/// </summary>
|
||||||
|
[Authorize(Policy = AuthPolicy.Receiver)]
|
||||||
|
[HttpGet("SignatureCapture/{envelopeKey}")]
|
||||||
|
public async Task<IActionResult> GetSignature([FromRoute] string envelopeKey, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var cacheKey = $"{SignatureCacheKeyPrefix}{User.ReceiverSignature()}";
|
||||||
|
var json = await cache.GetStringAsync(cacheKey, cancel);
|
||||||
|
|
||||||
|
if (json is null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var signature = JsonSerializer.Deserialize<SignatureCacheRequest>(json);
|
||||||
|
return Ok(signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes a cached signature for the specified envelope.
|
||||||
|
/// </summary>
|
||||||
|
[Authorize(Policy = AuthPolicy.Receiver)]
|
||||||
|
[HttpDelete("SignatureCapture/{envelopeKey}")]
|
||||||
|
public async Task<IActionResult> DeleteSignature([FromRoute] string envelopeKey, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var cacheKey = $"{SignatureCacheKeyPrefix}{User.ReceiverSignature()}";
|
||||||
|
await cache.RemoveAsync(cacheKey, cancel);
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request model for caching signature data.
|
||||||
|
/// </summary>
|
||||||
|
public sealed record SignatureCacheRequest(
|
||||||
|
string DataUrl,
|
||||||
|
string FullName,
|
||||||
|
string Place,
|
||||||
|
string? Position = null);
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using DigitalData.Auth.Claims;
|
||||||
using EnvelopeGenerator.API.Controllers.Interfaces;
|
using EnvelopeGenerator.API.Controllers.Interfaces;
|
||||||
using EnvelopeGenerator.API.Extensions;
|
using EnvelopeGenerator.API.Extensions;
|
||||||
using EnvelopeGenerator.Application.Documents.Queries;
|
using EnvelopeGenerator.Application.Documents.Queries;
|
||||||
@@ -14,10 +15,9 @@ namespace EnvelopeGenerator.API.Controllers;
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Initializes a new instance of the <see cref="DocumentController"/> class.
|
/// Initializes a new instance of the <see cref="DocumentController"/> class.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[Authorize]
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class DocumentController(IMediator mediator, IAuthorizationService authService) : ControllerBase, IAuthController
|
public class DocumentController(IMediator mediator, IAuthorizationService authService, ILogger<DocumentController> logger) : ControllerBase, IAuthController
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
@@ -51,7 +51,7 @@ public class DocumentController(IMediator mediator, IAuthorizationService authSe
|
|||||||
if (query is not null)
|
if (query is not null)
|
||||||
return BadRequest("Query parameters are not allowed for receiver role.");
|
return BadRequest("Query parameters are not allowed for receiver role.");
|
||||||
|
|
||||||
var envelopeId = User.GetEnvelopeIdOfReceiver();
|
var envelopeId = User.EnvelopeId();
|
||||||
var receiverDoc = await mediator.Send(new ReadDocumentQuery { EnvelopeId = envelopeId }, cancel);
|
var receiverDoc = await mediator.Send(new ReadDocumentQuery { EnvelopeId = envelopeId }, cancel);
|
||||||
return receiverDoc.ByteData is byte[] receiverDocByte
|
return receiverDoc.ByteData is byte[] receiverDocByte
|
||||||
? File(receiverDocByte, "application/octet-stream")
|
? File(receiverDocByte, "application/octet-stream")
|
||||||
@@ -60,4 +60,25 @@ public class DocumentController(IMediator mediator, IAuthorizationService authSe
|
|||||||
|
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the document for the specified envelope key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="envelopeKey"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Authorize(Policy = AuthPolicy.Receiver)]
|
||||||
|
[HttpGet("{envelopeKey}")]
|
||||||
|
public async Task<IActionResult> GetDocumentOfReceiver(string envelopeKey, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
int envelopeId = User.EnvelopeId();
|
||||||
|
|
||||||
|
var senderDoc = await mediator.Send(new ReadDocumentQuery() { EnvelopeId = envelopeId }, cancel);
|
||||||
|
|
||||||
|
if (senderDoc.ByteData is not byte[] senderDocByte)
|
||||||
|
return NotFound("Document is empty.");
|
||||||
|
|
||||||
|
Response.Headers.ContentDisposition = $"inline; filename=\"{envelopeKey}.pdf\"";
|
||||||
|
return File(senderDocByte, "application/pdf");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class EnvelopeController : ControllerBase
|
|||||||
/// <response code="401">Der Benutzer ist nicht authentifiziert.</response>
|
/// <response code="401">Der Benutzer ist nicht authentifiziert.</response>
|
||||||
/// <response code="403">Der Benutzer hat keine Berechtigung, auf die Ressource zuzugreifen.</response>
|
/// <response code="403">Der Benutzer hat keine Berechtigung, auf die Ressource zuzugreifen.</response>
|
||||||
/// <response code="500">Ein unerwarteter Fehler ist aufgetreten.</response>
|
/// <response code="500">Ein unerwarteter Fehler ist aufgetreten.</response>
|
||||||
[Authorize]
|
[Authorize(AuthenticationSchemes = AuthScheme.Sender)]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> GetAsync([FromQuery] ReadEnvelopeQuery envelope)
|
public async Task<IActionResult> GetAsync([FromQuery] ReadEnvelopeQuery envelope)
|
||||||
{
|
{
|
||||||
@@ -98,7 +98,7 @@ public class EnvelopeController : ControllerBase
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> CreateAsync([FromBody] CreateEnvelopeCommand command)
|
public async Task<IActionResult> CreateAsync([FromBody] CreateEnvelopeCommand command)
|
||||||
{
|
{
|
||||||
var res = await _mediator.Send(command.Authorize(User.GetId()));
|
var res = await _mediator.Send(command.WithAuth(User.GetId()));
|
||||||
|
|
||||||
if (res is null)
|
if (res is null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using EnvelopeGenerator.Application.Common.SQL;
|
|||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
||||||
using EnvelopeGenerator.API.Extensions;
|
using EnvelopeGenerator.API.Extensions;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Controllers;
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
@@ -73,6 +74,24 @@ public class EnvelopeReceiverController : ControllerBase
|
|||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="envelopeKey"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Authorize(Policy = AuthPolicy.Receiver)]
|
||||||
|
[HttpGet("{envelopeKey}")]
|
||||||
|
public async Task<IActionResult> GetEnvelopeReceiverOfReceiver([FromRoute] string envelopeKey, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var er = await _mediator.Send(new ReadEnvelopeReceiverQuery()
|
||||||
|
{
|
||||||
|
Key = envelopeKey
|
||||||
|
}, cancel);
|
||||||
|
|
||||||
|
return Ok(er.SingleOrDefault());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ruft den Namen des zuletzt verwendeten Empfängers basierend auf der angegebenen E-Mail-Adresse ab.
|
/// Ruft den Namen des zuletzt verwendeten Empfängers basierend auf der angegebenen E-Mail-Adresse ab.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -181,7 +200,7 @@ public class EnvelopeReceiverController : ControllerBase
|
|||||||
SELECT @OUT_SUCCESS as [@OUT_SUCCESS];";
|
SELECT @OUT_SUCCESS as [@OUT_SUCCESS];";
|
||||||
|
|
||||||
foreach (var rcv in res.SentReceiver)
|
foreach (var rcv in res.SentReceiver)
|
||||||
foreach (var sign in request.Receivers.Where(r => r.EmailAddress == rcv.EmailAddress).FirstOrDefault()?.Signatures ?? Enumerable.Empty<Application.EnvelopeReceivers.Commands.Signature>())
|
foreach (var sign in request.Receivers.Where(r => r.EmailAddress == rcv.EmailAddress).FirstOrDefault()?.DocReceiverElements ?? Enumerable.Empty<Application.EnvelopeReceivers.Commands.DocReceiverElementCreateDto>())
|
||||||
{
|
{
|
||||||
using SqlConnection conn = new(_cnnStr);
|
using SqlConnection conn = new(_cnnStr);
|
||||||
conn.Open();
|
conn.Open();
|
||||||
|
|||||||
@@ -41,14 +41,14 @@ public class ReadOnlyController : ControllerBase
|
|||||||
[Obsolete("Use MediatR")]
|
[Obsolete("Use MediatR")]
|
||||||
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
|
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
|
||||||
{
|
{
|
||||||
var authReceiverMail = User.GetReceiverMailOfReceiver();
|
var authReceiverMail = User.ReceiverMail();
|
||||||
if (authReceiverMail is null)
|
if (authReceiverMail is null)
|
||||||
{
|
{
|
||||||
_logger.LogError("EmailAddress claim is not found in envelope-receiver-read-only creation process. Create DTO is:\n {dto}", JsonConvert.SerializeObject(createDto));
|
_logger.LogError("EmailAddress claim is not found in envelope-receiver-read-only creation process. Create DTO is:\n {dto}", JsonConvert.SerializeObject(createDto));
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
var envelopeId = User.GetEnvelopeIdOfReceiver();
|
var envelopeId = User.EnvelopeId();
|
||||||
|
|
||||||
createDto.AddedWho = authReceiverMail;
|
createDto.AddedWho = authReceiverMail;
|
||||||
createDto.EnvelopeId = envelopeId;
|
createDto.EnvelopeId = envelopeId;
|
||||||
|
|||||||
57
EnvelopeGenerator.API/Controllers/SignatureController.cs
Normal file
57
EnvelopeGenerator.API/Controllers/SignatureController.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using EnvelopeGenerator.API.Extensions;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
using EnvelopeGenerator.Application.Documents.Queries;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.API.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Authorize(Policy = AuthPolicy.Receiver)]
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class SignatureController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IMediator _mediator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="SignatureController"/>.
|
||||||
|
/// </summary>
|
||||||
|
public SignatureController(IMediator mediator)
|
||||||
|
{
|
||||||
|
_mediator = mediator;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: update to use signature query
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="envelopeKey"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Authorize(Policy = AuthPolicy.Receiver)]
|
||||||
|
[HttpGet("{envelopeKey}")]
|
||||||
|
public async Task<IActionResult> Get(string envelopeKey, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
int envelopeId = User.EnvelopeId();
|
||||||
|
|
||||||
|
int receiverId = User.ReceiverId();
|
||||||
|
|
||||||
|
var doc = await _mediator.Send(new ReadDocumentQuery() { EnvelopeId = envelopeId }, cancel);
|
||||||
|
|
||||||
|
if (doc.Elements is not IEnumerable<DocReceiverElementDto> docSignatures)
|
||||||
|
return NotFound("Document is empty.");
|
||||||
|
|
||||||
|
var rcvSignatures = docSignatures.Where(s => s.ReceiverId == receiverId).ToList();
|
||||||
|
|
||||||
|
if (rcvSignatures is null)
|
||||||
|
return NotFound("No signatures found for the current receiver.");
|
||||||
|
else
|
||||||
|
return Ok(rcvSignatures);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,12 @@ public sealed class AuthProxyDocumentFilter : IDocumentFilter
|
|||||||
/// <param name="swaggerDoc"></param>
|
/// <param name="swaggerDoc"></param>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
||||||
|
{
|
||||||
|
AddLoginOperation(swaggerDoc, context);
|
||||||
|
AddEnvelopeReceiverLoginOperation(swaggerDoc, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddLoginOperation(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
||||||
{
|
{
|
||||||
const string path = "/api/auth";
|
const string path = "/api/auth";
|
||||||
|
|
||||||
@@ -67,4 +73,51 @@ public sealed class AuthProxyDocumentFilter : IDocumentFilter
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AddEnvelopeReceiverLoginOperation(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
||||||
|
{
|
||||||
|
const string path = "/api/Auth/envelope-receiver/{key}";
|
||||||
|
|
||||||
|
var bodySchema = context.SchemaGenerator.GenerateSchema(typeof(EnvelopeReceiverLogin), context.SchemaRepository);
|
||||||
|
|
||||||
|
var operation = new OpenApiOperation
|
||||||
|
{
|
||||||
|
Summary = "Envelope receiver login (auth-hub proxy)",
|
||||||
|
Description = "Proxies the envelope receiver login to the auth service. " +
|
||||||
|
"The `cookie` query parameter is always forwarded as `true` so the auth service sets the per-envelope cookie automatically.",
|
||||||
|
Tags = [new() { Name = "Auth" }],
|
||||||
|
Parameters =
|
||||||
|
{
|
||||||
|
new OpenApiParameter
|
||||||
|
{
|
||||||
|
Name = "key",
|
||||||
|
In = ParameterLocation.Path,
|
||||||
|
Required = true,
|
||||||
|
Schema = new OpenApiSchema { Type = "string" },
|
||||||
|
Description = "The unique envelope receiver key."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RequestBody = new OpenApiRequestBody
|
||||||
|
{
|
||||||
|
Required = false,
|
||||||
|
Content =
|
||||||
|
{
|
||||||
|
["multipart/form-data"] = new OpenApiMediaType { Schema = bodySchema }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Responses =
|
||||||
|
{
|
||||||
|
["200"] = new OpenApiResponse { Description = "OK – per-envelope cookie set by auth service." },
|
||||||
|
["401"] = new OpenApiResponse { Description = "Unauthorized – invalid or missing access code." }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
swaggerDoc.Paths[path] = new OpenApiPathItem
|
||||||
|
{
|
||||||
|
Operations =
|
||||||
|
{
|
||||||
|
[OperationType.Post] = operation
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.API;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides custom claim types for envelope-related information.
|
|
||||||
/// </summary>
|
|
||||||
public static class EnvelopeClaimTypes
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Claim type for the title of an envelope.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly string Title = $"Envelope{nameof(Title)}";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Claim type for the ID of an envelope.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly string Id = $"Envelope{nameof(Id)}";
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net9.0</TargetFrameworks>
|
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
@@ -10,9 +10,9 @@
|
|||||||
<Authors>Digital Data GmbH</Authors>
|
<Authors>Digital Data GmbH</Authors>
|
||||||
<Company>Digital Data GmbH</Company>
|
<Company>Digital Data GmbH</Company>
|
||||||
<Product>EnvelopeGenerator.GeneratorAPI</Product>
|
<Product>EnvelopeGenerator.GeneratorAPI</Product>
|
||||||
<Version>1.2.3</Version>
|
<Version>1.4.0</Version>
|
||||||
<FileVersion>1.2.3</FileVersion>
|
<FileVersion>1.4.0</FileVersion>
|
||||||
<AssemblyVersion>1.2.3</AssemblyVersion>
|
<AssemblyVersion>1.4.0</AssemblyVersion>
|
||||||
<PackageOutputPath>Copyright © 2025 Digital Data GmbH. All rights reserved.</PackageOutputPath>
|
<PackageOutputPath>Copyright © 2025 Digital Data GmbH. All rights reserved.</PackageOutputPath>
|
||||||
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -30,11 +30,17 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AspNetCore.Scalar" Version="1.1.8" />
|
<PackageReference Include="AspNetCore.Scalar" Version="1.1.8" />
|
||||||
|
<PackageReference Include="DigitalData.Auth.Claims" Version="1.0.3" />
|
||||||
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.7" />
|
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.7" />
|
||||||
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
|
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
|
||||||
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="8.0.11" Condition="'$(TargetFramework)' == 'net8.0'" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.6" />
|
<PackageReference Include="itext" Version="8.0.5" />
|
||||||
|
<PackageReference Include="itext.bouncy-castle-adapter" Version="8.0.5" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4" Condition="'$(TargetFramework)' == 'net9.0'" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.11" Condition="'$(TargetFramework)' == 'net8.0'" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.6" Condition="'$(TargetFramework)' == 'net9.0'" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.17" Condition="'$(TargetFramework)' == 'net8.0'" />
|
||||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
|
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
|
||||||
<PackageReference Include="NLog" Version="5.2.5" />
|
<PackageReference Include="NLog" Version="5.2.5" />
|
||||||
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.0" />
|
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.0" />
|
||||||
@@ -71,6 +77,7 @@
|
|||||||
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
|
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
|
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
|
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
|
||||||
|
<ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
using System.Linq;
|
using DigitalData.Auth.Claims;
|
||||||
|
using Microsoft.IdentityModel.JsonWebTokens;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.API.Extensions;
|
namespace EnvelopeGenerator.API.Extensions;
|
||||||
|
|
||||||
@@ -11,7 +9,14 @@ namespace EnvelopeGenerator.API.Extensions;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ReceiverClaimExtensions
|
public static class ReceiverClaimExtensions
|
||||||
{
|
{
|
||||||
private static string GetRequiredClaimOfReceiver(this ClaimsPrincipal user, string claimType)
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <param name="claimType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="InvalidOperationException"></exception>
|
||||||
|
private static string GetRequiredClaimValue(this ClaimsPrincipal user, string claimType)
|
||||||
{
|
{
|
||||||
var value = user.FindFirstValue(claimType);
|
var value = user.FindFirstValue(claimType);
|
||||||
if (value is not null)
|
if (value is not null)
|
||||||
@@ -27,75 +32,65 @@ public static class ReceiverClaimExtensions
|
|||||||
throw new InvalidOperationException(message);
|
throw new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetRequiredClaimValue(this ClaimsPrincipal user, params string[] claimTypes)
|
||||||
|
{
|
||||||
|
foreach (var claimType in claimTypes.Where(t => !string.IsNullOrWhiteSpace(t)).Distinct())
|
||||||
|
{
|
||||||
|
var value = user.FindFirstValue(claimType);
|
||||||
|
if (!string.IsNullOrWhiteSpace(value))
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var identity = user.Identity;
|
||||||
|
var principalName = identity?.Name ?? "(anonymous)";
|
||||||
|
var authType = identity?.AuthenticationType ?? "(none)";
|
||||||
|
var availableClaims = string.Join(", ", user.Claims.Select(c => $"{c.Type}={c.Value}"));
|
||||||
|
var message = $"Required claim(s) '{string.Join("', '", claimTypes)}' are missing for user '{principalName}' (auth: {authType}). Available claims: [{availableClaims}].";
|
||||||
|
throw new InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the authenticated envelope UUID from the claims.
|
/// Gets the authenticated envelope UUID from the claims.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetEnvelopeUuidOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.NameIdentifier);
|
public static string EnvelopeUuid(this ClaimsPrincipal user)
|
||||||
|
=> user.GetRequiredClaimValue(EnvelopeClaimNames.EnvelopeUuid);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the authenticated receiver signature from the claims.
|
/// Gets the authenticated receiver signature from the claims.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetReceiverSignatureOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Hash);
|
public static string ReceiverSignature(this ClaimsPrincipal user)
|
||||||
|
=> user.GetRequiredClaimValue(EnvelopeClaimNames.ReceiverSignature);
|
||||||
/// <summary>
|
|
||||||
/// Gets the authenticated receiver display name from the claims.
|
|
||||||
/// </summary>
|
|
||||||
public static string GetReceiverNameOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Name);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the authenticated receiver email address from the claims.
|
/// Gets the authenticated receiver email address from the claims.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetReceiverMailOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Email);
|
public static string ReceiverMail(this ClaimsPrincipal user)
|
||||||
|
=> user.GetRequiredClaimValue(JwtRegisteredClaimNames.Email);
|
||||||
/// <summary>
|
|
||||||
/// Gets the authenticated envelope title from the claims.
|
|
||||||
/// </summary>
|
|
||||||
public static string GetEnvelopeTitleOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(EnvelopeClaimTypes.Title);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the authenticated envelope identifier from the claims.
|
/// Gets the authenticated envelope identifier from the claims.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static int GetEnvelopeIdOfReceiver(this ClaimsPrincipal user)
|
public static int EnvelopeId(this ClaimsPrincipal user)
|
||||||
{
|
{
|
||||||
var envIdStr = user.GetRequiredClaimOfReceiver(EnvelopeClaimTypes.Id);
|
var envIdStr = user.GetRequiredClaimValue(EnvelopeClaimNames.EnvelopeId);
|
||||||
if (!int.TryParse(envIdStr, out var envId))
|
if (int.TryParse(envIdStr, out var envId))
|
||||||
{
|
return envId;
|
||||||
throw new InvalidOperationException($"Claim '{EnvelopeClaimTypes.Id}' is not a valid integer.");
|
else
|
||||||
}
|
throw new InvalidOperationException($"Claim '{EnvelopeClaimNames.EnvelopeId}' is not a valid integer.");
|
||||||
|
|
||||||
return envId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signs in an envelope receiver using cookie authentication and attaches envelope claims.
|
/// Gets the authenticated receiver identifier from the claims.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The current HTTP context.</param>
|
/// <param name="user"></param>
|
||||||
/// <param name="envelopeReceiver">Envelope receiver DTO to extract claims from.</param>
|
/// <returns></returns>
|
||||||
/// <param name="receiverRole">Role to attach to the authentication ticket.</param>
|
/// <exception cref="InvalidOperationException"></exception>
|
||||||
public static async Task SignInEnvelopeAsync(this HttpContext context, EnvelopeReceiverDto envelopeReceiver, string receiverRole)
|
public static int ReceiverId(this ClaimsPrincipal user)
|
||||||
{
|
{
|
||||||
var claims = new List<Claim>
|
var rcvIdStr = user.GetRequiredClaimValue(EnvelopeClaimNames.ReceiverId);
|
||||||
{
|
if (int.TryParse(rcvIdStr, out var rcvId))
|
||||||
new(ClaimTypes.NameIdentifier, envelopeReceiver.Envelope!.Uuid),
|
return rcvId;
|
||||||
new(ClaimTypes.Hash, envelopeReceiver.Receiver!.Signature),
|
else
|
||||||
new(ClaimTypes.Name, envelopeReceiver.Name ?? string.Empty),
|
throw new InvalidOperationException($"Claim '{EnvelopeClaimNames.ReceiverId}' is not a valid integer.");
|
||||||
new(ClaimTypes.Email, envelopeReceiver.Receiver.EmailAddress),
|
|
||||||
new(EnvelopeClaimTypes.Title, envelopeReceiver.Envelope.Title),
|
|
||||||
new(EnvelopeClaimTypes.Id, envelopeReceiver.Envelope.Id.ToString()),
|
|
||||||
new(ClaimTypes.Role, receiverRole)
|
|
||||||
};
|
|
||||||
|
|
||||||
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
|
|
||||||
|
|
||||||
var authProperties = new AuthenticationProperties
|
|
||||||
{
|
|
||||||
AllowRefresh = false,
|
|
||||||
IsPersistent = false
|
|
||||||
};
|
|
||||||
|
|
||||||
await context.SignInAsync(
|
|
||||||
CookieAuthenticationDefaults.AuthenticationScheme,
|
|
||||||
new ClaimsPrincipal(claimsIdentity),
|
|
||||||
authProperties);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
7
EnvelopeGenerator.API/Models/EnvelopeReceiverLogin.cs
Normal file
7
EnvelopeGenerator.API/Models/EnvelopeReceiverLogin.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace EnvelopeGenerator.API.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request body for the envelope-receiver login endpoint.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="AccessCode">The access code sent to the receiver.</param>
|
||||||
|
public record EnvelopeReceiverLogin(string? AccessCode = null);
|
||||||
18
EnvelopeGenerator.API/Options/CacheOptions.cs
Normal file
18
EnvelopeGenerator.API/Options/CacheOptions.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace EnvelopeGenerator.API.Options;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration options for distributed caching.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CacheOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration section name in appsettings.json.
|
||||||
|
/// </summary>
|
||||||
|
public const string SectionName = "Cache";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signature cache expiration time.
|
||||||
|
/// If null, signatures will not expire automatically.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan? SignatureCacheExpiration { get; set; }
|
||||||
|
}
|
||||||
@@ -17,8 +17,11 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using DigitalData.Core.Abstractions.Security.Extensions;
|
using DigitalData.Core.Abstractions.Security.Extensions;
|
||||||
using EnvelopeGenerator.API.Middleware;
|
using EnvelopeGenerator.API.Middleware;
|
||||||
|
using EnvelopeGenerator.API.Options;
|
||||||
using NLog.Web;
|
using NLog.Web;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using DigitalData.Auth.Claims;
|
||||||
|
using EnvelopeGenerator.API;
|
||||||
|
|
||||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||||
logger.Info("Logging initialized!");
|
logger.Info("Logging initialized!");
|
||||||
@@ -110,9 +113,11 @@ try
|
|||||||
|
|
||||||
options.DocumentFilter<EnvelopeGenerator.API.Documentation.AuthProxyDocumentFilter>();
|
options.DocumentFilter<EnvelopeGenerator.API.Documentation.AuthProxyDocumentFilter>();
|
||||||
});
|
});
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
|
#endif
|
||||||
|
|
||||||
//AddEF Core dbcontext
|
//Add EF Core dbcontext
|
||||||
var useDbMigration = Environment.GetEnvironmentVariable("MIGRATION_TEST_MODE") == true.ToString() || config.GetValue<bool>("UseDbMigration");
|
var useDbMigration = Environment.GetEnvironmentVariable("MIGRATION_TEST_MODE") == true.ToString() || config.GetValue<bool>("UseDbMigration");
|
||||||
var cnnStrName = useDbMigration ? "DbMigrationTest" : "Default";
|
var cnnStrName = useDbMigration ? "DbMigrationTest" : "Default";
|
||||||
var connStr = config.GetConnectionString(cnnStrName)
|
var connStr = config.GetConnectionString(cnnStrName)
|
||||||
@@ -131,7 +136,7 @@ try
|
|||||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
})
|
})
|
||||||
.AddJwtBearer(opt =>
|
.AddJwtBearer(AuthScheme.Sender, opt =>
|
||||||
{
|
{
|
||||||
opt.TokenValidationParameters = new TokenValidationParameters
|
opt.TokenValidationParameters = new TokenValidationParameters
|
||||||
{
|
{
|
||||||
@@ -163,6 +168,61 @@ try
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
})
|
||||||
|
// Per-envelope receiver scheme: reads the JWT from the cookie named
|
||||||
|
// AuthTokenSignFLOWReceiver.{envelope_key} where envelope_key is the
|
||||||
|
// last path segment of the request URL.
|
||||||
|
// This enables simultaneous authentication for multiple envelopes
|
||||||
|
// within the same browser session.
|
||||||
|
.AddJwtBearer(AuthScheme.Receiver, opt =>
|
||||||
|
{
|
||||||
|
opt.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
|
||||||
|
{
|
||||||
|
var clientParams = deferredProvider.GetOptions<ClientParams>();
|
||||||
|
var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience);
|
||||||
|
return [publicKey.SecurityKey];
|
||||||
|
},
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidIssuer = authTokenKeys.Issuer,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidAudience = authTokenKeys.Audience,
|
||||||
|
};
|
||||||
|
|
||||||
|
opt.Events = new JwtBearerEvents
|
||||||
|
{
|
||||||
|
OnMessageReceived = context =>
|
||||||
|
{
|
||||||
|
var paths = context.Request.Path.Value?.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
// Derive the envelope key from the last route segment: /{envelope_key}
|
||||||
|
var envelopeKey = paths?.LastOrDefault();
|
||||||
|
|
||||||
|
if (envelopeKey is not null)
|
||||||
|
{
|
||||||
|
var cookieName = CookieNames.GetEnvelopeReceiverCookieName(authTokenKeys.Cookie, envelopeKey);
|
||||||
|
if (context.Request.Cookies.TryGetValue(cookieName, out var cookieToken) && cookieToken is not null)
|
||||||
|
context.Token = cookieToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
},
|
||||||
|
OnTokenValidated = context =>
|
||||||
|
{
|
||||||
|
var paths = context.Request.Path.Value?.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var envelopeKey = paths?.LastOrDefault();
|
||||||
|
|
||||||
|
var sub = context.Principal?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value
|
||||||
|
?? context.Principal?.FindFirst("sub")?.Value;
|
||||||
|
|
||||||
|
if (envelopeKey is null || sub != envelopeKey)
|
||||||
|
context.Fail("Envelope key in the path does not match the token subject.");
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
@@ -178,14 +238,16 @@ try
|
|||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddAuthorizationBuilder()
|
builder.Services.AddAuthorizationBuilder()
|
||||||
.AddPolicy(AuthPolicy.SenderOrReceiver, policy =>
|
.AddPolicy(AuthPolicy.SenderOrReceiver, policy => policy.RequireRole(Role.Sender, Role.Receiver.Full))
|
||||||
policy.RequireRole(Role.Sender, Role.Receiver.Full))
|
|
||||||
.AddPolicy(AuthPolicy.Sender, policy =>
|
.AddPolicy(AuthPolicy.Sender, policy => policy
|
||||||
policy.RequireRole(Role.Sender))
|
.RequireRole(Role.Sender)
|
||||||
.AddPolicy(AuthPolicy.Receiver, policy =>
|
.AddAuthenticationSchemes(AuthScheme.Sender))
|
||||||
policy.RequireRole(Role.Receiver.Full))
|
.AddPolicy(AuthPolicy.Receiver, policy => policy
|
||||||
.AddPolicy(AuthPolicy.ReceiverTFA, policy =>
|
.AddAuthenticationSchemes(AuthScheme.Receiver)
|
||||||
policy.RequireRole(Role.Receiver.TFA));
|
.RequireAuthenticatedUser()
|
||||||
|
.RequireRole(Role.Receiver.Full, "receiver"))
|
||||||
|
.AddPolicy(AuthPolicy.ReceiverTFA, policy => policy.RequireRole(Role.Receiver.TFA));
|
||||||
|
|
||||||
// User manager
|
// User manager
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
@@ -199,6 +261,20 @@ try
|
|||||||
// Localizer
|
// Localizer
|
||||||
builder.Services.AddCookieBasedLocalizer();
|
builder.Services.AddCookieBasedLocalizer();
|
||||||
|
|
||||||
|
// Cache options
|
||||||
|
builder.Services.Configure<CacheOptions>(config.GetSection(CacheOptions.SectionName));
|
||||||
|
|
||||||
|
// Distributed Cache - SQL Server
|
||||||
|
builder.Services.AddDistributedSqlServerCache(options =>
|
||||||
|
{
|
||||||
|
config.GetSection("Cache:SqlServer").Bind(options);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(options.ConnectionString))
|
||||||
|
{
|
||||||
|
options.ConnectionString = connStr;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Envelope generator serives
|
// Envelope generator serives
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
builder.Services
|
builder.Services
|
||||||
@@ -224,7 +300,9 @@ try
|
|||||||
|
|
||||||
app.UseMiddleware<ExceptionHandlingMiddleware>();
|
app.UseMiddleware<ExceptionHandlingMiddleware>();
|
||||||
|
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
app.MapOpenApi();
|
app.MapOpenApi();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
if (app.Environment.IsDevelopment() || (app.IsDevOrDiP() && config.GetValue<bool>("UseSwagger")))
|
if (app.Environment.IsDevelopment() || (app.IsDevOrDiP() && config.GetValue<bool>("UseSwagger")))
|
||||||
@@ -258,9 +336,11 @@ try
|
|||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.MapReverseProxy();
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
|
// Catch-all YARP proxy — only forward requests that are not swagger/scalar/openapi paths.
|
||||||
|
app.MapReverseProxy();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||||
|
-->
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<WebPublishMethod>Package</WebPublishMethod>
|
||||||
|
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
||||||
|
<LastUsedPlatform>Any CPU</LastUsedPlatform>
|
||||||
|
<SiteUrlToLaunchAfterPublish />
|
||||||
|
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
|
||||||
|
<ExcludeApp_Data>false</ExcludeApp_Data>
|
||||||
|
<ProjectGuid>5e0e17c0-ff5a-4246-bf87-1add85376a27</ProjectGuid>
|
||||||
|
<DesktopBuildPackageLocation>M:\App&Service\0 DD - Smart UP\signFLOW\API\net8\$(Version)\EnvelopeGenerator.API.zip</DesktopBuildPackageLocation>
|
||||||
|
<PackageAsSingleFile>true</PackageAsSingleFile>
|
||||||
|
<DeployIisAppPath>EnvelopeGenerator</DeployIisAppPath>
|
||||||
|
<_TargetId>IISWebDeployPackage</_TargetId>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@@ -1,10 +1,21 @@
|
|||||||
{
|
{
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ReverseProxy": {
|
||||||
|
"Clusters": {
|
||||||
|
"receiver-ui": {
|
||||||
|
"Destinations": {
|
||||||
|
"primary": {
|
||||||
|
"Address": "https://localhost:52936"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
},
|
||||||
"AuthClientParams": {
|
"AuthClientParams": {
|
||||||
"Url": "http://172.24.12.39:9090/auth-hub",
|
"Url": "http://172.24.12.39:9090/auth-hub",
|
||||||
"PublicKeys": [
|
"PublicKeys": [
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"UseSwagger": true,
|
"UseSwagger": true,
|
||||||
"UseDbMigration": true,
|
"UseDbMigration": false,
|
||||||
"DiPMode": true,
|
"DiPMode": true,
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
@@ -174,6 +174,14 @@
|
|||||||
"Receiver": [],
|
"Receiver": [],
|
||||||
"EmailTemplate": [ "TBSIG_EMAIL_TEMPLATE_AFT_UPD" ]
|
"EmailTemplate": [ "TBSIG_EMAIL_TEMPLATE_AFT_UPD" ]
|
||||||
},
|
},
|
||||||
|
"Cache": {
|
||||||
|
"SignatureCacheExpiration": null,
|
||||||
|
"SqlServer": {
|
||||||
|
"ConnectionString": null,
|
||||||
|
"SchemaName": "dbo",
|
||||||
|
"TableName": "TBDD_CACHE"
|
||||||
|
}
|
||||||
|
},
|
||||||
"MainPageTitle": null,
|
"MainPageTitle": null,
|
||||||
"AnnotationParams": {
|
"AnnotationParams": {
|
||||||
"Background": {
|
"Background": {
|
||||||
|
|||||||
@@ -1,18 +1,177 @@
|
|||||||
{
|
{
|
||||||
"ReverseProxy": {
|
"ReverseProxy": {
|
||||||
"Routes": {
|
"Routes": {
|
||||||
"auth-login": {
|
"receiver-ui-root": {
|
||||||
"ClusterId": "auth-hub",
|
"ClusterId": "receiver-ui",
|
||||||
"Match": {
|
"Order": 300,
|
||||||
"Path": "/api/auth",
|
"Match": {
|
||||||
"Methods": [ "POST" ]
|
"Path": "/",
|
||||||
},
|
"Methods": [ "GET", "HEAD" ]
|
||||||
"Transforms": [
|
},
|
||||||
{ "PathSet": "/api/auth/sign-flow" }
|
"Transforms": [
|
||||||
]
|
{ "PathSet": "/index.html" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"receiver-ui-sender": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 100,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/sender/{**catch-all}",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
|
},
|
||||||
|
"Transforms": [
|
||||||
|
{ "PathSet": "/index.html" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"receiver-ui-envelope": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 100,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/envelope/{EnvelopeKey}",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
|
},
|
||||||
|
"Transforms": [
|
||||||
|
{ "PathSet": "/index.html" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"receiver-ui-envelope-dxreportviewer": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 90,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/envelope/{EnvelopeKey}/DxReportViewer",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
|
},
|
||||||
|
"Transforms": [
|
||||||
|
{ "PathSet": "/index.html" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"receiver-ui-blazor-framework": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 50,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/_framework/{**catch-all}",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"receiver-ui-blazor-content": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 50,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/_content/{**catch-all}",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"receiver-ui-static-css": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 200,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/css/{**catch-all}",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
|
},
|
||||||
|
"Transforms": [
|
||||||
|
{
|
||||||
|
"ResponseHeader": "Cache-Control",
|
||||||
|
"Set": "no-cache, no-store, must-revalidate",
|
||||||
|
"When": "Always"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"receiver-ui-static-js": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 200,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/js/{**catch-all}",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
|
},
|
||||||
|
"Transforms": [
|
||||||
|
{
|
||||||
|
"ResponseHeader": "Cache-Control",
|
||||||
|
"Set": "no-cache, no-store, must-revalidate",
|
||||||
|
"When": "Always"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"receiver-ui-fake-data": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 200,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/fake-data/{**catch-all}",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"receiver-ui-appsettings": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 50,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/appsettings.json",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"receiver-ui-appsettings-dev": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 50,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/appsettings.Development.json",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"receiver-ui-styles": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 50,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/EnvelopeGenerator.ReceiverUI.styles.css",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"receiver-ui-fonts": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 200,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/fonts/{**catch-all}",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"receiver-ui-images": {
|
||||||
|
"ClusterId": "receiver-ui",
|
||||||
|
"Order": 200,
|
||||||
|
"Match": {
|
||||||
|
"Path": "/images/{**catch-all}",
|
||||||
|
"Methods": [ "GET", "HEAD" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth-login": {
|
||||||
|
"ClusterId": "auth-hub",
|
||||||
|
"Match": {
|
||||||
|
"Path": "/api/auth",
|
||||||
|
"Methods": [ "POST" ]
|
||||||
|
},
|
||||||
|
"Transforms": [
|
||||||
|
{ "PathSet": "/api/auth/sign-flow" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"auth-envelope-receiver-login": {
|
||||||
|
"ClusterId": "auth-hub",
|
||||||
|
"Match": {
|
||||||
|
"Path": "/api/Auth/envelope-receiver/{key}",
|
||||||
|
"Methods": [ "POST" ]
|
||||||
|
},
|
||||||
|
"Transforms": [
|
||||||
|
{ "PathPattern": "/api/auth/envelope-receiver/{key}" },
|
||||||
|
{
|
||||||
|
"QueryValueParameter": "cookie",
|
||||||
|
"Set": "true"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"Clusters": {
|
"Clusters": {
|
||||||
|
"receiver-ui": {
|
||||||
|
"Destinations": {
|
||||||
|
"primary": {
|
||||||
|
"Address": "https://localhost:52936"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"auth-hub": {
|
"auth-hub": {
|
||||||
"Destinations": {
|
"Destinations": {
|
||||||
"primary": {
|
"primary": {
|
||||||
@@ -23,3 +182,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,42 +8,77 @@ public record AnnotationCreateDto
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int ElementId { get; init; }
|
public long Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; init; } = null!;
|
[Obsolete("Not required for DevExpress")]
|
||||||
|
public int ElementId { get; init; } = -1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Value { get; init; } = null!;
|
[Obsolete("Not required for DevExpress")]
|
||||||
|
public string Name { get; init; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Type { get; init; } = null!;
|
[Obsolete("Not required for DevExpress")]
|
||||||
|
public string Value { get; init; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Not required for DevExpress")]
|
||||||
|
public string Type { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Horizontal position of the signature field on the page.
|
||||||
|
/// <br/><br/>
|
||||||
|
/// <b>Unit:</b> INCHES (GdPicture14 native), origin at the <b>top-left</b> corner of the page, X increases to the right.
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Conversion to DevExpress:</b> Multiply by 100 (DX uses 1/100 inch).
|
||||||
|
/// Convert: <c>xDX = xInches * 100.0</c>
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Conversion to PDF Points:</b> Multiply by 72 (PSPDFKit, iText7 use 1/72 inch).
|
||||||
|
/// Convert: <c>xPt = xInches * 72.0</c>
|
||||||
|
/// </summary>
|
||||||
public double? X { get; init; }
|
public double? X { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Vertical position of the signature field on the page.
|
||||||
|
/// <br/><br/>
|
||||||
|
/// <b>Unit:</b> INCHES (GdPicture14 native), origin at the <b>top-left</b> corner of the page, Y increases downward.
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Conversion to DevExpress:</b> Multiply by 100 (DX uses 1/100 inch).
|
||||||
|
/// Convert: <c>yDX = yInches * 100.0</c>
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Conversion to PDF Points (top-left origin):</b> Multiply by 72.
|
||||||
|
/// Convert: <c>yPt = yInches * 72.0</c>
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Conversion to PDF Points (bottom-left origin - iText7):</b> Y-flip required.
|
||||||
|
/// Convert: <c>yPt = (pageHeightInches - yInches - elemHeightInches) * 72.0</c>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double? Y { get; init; }
|
public double? Y { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Not required for DevExpress")]
|
||||||
public double? Width { get; init; }
|
public double? Width { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Not required for DevExpress")]
|
||||||
public double? Height { get; init; }
|
public double? Height { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Added to eliminate the need for SignatureDto in DevExpress
|
||||||
|
/// </summary>
|
||||||
|
public int? Page { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using EnvelopeGenerator.Domain.Interfaces;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
using EnvelopeGenerator.Domain.Interfaces;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
@@ -7,7 +9,7 @@ namespace EnvelopeGenerator.Application.Common.Dto;
|
|||||||
/// Data Transfer Object representing a positioned element assigned to a document receiver.
|
/// Data Transfer Object representing a positioned element assigned to a document receiver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public class SignatureDto : ISignature
|
public class DocReceiverElementDto : IDocReceiverElement
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the unique identifier of the element.
|
/// Gets or sets the unique identifier of the element.
|
||||||
@@ -93,4 +95,34 @@ public class SignatureDto : ISignature
|
|||||||
/// Gets or sets the left position of the element (in layout terms).
|
/// Gets or sets the left position of the element (in layout terms).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double Left => X;
|
public double Left => X;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<AnnotationDto>? Annotations { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public SenderAppType SenderAppType { get; set; } = SenderAppType.LegacyFormApp;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string? FullName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string? Position { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string? Place { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public byte[]? Ink { get; set; }
|
||||||
}
|
}
|
||||||
@@ -31,5 +31,5 @@ public class DocumentDto
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the collection of elements associated with the document for receiver interactions, if any.
|
/// Gets or sets the collection of elements associated with the document for receiver interactions, if any.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<SignatureDto>? Elements { get; set; }
|
public IEnumerable<DocReceiverElementDto>? Elements { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
|
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
|
||||||
using DigitalData.UserManager.Application.DTOs.User;
|
using DigitalData.UserManager.Application.DTOs.User;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using EnvelopeGenerator.Domain.Interfaces;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
@@ -10,7 +14,7 @@ namespace EnvelopeGenerator.Application.Common.Dto;
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public record EnvelopeDto
|
public record EnvelopeDto : IEnvelope
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
@@ -74,10 +78,12 @@ public record EnvelopeDto
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int? EnvelopeTypeId { get; set; }
|
public int? EnvelopeTypeId { get; set; }
|
||||||
|
|
||||||
|
// TODO: use ReadAndConfirm property name
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ReadOnly => EnvelopeTypeId == 2;
|
[Obsolete("Use EnvelopeExtensions.IsReadAndConfirm extension metot instead.")]
|
||||||
|
public bool ReadOnly => this.IsReadAndConfirm();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
@@ -123,4 +129,9 @@ public record EnvelopeDto
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<DocumentDto>? Documents { get; set; }
|
public IEnumerable<DocumentDto>? Documents { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<ReceiverDto>? Receivers { get; set; }
|
||||||
}
|
}
|
||||||
@@ -23,10 +23,10 @@ public class MappingProfile : Profile
|
|||||||
{
|
{
|
||||||
// Entity to DTO mappings
|
// Entity to DTO mappings
|
||||||
CreateMap<Config, ConfigDto>();
|
CreateMap<Config, ConfigDto>();
|
||||||
CreateMap<Signature, SignatureDto>();
|
CreateMap<DocReceiverElement, DocReceiverElementDto>();
|
||||||
CreateMap<DocumentStatus, DocumentStatusDto>();
|
CreateMap<DocumentStatus, DocumentStatusDto>();
|
||||||
CreateMap<EmailTemplate, EmailTemplateDto>();
|
CreateMap<EmailTemplate, EmailTemplateDto>();
|
||||||
CreateMap<Envelope, EnvelopeDto>();
|
CreateMap<Envelope, EnvelopeDto>().ForMember(dest => dest.Receivers, opt => opt.MapFrom(src => src.EnvelopeReceivers.Select(er => er.Receiver)));
|
||||||
CreateMap<Document, DocumentDto>();
|
CreateMap<Document, DocumentDto>();
|
||||||
CreateMap<Domain.Entities.History, HistoryDto>().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen));
|
CreateMap<Domain.Entities.History, HistoryDto>().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen));
|
||||||
CreateMap<Domain.Entities.History, HistoryCreateDto>().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen));
|
CreateMap<Domain.Entities.History, HistoryCreateDto>().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen));
|
||||||
@@ -39,7 +39,10 @@ public class MappingProfile : Profile
|
|||||||
|
|
||||||
// DTO to Entity mappings
|
// DTO to Entity mappings
|
||||||
CreateMap<ConfigDto, Config>();
|
CreateMap<ConfigDto, Config>();
|
||||||
CreateMap<SignatureDto, Signature>();
|
CreateMap<DocReceiverElementDto, DocReceiverElement>();
|
||||||
|
CreateMap<Signature, DocReceiverElement>()
|
||||||
|
.ForMember(dest => dest.Ink, opt => opt.MapFrom(src => src.DataUrl.MapDataUrlToRequiredBytes()))
|
||||||
|
.MapChangedWhen();
|
||||||
CreateMap<DocumentStatusDto, DocumentStatus>();
|
CreateMap<DocumentStatusDto, DocumentStatus>();
|
||||||
CreateMap<EmailTemplateDto, EmailTemplate>();
|
CreateMap<EmailTemplateDto, EmailTemplate>();
|
||||||
CreateMap<EnvelopeDto, Envelope>();
|
CreateMap<EnvelopeDto, Envelope>();
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Dynamic;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents PSPDFKit annotation data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Instant">Instant annotation data.</param>
|
||||||
|
/// <param name="Structured">Structured annotation data.</param>
|
||||||
|
[Obsolete("The PSPDFKit library is deprecated.")]
|
||||||
|
public record PsPdfKitAnnotation(ExpandoObject Instant, IEnumerable<AnnotationCreateDto> Structured);
|
||||||
65
EnvelopeGenerator.Application/Common/Dto/Signature.cs
Normal file
65
EnvelopeGenerator.Application/Common/Dto/Signature.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a captured signature with metadata created by the receiver in the signature popup.
|
||||||
|
/// This model holds the signature image (as base64 data URL) along with signer information
|
||||||
|
/// used for rendering applied signatures on the PDF canvas.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <b>Used in:</b> EnvelopeViewer.razor signature popup workflow
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Creation:</b> User draws/types/uploads signature and fills required fields
|
||||||
|
/// </remarks>
|
||||||
|
public sealed record Signature
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// TBDD_DOCUMENT_RECEIVER_ELEMENT.ID - identifies the specific signature field on the PDF page.
|
||||||
|
/// </summary>
|
||||||
|
public required int Id { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base64-encoded data URL of the signature image.
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Format:</b> <c>data:image/png;base64,iVBORw0KG...</c>
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Source:</b> Canvas.toDataURL() from signature pad (draw/text/image tabs)
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Usage:</b> Set as <c>img.src</c> in applied signature overlay
|
||||||
|
/// </summary>
|
||||||
|
public required string DataUrl { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Full name of the signer (first and last name).
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Required:</b> Yes (validated in popup)
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Example:</b> "Max Mustermann"
|
||||||
|
/// </summary>
|
||||||
|
public required string FullName { get; init; }
|
||||||
|
|
||||||
|
private readonly string? _position = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Job title or position of the signer.
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Required:</b> No (optional field)
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Example:</b> "Geschäftsführer" or empty string
|
||||||
|
/// </summary>
|
||||||
|
public string? Position
|
||||||
|
{
|
||||||
|
get => _position;
|
||||||
|
init => _position = string.IsNullOrWhiteSpace(value) ? value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Location/place where the signature was created.
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Required:</b> Yes (validated in popup)
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Display:</b> Shown with current date in German format (dd.MM.yyyy)
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Example:</b> "Berlin" ? rendered as "Berlin, 26.01.2025"
|
||||||
|
/// </summary>
|
||||||
|
public required string Place { get; init; }
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using EnvelopeGenerator.Domain.Interfaces.Auditing;
|
using EnvelopeGenerator.Domain.Interfaces.Auditing;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
namespace EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
|
||||||
@@ -13,12 +14,29 @@ public static class AutoMapperAuditingExtensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static IMappingExpression<TSource, TDestination> MapAddedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
public static IMappingExpression<TSource, TDestination> MapAddedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
||||||
where TDestination : IHasAddedWhen
|
where TDestination : IHasAddedWhen
|
||||||
=> expression.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.UtcNow));
|
=> expression.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.Now));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps <see cref="IHasChangedWhen.ChangedWhen"/> to the current UTC time.
|
/// Maps <see cref="IHasChangedWhen.ChangedWhen"/> to the current UTC time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IMappingExpression<TSource, TDestination> MapChangedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
public static IMappingExpression<TSource, TDestination> MapChangedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
||||||
where TDestination : IHasChangedWhen
|
where TDestination : IHasChangedWhen
|
||||||
=> expression.ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(_ => DateTime.UtcNow));
|
=> expression.ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(_ => DateTime.Now));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a base64 data URL string to a byte array.
|
||||||
|
/// Handles data URLs in the format: "data:image/png;base64,iVBORw0KG..."
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataUrl">The base64 data URL string from Canvas.toDataURL()</param>
|
||||||
|
/// <returns>The decoded byte array, or null if the input is null or empty</returns>
|
||||||
|
public static byte[]? MapDataUrlToRequiredBytes(this string dataUrl)
|
||||||
|
{
|
||||||
|
// Remove data URL prefix (e.g., "data:image/png;base64,")
|
||||||
|
var base64Index = dataUrl.IndexOf(',', StringComparison.Ordinal);
|
||||||
|
if (base64Index == -1)
|
||||||
|
throw new ArgumentException("Invalid data URL format. Unable to extract base64 data.", nameof(dataUrl));
|
||||||
|
|
||||||
|
var base64Data = dataUrl[(base64Index + 1)..];
|
||||||
|
return Convert.FromBase64String(base64Data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,6 @@ namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Use IRepository")]
|
[Obsolete("Use IRepository")]
|
||||||
public interface IDocumentReceiverElementRepository : ICRUDRepository<Signature, int>
|
public interface IDocumentReceiverElementRepository : ICRUDRepository<DocReceiverElement, int>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,6 @@ namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Use MediatR")]
|
[Obsolete("Use MediatR")]
|
||||||
public interface IDocumentReceiverElementService : IBasicCRUDService<SignatureDto, Signature, int>
|
public interface IDocumentReceiverElementService : IBasicCRUDService<DocReceiverElementDto, DocReceiverElement, int>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -1,88 +1,37 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto;
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
using EnvelopeGenerator.Application.Common.Notifications.RemoveSignature;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using System.Dynamic;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned;
|
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Notification raised when a document is signed by a receiver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Instant"></param>
|
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
||||||
/// <param name="Structured"></param>
|
public record DocSignedNotification : INotification, ISendMailNotification
|
||||||
public record PsPdfKitAnnotation(ExpandoObject Instant, IEnumerable<AnnotationCreateDto> Structured);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Original"></param>
|
|
||||||
public record DocSignedNotification(EnvelopeReceiverDto Original) : EnvelopeReceiverDto(Original), INotification, ISendMailNotification
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// The envelope receiver information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
public required EnvelopeReceiverDto EnvelopeReceiver { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The PSPDFKit annotation data.
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete("The PSPDFKit library is deprecated.")]
|
||||||
public PsPdfKitAnnotation? PsPdfKitAnnotation { get; init; }
|
public PsPdfKitAnnotation? PsPdfKitAnnotation { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Gets the email template type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EmailTemplateType TemplateType => EmailTemplateType.DocumentSigned;
|
public EmailTemplateType TemplateType => EmailTemplateType.DocumentSigned;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Gets the email address of the receiver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string EmailAddress => Receiver?.EmailAddress
|
public string EmailAddress => EnvelopeReceiver.Receiver?.EmailAddress
|
||||||
?? throw new InvalidOperationException($"Receiver is null." +
|
?? throw new InvalidOperationException($"Receiver is null." +
|
||||||
$"DocSignedNotification:\n{this.ToJson(Format.Json.ForDiagnostics)}");
|
$"DocSignedNotification:\n{this.ToJson(Format.Json.ForDiagnostics)}");
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class DocSignedNotificationExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Converts an <see cref="EnvelopeReceiverDto"/> to a <see cref="DocSignedNotification"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dto">The DTO to convert.</param>
|
|
||||||
/// <param name="psPdfKitAnnotation"></param>
|
|
||||||
/// <returns>A new <see cref="DocSignedNotification"/> instance.</returns>
|
|
||||||
public static DocSignedNotification ToDocSignedNotification(this EnvelopeReceiverDto dto, PsPdfKitAnnotation psPdfKitAnnotation)
|
|
||||||
=> new(dto) { PsPdfKitAnnotation = psPdfKitAnnotation };
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
||||||
@@ -7,6 +8,7 @@ namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("The PSPDFKit library is deprecated.")]
|
||||||
public class AnnotationHandler : INotificationHandler<DocSignedNotification>
|
public class AnnotationHandler : INotificationHandler<DocSignedNotification>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using EnvelopeGenerator.Application.DocStatus.Commands;
|
using EnvelopeGenerator.Application.DocStatus.Commands;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@@ -8,6 +9,7 @@ namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
||||||
public class DocStatusHandler : INotificationHandler<DocSignedNotification>
|
public class DocStatusHandler : INotificationHandler<DocSignedNotification>
|
||||||
{
|
{
|
||||||
private const string BlankAnnotationJson = "{}";
|
private const string BlankAnnotationJson = "{}";
|
||||||
@@ -29,15 +31,13 @@ public class DocStatusHandler : INotificationHandler<DocSignedNotification>
|
|||||||
/// <param name="notification"></param>
|
/// <param name="notification"></param>
|
||||||
/// <param name="cancel"></param>
|
/// <param name="cancel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
|
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
||||||
|
public Task Handle(DocSignedNotification notification, CancellationToken cancel) => _sender.Send(new CreateDocStatusCommand()
|
||||||
{
|
{
|
||||||
await _sender.Send(new SaveDocStatusCommand()
|
EnvelopeId = notification.EnvelopeReceiver.EnvelopeId,
|
||||||
{
|
ReceiverId = notification.EnvelopeReceiver.ReceiverId,
|
||||||
Envelope = new() { Id = notification.EnvelopeId },
|
Value = notification.PsPdfKitAnnotation is PsPdfKitAnnotation annot
|
||||||
Receiver = new() { Id = notification.ReceiverId},
|
? JsonSerializer.Serialize(annot.Instant, Format.Json.ForAnnotations)
|
||||||
Value = notification.PsPdfKitAnnotation is PsPdfKitAnnotation annot
|
|
||||||
? JsonSerializer.Serialize(annot.Instant, Format.Json.ForAnnotations)
|
|
||||||
: BlankAnnotationJson
|
: BlankAnnotationJson
|
||||||
}, cancel);
|
}, cancel);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -29,13 +29,13 @@ public class HistoryHandler : INotificationHandler<DocSignedNotification>
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
|
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
if (notification.Receiver is null)
|
if (notification.EnvelopeReceiver.Receiver is null)
|
||||||
throw new InvalidOperationException($"Receiver information is missing in the notification. DocSignedNotification:\n {notification.ToJson(Format.Json.ForDiagnostics)}");
|
throw new InvalidOperationException($"Receiver information is missing in the notification. DocSignedNotification:\n {notification.ToJson(Format.Json.ForDiagnostics)}");
|
||||||
|
|
||||||
await _sender.Send(new CreateHistoryCommand()
|
await _sender.Send(new CreateHistoryCommand()
|
||||||
{
|
{
|
||||||
EnvelopeId = notification.EnvelopeId,
|
EnvelopeId = notification.EnvelopeReceiver.EnvelopeId,
|
||||||
UserReference = notification.Receiver.EmailAddress,
|
UserReference = notification.EnvelopeReceiver.Receiver.EmailAddress,
|
||||||
Status = EnvelopeStatus.DocumentSigned,
|
Status = EnvelopeStatus.DocumentSigned,
|
||||||
}, cancel);
|
}, cancel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
|||||||
using EnvelopeGenerator.Application.Common.Configurations;
|
using EnvelopeGenerator.Application.Common.Configurations;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using EnvelopeGenerator.Domain.Interfaces;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ public class SendSignedMailHandler : SendMailHandler<DocSignedNotification>
|
|||||||
protected override void ConfigureEmailOut(DocSignedNotification notification, EmailOut emailOut)
|
protected override void ConfigureEmailOut(DocSignedNotification notification, EmailOut emailOut)
|
||||||
{
|
{
|
||||||
emailOut.ReferenceString = notification.EmailAddress;
|
emailOut.ReferenceString = notification.EmailAddress;
|
||||||
emailOut.ReferenceId = notification.ReceiverId;
|
emailOut.ReferenceId = notification.EnvelopeReceiver.ReceiverId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -41,10 +42,29 @@ public class SendSignedMailHandler : SendMailHandler<DocSignedNotification>
|
|||||||
{
|
{
|
||||||
var placeHolders = new Dictionary<string, string>()
|
var placeHolders = new Dictionary<string, string>()
|
||||||
{
|
{
|
||||||
{ "[NAME_RECEIVER]", notification.Name ?? string.Empty },
|
{ "[NAME_RECEIVER]", notification.EnvelopeReceiver.Name ?? string.Empty },
|
||||||
{ "[DOCUMENT_TITLE]", notification.Envelope?.Title ?? string.Empty },
|
{ "[DOCUMENT_TITLE]", notification.EnvelopeReceiver.Envelope?.Title ?? string.Empty },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (notification.EnvelopeReceiver.Envelope.IsReadAndConfirm())
|
||||||
|
{
|
||||||
|
placeHolders["[SIGNATURE_TYPE]"] = "Lesen und bestätigen";
|
||||||
|
placeHolders["[DOCUMENT_PROCESS]"] = string.Empty;
|
||||||
|
placeHolders["[FINAL_STATUS]"] = "Lesebestätigung";
|
||||||
|
placeHolders["[FINAL_ACTION]"] = "Empfänger bestätigt";
|
||||||
|
placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Empfänger abgelehnt!";
|
||||||
|
placeHolders["[RECEIVER_ACTION]"] = "bestätigt";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
placeHolders["[SIGNATURE_TYPE]"] = "Signieren";
|
||||||
|
placeHolders["[DOCUMENT_PROCESS]"] = " und elektronisch unterschreiben";
|
||||||
|
placeHolders["[FINAL_STATUS]"] = "Signatur";
|
||||||
|
placeHolders["[FINAL_ACTION]"] = "Vertragspartner unterzeichnet";
|
||||||
|
placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Vertragspartner abgelehnt! Ihre notwendige Unterzeichnung wurde verworfen.";
|
||||||
|
placeHolders["[RECEIVER_ACTION]"] = "unterschrieben";
|
||||||
|
}
|
||||||
|
|
||||||
return placeHolders;
|
return placeHolders;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ public abstract class SendMailHandler<TNotification> : INotificationHandler<TNot
|
|||||||
EmailAddress = notification.EmailAddress,
|
EmailAddress = notification.EmailAddress,
|
||||||
EmailBody = temp.Body,
|
EmailBody = temp.Body,
|
||||||
EmailSubj = temp.Subject,
|
EmailSubj = temp.Subject,
|
||||||
AddedWhen = DateTime.UtcNow,
|
AddedWhen = DateTime.Now,
|
||||||
AddedWho = DispatcherParams.AddedWho,
|
AddedWho = DispatcherParams.AddedWho,
|
||||||
SendingProfile = DispatcherParams.SendingProfile,
|
SendingProfile = DispatcherParams.SendingProfile,
|
||||||
ReminderTypeId = DispatcherParams.ReminderTypeId,
|
ReminderTypeId = DispatcherParams.ReminderTypeId,
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using QRCoder;
|
using QRCoder;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using MediatR;
|
||||||
|
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
||||||
|
using EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application;
|
namespace EnvelopeGenerator.Application;
|
||||||
|
|
||||||
@@ -56,6 +59,22 @@ public static class DependencyInjection
|
|||||||
services.AddMediatR(cfg =>
|
services.AddMediatR(cfg =>
|
||||||
{
|
{
|
||||||
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
|
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
|
||||||
|
|
||||||
|
// Register SignCommand pipeline behaviors in execution order
|
||||||
|
// 0. EnvelopeReceiverResolutionBehavior - Resolves EnvelopeReceiver from query parameters (executes FIRST)
|
||||||
|
cfg.AddBehavior<IPipelineBehavior<SigningCommand, Unit>, EnvelopeReceiverResolutionBehavior>();
|
||||||
|
|
||||||
|
// 1. AnnotationBehavior - Saves annotations (executes second)
|
||||||
|
cfg.AddBehavior<IPipelineBehavior<SigningCommand, Unit>, AnnotationBehavior>();
|
||||||
|
|
||||||
|
// 2. DocStatusBehavior - Creates document status (executes third)
|
||||||
|
cfg.AddBehavior<IPipelineBehavior<SigningCommand, Unit>, DocStatusBehavior>();
|
||||||
|
|
||||||
|
// 3. HistoryBehavior - Records history (executes fourth)
|
||||||
|
cfg.AddBehavior<IPipelineBehavior<SigningCommand, Unit>, HistoryBehavior>();
|
||||||
|
|
||||||
|
// 4. SendSignedMailBehavior - Sends notification email (executes LAST, only if all previous succeed)
|
||||||
|
cfg.AddBehavior<IPipelineBehavior<SigningCommand, Unit>, SendSignedMailBehavior>();
|
||||||
});
|
});
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pipeline behavior that saves annotations.
|
||||||
|
/// Executes first in the signing process.
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete("The PSPDFKit library is deprecated.")]
|
||||||
|
public class AnnotationBehavior : IPipelineBehavior<SigningCommand, Unit>
|
||||||
|
{
|
||||||
|
private readonly IRepository<ElementAnnotation> _repo;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AnnotationBehavior"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="repository"></param>
|
||||||
|
public AnnotationBehavior(IRepository<ElementAnnotation> repository)
|
||||||
|
{
|
||||||
|
_repo = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="next"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<Unit> Handle(SigningCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
if(request.ReceiverAppType != ReceiverAppType.LegacyWeb)
|
||||||
|
if(request.PsPdfKitAnnotation is null)
|
||||||
|
return await next(cancel);
|
||||||
|
else
|
||||||
|
throw new BadRequestException("PsPdfKit Annotation are only supported for the legacy web receiver type.");
|
||||||
|
|
||||||
|
if (request.PsPdfKitAnnotation is PsPdfKitAnnotation annot)
|
||||||
|
await _repo.CreateAsync(annot.Structured, cancel);
|
||||||
|
else
|
||||||
|
throw new BadRequestException("Annotation data is missing or invalid.");
|
||||||
|
|
||||||
|
return await next(cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
using EnvelopeGenerator.Application.DocStatus.Commands;
|
||||||
|
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
using MediatR;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pipeline behavior that creates document status.
|
||||||
|
/// Executes second in the signing process.
|
||||||
|
/// </summary>
|
||||||
|
public class DocStatusBehavior : IPipelineBehavior<SigningCommand, Unit>
|
||||||
|
{
|
||||||
|
private const string BlankAnnotationJson = "{}";
|
||||||
|
|
||||||
|
private readonly ISender _sender;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
public DocStatusBehavior(ISender sender)
|
||||||
|
{
|
||||||
|
_sender = sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="next"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
||||||
|
public async Task<Unit> Handle(SigningCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _sender.Send(new CreateDocStatusCommand()
|
||||||
|
{
|
||||||
|
EnvelopeId = request.EnvelopeReceiver.EnvelopeId,
|
||||||
|
ReceiverId = request.EnvelopeReceiver.ReceiverId,
|
||||||
|
Value = request.PsPdfKitAnnotation is PsPdfKitAnnotation annot
|
||||||
|
? JsonSerializer.Serialize(annot.Instant, Format.Json.ForAnnotations)
|
||||||
|
: BlankAnnotationJson
|
||||||
|
}, cancellationToken);
|
||||||
|
|
||||||
|
return await next(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||||
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pipeline behavior that resolves and validates EnvelopeReceiver.
|
||||||
|
/// Executes FIRST in the signing process - before all other behaviors.
|
||||||
|
/// If EnvelopeReceiver is not provided, it queries the database using EnvelopeReceiverQueryBase parameters.
|
||||||
|
/// </summary>
|
||||||
|
public class EnvelopeReceiverResolutionBehavior : IPipelineBehavior<SigningCommand, Unit>
|
||||||
|
{
|
||||||
|
private readonly IRepository<EnvelopeReceiver> _erRepo;
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="erRepo"></param>
|
||||||
|
/// <param name="mapper"></param>
|
||||||
|
public EnvelopeReceiverResolutionBehavior(IRepository<EnvelopeReceiver> erRepo, IMapper mapper)
|
||||||
|
{
|
||||||
|
_erRepo = erRepo;
|
||||||
|
_mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="next"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<Unit> Handle(SigningCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// If EnvelopeReceiver is not provided, query it from database
|
||||||
|
if (request.EnvelopeReceiver is null)
|
||||||
|
{
|
||||||
|
var er = await _erRepo.Query.Where(request, notnull: true).SingleOrDefaultAsync(cancellationToken)
|
||||||
|
?? throw new NotFoundException("EnvelopeReceiver not found");
|
||||||
|
|
||||||
|
request.SetEnvelopeReceiver(_mapper.Map<EnvelopeReceiverDto>(er));
|
||||||
|
}
|
||||||
|
|
||||||
|
return await next(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
using EnvelopeGenerator.Application.Histories.Commands;
|
||||||
|
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pipeline behavior that records history.
|
||||||
|
/// Executes third in the signing process.
|
||||||
|
/// </summary>
|
||||||
|
public class HistoryBehavior : IPipelineBehavior<SigningCommand, Unit>
|
||||||
|
{
|
||||||
|
private readonly ISender _sender;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
public HistoryBehavior(ISender sender)
|
||||||
|
{
|
||||||
|
_sender = sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="next"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<Unit> Handle(SigningCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (request.EnvelopeReceiver.Receiver is null)
|
||||||
|
throw new InvalidOperationException($"Receiver information is missing in the notification. SignCommand:\n {request.ToJson(Format.Json.ForDiagnostics)}");
|
||||||
|
|
||||||
|
await _sender.Send(new CreateHistoryCommand()
|
||||||
|
{
|
||||||
|
EnvelopeId = request.EnvelopeReceiver.EnvelopeId,
|
||||||
|
UserReference = request.EnvelopeReceiver.Receiver.EmailAddress,
|
||||||
|
Status = EnvelopeStatus.DocumentSigned,
|
||||||
|
}, cancellationToken);
|
||||||
|
|
||||||
|
return await next(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pipeline behavior that creates document status.
|
||||||
|
/// Executes second in the signing process.
|
||||||
|
/// </summary>
|
||||||
|
public class SaveSignatureBehavior : IPipelineBehavior<SigningCommand, Unit>
|
||||||
|
{
|
||||||
|
private readonly ISender _sender;
|
||||||
|
|
||||||
|
private readonly IRepository<DocReceiverElement> _elementRepo;
|
||||||
|
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="elementRepo"></param>
|
||||||
|
/// <param name="mapper"></param>
|
||||||
|
public SaveSignatureBehavior(ISender sender, IRepository<DocReceiverElement> elementRepo, IMapper mapper)
|
||||||
|
{
|
||||||
|
_sender = sender;
|
||||||
|
_elementRepo = elementRepo;
|
||||||
|
_elementRepo = elementRepo;
|
||||||
|
_mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="next"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<Unit> Handle(SigningCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
if (request.ReceiverAppType == ReceiverAppType.LegacyWeb)
|
||||||
|
return await next(cancel);
|
||||||
|
else if(request.Signatures is not IEnumerable<Signature> signatures)
|
||||||
|
throw new BadRequestException($"Signatures are required for saving signature behavior.");
|
||||||
|
|
||||||
|
var elements = await _elementRepo
|
||||||
|
.Where(e => e.Document.EnvelopeId == request.Envelope.Id)
|
||||||
|
.Where(e => e.ReceiverId == request.Receiver.Id)
|
||||||
|
.ToListAsync(cancel);
|
||||||
|
|
||||||
|
foreach (var element in elements)
|
||||||
|
{
|
||||||
|
var signatures = request.Signatures.Where(s => s.Id == element.Id).ToList();
|
||||||
|
if(signatures.Count == 0)
|
||||||
|
throw new BadRequestException("No signature found for element with id {element.Id}.");
|
||||||
|
else if(signatures.Count > 1)
|
||||||
|
throw new BadRequestException("Multiple signatures found for element with id {element.Id}.");
|
||||||
|
|
||||||
|
await _elementRepo.UpdateAsync(signatures.First(), e => e.Id == element.Id, cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await next(cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
||||||
|
using EnvelopeGenerator.Application.Common.Configurations;
|
||||||
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using EnvelopeGenerator.Domain.Interfaces;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pipeline behavior that sends signed mail notification.
|
||||||
|
/// Executes LAST in the signing process - only if all previous behaviors succeed.
|
||||||
|
/// </summary>
|
||||||
|
public class SendSignedMailBehavior : IPipelineBehavior<SigningCommand, Unit>
|
||||||
|
{
|
||||||
|
private readonly IRepository<EmailTemplate> _tempRepo;
|
||||||
|
private readonly IRepository<EmailOut> _emailOutRepo;
|
||||||
|
private readonly MailParams _mailParams;
|
||||||
|
private readonly DispatcherParams _dispatcherParams;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tempRepo"></param>
|
||||||
|
/// <param name="emailOutRepo"></param>
|
||||||
|
/// <param name="mailParamsOptions"></param>
|
||||||
|
/// <param name="dispatcherParamsOptions"></param>
|
||||||
|
public SendSignedMailBehavior(
|
||||||
|
IRepository<EmailTemplate> tempRepo,
|
||||||
|
IRepository<EmailOut> emailOutRepo,
|
||||||
|
IOptions<MailParams> mailParamsOptions,
|
||||||
|
IOptions<DispatcherParams> dispatcherParamsOptions)
|
||||||
|
{
|
||||||
|
_tempRepo = tempRepo;
|
||||||
|
_emailOutRepo = emailOutRepo;
|
||||||
|
_mailParams = mailParamsOptions.Value;
|
||||||
|
_dispatcherParams = dispatcherParamsOptions.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="next"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<Unit> Handle(SigningCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var placeHolders = CreatePlaceHolders(request);
|
||||||
|
|
||||||
|
var temp = await _tempRepo
|
||||||
|
.Where(x => x.Name == EmailTemplateType.DocumentSigned.ToString())
|
||||||
|
.SingleOrDefaultAsync(cancellationToken)
|
||||||
|
?? throw new InvalidOperationException($"Email template not found. SignCommand:\n {request.ToJson(Format.Json.ForDiagnostics)}");
|
||||||
|
|
||||||
|
temp.Subject = ReplacePlaceHolders(temp.Subject, placeHolders, _mailParams.Placeholders);
|
||||||
|
temp.Body = ReplacePlaceHolders(temp.Body, placeHolders, _mailParams.Placeholders);
|
||||||
|
|
||||||
|
var emailOut = new EmailOut
|
||||||
|
{
|
||||||
|
EmailAddress = request.EnvelopeReceiver.Receiver!.EmailAddress,
|
||||||
|
EmailBody = temp.Body,
|
||||||
|
EmailSubj = temp.Subject,
|
||||||
|
AddedWhen = DateTime.Now,
|
||||||
|
AddedWho = _dispatcherParams.AddedWho,
|
||||||
|
SendingProfile = _dispatcherParams.SendingProfile,
|
||||||
|
ReminderTypeId = _dispatcherParams.ReminderTypeId,
|
||||||
|
EmailAttmt1 = _dispatcherParams.EmailAttmt1,
|
||||||
|
WfId = (int)EnvelopeStatus.MessageConfirmationSent,
|
||||||
|
ReferenceString = request.EnvelopeReceiver.Receiver!.EmailAddress,
|
||||||
|
ReferenceId = request.EnvelopeReceiver.ReceiverId
|
||||||
|
};
|
||||||
|
|
||||||
|
await _emailOutRepo.CreateAsync(emailOut, cancellationToken);
|
||||||
|
|
||||||
|
return await next(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<string, string> CreatePlaceHolders(SigningCommand request)
|
||||||
|
{
|
||||||
|
var placeHolders = new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "[NAME_RECEIVER]", request.EnvelopeReceiver.Name ?? string.Empty },
|
||||||
|
{ "[DOCUMENT_TITLE]", request.EnvelopeReceiver.Envelope?.Title ?? string.Empty },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (request.EnvelopeReceiver.Envelope.IsReadAndConfirm())
|
||||||
|
{
|
||||||
|
placeHolders["[SIGNATURE_TYPE]"] = "Lesen und bestätigen";
|
||||||
|
placeHolders["[DOCUMENT_PROCESS]"] = string.Empty;
|
||||||
|
placeHolders["[FINAL_STATUS]"] = "Lesebestätigung";
|
||||||
|
placeHolders["[FINAL_ACTION]"] = "Empfänger bestätigt";
|
||||||
|
placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Empfänger abgelehnt!";
|
||||||
|
placeHolders["[RECEIVER_ACTION]"] = "bestätigt";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
placeHolders["[SIGNATURE_TYPE]"] = "Signieren";
|
||||||
|
placeHolders["[DOCUMENT_PROCESS]"] = " und elektronisch unterschreiben";
|
||||||
|
placeHolders["[FINAL_STATUS]"] = "Signatur";
|
||||||
|
placeHolders["[FINAL_ACTION]"] = "Vertragspartner unterzeichnet";
|
||||||
|
placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Vertragspartner abgelehnt! Ihre notwendige Unterzeichnung wurde verworfen.";
|
||||||
|
placeHolders["[RECEIVER_ACTION]"] = "unterschrieben";
|
||||||
|
}
|
||||||
|
|
||||||
|
return placeHolders;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ReplacePlaceHolders(string text, params Dictionary<string, string>[] placeHoldersList)
|
||||||
|
{
|
||||||
|
foreach (var placeHolders in placeHoldersList)
|
||||||
|
foreach (var ph in placeHolders)
|
||||||
|
text = text.Replace(ph.Key, ph.Value);
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using MediatR;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||||
|
using EnvelopeGenerator.Application.Common.Query;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Command to sign a document by a receiver.
|
||||||
|
/// </summary>
|
||||||
|
public record SigningCommand : EnvelopeReceiverQueryBase, IRequest
|
||||||
|
{
|
||||||
|
private EnvelopeReceiverDto? _envelopeReceiver;
|
||||||
|
|
||||||
|
internal void SetEnvelopeReceiver(EnvelopeReceiverDto envelopeReceiver)
|
||||||
|
{
|
||||||
|
_envelopeReceiver = envelopeReceiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The envelope receiver information.
|
||||||
|
/// </summary>
|
||||||
|
public EnvelopeReceiverDto EnvelopeReceiver
|
||||||
|
{
|
||||||
|
get => _envelopeReceiver!;
|
||||||
|
init => _envelopeReceiver = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The PSPDFKit annotation data.
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
||||||
|
public PsPdfKitAnnotation? PsPdfKitAnnotation { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<Signature>? Signatures { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public ReceiverAppType ReceiverAppType { get; init; } = ReceiverAppType.ReceiverUI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the sign command. All work is done by pipeline behaviors.
|
||||||
|
/// This handler is intentionally empty - behaviors handle all the processing.
|
||||||
|
/// </summary>
|
||||||
|
public class SignCommandHandler : IRequestHandler<SigningCommand>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the signing command. Pipeline behaviors handle all processing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Task Handle(SigningCommand request, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public enum ReceiverAppType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
ReceiverUI = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
LegacyWeb = 1,
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
using EnvelopeGenerator.Application.Common.Query;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using MediatR;
|
||||||
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.DocReceiverElements.Queries;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public record ReadDocReceiverElementQuery : EnvelopeReceiverQueryBase, IRequest<IEnumerable<DocReceiverElementDto>>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class ReadDocReceiverElementQueryHandler : IRequestHandler<ReadDocReceiverElementQuery, IEnumerable<DocReceiverElementDto>>
|
||||||
|
{
|
||||||
|
private readonly IRepository<DocReceiverElement> _repository;
|
||||||
|
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="repository"></param>
|
||||||
|
/// <param name="mapper"></param>
|
||||||
|
public ReadDocReceiverElementQueryHandler(IRepository<DocReceiverElement> repository, IMapper mapper)
|
||||||
|
{
|
||||||
|
_repository = repository;
|
||||||
|
_mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public async Task<IEnumerable<DocReceiverElementDto>> Handle(ReadDocReceiverElementQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var q = _repository.Query;
|
||||||
|
|
||||||
|
if(request.Envelope.Id is int envelopeId)
|
||||||
|
q = q.Where(e => e.Document.EnvelopeId == envelopeId);
|
||||||
|
|
||||||
|
if (request.Envelope.Uuid is string envelopeUuid)
|
||||||
|
q = q.Where(e => e.Document.Envelope.Uuid == envelopeUuid);
|
||||||
|
|
||||||
|
if (request.Receiver.Id is int receiverId)
|
||||||
|
q = q.Where(e => e.ReceiverId == receiverId);
|
||||||
|
|
||||||
|
if (request.Receiver.Signature is string signature)
|
||||||
|
q = q.Where(e => e.Receiver.Signature == signature);
|
||||||
|
|
||||||
|
var elements = await q.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
return _mapper.Map<IEnumerable<DocReceiverElementDto>>(elements);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,12 +8,22 @@ namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record CreateDocStatusCommand : ModifyDocStatusCommandBase, IRequest<DocumentStatus>
|
public record CreateDocStatusCommand : IRequest<DocumentStatus>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets timestamp when this record was added. Returns the StatusChangedWhen value.
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime AddedWhen => StatusChangedWhen;
|
public int EnvelopeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int ReceiverId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display value associated with the status.
|
||||||
|
/// </summary>
|
||||||
|
public string? Value { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Query;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record ModifyDocStatusCommandBase : EnvelopeReceiverQueryBase
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int? EnvelopeId => Envelope.Id;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int? ReceiverId => Receiver.Id;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public override ReceiverQueryBase Receiver { get => base.Receiver; set => base.Receiver = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current status code.
|
|
||||||
/// </summary>
|
|
||||||
public DocumentStatus Status => Value is null ? DocumentStatus.Created : DocumentStatus.Signed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the display value associated with the status.
|
|
||||||
/// </summary>
|
|
||||||
public string? Value { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets timestamp when this record was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime StatusChangedWhen { get; } = DateTime.Now;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maps the current command to a new instance of the specified type.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TDest"></typeparam>
|
|
||||||
/// <returns></returns>
|
|
||||||
public TDest To<TDest>() where TDest : ModifyDocStatusCommandBase, new()
|
|
||||||
=> new()
|
|
||||||
{
|
|
||||||
Key = Key,
|
|
||||||
Envelope = Envelope,
|
|
||||||
Receiver = Receiver,
|
|
||||||
Value = Value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using AutoMapper;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a command to save the status of a document, either by creating a new status or updating an existing one based on the provided envelope and receiver identifiers.
|
|
||||||
/// It returns the identifier of the saved document status.
|
|
||||||
/// </summary>
|
|
||||||
public record SaveDocStatusCommand : ModifyDocStatusCommandBase, IRequest<DocumentStatusDto?>;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class SaveDocStatusCommandHandler : IRequestHandler<SaveDocStatusCommand, DocumentStatusDto?>
|
|
||||||
{
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
|
|
||||||
private readonly IRepository<DocumentStatus> _repo;
|
|
||||||
|
|
||||||
private readonly IRepository<Envelope> _envRepo;
|
|
||||||
|
|
||||||
private readonly IRepository<Receiver> _rcvRepo;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mapper"></param>
|
|
||||||
/// <param name="repo"></param>
|
|
||||||
/// <param name="rcvRepo"></param>
|
|
||||||
/// <param name="envRepo"></param>
|
|
||||||
public SaveDocStatusCommandHandler(IMapper mapper, IRepository<DocumentStatus> repo, IRepository<Receiver> rcvRepo, IRepository<Envelope> envRepo)
|
|
||||||
{
|
|
||||||
_mapper = mapper;
|
|
||||||
_repo = repo;
|
|
||||||
_rcvRepo = rcvRepo;
|
|
||||||
_envRepo = envRepo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<DocumentStatusDto?> Handle(SaveDocStatusCommand request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
// ceck if exists
|
|
||||||
bool isExists = await _repo.ReadOnly().Where(request).AnyAsync(cancel);
|
|
||||||
|
|
||||||
var env = await _envRepo.ReadOnly().Where(request.Envelope).FirstAsync(cancel);
|
|
||||||
var rcv = await _rcvRepo.ReadOnly().Where(request.Receiver).FirstAsync(cancel);
|
|
||||||
|
|
||||||
request.Envelope.Id = env.Id;
|
|
||||||
request.Receiver.Id = rcv.Id;
|
|
||||||
|
|
||||||
if (isExists)
|
|
||||||
{
|
|
||||||
var uReq = request.To<UpdateDocStatusCommand>();
|
|
||||||
await _repo.UpdateAsync(uReq, q => q.Where(request), cancel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var cReq = request.To<CreateDocStatusCommand>();
|
|
||||||
await _repo.CreateAsync(cReq, cancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
var docStatus = await _repo.ReadOnly().Where(request).SingleOrDefaultAsync(cancel);
|
|
||||||
|
|
||||||
return _mapper.Map<DocumentStatusDto>(docStatus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,41 @@
|
|||||||
using EnvelopeGenerator.Domain;
|
using EnvelopeGenerator.Application.Common.Commands;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record UpdateDocStatusCommand : ModifyDocStatusCommandBase
|
/// <param name="Value"></param>
|
||||||
|
public record DocStatusUpdateDto(string? Value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public record UpdateDocStatusCommand : UpdateCommand<DocStatusUpdateDto, DocumentStatus>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets timestamp when this record was added. Returns the StatusChangedWhen value.
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime? ChangedWhen => StatusChangedWhen;
|
public int EnvelopeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int ReceiverId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display value associated with the status.
|
||||||
|
/// </summary>
|
||||||
|
public string? Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override Expression<Func<DocumentStatus, bool>> BuildQueryExpression()
|
||||||
|
{
|
||||||
|
return ds => ds.EnvelopeId == EnvelopeId && ds.ReceiverId == ReceiverId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -18,11 +18,16 @@ public class MappingProfile : Profile
|
|||||||
CreateMap<CreateDocStatusCommand, DocumentStatus>()
|
CreateMap<CreateDocStatusCommand, DocumentStatus>()
|
||||||
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
||||||
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
|
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
|
||||||
|
.ForMember(dest => dest.Status, opt => opt.MapFrom(
|
||||||
|
src => src.Value == null
|
||||||
|
? Domain.Constants.DocumentStatus.Created
|
||||||
|
: Domain.Constants.DocumentStatus.Signed))
|
||||||
.MapAddedWhen();
|
.MapAddedWhen();
|
||||||
|
|
||||||
CreateMap<UpdateDocStatusCommand, DocumentStatus>()
|
CreateMap<UpdateDocStatusCommand, DocumentStatus>()
|
||||||
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
||||||
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
|
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
|
||||||
|
.ForMember(dest => dest.StatusChangedWhen, opt => opt.MapFrom(src => DateTime.Now))
|
||||||
.MapChangedWhen();
|
.MapChangedWhen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,14 +53,17 @@ public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, Docum
|
|||||||
/// </exception>
|
/// </exception>
|
||||||
public async Task<DocumentDto> Handle(ReadDocumentQuery query, CancellationToken cancel)
|
public async Task<DocumentDto> Handle(ReadDocumentQuery query, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
|
var docQuery = _repo.Query.Include(doc => doc.Elements).ThenInclude(e => e.Annotations);
|
||||||
|
|
||||||
if (query.Id is not null)
|
if (query.Id is not null)
|
||||||
{
|
{
|
||||||
var doc = await _repo.Query.Where(d => d.Id == query.Id).FirstOrDefaultAsync(cancel);
|
var doc = await docQuery.Where(d => d.Id == query.Id).FirstOrDefaultAsync(cancel);
|
||||||
|
|
||||||
return _mapper.Map<DocumentDto>(doc);
|
return _mapper.Map<DocumentDto>(doc);
|
||||||
}
|
}
|
||||||
else if (query.EnvelopeId is not null)
|
else if (query.EnvelopeId is not null)
|
||||||
{
|
{
|
||||||
var doc = await _repo.Query.Where(d => d.EnvelopeId == query.EnvelopeId).FirstOrDefaultAsync(cancel);
|
var doc = await docQuery.Where(d => d.EnvelopeId == query.EnvelopeId).FirstOrDefaultAsync(cancel);
|
||||||
return _mapper.Map<DocumentDto>(doc);
|
return _mapper.Map<DocumentDto>(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
|
||||||
<PackageReference Include="CommandDotNet">
|
<PackageReference Include="CommandDotNet">
|
||||||
<Version>7.0.5</Version>
|
<Version>7.0.5</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@@ -88,7 +88,6 @@
|
|||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
||||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
|
|
||||||
<PackageReference Include="CommandDotNet">
|
<PackageReference Include="CommandDotNet">
|
||||||
<Version>8.1.1</Version>
|
<Version>8.1.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@@ -96,7 +95,6 @@
|
|||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
||||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
|
|
||||||
<PackageReference Include="CommandDotNet">
|
<PackageReference Include="CommandDotNet">
|
||||||
<Version>8.1.1</Version>
|
<Version>8.1.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public record CreateEnvelopeReceiverCommand : CreateEnvelopeCommand, IRequest<Cr
|
|||||||
/// <param name="X">X-Position</param>
|
/// <param name="X">X-Position</param>
|
||||||
/// <param name="Y">Y-Position</param>
|
/// <param name="Y">Y-Position</param>
|
||||||
/// <param name="Page">Seite, auf der sie sich befindet</param>
|
/// <param name="Page">Seite, auf der sie sich befindet</param>
|
||||||
public record Signature([Required] double X, [Required] double Y, [Required] int Page);
|
public record DocReceiverElementCreateDto([Required] double X, [Required] double Y, [Required] int Page);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DTO für Empfänger, die erstellt oder abgerufen werden sollen.
|
/// DTO für Empfänger, die erstellt oder abgerufen werden sollen.
|
||||||
@@ -41,7 +41,7 @@ public class ReceiverGetOrCreateCommand
|
|||||||
/// Unterschriften auf Dokumenten.
|
/// Unterschriften auf Dokumenten.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required]
|
[Required]
|
||||||
public List<Signature> Signatures { get; init; } = new();
|
public List<DocReceiverElementCreateDto> DocReceiverElements { get; init; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Der Name, mit dem der Empfänger angesprochen werden soll.
|
/// Der Name, mit dem der Empfänger angesprochen werden soll.
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using EnvelopeGenerator.Application.Envelopes.Queries;
|
||||||
|
using EnvelopeGenerator.Application.Receivers.Queries;
|
||||||
|
using MediatR;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||||
|
using EnvelopeGenerator.Application.Common.Query;
|
||||||
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a query for reading an envelope receiver including sensitive fields
|
||||||
|
/// (access code, phone number) that are excluded from the standard <see cref="ReadEnvelopeReceiverQuery"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Returns a single <see cref="EnvelopeReceiverSecretDto"/> matched by UUID and receiver signature.
|
||||||
|
/// Equivalent to the legacy <c>ReadWithSecretByUuidSignatureAsync</c> service method.
|
||||||
|
/// </remarks>
|
||||||
|
public record ReadEnvelopeReceiverSecretQuery
|
||||||
|
: EnvelopeReceiverQueryBase<ReadEnvelopeQuery, ReadReceiverQuery>,
|
||||||
|
IRequest<EnvelopeReceiverSecretDto?>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for dispatching <see cref="ReadEnvelopeReceiverSecretQuery"/> via <see cref="IMediator"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class ReadEnvelopeReceiverSecretQueryExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a <see cref="ReadEnvelopeReceiverSecretQuery"/> using the composite key (uuid::signature).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mediator">The mediator instance.</param>
|
||||||
|
/// <param name="key">Composite key in the format <c>uuid::signature</c>.</param>
|
||||||
|
/// <param name="cancel">Cancellation token.</param>
|
||||||
|
/// <returns>The matching <see cref="EnvelopeReceiverSecretDto"/>, or <c>null</c> if not found.</returns>
|
||||||
|
public static Task<EnvelopeReceiverSecretDto?> ReadEnvelopeReceiverSecretAsync(
|
||||||
|
this IMediator mediator,
|
||||||
|
string key,
|
||||||
|
CancellationToken cancel = default)
|
||||||
|
=> mediator.Send(new ReadEnvelopeReceiverSecretQuery { Key = key }, cancel);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a <see cref="ReadEnvelopeReceiverSecretQuery"/> using UUID and receiver signature.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mediator">The mediator instance.</param>
|
||||||
|
/// <param name="uuid">Envelope UUID.</param>
|
||||||
|
/// <param name="signature">Receiver signature.</param>
|
||||||
|
/// <param name="cancel">Cancellation token.</param>
|
||||||
|
/// <returns>The matching <see cref="EnvelopeReceiverSecretDto"/>, or <c>null</c> if not found.</returns>
|
||||||
|
public static Task<EnvelopeReceiverSecretDto?> ReadEnvelopeReceiverSecretAsync(
|
||||||
|
this IMediator mediator,
|
||||||
|
string uuid,
|
||||||
|
string signature,
|
||||||
|
CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var q = new ReadEnvelopeReceiverSecretQuery();
|
||||||
|
q.Envelope.Uuid = uuid;
|
||||||
|
q.Receiver.Signature = signature;
|
||||||
|
return mediator.Send(q, cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles <see cref="ReadEnvelopeReceiverSecretQuery"/> and returns a
|
||||||
|
/// <see cref="EnvelopeReceiverSecretDto"/> containing sensitive fields.
|
||||||
|
/// </summary>
|
||||||
|
public class ReadEnvelopeReceiverSecretQueryHandler
|
||||||
|
: IRequestHandler<ReadEnvelopeReceiverSecretQuery, EnvelopeReceiverSecretDto?>
|
||||||
|
{
|
||||||
|
private readonly IRepository<EnvelopeReceiver> _repo;
|
||||||
|
private readonly IRepository<Receiver> _rcvRepo;
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="ReadEnvelopeReceiverSecretQueryHandler"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="envelopeReceiver">Repository for <see cref="EnvelopeReceiver"/>.</param>
|
||||||
|
/// <param name="rcvRepo">Repository for <see cref="Receiver"/>.</param>
|
||||||
|
/// <param name="mapper">AutoMapper instance.</param>
|
||||||
|
public ReadEnvelopeReceiverSecretQueryHandler(
|
||||||
|
IRepository<EnvelopeReceiver> envelopeReceiver,
|
||||||
|
IRepository<Receiver> rcvRepo,
|
||||||
|
IMapper mapper)
|
||||||
|
{
|
||||||
|
_repo = envelopeReceiver;
|
||||||
|
_rcvRepo = rcvRepo;
|
||||||
|
_mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the query and returns the matching <see cref="EnvelopeReceiverSecretDto"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The query containing filter criteria.</param>
|
||||||
|
/// <param name="cancel">Cancellation token.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The matched <see cref="EnvelopeReceiverSecretDto"/>, or <c>null</c> if no record is found.
|
||||||
|
/// </returns>
|
||||||
|
public async Task<EnvelopeReceiverSecretDto?> Handle(
|
||||||
|
ReadEnvelopeReceiverSecretQuery request,
|
||||||
|
CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var q = _repo.Query.Where(request, notnull: false);
|
||||||
|
|
||||||
|
var envRcvs = await q
|
||||||
|
.Include(er => er.Envelope).ThenInclude(e => e!.Documents!).ThenInclude(d => d.Elements)
|
||||||
|
.Include(er => er.Envelope).ThenInclude(e => e!.Histories)
|
||||||
|
.Include(er => er.Envelope).ThenInclude(e => e!.User)
|
||||||
|
.Include(er => er.Receiver)
|
||||||
|
.ToListAsync(cancel);
|
||||||
|
|
||||||
|
if (request.Receiver.HasAnyCriteria && envRcvs.Count != 0)
|
||||||
|
{
|
||||||
|
var receiver = await _rcvRepo.Query.Where(request.Receiver).FirstAsync(cancel);
|
||||||
|
|
||||||
|
foreach (var item in envRcvs)
|
||||||
|
item.Envelope?.Documents?.FirstOrDefault()?.Elements?.RemoveAll(s => s.ReceiverId != receiver.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
var envRcv = envRcvs.FirstOrDefault();
|
||||||
|
if (envRcv is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return _mapper.Map<EnvelopeReceiverSecretDto>(envRcv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,10 +40,10 @@ public record CreateEnvelopeCommand : IRequest<EnvelopeDto?>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="userId"></param>
|
/// <param name="userId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public bool Authorize(int userId)
|
public CreateEnvelopeCommand WithAuth(int userId)
|
||||||
{
|
{
|
||||||
UserId = userId;
|
UserId = userId;
|
||||||
return true;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public record ReadEnvelopeQuery : EnvelopeQueryBase, IRequest<IEnumerable<Envelo
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Optionaler Benutzerfilter; wenn gesetzt, werden nur Umschläge des Benutzers geladen.
|
/// Optionaler Benutzerfilter; wenn gesetzt, werden nur Umschläge des Benutzers geladen.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? UserId { get; init; }
|
internal int? UserId { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Setzt den Benutzerkontext für die Abfrage.
|
/// Setzt den Benutzerkontext für die Abfrage.
|
||||||
@@ -133,7 +133,7 @@ public class ReadEnvelopeQueryHandler : IRequestHandler<ReadEnvelopeQuery, IEnum
|
|||||||
}
|
}
|
||||||
|
|
||||||
var envelopes = await query
|
var envelopes = await query
|
||||||
.Include(e => e.Documents)
|
.Include(e => e.EnvelopeReceivers).ThenInclude(er => er.Receiver)
|
||||||
.ToListAsync(cancel);
|
.ToListAsync(cancel);
|
||||||
|
|
||||||
return _mapper.Map<IEnumerable<EnvelopeDto>>(envelopes);
|
return _mapper.Map<IEnumerable<EnvelopeDto>>(envelopes);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
using EnvelopeGenerator.Application.Histories.Commands;
|
using EnvelopeGenerator.Application.Histories.Commands;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ public class MappingProfile: Profile
|
|||||||
CreateMap<CreateHistoryCommand, History>()
|
CreateMap<CreateHistoryCommand, History>()
|
||||||
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
||||||
.ForMember(dest => dest.Sender, opt => opt.Ignore())
|
.ForMember(dest => dest.Sender, opt => opt.Ignore())
|
||||||
.ForMember(dest => dest.Receiver, opt => opt.Ignore());
|
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
|
||||||
|
.MapAddedWhen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,6 +190,13 @@ public static class Extensions
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string SignDoc(this IStringLocalizer localizer) => localizer[nameof(SignDoc)].Value;
|
public static string SignDoc(this IStringLocalizer localizer) => localizer[nameof(SignDoc)].Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localizer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ConfirmDoc(this IStringLocalizer localizer) => localizer[nameof(ConfirmDoc)].Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -204,6 +211,13 @@ public static class Extensions
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string DocSigned(this IStringLocalizer localizer) => localizer[nameof(DocSigned)].Value;
|
public static string DocSigned(this IStringLocalizer localizer) => localizer[nameof(DocSigned)].Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localizer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string DocConfirmed(this IStringLocalizer localizer) => localizer[nameof(DocConfirmed)].Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -239,6 +253,13 @@ public static class Extensions
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string SigAgree(this IStringLocalizer localizer) => localizer[nameof(SigAgree)].Value;
|
public static string SigAgree(this IStringLocalizer localizer) => localizer[nameof(SigAgree)].Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localizer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ConfirmAgree(this IStringLocalizer localizer) => localizer[nameof(ConfirmAgree)].Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -267,6 +288,13 @@ public static class Extensions
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string RejectionInfo1(this IStringLocalizer localizer) => localizer[nameof(RejectionInfo1)].Value;
|
public static string RejectionInfo1(this IStringLocalizer localizer) => localizer[nameof(RejectionInfo1)].Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localizer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string RejectionInfo1ForConfirmation(this IStringLocalizer localizer) => localizer[nameof(RejectionInfo1ForConfirmation)].Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -295,6 +323,13 @@ public static class Extensions
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string SigningProcessTitle(this IStringLocalizer localizer) => localizer[nameof(SigningProcessTitle)].Value;
|
public static string SigningProcessTitle(this IStringLocalizer localizer) => localizer[nameof(SigningProcessTitle)].Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localizer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ConfirmationProcessTitle(this IStringLocalizer localizer) => localizer[nameof(ConfirmationProcessTitle)].Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -250,7 +250,7 @@
|
|||||||
<value>Sie können bei Bedarf mit {0}, <a href="mailto:{1}?subject={2}&body=Sehr geehrte(r)%20{0},%0A%0A%0A">{1}</a> Kontakt aufnehmen.</value>
|
<value>Sie können bei Bedarf mit {0}, <a href="mailto:{1}?subject={2}&body=Sehr geehrte(r)%20{0},%0A%0A%0A">{1}</a> Kontakt aufnehmen.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RejectionInfo2_ext" xml:space="preserve">
|
<data name="RejectionInfo2_ext" xml:space="preserve">
|
||||||
<value>Das Vorgang wurde von einer der beteiligten Parteien abgelehnt. Sie können bei Bedarf mit {0}, <a href="mailto:{1}?subject={2}&body=Sehr geehrte(r)%20{0},%0A%0A%0A">{1}</a> Kontakt aufnehmen.</value>
|
<value>Der Vorgang wurde von einer der beteiligten Parteien abgelehnt. Sie können bei Bedarf mit {0}, <a href="mailto:{1}?subject={2}&body=Sehr geehrte(r)%20{0},%0A%0A%0A">{1}</a> Kontakt aufnehmen.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RejectionReasonQ" xml:space="preserve">
|
<data name="RejectionReasonQ" xml:space="preserve">
|
||||||
<value>Bitte geben Sie einen Grund an:</value>
|
<value>Bitte geben Sie einen Grund an:</value>
|
||||||
@@ -447,4 +447,34 @@
|
|||||||
<data name="DocumentReset" xml:space="preserve">
|
<data name="DocumentReset" xml:space="preserve">
|
||||||
<value>Dokument wurde zurückgesetzt.</value>
|
<value>Dokument wurde zurückgesetzt.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DocumentSuccessfullyConfirmed" xml:space="preserve">
|
||||||
|
<value>Dokument erfolgreich gelesen und bestätigt!</value>
|
||||||
|
</data>
|
||||||
|
<data name="DocumentConfirmedConfirmationMessage" xml:space="preserve">
|
||||||
|
<value>Sie haben das Dokument gelesen und bestätigt. Im Anschluss erhalten Sie eine schriftliche Bestätigung.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirm" xml:space="preserve">
|
||||||
|
<value>Bestätigen</value>
|
||||||
|
</data>
|
||||||
|
<data name="RejectionInfo1Confirmation" xml:space="preserve">
|
||||||
|
<value>Dieser Bestätigungsvorgang wurde abgelehnt!</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmDoc" xml:space="preserve">
|
||||||
|
<value>Dokument bestätigen</value>
|
||||||
|
</data>
|
||||||
|
<data name="DocConfirmed" xml:space="preserve">
|
||||||
|
<value>Dokument bestätigt</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmAgree" xml:space="preserve">
|
||||||
|
<value>Durch Klick auf Abschließen bestätige ich, das Dokument gelesen und zur Kenntnis genommen zu haben.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmedBy" xml:space="preserve">
|
||||||
|
<value>Bestätigt von</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmationProcessTitle" xml:space="preserve">
|
||||||
|
<value>Titel des Lesebetätigungs-Vorgangs</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirmations" xml:space="preserve">
|
||||||
|
<value>Bestätigungen</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -447,4 +447,34 @@
|
|||||||
<data name="DocumentReset" xml:space="preserve">
|
<data name="DocumentReset" xml:space="preserve">
|
||||||
<value>Document has been reset.</value>
|
<value>Document has been reset.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DocumentSuccessfullyConfirmed" xml:space="preserve">
|
||||||
|
<value>Document successfully read and confirmed!</value>
|
||||||
|
</data>
|
||||||
|
<data name="DocumentConfirmedConfirmationMessage" xml:space="preserve">
|
||||||
|
<value>You have read and confirmed the document. You will receive a written confirmation afterwards.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirm" xml:space="preserve">
|
||||||
|
<value>Confirm</value>
|
||||||
|
</data>
|
||||||
|
<data name="RejectionInfo1Confirmation" xml:space="preserve">
|
||||||
|
<value>This confirmation process has been rejected!</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmDoc" xml:space="preserve">
|
||||||
|
<value>Confirm Document</value>
|
||||||
|
</data>
|
||||||
|
<data name="DocConfirmed" xml:space="preserve">
|
||||||
|
<value>Document confirmed</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmAgree" xml:space="preserve">
|
||||||
|
<value>By clicking on “Complete”, I confirm that I have read and taken note of the document.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmedBy" xml:space="preserve">
|
||||||
|
<value>Confirmed by</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmationProcessTitle" xml:space="preserve">
|
||||||
|
<value>Title of the read confirmation process</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirmations" xml:space="preserve">
|
||||||
|
<value>Confirmations</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -447,4 +447,34 @@
|
|||||||
<data name="DocumentReset" xml:space="preserve">
|
<data name="DocumentReset" xml:space="preserve">
|
||||||
<value>Le document a été réinitialisé.</value>
|
<value>Le document a été réinitialisé.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DocumentSuccessfullyConfirmed" xml:space="preserve">
|
||||||
|
<value>Document lu et confirmé avec succès !</value>
|
||||||
|
</data>
|
||||||
|
<data name="DocumentConfirmedConfirmationMessage" xml:space="preserve">
|
||||||
|
<value>Vous avez lu et confirmé le document. Vous recevrez une confirmation écrite par la suite.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirm" xml:space="preserve">
|
||||||
|
<value>Confirmer</value>
|
||||||
|
</data>
|
||||||
|
<data name="RejectionInfo1Confirmation" xml:space="preserve">
|
||||||
|
<value>Cette procédure de confirmation a été rejetée !</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmDoc" xml:space="preserve">
|
||||||
|
<value>Confirmer le document</value>
|
||||||
|
</data>
|
||||||
|
<data name="DocConfirmed" xml:space="preserve">
|
||||||
|
<value>Document confirmé</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmAgree" xml:space="preserve">
|
||||||
|
<value>En cliquant sur « Terminer », je confirme avoir lu et pris connaissance du document.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmedBy" xml:space="preserve">
|
||||||
|
<value>Confirmé par</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmationProcessTitle" xml:space="preserve">
|
||||||
|
<value>Titre de la procédure de confirmation de lecture</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirmations" xml:space="preserve">
|
||||||
|
<value>Confirmations</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using DigitalData.Core.Application;
|
using DigitalData.Core.Application;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Services;
|
namespace EnvelopeGenerator.Application.Services;
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ namespace EnvelopeGenerator.Application.Services;
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Use MediatR")]
|
[Obsolete("Use MediatR")]
|
||||||
public class DocumentReceiverElementService : BasicCRUDService<IDocumentReceiverElementRepository, SignatureDto, Signature, int>, IDocumentReceiverElementService
|
public class DocumentReceiverElementService : BasicCRUDService<IDocumentReceiverElementRepository, DocReceiverElementDto, DocReceiverElement, int>, IDocumentReceiverElementService
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
using EnvelopeGenerator.Domain.Interfaces;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Services;
|
namespace EnvelopeGenerator.Application.Services;
|
||||||
|
|
||||||
@@ -49,14 +50,33 @@ public class EnvelopeMailService : EmailOutService, IEnvelopeMailService
|
|||||||
_sender = sender;
|
_sender = sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? envelopeReceiverDto = null)
|
private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? er = null)
|
||||||
{
|
{
|
||||||
|
if (er!.Envelope.IsReadAndConfirm())
|
||||||
|
{
|
||||||
|
_placeholders["[SIGNATURE_TYPE]"] = "Lesen und bestätigen";
|
||||||
|
_placeholders["[DOCUMENT_PROCESS]"] = string.Empty;
|
||||||
|
_placeholders["[FINAL_STATUS]"] = "Lesebestätigung";
|
||||||
|
_placeholders["[FINAL_ACTION]"] = "Empfänger bestätigt";
|
||||||
|
_placeholders["[REJECTED_BY_OTHERS]"] = "anderen Empfänger abgelehnt!";
|
||||||
|
_placeholders["[RECEIVER_ACTION]"] = "bestätigt";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_placeholders["[SIGNATURE_TYPE]"] = "Signieren";
|
||||||
|
_placeholders["[DOCUMENT_PROCESS]"] = " und elektronisch unterschreiben";
|
||||||
|
_placeholders["[FINAL_STATUS]"] = "Signatur";
|
||||||
|
_placeholders["[FINAL_ACTION]"] = "Vertragspartner unterzeichnet";
|
||||||
|
_placeholders["[REJECTED_BY_OTHERS]"] = "anderen Vertragspartner abgelehnt! Ihre notwendige Unterzeichnung wurde verworfen.";
|
||||||
|
_placeholders["[RECEIVER_ACTION]"] = "unterschrieben";
|
||||||
|
}
|
||||||
|
|
||||||
if (accessCode is not null)
|
if (accessCode is not null)
|
||||||
_placeholders["[DOCUMENT_ACCESS_CODE]"] = accessCode;
|
_placeholders["[DOCUMENT_ACCESS_CODE]"] = accessCode;
|
||||||
|
|
||||||
if (envelopeReceiverDto?.Envelope is not null && envelopeReceiverDto.Receiver is not null)
|
if (er?.Envelope is not null && er.Receiver is not null)
|
||||||
{
|
{
|
||||||
var erId = (envelopeReceiverDto.Envelope.Uuid, envelopeReceiverDto.Receiver.Signature).ToEnvelopeKey();
|
var erId = (er.Envelope.Uuid, er.Receiver.Signature).ToEnvelopeKey();
|
||||||
var sigHost = await _configService.ReadDefaultSignatureHost();
|
var sigHost = await _configService.ReadDefaultSignatureHost();
|
||||||
var linkToDoc = $"{sigHost}/EnvelopeKey/{erId}";
|
var linkToDoc = $"{sigHost}/EnvelopeKey/{erId}";
|
||||||
_placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
|
_placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
|
||||||
@@ -66,7 +86,8 @@ public class EnvelopeMailService : EmailOutService, IEnvelopeMailService
|
|||||||
return _placeholders;
|
return _placeholders;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Dictionary<string, string>> CreatePlaceholders(EnvelopeReceiverReadOnlyDto? readOnlyDto = null)
|
// TODO: merge the two CreatePlaceholders methods by using a common parameter object containing all the required information to create the place holders.
|
||||||
|
private async Task<Dictionary<string, string>> CreatePlaceholders(EnvelopeReceiverReadOnlyDto? readOnlyDto = null)
|
||||||
{
|
{
|
||||||
if (readOnlyDto?.Envelope is not null && readOnlyDto.Receiver is not null)
|
if (readOnlyDto?.Envelope is not null && readOnlyDto.Receiver is not null)
|
||||||
{
|
{
|
||||||
@@ -124,7 +145,7 @@ public class EnvelopeMailService : EmailOutService, IEnvelopeMailService
|
|||||||
return acResult.ToFail<int>().Notice(LogLevel.Error, "Therefore, access code cannot be sent");
|
return acResult.ToFail<int>().Notice(LogLevel.Error, "Therefore, access code cannot be sent");
|
||||||
var accessCode = acResult.Data;
|
var accessCode = acResult.Data;
|
||||||
|
|
||||||
var placeholders = await CreatePlaceholders(accessCode: accessCode, envelopeReceiverDto: dto);
|
var placeholders = await CreatePlaceholders(accessCode: accessCode, er: dto);
|
||||||
|
|
||||||
// Add optional place holders.
|
// Add optional place holders.
|
||||||
if (optionalPlaceholders is not null)
|
if (optionalPlaceholders is not null)
|
||||||
|
|||||||
@@ -487,10 +487,6 @@
|
|||||||
<Project>{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8}</Project>
|
<Project>{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8}</Project>
|
||||||
<Name>EnvelopeGenerator.CommonServices</Name>
|
<Name>EnvelopeGenerator.CommonServices</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.CommonServices\EnvelopeGenerator.CommonServices.vbproj">
|
|
||||||
<Project>{6ea0c51f-c2b1-4462-8198-3de0b32b74f8}</Project>
|
|
||||||
<Name>EnvelopeGenerator.CommonServices</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj">
|
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj">
|
||||||
<Project>{4f32a98d-e6f0-4a09-bd97-1cf26107e837}</Project>
|
<Project>{4f32a98d-e6f0-4a09-bd97-1cf26107e837}</Project>
|
||||||
<Name>EnvelopeGenerator.Domain</Name>
|
<Name>EnvelopeGenerator.Domain</Name>
|
||||||
|
|||||||
12
EnvelopeGenerator.BBTests/frmFinalizePDF.Designer.vb
generated
12
EnvelopeGenerator.BBTests/frmFinalizePDF.Designer.vb
generated
@@ -27,6 +27,7 @@ Partial Class frmFinalizePDF
|
|||||||
Me.Label2 = New System.Windows.Forms.Label()
|
Me.Label2 = New System.Windows.Forms.Label()
|
||||||
Me.Button1 = New System.Windows.Forms.Button()
|
Me.Button1 = New System.Windows.Forms.Button()
|
||||||
Me.Button2 = New System.Windows.Forms.Button()
|
Me.Button2 = New System.Windows.Forms.Button()
|
||||||
|
Me.Button3 = New System.Windows.Forms.Button()
|
||||||
Me.txtResult = New System.Windows.Forms.TextBox()
|
Me.txtResult = New System.Windows.Forms.TextBox()
|
||||||
Me.txtEnvelope = New System.Windows.Forms.TextBox()
|
Me.txtEnvelope = New System.Windows.Forms.TextBox()
|
||||||
Me.SuspendLayout()
|
Me.SuspendLayout()
|
||||||
@@ -75,6 +76,15 @@ Partial Class frmFinalizePDF
|
|||||||
Me.Button2.Text = "Merge Json"
|
Me.Button2.Text = "Merge Json"
|
||||||
Me.Button2.UseVisualStyleBackColor = True
|
Me.Button2.UseVisualStyleBackColor = True
|
||||||
'
|
'
|
||||||
|
'Button3
|
||||||
|
'
|
||||||
|
Me.Button3.Location = New System.Drawing.Point(15, 160)
|
||||||
|
Me.Button3.Name = "Button3"
|
||||||
|
Me.Button3.Size = New System.Drawing.Size(166, 23)
|
||||||
|
Me.Button3.TabIndex = 5
|
||||||
|
Me.Button3.Text = "Full Finalize Test"
|
||||||
|
Me.Button3.UseVisualStyleBackColor = True
|
||||||
|
'
|
||||||
'txtResult
|
'txtResult
|
||||||
'
|
'
|
||||||
Me.txtResult.Location = New System.Drawing.Point(333, 12)
|
Me.txtResult.Location = New System.Drawing.Point(333, 12)
|
||||||
@@ -97,6 +107,7 @@ Partial Class frmFinalizePDF
|
|||||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||||
Me.ClientSize = New System.Drawing.Size(800, 450)
|
Me.ClientSize = New System.Drawing.Size(800, 450)
|
||||||
Me.Controls.Add(Me.txtResult)
|
Me.Controls.Add(Me.txtResult)
|
||||||
|
Me.Controls.Add(Me.Button3)
|
||||||
Me.Controls.Add(Me.Button2)
|
Me.Controls.Add(Me.Button2)
|
||||||
Me.Controls.Add(Me.Button1)
|
Me.Controls.Add(Me.Button1)
|
||||||
Me.Controls.Add(Me.Label2)
|
Me.Controls.Add(Me.Label2)
|
||||||
@@ -116,5 +127,6 @@ Partial Class frmFinalizePDF
|
|||||||
Friend WithEvents Label2 As Label
|
Friend WithEvents Label2 As Label
|
||||||
Friend WithEvents Button1 As Button
|
Friend WithEvents Button1 As Button
|
||||||
Friend WithEvents Button2 As Button
|
Friend WithEvents Button2 As Button
|
||||||
|
Friend WithEvents Button3 As Button
|
||||||
Friend WithEvents txtResult As TextBox
|
Friend WithEvents txtResult As TextBox
|
||||||
End Class
|
End Class
|
||||||
|
|||||||
@@ -7,11 +7,12 @@ Imports GdPicture14
|
|||||||
Imports Newtonsoft.Json.Linq
|
Imports Newtonsoft.Json.Linq
|
||||||
Imports EnvelopeGenerator.Infrastructure
|
Imports EnvelopeGenerator.Infrastructure
|
||||||
Imports Microsoft.EntityFrameworkCore
|
Imports Microsoft.EntityFrameworkCore
|
||||||
Imports System.Text
|
|
||||||
Imports DigitalData.Core.Abstractions
|
Imports DigitalData.Core.Abstractions
|
||||||
|
Imports DigitalData.Core.Abstraction.Application.Repository
|
||||||
|
Imports EnvelopeGenerator.Domain.Entities
|
||||||
|
|
||||||
Public Class frmFinalizePDF
|
Public Class frmFinalizePDF
|
||||||
Private Const CONNECTIONSTRING = "Server=sDD-VMP04-SQL17\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=+bk8oAbbQP1AzoHtvZUbd+Mbok2f8Fl4miEx1qssJ5yEaEWoQJ9prg4L14fURpPnqi1WMNs9fE4=;"
|
Private Const CONNECTIONSTRING = "Server=sDD-VMP04-SQL17\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=+bk8oAbbQP1AzoHtvZUbd+Mbok2f8Fl4miEx1qssJ5yEaEWoQJ9prg4L14fURpPnqi1WMNs9fE4=;" + "Encrypt=True;TrustServerCertificate=True;"
|
||||||
|
|
||||||
Private Database As MSSQLServer
|
Private Database As MSSQLServer
|
||||||
Private LogConfig As LogConfig
|
Private LogConfig As LogConfig
|
||||||
@@ -93,56 +94,118 @@ Public Class frmFinalizePDF
|
|||||||
End Function
|
End Function
|
||||||
|
|
||||||
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
|
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
|
||||||
Try
|
Dim oTable = LoadAnnotationDataForEnvelope()
|
||||||
|
Dim oJsonList = oTable.Rows.
|
||||||
|
Cast(Of DataRow).
|
||||||
|
Select(Function(r As DataRow) r.Item("VALUE").ToString()).
|
||||||
|
ToList()
|
||||||
|
|
||||||
Dim oTable = LoadAnnotationDataForEnvelope()
|
Dim envelopeId As Integer = CInt(txtEnvelope.Text)
|
||||||
Dim oJsonList = oTable.Rows.
|
Dim oBuffer As Byte() = ReadEnvelope(envelopeId)
|
||||||
Cast(Of DataRow).
|
Dim oNewBuffer = PDFBurner.BurnAnnotsToPDF(oBuffer, oJsonList, envelopeId)
|
||||||
Select(Function(r As DataRow) r.Item("VALUE").ToString()).
|
Dim desktopPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
|
||||||
ToList()
|
Dim oNewPath = Path.Combine(desktopPath, $"E{txtEnvelope.Text}R{txtReceiver.Text}.burned.pdf")
|
||||||
|
|
||||||
Dim envelopeId As Integer = CInt(txtEnvelope.Text)
|
File.WriteAllBytes(oNewPath, oNewBuffer)
|
||||||
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")
|
|
||||||
|
|
||||||
File.WriteAllBytes(oNewPath, oNewBuffer)
|
|
||||||
|
|
||||||
Process.Start(oNewPath)
|
|
||||||
Catch ex As Exception
|
|
||||||
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
|
|
||||||
|
|
||||||
|
Process.Start(oNewPath)
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
|
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
|
||||||
|
Dim oTable = LoadAnnotationDataForEnvelope()
|
||||||
|
Dim oJsonList = oTable.Rows.
|
||||||
|
Cast(Of DataRow).
|
||||||
|
Select(Function(r As DataRow) r.Item("VALUE").ToString()).
|
||||||
|
Select(Function(s As String) JObject.Parse(s)).
|
||||||
|
ToList()
|
||||||
|
|
||||||
|
Dim oJObject1 = oJsonList.First()
|
||||||
|
Dim oJObject2 = oJsonList.ElementAt(1)
|
||||||
|
|
||||||
|
oJObject1.Merge(oJObject2)
|
||||||
|
|
||||||
|
txtResult.Text = oJObject1.ToString()
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
|
||||||
Try
|
Try
|
||||||
|
Dim envelopeId As Integer = CInt(txtEnvelope.Text)
|
||||||
|
Dim log As New System.Text.StringBuilder()
|
||||||
|
|
||||||
|
' 1. Load annotation JSON data (same as Service)
|
||||||
Dim oTable = LoadAnnotationDataForEnvelope()
|
Dim oTable = LoadAnnotationDataForEnvelope()
|
||||||
Dim oJsonList = oTable.Rows.
|
Dim oJsonList = oTable.Rows.
|
||||||
Cast(Of DataRow).
|
Cast(Of DataRow).
|
||||||
Select(Function(r As DataRow) r.Item("VALUE").ToString()).
|
Select(Function(r As DataRow) r.Item("VALUE").ToString()).
|
||||||
Select(Function(s As String) JObject.Parse(s)).
|
|
||||||
ToList()
|
ToList()
|
||||||
|
log.AppendLine($"Annotation JSON count: {oJsonList.Count}")
|
||||||
|
|
||||||
Dim oJObject1 = oJsonList.First()
|
' 2. Load document bytes (same as Service)
|
||||||
Dim oJObject2 = oJsonList.ElementAt(1)
|
Dim oBuffer As Byte() = ReadEnvelope(envelopeId)
|
||||||
|
log.AppendLine($"Document bytes: {oBuffer.Length}")
|
||||||
|
|
||||||
oJObject1.Merge(oJObject2)
|
' 3. Check what BurnAnnotsToPDF will do internally
|
||||||
|
Using scope = Factory.Shared.ScopeFactory.CreateScope()
|
||||||
|
Dim envRepo = scope.ServiceProvider.Repository(Of Envelope)()
|
||||||
|
Dim envelope = envRepo.Where(Function(env) env.Id = envelopeId).FirstOrDefault()
|
||||||
|
|
||||||
txtResult.Text = oJObject1.ToString()
|
If envelope Is Nothing Then
|
||||||
|
log.AppendLine("ERROR: Envelope not found in EF Core!")
|
||||||
|
txtResult.Text = log.ToString()
|
||||||
|
Return
|
||||||
|
End If
|
||||||
|
|
||||||
|
log.AppendLine($"Envelope found: Id={envelope.Id}, EnvelopeTypeId={envelope.EnvelopeTypeId}")
|
||||||
|
log.AppendLine($"ReadOnly (IsReadAndConfirm): {envelope.ReadOnly}")
|
||||||
|
|
||||||
|
If envelope.ReadOnly Then
|
||||||
|
log.AppendLine(">>> EARLY RETURN: ReadOnly=True, original PDF returned without burning")
|
||||||
|
txtResult.Text = log.ToString()
|
||||||
|
Return
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim sigRepo = scope.ServiceProvider.Repository(Of DocReceiverElement)()
|
||||||
|
Dim elements = sigRepo _
|
||||||
|
.Where(Function(sig) sig.Document.EnvelopeId = envelopeId) _
|
||||||
|
.Include(Function(sig) sig.Annotations) _
|
||||||
|
.ToList()
|
||||||
|
|
||||||
|
log.AppendLine($"Elements (Signature) count: {elements.Count}")
|
||||||
|
|
||||||
|
If elements.Any() Then
|
||||||
|
log.AppendLine(">>> PATH: BurnElementAnnotsToPDF (new element-based path)")
|
||||||
|
For Each elem In elements
|
||||||
|
Dim annotCount = If(elem.Annotations IsNot Nothing, elem.Annotations.Count(), 0)
|
||||||
|
log.AppendLine($" Element Id={elem.Id}, Page={elem.Page}, X={elem.X}, Y={elem.Y}, W={elem.Width}, H={elem.Height}, Annotations={annotCount}")
|
||||||
|
If elem.Annotations IsNot Nothing Then
|
||||||
|
For Each annot In elem.Annotations
|
||||||
|
log.AppendLine($" Annot: Name={annot.Name}, Type={annot.Type}, X={annot.X}, Y={annot.Y}, W={annot.Width}, H={annot.Height}")
|
||||||
|
Next
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
Else
|
||||||
|
log.AppendLine(">>> PATH: BurnInstantJSONAnnotsToPDF (old JSON-based path)")
|
||||||
|
End If
|
||||||
|
End Using
|
||||||
|
|
||||||
|
' 4. Actually call BurnAnnotsToPDF (same as Service)
|
||||||
|
log.AppendLine("")
|
||||||
|
log.AppendLine("Calling BurnAnnotsToPDF...")
|
||||||
|
Dim oNewBuffer = PDFBurner.BurnAnnotsToPDF(oBuffer, oJsonList, envelopeId)
|
||||||
|
log.AppendLine($"Result bytes: {oNewBuffer.Length}")
|
||||||
|
log.AppendLine($"Same as input: {oBuffer.Length = oNewBuffer.Length AndAlso oBuffer.SequenceEqual(oNewBuffer)}")
|
||||||
|
|
||||||
|
' 5. Write output
|
||||||
|
Dim desktopPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
|
||||||
|
Dim oNewPath = Path.Combine(desktopPath, $"E{txtEnvelope.Text}_FullTest.burned.pdf")
|
||||||
|
File.WriteAllBytes(oNewPath, oNewBuffer)
|
||||||
|
log.AppendLine($"Output: {oNewPath}")
|
||||||
|
|
||||||
|
txtResult.Text = log.ToString()
|
||||||
|
Process.Start(oNewPath)
|
||||||
|
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
MsgBox(ex.Message, MsgBoxStyle.Critical)
|
txtResult.Text = $"ERROR: {ex.Message}{vbCrLf}{vbCrLf}{ex.ToString()}"
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
End Class
|
End Class
|
||||||
@@ -15,13 +15,13 @@
|
|||||||
<package id="DigitalData.Modules.Messaging" version="1.9.8" targetFramework="net462" />
|
<package id="DigitalData.Modules.Messaging" version="1.9.8" targetFramework="net462" />
|
||||||
<package id="DocumentFormat.OpenXml" version="3.2.0" targetFramework="net462" />
|
<package id="DocumentFormat.OpenXml" version="3.2.0" targetFramework="net462" />
|
||||||
<package id="DocumentFormat.OpenXml.Framework" version="3.2.0" targetFramework="net462" />
|
<package id="DocumentFormat.OpenXml.Framework" version="3.2.0" targetFramework="net462" />
|
||||||
<package id="EntityFramework" version="6.5.1" targetFramework="net462" />
|
<package id="EntityFramework" version="6.4.4" targetFramework="net462" />
|
||||||
<package id="EntityFramework.Firebird" version="6.4.0" targetFramework="net462" />
|
<package id="EntityFramework.Firebird" version="6.4.0" targetFramework="net462" />
|
||||||
<package id="FirebirdSql.Data.FirebirdClient" version="7.5.0" targetFramework="net462" />
|
<package id="FirebirdSql.Data.FirebirdClient" version="7.5.0" targetFramework="net462" />
|
||||||
<package id="GdPicture" version="14.3.3" targetFramework="net462" />
|
<package id="GdPicture" version="14.3.3" targetFramework="net462" />
|
||||||
<package id="GdPicture.runtimes.windows" version="14.3.3" targetFramework="net462" />
|
<package id="GdPicture.runtimes.windows" version="14.3.3" targetFramework="net462" />
|
||||||
<package id="Microsoft.AspNet.WebApi.Client" version="6.0.0" targetFramework="net462" />
|
<package id="Microsoft.AspNet.WebApi.Client" version="6.0.0" targetFramework="net462" />
|
||||||
<package id="Microsoft.Bcl.AsyncInterfaces" version="9.0.0" targetFramework="net462" />
|
<package id="Microsoft.Bcl.AsyncInterfaces" version="8.0.0" targetFramework="net462" />
|
||||||
<package id="Microsoft.Bcl.Cryptography" version="9.0.0" targetFramework="net462" />
|
<package id="Microsoft.Bcl.Cryptography" version="9.0.0" targetFramework="net462" />
|
||||||
<package id="Microsoft.Bcl.HashCode" version="1.1.1" targetFramework="net462" />
|
<package id="Microsoft.Bcl.HashCode" version="1.1.1" targetFramework="net462" />
|
||||||
<package id="Microsoft.CSharp" version="4.7.0" targetFramework="net462" />
|
<package id="Microsoft.CSharp" version="4.7.0" targetFramework="net462" />
|
||||||
@@ -62,10 +62,10 @@
|
|||||||
<package id="protobuf-net.Core" version="3.2.46" targetFramework="net462" />
|
<package id="protobuf-net.Core" version="3.2.46" targetFramework="net462" />
|
||||||
<package id="RtfPipe" version="2.0.7677.4303" targetFramework="net462" />
|
<package id="RtfPipe" version="2.0.7677.4303" targetFramework="net462" />
|
||||||
<package id="S22.Imap" version="3.6.0.0" targetFramework="net462" />
|
<package id="S22.Imap" version="3.6.0.0" targetFramework="net462" />
|
||||||
<package id="System.Buffers" version="4.6.1" targetFramework="net462" />
|
<package id="System.Buffers" version="4.6.0" targetFramework="net462" />
|
||||||
<package id="System.ClientModel" version="1.8.0" targetFramework="net462" />
|
<package id="System.ClientModel" version="1.8.0" targetFramework="net462" />
|
||||||
<package id="System.CodeDom" version="9.0.0" targetFramework="net462" />
|
<package id="System.CodeDom" version="8.0.0" targetFramework="net462" />
|
||||||
<package id="System.Collections.Immutable" version="9.0.0" targetFramework="net462" />
|
<package id="System.Collections.Immutable" version="8.0.0" targetFramework="net462" />
|
||||||
<package id="System.ComponentModel.Annotations" version="4.7.0" targetFramework="net462" />
|
<package id="System.ComponentModel.Annotations" version="4.7.0" targetFramework="net462" />
|
||||||
<package id="System.Data.Common" version="4.3.0" targetFramework="net462" />
|
<package id="System.Data.Common" version="4.3.0" targetFramework="net462" />
|
||||||
<package id="System.Data.Odbc" version="6.0.1" targetFramework="net462" />
|
<package id="System.Data.Odbc" version="6.0.1" targetFramework="net462" />
|
||||||
@@ -74,23 +74,23 @@
|
|||||||
<package id="System.Formats.Asn1" version="10.0.3" targetFramework="net462" />
|
<package id="System.Formats.Asn1" version="10.0.3" targetFramework="net462" />
|
||||||
<package id="System.IdentityModel.Tokens.Jwt" version="7.7.1" targetFramework="net462" />
|
<package id="System.IdentityModel.Tokens.Jwt" version="7.7.1" targetFramework="net462" />
|
||||||
<package id="System.IO.FileSystem.AccessControl" version="5.0.0" targetFramework="net462" />
|
<package id="System.IO.FileSystem.AccessControl" version="5.0.0" targetFramework="net462" />
|
||||||
<package id="System.IO.Packaging" version="9.0.0" targetFramework="net462" />
|
<package id="System.IO.Packaging" version="8.0.1" targetFramework="net462" />
|
||||||
<package id="System.IO.Pipelines" version="9.0.0" targetFramework="net462" />
|
<package id="System.IO.Pipelines" version="9.0.0" targetFramework="net462" />
|
||||||
<package id="System.Management" version="9.0.0" targetFramework="net462" />
|
<package id="System.Management" version="8.0.0" targetFramework="net462" />
|
||||||
<package id="System.Memory" version="4.6.3" targetFramework="net462" />
|
<package id="System.Memory" version="4.6.0" targetFramework="net462" />
|
||||||
<package id="System.Memory.Data" version="8.0.1" targetFramework="net462" />
|
<package id="System.Memory.Data" version="8.0.1" targetFramework="net462" />
|
||||||
<package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net462" />
|
<package id="System.Numerics.Vectors" version="4.6.0" targetFramework="net462" />
|
||||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net462" />
|
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.0" targetFramework="net462" />
|
||||||
<package id="System.Security.AccessControl" version="6.0.1" targetFramework="net462" />
|
<package id="System.Security.AccessControl" version="6.0.1" targetFramework="net462" />
|
||||||
<package id="System.Security.Cryptography.Algorithms" version="4.3.1" 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.Cng" version="5.0.0" targetFramework="net462" />
|
||||||
<package id="System.Security.Cryptography.Pkcs" version="9.0.0" targetFramework="net462" />
|
<package id="System.Security.Cryptography.Pkcs" version="8.0.1" targetFramework="net462" />
|
||||||
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net462" />
|
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net462" />
|
||||||
<package id="System.Security.Cryptography.ProtectedData" version="4.5.0" targetFramework="net462" />
|
<package id="System.Security.Cryptography.ProtectedData" version="4.5.0" targetFramework="net462" />
|
||||||
<package id="System.Security.Principal.Windows" version="5.0.0" targetFramework="net462" />
|
<package id="System.Security.Principal.Windows" version="5.0.0" targetFramework="net462" />
|
||||||
<package id="System.Text.Encodings.Web" version="9.0.0" targetFramework="net462" />
|
<package id="System.Text.Encodings.Web" version="8.0.0" targetFramework="net462" />
|
||||||
<package id="System.Text.Json" version="9.0.0" targetFramework="net462" />
|
<package id="System.Text.Json" version="8.0.6" targetFramework="net462" />
|
||||||
<package id="System.Text.RegularExpressions" version="4.3.1" targetFramework="net462" />
|
<package id="System.Text.RegularExpressions" version="4.3.1" targetFramework="net462" />
|
||||||
<package id="System.Threading.Tasks.Extensions" version="4.6.0" targetFramework="net462" />
|
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net462" />
|
||||||
<package id="System.ValueTuple" version="4.6.1" targetFramework="net462" />
|
<package id="System.ValueTuple" version="4.5.0" targetFramework="net462" />
|
||||||
</packages>
|
</packages>
|
||||||
@@ -15,6 +15,7 @@ Imports DigitalData.Core.Abstraction.Application
|
|||||||
Imports EnvelopeGenerator.Infrastructure
|
Imports EnvelopeGenerator.Infrastructure
|
||||||
Imports Microsoft.EntityFrameworkCore
|
Imports Microsoft.EntityFrameworkCore
|
||||||
Imports DigitalData.Core.Abstractions
|
Imports DigitalData.Core.Abstractions
|
||||||
|
Imports EnvelopeGenerator.Domain.Interfaces
|
||||||
|
|
||||||
Namespace Jobs
|
Namespace Jobs
|
||||||
Public Class FinalizeDocumentJob
|
Public Class FinalizeDocumentJob
|
||||||
@@ -230,6 +231,29 @@ Namespace Jobs
|
|||||||
|
|
||||||
Return Task.FromResult(True)
|
Return Task.FromResult(True)
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
|
#Region "From BBTests"
|
||||||
|
Private Function ReadEnvelope(pEnvID As Integer) As Byte()
|
||||||
|
Dim strSql As String = "Select [BYTE_DATA] from [TBSIG_ENVELOPE_DOCUMENT] WHERE ENVELOPE_ID = " & pEnvID
|
||||||
|
Dim obyteDB = Database.GetScalarValue(strSql)
|
||||||
|
If Not IsDBNull(obyteDB) Then
|
||||||
|
Dim fileData As Byte() = DirectCast(Database.GetScalarValue(strSql), Byte())
|
||||||
|
If fileData IsNot Nothing Then
|
||||||
|
Return fileData
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
|
Throw New InvalidOperationException($"Byte data is null. Envelope ID: {pEnvID}")
|
||||||
|
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function LoadAnnotationDataForEnvelope(pEnvID As Integer) As DataTable
|
||||||
|
Dim oSql = $"SELECT VALUE FROM [TBSIG_DOCUMENT_STATUS] WHERE ENVELOPE_ID = {pEnvID}"
|
||||||
|
Return Database.GetDatatable(oSql)
|
||||||
|
|
||||||
|
End Function
|
||||||
|
#End Region
|
||||||
|
|
||||||
Private Sub Update_File_DB(pFilePath As String, pEnvelopeID As Long)
|
Private Sub Update_File_DB(pFilePath As String, pEnvelopeID As Long)
|
||||||
Dim SqlCom As SqlCommand
|
Dim SqlCom As SqlCommand
|
||||||
Dim imageData As Byte()
|
Dim imageData As Byte()
|
||||||
@@ -327,7 +351,7 @@ Namespace Jobs
|
|||||||
Logger.Warn($"No SendFinalEmailToCreator - oMailToCreator [{oMailToCreator}] <> [{FinalEmailType.No}] ")
|
Logger.Warn($"No SendFinalEmailToCreator - oMailToCreator [{oMailToCreator}] <> [{FinalEmailType.No}] ")
|
||||||
End If
|
End If
|
||||||
|
|
||||||
If oMailToReceivers <> FinalEmailType.No Then
|
If oMailToReceivers <> FinalEmailType.No And pEnvelope.IsReadAndSign() Then
|
||||||
Logger.Debug("Sending emails to receivers..")
|
Logger.Debug("Sending emails to receivers..")
|
||||||
SendFinalEmailToReceivers(pEnvelope) ', pAttachment
|
SendFinalEmailToReceivers(pEnvelope) ', pAttachment
|
||||||
Else
|
Else
|
||||||
@@ -418,7 +442,18 @@ Namespace Jobs
|
|||||||
End Try
|
End Try
|
||||||
End If
|
End If
|
||||||
|
|
||||||
Return PDFBurner.BurnAnnotsToPDF(oInputDocumentBuffer, oAnnotations, pEnvelopeData.EnvelopeId)
|
#Region "From BBTests"
|
||||||
|
Dim oTable = LoadAnnotationDataForEnvelope(pEnvelopeId)
|
||||||
|
Dim oJsonList = oTable.Rows.
|
||||||
|
Cast(Of DataRow).
|
||||||
|
Select(Function(r As DataRow) r.Item("VALUE").ToString()).
|
||||||
|
ToList()
|
||||||
|
|
||||||
|
Dim oBuffer As Byte() = ReadEnvelope(pEnvelopeId)
|
||||||
|
|
||||||
|
#End Region
|
||||||
|
|
||||||
|
Return PDFBurner.BurnAnnotsToPDF(oBuffer, oJsonList, pEnvelopeId)
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Private Function GetEnvelopeData(pEnvelopeId As Integer) As EnvelopeData
|
Private Function GetEnvelopeData(pEnvelopeId As Integer) As EnvelopeData
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ Namespace Jobs.FinalizeDocument
|
|||||||
Return pSourceBuffer
|
Return pSourceBuffer
|
||||||
End If
|
End If
|
||||||
|
|
||||||
Dim sigRepo = scope.ServiceProvider.Repository(Of Signature)()
|
Dim sigRepo = scope.ServiceProvider.Repository(Of DocReceiverElement)()
|
||||||
Dim elements = sigRepo _
|
Dim elements = sigRepo _
|
||||||
.Where(Function(sig) sig.Document.EnvelopeId = envelopeId) _
|
.Where(Function(sig) sig.Document.EnvelopeId = envelopeId) _
|
||||||
.Include(Function(sig) sig.Annotations) _
|
.Include(Function(sig) sig.Annotations) _
|
||||||
@@ -58,7 +58,7 @@ Namespace Jobs.FinalizeDocument
|
|||||||
End Using
|
End Using
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function BurnElementAnnotsToPDF(pSourceBuffer As Byte(), elements As List(Of Signature)) As Byte()
|
Public Function BurnElementAnnotsToPDF(pSourceBuffer As Byte(), elements As List(Of DocReceiverElement)) As Byte()
|
||||||
' Add background
|
' Add background
|
||||||
Using doc As Pdf(Of MemoryStream, MemoryStream) = Pdf.FromMemory(pSourceBuffer)
|
Using doc As Pdf(Of MemoryStream, MemoryStream) = Pdf.FromMemory(pSourceBuffer)
|
||||||
'TODO: take the length from the largest y
|
'TODO: take the length from the largest y
|
||||||
@@ -85,16 +85,37 @@ Namespace Jobs.FinalizeDocument
|
|||||||
|
|
||||||
'Add annotations
|
'Add annotations
|
||||||
For Each element In elements
|
For Each element In elements
|
||||||
|
If element Is Nothing Then
|
||||||
|
Continue For
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim elementAnnotations = If(element.Annotations, Enumerable.Empty(Of ElementAnnotation)())
|
||||||
|
If Not elementAnnotations.Any() Then
|
||||||
|
Continue For
|
||||||
|
End If
|
||||||
|
|
||||||
Dim frameX = (element.Left - 0.7 - margin)
|
Dim frameX = (element.Left - 0.7 - margin)
|
||||||
|
|
||||||
Dim frame = element.Annotations.FirstOrDefault(Function(a) a.Name = "frame")
|
Dim frame = elementAnnotations.FirstOrDefault(Function(a) a.Name = "frame")
|
||||||
Dim frameY = element.Top - 0.5 - margin
|
Dim frameY = element.Top - 0.5 - margin
|
||||||
Dim frameYShift = frame.Y - frameY * inchFactor
|
Dim frameYShift As Double = 0
|
||||||
Dim frameXShift = frame.X - frameX * inchFactor
|
Dim frameXShift As Double = 0
|
||||||
|
|
||||||
|
If frame IsNot Nothing Then
|
||||||
|
frameYShift = frame.Y - frameY * inchFactor
|
||||||
|
frameXShift = frame.X - frameX * inchFactor
|
||||||
|
End If
|
||||||
|
|
||||||
|
For Each annot In elementAnnotations
|
||||||
|
If annot Is Nothing Then
|
||||||
|
Continue For
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim yOffsetofFF As Double = 0
|
||||||
|
If Not String.IsNullOrEmpty(annot.Name) Then
|
||||||
|
yOffsetsOfFF.TryGetValue(annot.Name, yOffsetofFF)
|
||||||
|
End If
|
||||||
|
|
||||||
For Each annot In element.Annotations
|
|
||||||
Dim yOffsetofFF As Double = If(yOffsetsOfFF.TryGetValue(annot.Name, yOffsetofFF), yOffsetofFF, 0)
|
|
||||||
Dim y = frameY + yOffsetofFF
|
Dim y = frameY + yOffsetofFF
|
||||||
|
|
||||||
If annot.Type = AnnotationType.FormField Then
|
If annot.Type = AnnotationType.FormField Then
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ Public Class PDFMerger
|
|||||||
|
|
||||||
' Convert to PDF/A
|
' Convert to PDF/A
|
||||||
oMergedPDF.ConvertToPDFA(oFinalStream, PDFAConformanceLevel, ALLOW_VECTORIZATION, ALLOW_RASTERIZATION)
|
oMergedPDF.ConvertToPDFA(oFinalStream, PDFAConformanceLevel, ALLOW_VECTORIZATION, ALLOW_RASTERIZATION)
|
||||||
oStatus = oDocumentPDF.GetStat()
|
oStatus = oMergedPDF.GetStat()
|
||||||
If oStatus <> GdPictureStatus.OK Then
|
If oStatus <> GdPictureStatus.OK Then
|
||||||
Throw New MergeDocumentException($"Document could not be converted to PDF/A: {oStatus}")
|
Throw New MergeDocumentException($"Document could not be converted to PDF/A: {oStatus}")
|
||||||
End If
|
End If
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ Public Class ElementModel
|
|||||||
MyBase.New(pState)
|
MyBase.New(pState)
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
Private Function ToElement(pRow As DataRow) As Signature
|
Private Function ToElement(pRow As DataRow) As DocReceiverElement
|
||||||
Return New Signature() With {
|
Return New DocReceiverElement() With {
|
||||||
.Id = pRow.ItemEx("GUID", 0),
|
.Id = pRow.ItemEx("GUID", 0),
|
||||||
.DocumentId = pRow.ItemEx("DOCUMENT_ID", 0),
|
.DocumentId = pRow.ItemEx("DOCUMENT_ID", 0),
|
||||||
.ReceiverId = pRow.ItemEx("RECEIVER_ID", 0),
|
.ReceiverId = pRow.ItemEx("RECEIVER_ID", 0),
|
||||||
@@ -24,7 +24,7 @@ Public Class ElementModel
|
|||||||
}
|
}
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Private Function ToElements(pTable As DataTable) As List(Of Signature)
|
Private Function ToElements(pTable As DataTable) As List(Of DocReceiverElement)
|
||||||
Return pTable?.Rows.Cast(Of DataRow).
|
Return pTable?.Rows.Cast(Of DataRow).
|
||||||
Select(AddressOf ToElement).
|
Select(AddressOf ToElement).
|
||||||
ToList()
|
ToList()
|
||||||
@@ -80,7 +80,7 @@ Public Class ElementModel
|
|||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function List(pDocumentId As Integer) As List(Of Signature)
|
Public Function List(pDocumentId As Integer) As List(Of DocReceiverElement)
|
||||||
Try
|
Try
|
||||||
Dim oSql = $"SELECT * FROM [dbo].[TBSIG_DOCUMENT_RECEIVER_ELEMENT] WHERE DOCUMENT_ID = {pDocumentId} ORDER BY PAGE ASC"
|
Dim oSql = $"SELECT * FROM [dbo].[TBSIG_DOCUMENT_RECEIVER_ELEMENT] WHERE DOCUMENT_ID = {pDocumentId} ORDER BY PAGE ASC"
|
||||||
Dim oTable = Database.GetDatatable(oSql)
|
Dim oTable = Database.GetDatatable(oSql)
|
||||||
@@ -93,7 +93,7 @@ Public Class ElementModel
|
|||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function List(pDocumentId As Integer, pReceiverId As Integer) As List(Of Signature)
|
Public Function List(pDocumentId As Integer, pReceiverId As Integer) As List(Of DocReceiverElement)
|
||||||
Try
|
Try
|
||||||
Dim oReceiverConstraint = ""
|
Dim oReceiverConstraint = ""
|
||||||
If pReceiverId > 0 Then
|
If pReceiverId > 0 Then
|
||||||
@@ -111,7 +111,7 @@ Public Class ElementModel
|
|||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function Insert(pElement As Signature) As Boolean
|
Public Function Insert(pElement As DocReceiverElement) As Boolean
|
||||||
Try
|
Try
|
||||||
Dim oSql = "INSERT INTO [dbo].[TBSIG_DOCUMENT_RECEIVER_ELEMENT]
|
Dim oSql = "INSERT INTO [dbo].[TBSIG_DOCUMENT_RECEIVER_ELEMENT]
|
||||||
([DOCUMENT_ID]
|
([DOCUMENT_ID]
|
||||||
@@ -161,7 +161,7 @@ Public Class ElementModel
|
|||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function Update(pElement As Signature) As Boolean
|
Public Function Update(pElement As DocReceiverElement) As Boolean
|
||||||
Try
|
Try
|
||||||
Dim oSql = "UPDATE [dbo].[TBSIG_DOCUMENT_RECEIVER_ELEMENT]
|
Dim oSql = "UPDATE [dbo].[TBSIG_DOCUMENT_RECEIVER_ELEMENT]
|
||||||
SET [POSITION_X] = @POSITION_X
|
SET [POSITION_X] = @POSITION_X
|
||||||
@@ -185,7 +185,7 @@ Public Class ElementModel
|
|||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Private Function GetElementId(pElement As Signature) As Integer
|
Private Function GetElementId(pElement As DocReceiverElement) As Integer
|
||||||
Try
|
Try
|
||||||
Return Database.GetScalarValue($"SELECT MAX(GUID) FROM TBSIG_DOCUMENT_RECEIVER_ELEMENT
|
Return Database.GetScalarValue($"SELECT MAX(GUID) FROM TBSIG_DOCUMENT_RECEIVER_ELEMENT
|
||||||
WHERE DOCUMENT_ID = {pElement.DocumentId} AND RECEIVER_ID = {pElement.ReceiverId}")
|
WHERE DOCUMENT_ID = {pElement.DocumentId} AND RECEIVER_ID = {pElement.ReceiverId}")
|
||||||
@@ -196,7 +196,7 @@ Public Class ElementModel
|
|||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function DeleteElement(pElement As Signature) As Boolean
|
Public Function DeleteElement(pElement As DocReceiverElement) As Boolean
|
||||||
Try
|
Try
|
||||||
Dim oSql = $"DELETE FROM TBSIG_DOCUMENT_RECEIVER_ELEMENT WHERE GUID = {pElement.Id}"
|
Dim oSql = $"DELETE FROM TBSIG_DOCUMENT_RECEIVER_ELEMENT WHERE GUID = {pElement.Id}"
|
||||||
Return Database.ExecuteNonQuery(oSql)
|
Return Database.ExecuteNonQuery(oSql)
|
||||||
|
|||||||
@@ -270,12 +270,26 @@ Public Class ReceiverModel
|
|||||||
Private Function GetSignedDate(pEmailAddress As String, pEnvelopeId As Integer) As Date
|
Private Function GetSignedDate(pEmailAddress As String, pEnvelopeId As Integer) As Date
|
||||||
Try
|
Try
|
||||||
Dim oStatusInt As Integer = EnvelopeStatus.DocumentSigned
|
Dim oStatusInt As Integer = EnvelopeStatus.DocumentSigned
|
||||||
Return Database.GetScalarValue($"SELECT ACTION_DATE FROM [DD_ECM].[dbo].[TBSIG_ENVELOPE_HISTORY] WHERE ENVELOPE_ID = {pEnvelopeId}
|
Dim value = Database.GetScalarValue($"SELECT ACTION_DATE FROM [DD_ECM].[dbo].[TBSIG_ENVELOPE_HISTORY] WHERE ENVELOPE_ID = {pEnvelopeId}
|
||||||
And USER_REFERENCE = '{pEmailAddress}' AND [STATUS] = {oStatusInt}")
|
And USER_REFERENCE = '{pEmailAddress}' AND [STATUS] = {oStatusInt}")
|
||||||
|
|
||||||
|
If value Is Nothing OrElse value Is DBNull.Value Then
|
||||||
|
Return DateTime.MinValue
|
||||||
|
End If
|
||||||
|
|
||||||
|
If TypeOf value Is DateTime Then
|
||||||
|
Return DirectCast(value, DateTime)
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim parsedDate As DateTime
|
||||||
|
If DateTime.TryParse(value.ToString(), parsedDate) Then
|
||||||
|
Return parsedDate
|
||||||
|
End If
|
||||||
|
|
||||||
|
Return DateTime.MinValue
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
Logger.Error(ex)
|
Logger.Error(ex)
|
||||||
Return Nothing
|
Return DateTime.MinValue
|
||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
'------------------------------------------------------------------------------
|
'------------------------------------------------------------------------------
|
||||||
' <auto-generated>
|
' <auto-generated>
|
||||||
' Dieser Code wurde von einem Tool generiert.
|
' This code was generated by a tool.
|
||||||
' Laufzeitversion:4.0.30319.42000
|
' Runtime Version:4.0.30319.42000
|
||||||
'
|
'
|
||||||
' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
|
' Changes to this file may cause incorrect behavior and will be lost if
|
||||||
' der Code erneut generiert wird.
|
' the code is regenerated.
|
||||||
' </auto-generated>
|
' </auto-generated>
|
||||||
'------------------------------------------------------------------------------
|
'------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -15,12 +15,12 @@ Imports System
|
|||||||
|
|
||||||
Namespace My.Resources
|
Namespace My.Resources
|
||||||
|
|
||||||
'Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
|
'This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
'-Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
|
'class via a tool like ResGen or Visual Studio.
|
||||||
'Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
|
'To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
'mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
|
'with the /str option, or rebuild your VS project.
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
|
''' A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
<Global.System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0"), _
|
<Global.System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0"), _
|
||||||
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
|
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
|
||||||
@@ -37,7 +37,7 @@ Namespace My.Resources
|
|||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
|
''' Returns the cached ResourceManager instance used by this class.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
|
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
|
||||||
Public Shared ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager
|
Public Shared ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager
|
||||||
@@ -51,8 +51,8 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
|
''' Overrides the current thread's CurrentUICulture property for all
|
||||||
''' Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
|
''' resource lookups using this strongly typed resource class.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
|
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
|
||||||
Public Shared Property Culture() As Global.System.Globalization.CultureInfo
|
Public Shared Property Culture() As Global.System.Globalization.CultureInfo
|
||||||
@@ -65,7 +65,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Zugriffscode korrekt eingegeben ähnelt.
|
''' Looks up a localized string similar to Zugriffscode korrekt eingegeben.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property AccessCodeCorrect() As String
|
Public Shared ReadOnly Property AccessCodeCorrect() As String
|
||||||
Get
|
Get
|
||||||
@@ -74,7 +74,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Zugriffscode falsch eingegeben ähnelt.
|
''' Looks up a localized string similar to Zugriffscode falsch eingegeben.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property AccessCodeIncorrect() As String
|
Public Shared ReadOnly Property AccessCodeIncorrect() As String
|
||||||
Get
|
Get
|
||||||
@@ -83,7 +83,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Zugriffscode angefordert ähnelt.
|
''' Looks up a localized string similar to Zugriffscode angefordert.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property AccessCodeRequested() As String
|
Public Shared ReadOnly Property AccessCodeRequested() As String
|
||||||
Get
|
Get
|
||||||
@@ -92,7 +92,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Fortgeschrittene Elektronische Signatur ähnelt.
|
''' Looks up a localized string similar to Fortgeschrittene Elektronische Signatur.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property AdvancedElectronicSignature() As String
|
Public Shared ReadOnly Property AdvancedElectronicSignature() As String
|
||||||
Get
|
Get
|
||||||
@@ -101,7 +101,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Abgeschlossen ähnelt.
|
''' Looks up a localized string similar to Abgeschlossen.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property Completed() As String
|
Public Shared ReadOnly Property Completed() As String
|
||||||
Get
|
Get
|
||||||
@@ -110,7 +110,16 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Vollständig Signiert ähnelt.
|
''' Looks up a localized string similar to Vollständig bestätigt.
|
||||||
|
'''</summary>
|
||||||
|
Public Shared ReadOnly Property CompletelyConfirmed() As String
|
||||||
|
Get
|
||||||
|
Return ResourceManager.GetString("CompletelyConfirmed", resourceCulture)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Looks up a localized string similar to Vollständig signiert.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property CompletelySigned() As String
|
Public Shared ReadOnly Property CompletelySigned() As String
|
||||||
Get
|
Get
|
||||||
@@ -119,7 +128,25 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Vertrag ähnelt.
|
''' Looks up a localized string similar to Lesebestätigung.
|
||||||
|
'''</summary>
|
||||||
|
Public Shared ReadOnly Property Confirmation() As String
|
||||||
|
Get
|
||||||
|
Return ResourceManager.GetString("Confirmation", resourceCulture)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Looks up a localized string similar to Gelesen und Bestätigt.
|
||||||
|
'''</summary>
|
||||||
|
Public Shared ReadOnly Property Confirmed() As String
|
||||||
|
Get
|
||||||
|
Return ResourceManager.GetString("Confirmed", resourceCulture)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Looks up a localized string similar to Vertrag.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property Contract() As String
|
Public Shared ReadOnly Property Contract() As String
|
||||||
Get
|
Get
|
||||||
@@ -128,7 +155,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Erstellt ähnelt.
|
''' Looks up a localized string similar to Erstellt.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property Created() As String
|
Public Shared ReadOnly Property Created() As String
|
||||||
Get
|
Get
|
||||||
@@ -137,7 +164,16 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Dokument Rotation geändert ähnelt.
|
''' Looks up a localized string similar to Dokument gelesen und bestätigt.
|
||||||
|
'''</summary>
|
||||||
|
Public Shared ReadOnly Property DocumentConfirmed() As String
|
||||||
|
Get
|
||||||
|
Return ResourceManager.GetString("DocumentConfirmed", resourceCulture)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Looks up a localized string similar to Dokument Rotation geändert.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property DocumentMod_Rotation() As String
|
Public Shared ReadOnly Property DocumentMod_Rotation() As String
|
||||||
Get
|
Get
|
||||||
@@ -146,7 +182,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Dokument geöffnet ähnelt.
|
''' Looks up a localized string similar to Dokument geöffnet.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property DocumentOpened() As String
|
Public Shared ReadOnly Property DocumentOpened() As String
|
||||||
Get
|
Get
|
||||||
@@ -155,7 +191,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Unterzeichnung abgelehnt ähnelt.
|
''' Looks up a localized string similar to Unterzeichnung abgelehnt.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property DocumentRejected() As String
|
Public Shared ReadOnly Property DocumentRejected() As String
|
||||||
Get
|
Get
|
||||||
@@ -164,7 +200,16 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Dokument unterzeichnet ähnelt.
|
''' Looks up a localized string similar to Lesebestätigung abgelehnt.
|
||||||
|
'''</summary>
|
||||||
|
Public Shared ReadOnly Property DocumentRejectedRaC() As String
|
||||||
|
Get
|
||||||
|
Return ResourceManager.GetString("DocumentRejectedRaC", resourceCulture)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Looks up a localized string similar to Dokument unterzeichnet.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property DocumentSigned() As String
|
Public Shared ReadOnly Property DocumentSigned() As String
|
||||||
Get
|
Get
|
||||||
@@ -173,7 +218,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Entwurf ähnelt.
|
''' Looks up a localized string similar to Entwurf.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property Draft() As String
|
Public Shared ReadOnly Property Draft() As String
|
||||||
Get
|
Get
|
||||||
@@ -182,7 +227,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Archiviert ähnelt.
|
''' Looks up a localized string similar to Archiviert.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property EnvelopeArchived() As String
|
Public Shared ReadOnly Property EnvelopeArchived() As String
|
||||||
Get
|
Get
|
||||||
@@ -191,7 +236,16 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Vollständig signiert ähnelt.
|
''' Looks up a localized string similar to Vollständig bestätigt.
|
||||||
|
'''</summary>
|
||||||
|
Public Shared ReadOnly Property EnvelopeCompletelyConfirmed() As String
|
||||||
|
Get
|
||||||
|
Return ResourceManager.GetString("EnvelopeCompletelyConfirmed", resourceCulture)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Looks up a localized string similar to Vollständig signiert.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property EnvelopeCompletelySigned() As String
|
Public Shared ReadOnly Property EnvelopeCompletelySigned() As String
|
||||||
Get
|
Get
|
||||||
@@ -200,7 +254,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Umschlag Erstellt ähnelt.
|
''' Looks up a localized string similar to Umschlag Erstellt.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property EnvelopeCreated() As String
|
Public Shared ReadOnly Property EnvelopeCreated() As String
|
||||||
Get
|
Get
|
||||||
@@ -209,7 +263,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Umschlag Gelöscht ähnelt.
|
''' Looks up a localized string similar to Umschlag Gelöscht.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property EnvelopeDeleted() As String
|
Public Shared ReadOnly Property EnvelopeDeleted() As String
|
||||||
Get
|
Get
|
||||||
@@ -218,7 +272,16 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Teil-Signiert ähnelt.
|
''' Looks up a localized string similar to Teil-Bestätigt.
|
||||||
|
'''</summary>
|
||||||
|
Public Shared ReadOnly Property EnvelopePartlyConfirmed() As String
|
||||||
|
Get
|
||||||
|
Return ResourceManager.GetString("EnvelopePartlyConfirmed", resourceCulture)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Looks up a localized string similar to Teil-Signiert.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property EnvelopePartlySigned() As String
|
Public Shared ReadOnly Property EnvelopePartlySigned() As String
|
||||||
Get
|
Get
|
||||||
@@ -227,7 +290,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Umschlag in Queue ähnelt.
|
''' Looks up a localized string similar to Umschlag in Queue.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property EnvelopeQueued() As String
|
Public Shared ReadOnly Property EnvelopeQueued() As String
|
||||||
Get
|
Get
|
||||||
@@ -236,7 +299,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Umschlag abgelehnt ähnelt.
|
''' Looks up a localized string similar to Umschlag abgelehnt.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property EnvelopeRejected() As String
|
Public Shared ReadOnly Property EnvelopeRejected() As String
|
||||||
Get
|
Get
|
||||||
@@ -245,7 +308,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Signierungszertifikat erstellt ähnelt.
|
''' Looks up a localized string similar to Signierungszertifikat erstellt.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property EnvelopeReportCreated() As String
|
Public Shared ReadOnly Property EnvelopeReportCreated() As String
|
||||||
Get
|
Get
|
||||||
@@ -254,7 +317,16 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Gespeichert ähnelt.
|
''' Looks up a localized string similar to Lesebestätigungszertifikat erstellt.
|
||||||
|
'''</summary>
|
||||||
|
Public Shared ReadOnly Property EnvelopeReportCreatedRaC() As String
|
||||||
|
Get
|
||||||
|
Return ResourceManager.GetString("EnvelopeReportCreatedRaC", resourceCulture)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Looks up a localized string similar to Gespeichert.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property EnvelopeSaved() As String
|
Public Shared ReadOnly Property EnvelopeSaved() As String
|
||||||
Get
|
Get
|
||||||
@@ -263,7 +335,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Gesendet ähnelt.
|
''' Looks up a localized string similar to Gesendet.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property EnvelopeSent() As String
|
Public Shared ReadOnly Property EnvelopeSent() As String
|
||||||
Get
|
Get
|
||||||
@@ -272,7 +344,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Umschlag zurückgezogen ähnelt.
|
''' Looks up a localized string similar to Umschlag zurückgezogen.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property EnvelopeWithdrawn() As String
|
Public Shared ReadOnly Property EnvelopeWithdrawn() As String
|
||||||
Get
|
Get
|
||||||
@@ -281,7 +353,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Zugriffscode versendet ähnelt.
|
''' Looks up a localized string similar to Zugriffscode versendet.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property MessageAccessCodeSent() As String
|
Public Shared ReadOnly Property MessageAccessCodeSent() As String
|
||||||
Get
|
Get
|
||||||
@@ -290,7 +362,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Abschlussemail versendet ähnelt.
|
''' Looks up a localized string similar to Abschlussemail versendet.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property MessageCompletionSent() As String
|
Public Shared ReadOnly Property MessageCompletionSent() As String
|
||||||
Get
|
Get
|
||||||
@@ -299,7 +371,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Signaturbestätigung versendet ähnelt.
|
''' Looks up a localized string similar to Signaturbestätigung versendet.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property MessageConfirmationSent() As String
|
Public Shared ReadOnly Property MessageConfirmationSent() As String
|
||||||
Get
|
Get
|
||||||
@@ -308,7 +380,16 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Löschinformation versendet ähnelt.
|
''' Looks up a localized string similar to Lesebestätigung versendet.
|
||||||
|
'''</summary>
|
||||||
|
Public Shared ReadOnly Property MessageConfirmationSentRaC() As String
|
||||||
|
Get
|
||||||
|
Return ResourceManager.GetString("MessageConfirmationSentRaC", resourceCulture)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Looks up a localized string similar to Löschinformation versendet.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property MessageDeletionSent() As String
|
Public Shared ReadOnly Property MessageDeletionSent() As String
|
||||||
Get
|
Get
|
||||||
@@ -317,7 +398,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Dokumentenlink versendet ähnelt.
|
''' Looks up a localized string similar to Dokumentenlink versendet.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property MessageInvitationSent() As String
|
Public Shared ReadOnly Property MessageInvitationSent() As String
|
||||||
Get
|
Get
|
||||||
@@ -326,7 +407,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Nein ähnelt.
|
''' Looks up a localized string similar to Nein.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property No() As String
|
Public Shared ReadOnly Property No() As String
|
||||||
Get
|
Get
|
||||||
@@ -335,7 +416,16 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Teil-Signiert ähnelt.
|
''' Looks up a localized string similar to Teil-Bestätigt.
|
||||||
|
'''</summary>
|
||||||
|
Public Shared ReadOnly Property PartlyConfirmed() As String
|
||||||
|
Get
|
||||||
|
Return ResourceManager.GetString("PartlyConfirmed", resourceCulture)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Looks up a localized string similar to Teil-Signiert.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property PartlySigned() As String
|
Public Shared ReadOnly Property PartlySigned() As String
|
||||||
Get
|
Get
|
||||||
@@ -344,7 +434,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Qualifizierte Signatur ähnelt.
|
''' Looks up a localized string similar to Qualifizierte Signatur.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property QualifiedSignature() As String
|
Public Shared ReadOnly Property QualifiedSignature() As String
|
||||||
Get
|
Get
|
||||||
@@ -353,7 +443,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Arbeitsanweisung ähnelt.
|
''' Looks up a localized string similar to Lesebestätigung.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property ReadAndSign() As String
|
Public Shared ReadOnly Property ReadAndSign() As String
|
||||||
Get
|
Get
|
||||||
@@ -362,7 +452,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren! ähnelt.
|
''' Looks up a localized string similar to Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren!.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property ResetTOTPUser() As String
|
Public Shared ReadOnly Property ResetTOTPUser() As String
|
||||||
Get
|
Get
|
||||||
@@ -371,7 +461,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Gespeichert ähnelt.
|
''' Looks up a localized string similar to Gespeichert.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property Saved() As String
|
Public Shared ReadOnly Property Saved() As String
|
||||||
Get
|
Get
|
||||||
@@ -380,7 +470,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Gesendet ähnelt.
|
''' Looks up a localized string similar to Gesendet.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property Sent() As String
|
Public Shared ReadOnly Property Sent() As String
|
||||||
Get
|
Get
|
||||||
@@ -389,7 +479,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Signatur ähnelt.
|
''' Looks up a localized string similar to Signatur.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property Signature() As String
|
Public Shared ReadOnly Property Signature() As String
|
||||||
Get
|
Get
|
||||||
@@ -398,7 +488,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Signatur bestätigt ähnelt.
|
''' Looks up a localized string similar to Abschluss bestätigt.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property SignatureConfirmed() As String
|
Public Shared ReadOnly Property SignatureConfirmed() As String
|
||||||
Get
|
Get
|
||||||
@@ -407,7 +497,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Signiert ähnelt.
|
''' Looks up a localized string similar to Signiert.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property Signed() As String
|
Public Shared ReadOnly Property Signed() As String
|
||||||
Get
|
Get
|
||||||
@@ -416,7 +506,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Erfolgreich! Dialog wird geschlossen. ähnelt.
|
''' Looks up a localized string similar to Erfolgreich! Dialog wird geschlossen..
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property Success_FormClose() As String
|
Public Shared ReadOnly Property Success_FormClose() As String
|
||||||
Get
|
Get
|
||||||
@@ -425,7 +515,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Unsigniert ähnelt.
|
''' Looks up a localized string similar to Unsigniert.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property Unsigned() As String
|
Public Shared ReadOnly Property Unsigned() As String
|
||||||
Get
|
Get
|
||||||
@@ -434,7 +524,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Ja ähnelt.
|
''' Looks up a localized string similar to Ja.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property Yes() As String
|
Public Shared ReadOnly Property Yes() As String
|
||||||
Get
|
Get
|
||||||
@@ -443,7 +533,7 @@ Namespace My.Resources
|
|||||||
End Property
|
End Property
|
||||||
|
|
||||||
'''<summary>
|
'''<summary>
|
||||||
''' Sucht eine lokalisierte Zeichenfolge, die Ja, mit Anhang ähnelt.
|
''' Looks up a localized string similar to Ja, mit Anhang.
|
||||||
'''</summary>
|
'''</summary>
|
||||||
Public Shared ReadOnly Property YesWithAttachment() As String
|
Public Shared ReadOnly Property YesWithAttachment() As String
|
||||||
Get
|
Get
|
||||||
|
|||||||
@@ -132,12 +132,27 @@
|
|||||||
<data name="Completed" xml:space="preserve">
|
<data name="Completed" xml:space="preserve">
|
||||||
<value>Completed</value>
|
<value>Completed</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CompletelyConfirmed" xml:space="preserve">
|
||||||
|
<value>Completely confirmed</value>
|
||||||
|
</data>
|
||||||
|
<data name="CompletelySigned" xml:space="preserve">
|
||||||
|
<value>Completely signed</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirmation" xml:space="preserve">
|
||||||
|
<value>Read Confirmation</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirmed" xml:space="preserve">
|
||||||
|
<value>Read and signed</value>
|
||||||
|
</data>
|
||||||
<data name="Contract" xml:space="preserve">
|
<data name="Contract" xml:space="preserve">
|
||||||
<value>Contract</value>
|
<value>Contract</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Created" xml:space="preserve">
|
<data name="Created" xml:space="preserve">
|
||||||
<value>Created</value>
|
<value>Created</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DocumentConfirmed" xml:space="preserve">
|
||||||
|
<value>Document read and confirmed</value>
|
||||||
|
</data>
|
||||||
<data name="DocumentMod_Rotation" xml:space="preserve">
|
<data name="DocumentMod_Rotation" xml:space="preserve">
|
||||||
<value>Document rotation adapted</value>
|
<value>Document rotation adapted</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -147,6 +162,9 @@
|
|||||||
<data name="DocumentRejected" xml:space="preserve">
|
<data name="DocumentRejected" xml:space="preserve">
|
||||||
<value>Signing rejected</value>
|
<value>Signing rejected</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DocumentRejectedRaC" xml:space="preserve">
|
||||||
|
<value>Read confirmation rejected</value>
|
||||||
|
</data>
|
||||||
<data name="DocumentSigned" xml:space="preserve">
|
<data name="DocumentSigned" xml:space="preserve">
|
||||||
<value>Document signed</value>
|
<value>Document signed</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -156,6 +174,9 @@
|
|||||||
<data name="EnvelopeArchived" xml:space="preserve">
|
<data name="EnvelopeArchived" xml:space="preserve">
|
||||||
<value>Archived</value>
|
<value>Archived</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EnvelopeCompletelyConfirmed" xml:space="preserve">
|
||||||
|
<value>Completely confirmed</value>
|
||||||
|
</data>
|
||||||
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
||||||
<value>Completely signed</value>
|
<value>Completely signed</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -165,8 +186,11 @@
|
|||||||
<data name="EnvelopeDeleted" xml:space="preserve">
|
<data name="EnvelopeDeleted" xml:space="preserve">
|
||||||
<value>Envelope Deleted</value>
|
<value>Envelope Deleted</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EnvelopePartlyConfirmed" xml:space="preserve">
|
||||||
|
<value>Partially confirmed</value>
|
||||||
|
</data>
|
||||||
<data name="EnvelopePartlySigned" xml:space="preserve">
|
<data name="EnvelopePartlySigned" xml:space="preserve">
|
||||||
<value>Partly signed</value>
|
<value>Partially signed</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EnvelopeQueued" xml:space="preserve">
|
<data name="EnvelopeQueued" xml:space="preserve">
|
||||||
<value>Envelope Queued</value>
|
<value>Envelope Queued</value>
|
||||||
@@ -177,6 +201,9 @@
|
|||||||
<data name="EnvelopeReportCreated" xml:space="preserve">
|
<data name="EnvelopeReportCreated" xml:space="preserve">
|
||||||
<value>Signature certificate created</value>
|
<value>Signature certificate created</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EnvelopeReportCreatedRaC" xml:space="preserve">
|
||||||
|
<value>Read Confirmation Certificate Created</value>
|
||||||
|
</data>
|
||||||
<data name="EnvelopeSaved" xml:space="preserve">
|
<data name="EnvelopeSaved" xml:space="preserve">
|
||||||
<value>Saved</value>
|
<value>Saved</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -195,6 +222,9 @@
|
|||||||
<data name="MessageConfirmationSent" xml:space="preserve">
|
<data name="MessageConfirmationSent" xml:space="preserve">
|
||||||
<value>Confirmation Sent</value>
|
<value>Confirmation Sent</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="MessageConfirmationSentRaC" xml:space="preserve">
|
||||||
|
<value>Read Confirmation Sent</value>
|
||||||
|
</data>
|
||||||
<data name="MessageDeletionSent" xml:space="preserve">
|
<data name="MessageDeletionSent" xml:space="preserve">
|
||||||
<value>Deletion Notice Sent</value>
|
<value>Deletion Notice Sent</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -204,6 +234,12 @@
|
|||||||
<data name="No" xml:space="preserve">
|
<data name="No" xml:space="preserve">
|
||||||
<value>No</value>
|
<value>No</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PartlyConfirmed" xml:space="preserve">
|
||||||
|
<value>Partially confirmed</value>
|
||||||
|
</data>
|
||||||
|
<data name="PartlySigned" xml:space="preserve">
|
||||||
|
<value>Partially signed</value>
|
||||||
|
</data>
|
||||||
<data name="QualifiedSignature" xml:space="preserve">
|
<data name="QualifiedSignature" xml:space="preserve">
|
||||||
<value>Qualified Signature</value>
|
<value>Qualified Signature</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -223,7 +259,7 @@
|
|||||||
<value>Signature</value>
|
<value>Signature</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SignatureConfirmed" xml:space="preserve">
|
<data name="SignatureConfirmed" xml:space="preserve">
|
||||||
<value>Signature confirmed</value>
|
<value>Finalization confirmed</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Signed" xml:space="preserve">
|
<data name="Signed" xml:space="preserve">
|
||||||
<value>Signed</value>
|
<value>Signed</value>
|
||||||
|
|||||||
@@ -132,8 +132,17 @@
|
|||||||
<data name="Completed" xml:space="preserve">
|
<data name="Completed" xml:space="preserve">
|
||||||
<value>Abgeschlossen</value>
|
<value>Abgeschlossen</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CompletelyConfirmed" xml:space="preserve">
|
||||||
|
<value>Vollständig bestätigt</value>
|
||||||
|
</data>
|
||||||
<data name="CompletelySigned" xml:space="preserve">
|
<data name="CompletelySigned" xml:space="preserve">
|
||||||
<value>Vollständig Signiert</value>
|
<value>Vollständig signiert</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirmation" xml:space="preserve">
|
||||||
|
<value>Lesebestätigung</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirmed" xml:space="preserve">
|
||||||
|
<value>Gelesen und Bestätigt</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Contract" xml:space="preserve">
|
<data name="Contract" xml:space="preserve">
|
||||||
<value>Vertrag</value>
|
<value>Vertrag</value>
|
||||||
@@ -141,6 +150,9 @@
|
|||||||
<data name="Created" xml:space="preserve">
|
<data name="Created" xml:space="preserve">
|
||||||
<value>Erstellt</value>
|
<value>Erstellt</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DocumentConfirmed" xml:space="preserve">
|
||||||
|
<value>Dokument gelesen und bestätigt</value>
|
||||||
|
</data>
|
||||||
<data name="DocumentMod_Rotation" xml:space="preserve">
|
<data name="DocumentMod_Rotation" xml:space="preserve">
|
||||||
<value>Dokument Rotation geändert</value>
|
<value>Dokument Rotation geändert</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -150,6 +162,9 @@
|
|||||||
<data name="DocumentRejected" xml:space="preserve">
|
<data name="DocumentRejected" xml:space="preserve">
|
||||||
<value>Unterzeichnung abgelehnt</value>
|
<value>Unterzeichnung abgelehnt</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DocumentRejectedRaC" xml:space="preserve">
|
||||||
|
<value>Lesebestätigung abgelehnt</value>
|
||||||
|
</data>
|
||||||
<data name="DocumentSigned" xml:space="preserve">
|
<data name="DocumentSigned" xml:space="preserve">
|
||||||
<value>Dokument unterzeichnet</value>
|
<value>Dokument unterzeichnet</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -159,6 +174,9 @@
|
|||||||
<data name="EnvelopeArchived" xml:space="preserve">
|
<data name="EnvelopeArchived" xml:space="preserve">
|
||||||
<value>Archiviert</value>
|
<value>Archiviert</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EnvelopeCompletelyConfirmed" xml:space="preserve">
|
||||||
|
<value>Vollständig bestätigt</value>
|
||||||
|
</data>
|
||||||
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
||||||
<value>Vollständig signiert</value>
|
<value>Vollständig signiert</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -168,6 +186,9 @@
|
|||||||
<data name="EnvelopeDeleted" xml:space="preserve">
|
<data name="EnvelopeDeleted" xml:space="preserve">
|
||||||
<value>Umschlag Gelöscht</value>
|
<value>Umschlag Gelöscht</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EnvelopePartlyConfirmed" xml:space="preserve">
|
||||||
|
<value>Teil-Bestätigt</value>
|
||||||
|
</data>
|
||||||
<data name="EnvelopePartlySigned" xml:space="preserve">
|
<data name="EnvelopePartlySigned" xml:space="preserve">
|
||||||
<value>Teil-Signiert</value>
|
<value>Teil-Signiert</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -180,6 +201,9 @@
|
|||||||
<data name="EnvelopeReportCreated" xml:space="preserve">
|
<data name="EnvelopeReportCreated" xml:space="preserve">
|
||||||
<value>Signierungszertifikat erstellt</value>
|
<value>Signierungszertifikat erstellt</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EnvelopeReportCreatedRaC" xml:space="preserve">
|
||||||
|
<value>Lesebestätigungszertifikat erstellt</value>
|
||||||
|
</data>
|
||||||
<data name="EnvelopeSaved" xml:space="preserve">
|
<data name="EnvelopeSaved" xml:space="preserve">
|
||||||
<value>Gespeichert</value>
|
<value>Gespeichert</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -198,6 +222,9 @@
|
|||||||
<data name="MessageConfirmationSent" xml:space="preserve">
|
<data name="MessageConfirmationSent" xml:space="preserve">
|
||||||
<value>Signaturbestätigung versendet</value>
|
<value>Signaturbestätigung versendet</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="MessageConfirmationSentRaC" xml:space="preserve">
|
||||||
|
<value>Lesebestätigung versendet</value>
|
||||||
|
</data>
|
||||||
<data name="MessageDeletionSent" xml:space="preserve">
|
<data name="MessageDeletionSent" xml:space="preserve">
|
||||||
<value>Löschinformation versendet</value>
|
<value>Löschinformation versendet</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -207,6 +234,9 @@
|
|||||||
<data name="No" xml:space="preserve">
|
<data name="No" xml:space="preserve">
|
||||||
<value>Nein</value>
|
<value>Nein</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PartlyConfirmed" xml:space="preserve">
|
||||||
|
<value>Teil-Bestätigt</value>
|
||||||
|
</data>
|
||||||
<data name="PartlySigned" xml:space="preserve">
|
<data name="PartlySigned" xml:space="preserve">
|
||||||
<value>Teil-Signiert</value>
|
<value>Teil-Signiert</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -214,7 +244,7 @@
|
|||||||
<value>Qualifizierte Signatur</value>
|
<value>Qualifizierte Signatur</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ReadAndSign" xml:space="preserve">
|
<data name="ReadAndSign" xml:space="preserve">
|
||||||
<value>Arbeitsanweisung</value>
|
<value>Lesebestätigung</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ResetTOTPUser" xml:space="preserve">
|
<data name="ResetTOTPUser" xml:space="preserve">
|
||||||
<value>Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren!</value>
|
<value>Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren!</value>
|
||||||
@@ -229,7 +259,7 @@
|
|||||||
<value>Signatur</value>
|
<value>Signatur</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SignatureConfirmed" xml:space="preserve">
|
<data name="SignatureConfirmed" xml:space="preserve">
|
||||||
<value>Signatur bestätigt</value>
|
<value>Abschluss bestätigt</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Signed" xml:space="preserve">
|
<data name="Signed" xml:space="preserve">
|
||||||
<value>Signiert</value>
|
<value>Signiert</value>
|
||||||
|
|||||||
145
EnvelopeGenerator.DependencyInjection/DependencyInjection.cs
Normal file
145
EnvelopeGenerator.DependencyInjection/DependencyInjection.cs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using EnvelopeGenerator.Application;
|
||||||
|
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||||
|
using EnvelopeGenerator.Application.Services;
|
||||||
|
using EnvelopeGenerator.Infrastructure;
|
||||||
|
using DigitalData.EmailProfilerDispatcher;
|
||||||
|
using DigitalData.UserManager.DependencyInjection;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.DependencyInjection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controls which optional services are registered by <see cref="DependencyInjection.AddEnvelopeGenerator"/>.
|
||||||
|
/// All flags default to <c>true</c>. Set a flag to <c>false</c> if the consuming project
|
||||||
|
/// already registers that service itself or simply does not need it.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class EnvelopeGeneratorOptions
|
||||||
|
{
|
||||||
|
/// <summary>Calls <c>AddHttpContextAccessor()</c>. Default: <c>true</c>.</summary>
|
||||||
|
public bool AddHttpContextAccessor { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calls <c>AddDistributedSqlServerCache()</c> with the supplied <see cref="SqlCacheOptions"/>.
|
||||||
|
/// Requires <see cref="SqlCacheOptions"/> to be configured. Default: <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
|
public bool AddDistributedSqlServerCache { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Options for the distributed SQL Server cache.
|
||||||
|
/// Required when <see cref="AddDistributedSqlServerCache"/> is <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
|
public SqlCacheOptions? SqlCacheOptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Calls <c>AddDispatcher<TDbContext>()</c>. Default: <c>true</c>.</summary>
|
||||||
|
public bool AddDispatcher { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>Calls <c>AddMemoryCache()</c>. Default: <c>true</c>.</summary>
|
||||||
|
public bool AddMemoryCache { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>Calls <c>AddUserManager<TDbContext>()</c>. Default: <c>true</c>.</summary>
|
||||||
|
public bool AddUserManager { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Options for <c>AddDistributedSqlServerCache</c>.</summary>
|
||||||
|
public sealed class SqlCacheOptions
|
||||||
|
{
|
||||||
|
/// <summary>SQL Server connection string.</summary>
|
||||||
|
public string ConnectionString { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>Schema name. Default: <c>dbo</c>.</summary>
|
||||||
|
public string SchemaName { get; set; } = "dbo";
|
||||||
|
|
||||||
|
/// <summary>Table name. Default: <c>TBDD_CACHE</c>.</summary>
|
||||||
|
public string TableName { get; set; } = "TBDD_CACHE";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for registering EnvelopeGenerator services into an <see cref="IServiceCollection"/>.
|
||||||
|
/// Use <see cref="AddEnvelopeGenerator{TDbContext}"/> as the single entry-point for projects that need both the
|
||||||
|
/// application layer (MediatR, AutoMapper, CRUD services, configuration sections) and the infrastructure
|
||||||
|
/// layer (repositories, DbContext, SQL executors).
|
||||||
|
/// For projects that do not need a database (e.g. lightweight API gateways or unit-test hosts), use
|
||||||
|
/// <see cref="AddEnvelopeGeneratorCore"/> to register only the application layer.
|
||||||
|
/// </summary>
|
||||||
|
public static class DependencyInjection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Registers the full EnvelopeGenerator stack using <see cref="EGDbContext"/> as the DbContext type.
|
||||||
|
/// </summary>
|
||||||
|
public static IServiceCollection AddEnvelopeGenerator(
|
||||||
|
this IServiceCollection services,
|
||||||
|
IConfiguration configuration,
|
||||||
|
Action<EnvelopeGenerator.Infrastructure.DependencyInjection.Config>? infrastructureOptions = null,
|
||||||
|
Action<EnvelopeGeneratorOptions>? options = null)
|
||||||
|
{
|
||||||
|
var opt = new EnvelopeGeneratorOptions();
|
||||||
|
options?.Invoke(opt);
|
||||||
|
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
// Application layer: CRUD services, MediatR, AutoMapper, configuration sections.
|
||||||
|
services.AddEnvelopeGeneratorServices(configuration);
|
||||||
|
|
||||||
|
// Infrastructure layer: repositories, DbContext, Dapper type maps, SQL executors.
|
||||||
|
services.AddEnvelopeGeneratorInfrastructureServices(cfg =>
|
||||||
|
{
|
||||||
|
infrastructureOptions?.Invoke(cfg);
|
||||||
|
});
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
|
if (opt.AddHttpContextAccessor)
|
||||||
|
services.AddHttpContextAccessor();
|
||||||
|
|
||||||
|
if (opt.AddDistributedSqlServerCache && opt.SqlCacheOptions is { } cacheOpts)
|
||||||
|
services.AddDistributedSqlServerCache(o =>
|
||||||
|
{
|
||||||
|
o.ConnectionString = cacheOpts.ConnectionString;
|
||||||
|
o.SchemaName = cacheOpts.SchemaName;
|
||||||
|
o.TableName = cacheOpts.TableName;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (opt.AddDispatcher)
|
||||||
|
services.AddDispatcher<EGDbContext>();
|
||||||
|
|
||||||
|
if (opt.AddMemoryCache)
|
||||||
|
services.AddMemoryCache();
|
||||||
|
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
if (opt.AddUserManager)
|
||||||
|
services.AddUserManager<EGDbContext>();
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers only the <em>application</em> layer services (MediatR handlers, AutoMapper profiles,
|
||||||
|
/// CRUD services, configuration sections) without any infrastructure / database dependencies.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">Service collection to register services into.</param>
|
||||||
|
/// <param name="configuration">Application configuration used to bind application-level option sections.</param>
|
||||||
|
/// <returns>The updated <see cref="IServiceCollection"/>.</returns>
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
public static IServiceCollection AddEnvelopeGeneratorCore(
|
||||||
|
this IServiceCollection services,
|
||||||
|
IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddEnvelopeGeneratorServices(configuration);
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers <see cref="EnvelopeMailService"/> as the <see cref="IEnvelopeMailService"/> scoped
|
||||||
|
/// implementation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">Service collection to register services into.</param>
|
||||||
|
/// <returns>The updated <see cref="IServiceCollection"/>.</returns>
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
public static IServiceCollection AddEnvelopeMailService(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddScoped<IEnvelopeMailService, EnvelopeMailService>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<!-- NuGet package metadata -->
|
||||||
|
<PackageId>EnvelopeGenerator</PackageId>
|
||||||
|
<Authors>Digital Data GmbH</Authors>
|
||||||
|
<Company>Digital Data GmbH</Company>
|
||||||
|
<Product>EnvelopeGenerator</Product>
|
||||||
|
<Description>
|
||||||
|
Envelope Generator ist eine Bibliothek zur Verwaltung und Verarbeitung digitaler Umschläge (Envelopes).
|
||||||
|
Dieses Paket enthält die Dependency-Injection-Erweiterungsmethoden und bündelt die Application-
|
||||||
|
sowie Infrastructure-Schicht in einer einzigen NuGet-Referenz.
|
||||||
|
</Description>
|
||||||
|
<Copyright>Copyright 2024 Digital Data GmbH</Copyright>
|
||||||
|
<RepositoryUrl>http://git.dd:3000/AppStd/EnvelopeGenerator.git</RepositoryUrl>
|
||||||
|
<PackageTags>digital data envelope generator di dependency injection</PackageTags>
|
||||||
|
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||||
|
<Version>1.2.0.3</Version>
|
||||||
|
<AssemblyVersion>1.2.0.3</AssemblyVersion>
|
||||||
|
<FileVersion>1.2.0.3</FileVersion>
|
||||||
|
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- ASP.NET Core shared framework (AddHttpContextAccessor, AddMemoryCache, etc.) -->
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
All dependencies are declared here.
|
||||||
|
Because Application and Infrastructure DLLs are bundled via PrivateAssets=all,
|
||||||
|
their PackageReferences have been moved to this project so that the correct
|
||||||
|
dependencies are written to the nuspec and a consuming project works with
|
||||||
|
a single package install.
|
||||||
|
-->
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- DigitalData BaGet packages -->
|
||||||
|
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.6.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" />
|
||||||
|
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.6.1" />
|
||||||
|
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
|
||||||
|
<PackageReference Include="UserManager" Version="1.1.3" />
|
||||||
|
|
||||||
|
<!-- ORM / Database -->
|
||||||
|
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||||
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
|
||||||
|
|
||||||
|
<!-- Application services -->
|
||||||
|
<PackageReference Include="MediatR" Version="12.5.0" />
|
||||||
|
<PackageReference Include="QRCoder" Version="1.6.0" />
|
||||||
|
<PackageReference Include="QRCoder-ImageSharp" Version="0.10.0" />
|
||||||
|
<PackageReference Include="QuestPDF" Version="2025.7.1" />
|
||||||
|
<PackageReference Include="Otp.NET" Version="1.4.0" />
|
||||||
|
|
||||||
|
<!-- Security / Identity -->
|
||||||
|
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
|
||||||
|
|
||||||
|
<!-- Utilities -->
|
||||||
|
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
||||||
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
||||||
|
<PackageReference Include="System.Formats.Asn1" Version="10.0.3" />
|
||||||
|
<PackageReference Include="System.Security.AccessControl" Version="6.0.1" />
|
||||||
|
|
||||||
|
<!-- DI abstractions -->
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.6" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- TFM-specific packages -->
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
||||||
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
|
<PackageReference Include="CommandDotNet" Version="7.0.5" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.20" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="7.0.20" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.20" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.20" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.20" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
||||||
|
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||||
|
<PackageReference Include="CommandDotNet" Version="8.1.1" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.17" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="8.0.17" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.17" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.17" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.17" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
||||||
|
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||||
|
<PackageReference Include="CommandDotNet" Version="8.1.1" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="9.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.6" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Bundle dependency DLLs into the package lib folder -->
|
||||||
|
<Target Name="IncludeDependencyDlls" BeforeTargets="_GetPackageFiles">
|
||||||
|
<ItemGroup>
|
||||||
|
<_DepDlls Include="
 ..\EnvelopeGenerator.Application\bin\$(Configuration)\%(ProjectReference.TargetFramework)\EnvelopeGenerator.Application.dll;
 ..\EnvelopeGenerator.Domain\bin\$(Configuration)\%(ProjectReference.TargetFramework)\EnvelopeGenerator.Domain.dll;
 ..\EnvelopeGenerator.Infrastructure\bin\$(Configuration)\%(ProjectReference.TargetFramework)\EnvelopeGenerator.Infrastructure.dll" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Rebuild all dependency projects for every TFM before packing so that
|
||||||
|
the DLLs bundled into the package are always up-to-date.
|
||||||
|
Run with: dotnet pack -c Release
|
||||||
|
-->
|
||||||
|
<Target Name="RebuildDependenciesBeforePack" BeforeTargets="GenerateNuspec">
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net7.0" />
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net8.0" />
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net9.0" />
|
||||||
|
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net7.0" />
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net8.0" />
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net9.0" />
|
||||||
|
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net7.0" />
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net8.0" />
|
||||||
|
<MSBuild Projects="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj"
|
||||||
|
Targets="Build"
|
||||||
|
Properties="Configuration=$(Configuration);TargetFramework=net9.0" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="BundleReferencedDlls" AfterTargets="Build">
|
||||||
|
<ItemGroup>
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Application\bin\$(Configuration)\net7.0\EnvelopeGenerator.Application.dll" TargetFramework="net7.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Application\bin\$(Configuration)\net8.0\EnvelopeGenerator.Application.dll" TargetFramework="net8.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Application\bin\$(Configuration)\net9.0\EnvelopeGenerator.Application.dll" TargetFramework="net9.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Domain\bin\$(Configuration)\net7.0\EnvelopeGenerator.Domain.dll" TargetFramework="net7.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Domain\bin\$(Configuration)\net8.0\EnvelopeGenerator.Domain.dll" TargetFramework="net8.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Domain\bin\$(Configuration)\net9.0\EnvelopeGenerator.Domain.dll" TargetFramework="net9.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Infrastructure\bin\$(Configuration)\net7.0\EnvelopeGenerator.Infrastructure.dll" TargetFramework="net7.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Infrastructure\bin\$(Configuration)\net8.0\EnvelopeGenerator.Infrastructure.dll" TargetFramework="net8.0" />
|
||||||
|
<BuildOutputInPackage Include="..\EnvelopeGenerator.Infrastructure\bin\$(Configuration)\net9.0\EnvelopeGenerator.Infrastructure.dll" TargetFramework="net9.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
</Project>
|
||||||
8
EnvelopeGenerator.Domain/Constants/SenderAppType.cs
Normal file
8
EnvelopeGenerator.Domain/Constants/SenderAppType.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace EnvelopeGenerator.Domain.Constants
|
||||||
|
{
|
||||||
|
public enum SenderAppType
|
||||||
|
{
|
||||||
|
LegacyFormApp = 0,
|
||||||
|
ReceiverUIBlazorApp = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,9 +11,9 @@ using System.Collections.Generic;
|
|||||||
namespace EnvelopeGenerator.Domain.Entities
|
namespace EnvelopeGenerator.Domain.Entities
|
||||||
{
|
{
|
||||||
[Table("TBSIG_DOCUMENT_RECEIVER_ELEMENT", Schema = "dbo")]
|
[Table("TBSIG_DOCUMENT_RECEIVER_ELEMENT", Schema = "dbo")]
|
||||||
public class Signature : ISignature, IHasReceiver, IHasAddedWhen, IUpdateAuditable
|
public class DocReceiverElement : IDocReceiverElement, IHasReceiver, IHasAddedWhen, IUpdateAuditable
|
||||||
{
|
{
|
||||||
public Signature()
|
public DocReceiverElement()
|
||||||
{
|
{
|
||||||
// TODO: * Check the Form App and remove the default value
|
// TODO: * Check the Form App and remove the default value
|
||||||
#if NETFRAMEWORK
|
#if NETFRAMEWORK
|
||||||
@@ -126,5 +126,33 @@ namespace EnvelopeGenerator.Domain.Entities
|
|||||||
[NotMapped]
|
[NotMapped]
|
||||||
public double Left => Math.Round(X, 5);
|
public double Left => Math.Round(X, 5);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
[Column("FULL_NAME", TypeName = "nvarchar(100)")]
|
||||||
|
public string
|
||||||
|
#if nullable
|
||||||
|
?
|
||||||
|
# endif
|
||||||
|
FullName { get; set; }
|
||||||
|
|
||||||
|
[Column("POSITION", TypeName = "nvarchar(100)")]
|
||||||
|
public string
|
||||||
|
#if nullable
|
||||||
|
?
|
||||||
|
#endif
|
||||||
|
Position { get; set; }
|
||||||
|
|
||||||
|
[Column("PLACE", TypeName = "nvarchar(100)")]
|
||||||
|
public string
|
||||||
|
#if nullable
|
||||||
|
?
|
||||||
|
#endif
|
||||||
|
Place { get; set; }
|
||||||
|
|
||||||
|
[Column("INK", TypeName = "varbinary(MAX)")]
|
||||||
|
public byte[]
|
||||||
|
#if nullable
|
||||||
|
?
|
||||||
|
#endif
|
||||||
|
Ink { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ namespace EnvelopeGenerator.Domain.Entities
|
|||||||
public Document()
|
public Document()
|
||||||
{
|
{
|
||||||
#if NETFRAMEWORK
|
#if NETFRAMEWORK
|
||||||
Elements = Enumerable.Empty<Signature>().ToList();
|
Elements = Enumerable.Empty<DocReceiverElement>().ToList();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ namespace EnvelopeGenerator.Domain.Entities
|
|||||||
FileNameOriginal { get; set; }
|
FileNameOriginal { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public virtual List<Signature>
|
public virtual List<DocReceiverElement>
|
||||||
#if nullable
|
#if nullable
|
||||||
?
|
?
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ namespace EnvelopeGenerator.Domain.Entities
|
|||||||
[Column("STATUS")]
|
[Column("STATUS")]
|
||||||
public Constants.DocumentStatus Status { get; set; }
|
public Constants.DocumentStatus Status { get; set; }
|
||||||
|
|
||||||
|
[Column("STATUS_CHANGED_WHEN", TypeName = "datetime")]
|
||||||
|
public DateTime? StatusChangedWhen { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[Column("ADDED_WHEN", TypeName = "datetime")]
|
[Column("ADDED_WHEN", TypeName = "datetime")]
|
||||||
public DateTime AddedWhen { get; set; }
|
public DateTime AddedWhen { get; set; }
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ namespace EnvelopeGenerator.Domain.Entities
|
|||||||
ChangedWho { get; set; }
|
ChangedWho { get; set; }
|
||||||
|
|
||||||
[ForeignKey("ElementId")]
|
[ForeignKey("ElementId")]
|
||||||
public virtual Signature
|
public virtual DocReceiverElement
|
||||||
#if nullable
|
#if nullable
|
||||||
?
|
?
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
|||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using EnvelopeGenerator.Domain.Interfaces.Auditing;
|
using EnvelopeGenerator.Domain.Interfaces.Auditing;
|
||||||
|
using EnvelopeGenerator.Domain.Interfaces;
|
||||||
#if NETFRAMEWORK
|
#if NETFRAMEWORK
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -13,7 +14,7 @@ using System.Linq;
|
|||||||
namespace EnvelopeGenerator.Domain.Entities
|
namespace EnvelopeGenerator.Domain.Entities
|
||||||
{
|
{
|
||||||
[Table("TBSIG_ENVELOPE", Schema = "dbo")]
|
[Table("TBSIG_ENVELOPE", Schema = "dbo")]
|
||||||
public class Envelope : IHasAddedWhen, IHasChangedWhen
|
public class Envelope : IHasAddedWhen, IHasChangedWhen, IEnvelope
|
||||||
{
|
{
|
||||||
public Envelope()
|
public Envelope()
|
||||||
{
|
{
|
||||||
@@ -93,7 +94,7 @@ namespace EnvelopeGenerator.Domain.Entities
|
|||||||
public string Language { get; set; }
|
public string Language { get; set; }
|
||||||
|
|
||||||
[Column("SEND_REMINDER_EMAILS")]
|
[Column("SEND_REMINDER_EMAILS")]
|
||||||
public bool SendReminderEmails { get; set; }
|
public bool? SendReminderEmails { get; set; }
|
||||||
|
|
||||||
[Column("FIRST_REMINDER_DAYS")]
|
[Column("FIRST_REMINDER_DAYS")]
|
||||||
public int? FirstReminderDays { get; set; }
|
public int? FirstReminderDays { get; set; }
|
||||||
@@ -106,13 +107,14 @@ namespace EnvelopeGenerator.Domain.Entities
|
|||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public bool ReadOnly => EnvelopeTypeId == 2;
|
[Obsolete("Use EnvelopeGenerator.Domain.Interfaces.EnvelopeExtensions.IsReadAndConfirm extension method instead.")]
|
||||||
|
public bool ReadOnly => this.IsReadAndConfirm();
|
||||||
|
|
||||||
[Column("CERTIFICATION_TYPE")]
|
[Column("CERTIFICATION_TYPE")]
|
||||||
public int? CertificationType { get; set; }
|
public int? CertificationType { get; set; }
|
||||||
|
|
||||||
[Column("USE_ACCESS_CODE")]
|
[Column("USE_ACCESS_CODE")]
|
||||||
public bool UseAccessCode { get; set; }
|
public bool? UseAccessCode { get; set; }
|
||||||
|
|
||||||
[Column("FINAL_EMAIL_TO_CREATOR")]
|
[Column("FINAL_EMAIL_TO_CREATOR")]
|
||||||
public int? FinalEmailToCreator { get; set; }
|
public int? FinalEmailToCreator { get; set; }
|
||||||
@@ -130,7 +132,7 @@ namespace EnvelopeGenerator.Domain.Entities
|
|||||||
public User User { get; set; }
|
public User User { get; set; }
|
||||||
|
|
||||||
[Column("TFA_ENABLED")]
|
[Column("TFA_ENABLED")]
|
||||||
public bool TfaEnabled { get; set; }
|
public bool? TfaEnabled { get; set; }
|
||||||
#if NETFRAMEWORK
|
#if NETFRAMEWORK
|
||||||
= false;
|
= false;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -35,8 +35,11 @@ namespace EnvelopeGenerator.Domain.Entities
|
|||||||
public DateTime AddedWhen { get; set; }
|
public DateTime AddedWhen { get; set; }
|
||||||
|
|
||||||
[Column("ACTION_DATE", TypeName = "datetime")]
|
[Column("ACTION_DATE", TypeName = "datetime")]
|
||||||
public DateTime? ChangedWhen { get; set; }
|
public DateTime? ActionDate { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ChangedWhen { get => ActionDate; set => ActionDate = value; }
|
||||||
|
|
||||||
[Column("COMMENT", TypeName = "nvarchar(max)")]
|
[Column("COMMENT", TypeName = "nvarchar(max)")]
|
||||||
public string
|
public string
|
||||||
#if nullable
|
#if nullable
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace EnvelopeGenerator.Domain.Interfaces
|
namespace EnvelopeGenerator.Domain.Interfaces
|
||||||
{
|
{
|
||||||
public interface ISignature
|
public interface IDocReceiverElement
|
||||||
{
|
{
|
||||||
int Page { get; set; }
|
int Page { get; set; }
|
||||||
|
|
||||||
20
EnvelopeGenerator.Domain/Interfaces/IEnvelope.cs
Normal file
20
EnvelopeGenerator.Domain/Interfaces/IEnvelope.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
namespace EnvelopeGenerator.Domain.Interfaces
|
||||||
|
{
|
||||||
|
public interface IEnvelope
|
||||||
|
{
|
||||||
|
int? EnvelopeTypeId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EnvelopeExtensions
|
||||||
|
{
|
||||||
|
public static bool IsReadAndConfirm(this IEnvelope envelope)
|
||||||
|
{
|
||||||
|
return envelope.EnvelopeTypeId == 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsReadAndSign(this IEnvelope envelope)
|
||||||
|
{
|
||||||
|
return envelope.EnvelopeTypeId != 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
200
EnvelopeGenerator.Domain/Resources/Model.Designer.cs
generated
200
EnvelopeGenerator.Domain/Resources/Model.Designer.cs
generated
@@ -1,10 +1,10 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// <auto-generated>
|
// <auto-generated>
|
||||||
// Dieser Code wurde von einem Tool generiert.
|
// This code was generated by a tool.
|
||||||
// Laufzeitversion:4.0.30319.42000
|
// Runtime Version:4.0.30319.42000
|
||||||
//
|
//
|
||||||
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
// der Code erneut generiert wird.
|
// the code is regenerated.
|
||||||
// </auto-generated>
|
// </auto-generated>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -13,12 +13,12 @@ namespace My.Resources {
|
|||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
|
// class via a tool like ResGen or Visual Studio.
|
||||||
// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
|
// with the /str option, or rebuild your VS project.
|
||||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
@@ -33,7 +33,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
public static global::System.Resources.ResourceManager ResourceManager {
|
public static global::System.Resources.ResourceManager ResourceManager {
|
||||||
@@ -47,8 +47,8 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
/// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
|
/// resource lookups using this strongly typed resource class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
public static global::System.Globalization.CultureInfo Culture {
|
public static global::System.Globalization.CultureInfo Culture {
|
||||||
@@ -61,7 +61,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Zugriffscode korrekt eingegeben ähnelt.
|
/// Looks up a localized string similar to Zugriffscode korrekt eingegeben.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string AccessCodeCorrect {
|
public static string AccessCodeCorrect {
|
||||||
get {
|
get {
|
||||||
@@ -70,7 +70,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Zugriffscode falsch eingegeben ähnelt.
|
/// Looks up a localized string similar to Zugriffscode falsch eingegeben.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string AccessCodeIncorrect {
|
public static string AccessCodeIncorrect {
|
||||||
get {
|
get {
|
||||||
@@ -79,7 +79,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Zugriffscode angefordert ähnelt.
|
/// Looks up a localized string similar to Zugriffscode angefordert.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string AccessCodeRequested {
|
public static string AccessCodeRequested {
|
||||||
get {
|
get {
|
||||||
@@ -88,7 +88,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Fortgeschrittene Elektronische Signatur ähnelt.
|
/// Looks up a localized string similar to Fortgeschrittene Elektronische Signatur.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string AdvancedElectronicSignature {
|
public static string AdvancedElectronicSignature {
|
||||||
get {
|
get {
|
||||||
@@ -97,7 +97,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Abgeschlossen ähnelt.
|
/// Looks up a localized string similar to Abgeschlossen.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Completed {
|
public static string Completed {
|
||||||
get {
|
get {
|
||||||
@@ -106,7 +106,16 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Vollständig Signiert ähnelt.
|
/// Looks up a localized string similar to Vollständig bestätigt.
|
||||||
|
/// </summary>
|
||||||
|
public static string CompletelyConfirmed {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CompletelyConfirmed", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Vollständig signiert.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string CompletelySigned {
|
public static string CompletelySigned {
|
||||||
get {
|
get {
|
||||||
@@ -115,7 +124,25 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Vertrag ähnelt.
|
/// Looks up a localized string similar to Lesebestätigung.
|
||||||
|
/// </summary>
|
||||||
|
public static string Confirmation {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Confirmation", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Gelesen und bestätigt.
|
||||||
|
/// </summary>
|
||||||
|
public static string Confirmed {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Confirmed", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Vertrag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Contract {
|
public static string Contract {
|
||||||
get {
|
get {
|
||||||
@@ -124,7 +151,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Erstellt ähnelt.
|
/// Looks up a localized string similar to Erstellt.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Created {
|
public static string Created {
|
||||||
get {
|
get {
|
||||||
@@ -133,7 +160,16 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Dokument Rotation geändert ähnelt.
|
/// Looks up a localized string similar to Dokument gelesen und bestätigt.
|
||||||
|
/// </summary>
|
||||||
|
public static string DocumentConfirmed {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("DocumentConfirmed", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Dokument Rotation geändert.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string DocumentMod_Rotation {
|
public static string DocumentMod_Rotation {
|
||||||
get {
|
get {
|
||||||
@@ -142,7 +178,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Dokument geöffnet ähnelt.
|
/// Looks up a localized string similar to Dokument geöffnet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string DocumentOpened {
|
public static string DocumentOpened {
|
||||||
get {
|
get {
|
||||||
@@ -151,7 +187,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Unterzeichnung abgelehnt ähnelt.
|
/// Looks up a localized string similar to Unterzeichnung abgelehnt.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string DocumentRejected {
|
public static string DocumentRejected {
|
||||||
get {
|
get {
|
||||||
@@ -160,7 +196,16 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Dokument unterzeichnet ähnelt.
|
/// Looks up a localized string similar to Lesebestätigung abgelehnt.
|
||||||
|
/// </summary>
|
||||||
|
public static string DocumentRejectedRaC {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("DocumentRejectedRaC", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Dokument unterzeichnet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string DocumentSigned {
|
public static string DocumentSigned {
|
||||||
get {
|
get {
|
||||||
@@ -169,7 +214,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Entwurf ähnelt.
|
/// Looks up a localized string similar to Entwurf.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Draft {
|
public static string Draft {
|
||||||
get {
|
get {
|
||||||
@@ -178,7 +223,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Archiviert ähnelt.
|
/// Looks up a localized string similar to Archiviert.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string EnvelopeArchived {
|
public static string EnvelopeArchived {
|
||||||
get {
|
get {
|
||||||
@@ -187,7 +232,16 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Vollständig signiert ähnelt.
|
/// Looks up a localized string similar to Vollständig gelesen und bestätigt.
|
||||||
|
/// </summary>
|
||||||
|
public static string EnvelopeCompletelyConfirmed {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("EnvelopeCompletelyConfirmed", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Vollständig signiert.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string EnvelopeCompletelySigned {
|
public static string EnvelopeCompletelySigned {
|
||||||
get {
|
get {
|
||||||
@@ -196,7 +250,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Umschlag Erstellt ähnelt.
|
/// Looks up a localized string similar to Umschlag Erstellt.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string EnvelopeCreated {
|
public static string EnvelopeCreated {
|
||||||
get {
|
get {
|
||||||
@@ -205,7 +259,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Umschlag Gelöscht ähnelt.
|
/// Looks up a localized string similar to Umschlag Gelöscht.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string EnvelopeDeleted {
|
public static string EnvelopeDeleted {
|
||||||
get {
|
get {
|
||||||
@@ -214,7 +268,16 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Teil-Signiert ähnelt.
|
/// Looks up a localized string similar to Teil-Bestätigt.
|
||||||
|
/// </summary>
|
||||||
|
public static string EnvelopePartlyConfirmed {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("EnvelopePartlyConfirmed", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Teil-Signiert.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string EnvelopePartlySigned {
|
public static string EnvelopePartlySigned {
|
||||||
get {
|
get {
|
||||||
@@ -223,7 +286,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Umschlag in Queue ähnelt.
|
/// Looks up a localized string similar to Umschlag in Queue.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string EnvelopeQueued {
|
public static string EnvelopeQueued {
|
||||||
get {
|
get {
|
||||||
@@ -232,7 +295,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Umschlag abgelehnt ähnelt.
|
/// Looks up a localized string similar to Umschlag abgelehnt.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string EnvelopeRejected {
|
public static string EnvelopeRejected {
|
||||||
get {
|
get {
|
||||||
@@ -241,7 +304,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Signierungszertifikat erstellt ähnelt.
|
/// Looks up a localized string similar to Signierungszertifikat erstellt.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string EnvelopeReportCreated {
|
public static string EnvelopeReportCreated {
|
||||||
get {
|
get {
|
||||||
@@ -250,7 +313,16 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Gespeichert ähnelt.
|
/// Looks up a localized string similar to Lesebestätigungszertifikat erstellt.
|
||||||
|
/// </summary>
|
||||||
|
public static string EnvelopeReportCreatedRaC {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("EnvelopeReportCreatedRaC", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Gespeichert.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string EnvelopeSaved {
|
public static string EnvelopeSaved {
|
||||||
get {
|
get {
|
||||||
@@ -259,7 +331,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Gesendet ähnelt.
|
/// Looks up a localized string similar to Gesendet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string EnvelopeSent {
|
public static string EnvelopeSent {
|
||||||
get {
|
get {
|
||||||
@@ -268,7 +340,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Umschlag zurückgezogen ähnelt.
|
/// Looks up a localized string similar to Umschlag zurückgezogen.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string EnvelopeWithdrawn {
|
public static string EnvelopeWithdrawn {
|
||||||
get {
|
get {
|
||||||
@@ -277,7 +349,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Zugriffscode versendet ähnelt.
|
/// Looks up a localized string similar to Zugriffscode versendet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string MessageAccessCodeSent {
|
public static string MessageAccessCodeSent {
|
||||||
get {
|
get {
|
||||||
@@ -286,7 +358,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Abschlussemail versendet ähnelt.
|
/// Looks up a localized string similar to Abschlussemail versendet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string MessageCompletionSent {
|
public static string MessageCompletionSent {
|
||||||
get {
|
get {
|
||||||
@@ -295,7 +367,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Signaturbestätigung versendet ähnelt.
|
/// Looks up a localized string similar to Abschlussbestätigung versendet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string MessageConfirmationSent {
|
public static string MessageConfirmationSent {
|
||||||
get {
|
get {
|
||||||
@@ -304,7 +376,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Löschinformation versendet ähnelt.
|
/// Looks up a localized string similar to Löschinformation versendet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string MessageDeletionSent {
|
public static string MessageDeletionSent {
|
||||||
get {
|
get {
|
||||||
@@ -313,7 +385,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Dokumentenlink versendet ähnelt.
|
/// Looks up a localized string similar to Dokumentenlink versendet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string MessageInvitationSent {
|
public static string MessageInvitationSent {
|
||||||
get {
|
get {
|
||||||
@@ -322,7 +394,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Nein ähnelt.
|
/// Looks up a localized string similar to Nein.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string No {
|
public static string No {
|
||||||
get {
|
get {
|
||||||
@@ -331,7 +403,16 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Teil-Signiert ähnelt.
|
/// Looks up a localized string similar to Teil-Bestätigt.
|
||||||
|
/// </summary>
|
||||||
|
public static string PartlyConfirmed {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("PartlyConfirmed", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Teil-Signiert.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string PartlySigned {
|
public static string PartlySigned {
|
||||||
get {
|
get {
|
||||||
@@ -340,7 +421,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Qualifizierte Signatur ähnelt.
|
/// Looks up a localized string similar to Qualifizierte Signatur.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string QualifiedSignature {
|
public static string QualifiedSignature {
|
||||||
get {
|
get {
|
||||||
@@ -349,7 +430,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Arbeitsanweisung ähnelt.
|
/// Looks up a localized string similar to Lesebestätigung.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ReadAndSign {
|
public static string ReadAndSign {
|
||||||
get {
|
get {
|
||||||
@@ -358,7 +439,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren! ähnelt.
|
/// Looks up a localized string similar to Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren!.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ResetTOTPUser {
|
public static string ResetTOTPUser {
|
||||||
get {
|
get {
|
||||||
@@ -367,7 +448,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Gespeichert ähnelt.
|
/// Looks up a localized string similar to Gespeichert.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Saved {
|
public static string Saved {
|
||||||
get {
|
get {
|
||||||
@@ -376,7 +457,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Gesendet ähnelt.
|
/// Looks up a localized string similar to Gesendet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Sent {
|
public static string Sent {
|
||||||
get {
|
get {
|
||||||
@@ -385,7 +466,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Signatur ähnelt.
|
/// Looks up a localized string similar to Signatur.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Signature {
|
public static string Signature {
|
||||||
get {
|
get {
|
||||||
@@ -394,7 +475,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Signatur bestätigt ähnelt.
|
/// Looks up a localized string similar to Abschluss bestätigt.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string SignatureConfirmed {
|
public static string SignatureConfirmed {
|
||||||
get {
|
get {
|
||||||
@@ -403,7 +484,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Signiert ähnelt.
|
/// Looks up a localized string similar to Signiert.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Signed {
|
public static string Signed {
|
||||||
get {
|
get {
|
||||||
@@ -412,7 +493,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Erfolgreich! Dialog wird geschlossen. ähnelt.
|
/// Looks up a localized string similar to Erfolgreich! Dialog wird geschlossen..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Success_FormClose {
|
public static string Success_FormClose {
|
||||||
get {
|
get {
|
||||||
@@ -421,7 +502,16 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Unsigniert ähnelt.
|
/// Looks up a localized string similar to Unbestätigt.
|
||||||
|
/// </summary>
|
||||||
|
public static string Unconfirmed {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Unconfirmed", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Unsigniert.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Unsigned {
|
public static string Unsigned {
|
||||||
get {
|
get {
|
||||||
@@ -430,7 +520,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Ja ähnelt.
|
/// Looks up a localized string similar to Ja.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Yes {
|
public static string Yes {
|
||||||
get {
|
get {
|
||||||
@@ -439,7 +529,7 @@ namespace My.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Ja, mit Anhang ähnelt.
|
/// Looks up a localized string similar to Ja, mit Anhang.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string YesWithAttachment {
|
public static string YesWithAttachment {
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -132,15 +132,27 @@
|
|||||||
<data name="Completed" xml:space="preserve">
|
<data name="Completed" xml:space="preserve">
|
||||||
<value>Completed</value>
|
<value>Completed</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CompletelyConfirmed" xml:space="preserve">
|
||||||
|
<value>Completely confirmed</value>
|
||||||
|
</data>
|
||||||
<data name="CompletelySigned" xml:space="preserve">
|
<data name="CompletelySigned" xml:space="preserve">
|
||||||
<value>Completely signed</value>
|
<value>Completely signed</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Confirmation" xml:space="preserve">
|
||||||
|
<value>Read Confirmation</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirmed" xml:space="preserve">
|
||||||
|
<value>Read and signed</value>
|
||||||
|
</data>
|
||||||
<data name="Contract" xml:space="preserve">
|
<data name="Contract" xml:space="preserve">
|
||||||
<value>Contract</value>
|
<value>Contract</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Created" xml:space="preserve">
|
<data name="Created" xml:space="preserve">
|
||||||
<value>Created</value>
|
<value>Created</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DocumentConfirmed" xml:space="preserve">
|
||||||
|
<value>Document read and signed</value>
|
||||||
|
</data>
|
||||||
<data name="DocumentMod_Rotation" xml:space="preserve">
|
<data name="DocumentMod_Rotation" xml:space="preserve">
|
||||||
<value>Document rotation adapted</value>
|
<value>Document rotation adapted</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -150,6 +162,9 @@
|
|||||||
<data name="DocumentRejected" xml:space="preserve">
|
<data name="DocumentRejected" xml:space="preserve">
|
||||||
<value>Signing rejected</value>
|
<value>Signing rejected</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DocumentRejectedRaC" xml:space="preserve">
|
||||||
|
<value>Read confirmation rejected</value>
|
||||||
|
</data>
|
||||||
<data name="DocumentSigned" xml:space="preserve">
|
<data name="DocumentSigned" xml:space="preserve">
|
||||||
<value>Document signed</value>
|
<value>Document signed</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -159,6 +174,9 @@
|
|||||||
<data name="EnvelopeArchived" xml:space="preserve">
|
<data name="EnvelopeArchived" xml:space="preserve">
|
||||||
<value>Archived</value>
|
<value>Archived</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EnvelopeCompletelyConfirmed" xml:space="preserve">
|
||||||
|
<value>Completely confirmed</value>
|
||||||
|
</data>
|
||||||
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
||||||
<value>Completely signed</value>
|
<value>Completely signed</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -168,8 +186,11 @@
|
|||||||
<data name="EnvelopeDeleted" xml:space="preserve">
|
<data name="EnvelopeDeleted" xml:space="preserve">
|
||||||
<value>Envelope Deleted</value>
|
<value>Envelope Deleted</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EnvelopePartlyConfirmed" xml:space="preserve">
|
||||||
|
<value>Partially confirmed</value>
|
||||||
|
</data>
|
||||||
<data name="EnvelopePartlySigned" xml:space="preserve">
|
<data name="EnvelopePartlySigned" xml:space="preserve">
|
||||||
<value>Partly signed</value>
|
<value>Partially signed</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EnvelopeQueued" xml:space="preserve">
|
<data name="EnvelopeQueued" xml:space="preserve">
|
||||||
<value>Envelope Queued</value>
|
<value>Envelope Queued</value>
|
||||||
@@ -180,6 +201,9 @@
|
|||||||
<data name="EnvelopeReportCreated" xml:space="preserve">
|
<data name="EnvelopeReportCreated" xml:space="preserve">
|
||||||
<value>Signature certificate created</value>
|
<value>Signature certificate created</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EnvelopeReportCreatedRaC" xml:space="preserve">
|
||||||
|
<value>Read confirmartion certificate created</value>
|
||||||
|
</data>
|
||||||
<data name="EnvelopeSaved" xml:space="preserve">
|
<data name="EnvelopeSaved" xml:space="preserve">
|
||||||
<value>Saved</value>
|
<value>Saved</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -196,7 +220,7 @@
|
|||||||
<value>Final email sent</value>
|
<value>Final email sent</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MessageConfirmationSent" xml:space="preserve">
|
<data name="MessageConfirmationSent" xml:space="preserve">
|
||||||
<value>Confirmation Sent</value>
|
<value>Finalization Confirmation Sent</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MessageDeletionSent" xml:space="preserve">
|
<data name="MessageDeletionSent" xml:space="preserve">
|
||||||
<value>Deletion Notice Sent</value>
|
<value>Deletion Notice Sent</value>
|
||||||
@@ -207,6 +231,12 @@
|
|||||||
<data name="No" xml:space="preserve">
|
<data name="No" xml:space="preserve">
|
||||||
<value>No</value>
|
<value>No</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PartlyConfirmed" xml:space="preserve">
|
||||||
|
<value>Partially confirmed</value>
|
||||||
|
</data>
|
||||||
|
<data name="PartlySigned" xml:space="preserve">
|
||||||
|
<value>Partially signed</value>
|
||||||
|
</data>
|
||||||
<data name="QualifiedSignature" xml:space="preserve">
|
<data name="QualifiedSignature" xml:space="preserve">
|
||||||
<value>Qualified Signature</value>
|
<value>Qualified Signature</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -226,7 +256,7 @@
|
|||||||
<value>Signature</value>
|
<value>Signature</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SignatureConfirmed" xml:space="preserve">
|
<data name="SignatureConfirmed" xml:space="preserve">
|
||||||
<value>Signature confirmed</value>
|
<value>Finalization confirmed</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Signed" xml:space="preserve">
|
<data name="Signed" xml:space="preserve">
|
||||||
<value>Signed</value>
|
<value>Signed</value>
|
||||||
@@ -234,6 +264,9 @@
|
|||||||
<data name="Success_FormClose" xml:space="preserve">
|
<data name="Success_FormClose" xml:space="preserve">
|
||||||
<value>Successful! Dialog is closed.successful! Dialog is closed.</value>
|
<value>Successful! Dialog is closed.successful! Dialog is closed.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Unconfirmed" xml:space="preserve">
|
||||||
|
<value>Unconfirmed</value>
|
||||||
|
</data>
|
||||||
<data name="Unsigned" xml:space="preserve">
|
<data name="Unsigned" xml:space="preserve">
|
||||||
<value>Unsigned</value>
|
<value>Unsigned</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -132,8 +132,17 @@
|
|||||||
<data name="Completed" xml:space="preserve">
|
<data name="Completed" xml:space="preserve">
|
||||||
<value>Abgeschlossen</value>
|
<value>Abgeschlossen</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CompletelyConfirmed" xml:space="preserve">
|
||||||
|
<value>Vollständig bestätigt</value>
|
||||||
|
</data>
|
||||||
<data name="CompletelySigned" xml:space="preserve">
|
<data name="CompletelySigned" xml:space="preserve">
|
||||||
<value>Vollständig Signiert</value>
|
<value>Vollständig signiert</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirmation" xml:space="preserve">
|
||||||
|
<value>Lesebestätigung</value>
|
||||||
|
</data>
|
||||||
|
<data name="Confirmed" xml:space="preserve">
|
||||||
|
<value>Gelesen und bestätigt</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Contract" xml:space="preserve">
|
<data name="Contract" xml:space="preserve">
|
||||||
<value>Vertrag</value>
|
<value>Vertrag</value>
|
||||||
@@ -141,6 +150,9 @@
|
|||||||
<data name="Created" xml:space="preserve">
|
<data name="Created" xml:space="preserve">
|
||||||
<value>Erstellt</value>
|
<value>Erstellt</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DocumentConfirmed" xml:space="preserve">
|
||||||
|
<value>Dokument gelesen und bestätigt</value>
|
||||||
|
</data>
|
||||||
<data name="DocumentMod_Rotation" xml:space="preserve">
|
<data name="DocumentMod_Rotation" xml:space="preserve">
|
||||||
<value>Dokument Rotation geändert</value>
|
<value>Dokument Rotation geändert</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -150,6 +162,9 @@
|
|||||||
<data name="DocumentRejected" xml:space="preserve">
|
<data name="DocumentRejected" xml:space="preserve">
|
||||||
<value>Unterzeichnung abgelehnt</value>
|
<value>Unterzeichnung abgelehnt</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DocumentRejectedRaC" xml:space="preserve">
|
||||||
|
<value>Lesebestätigung abgelehnt</value>
|
||||||
|
</data>
|
||||||
<data name="DocumentSigned" xml:space="preserve">
|
<data name="DocumentSigned" xml:space="preserve">
|
||||||
<value>Dokument unterzeichnet</value>
|
<value>Dokument unterzeichnet</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -159,6 +174,9 @@
|
|||||||
<data name="EnvelopeArchived" xml:space="preserve">
|
<data name="EnvelopeArchived" xml:space="preserve">
|
||||||
<value>Archiviert</value>
|
<value>Archiviert</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EnvelopeCompletelyConfirmed" xml:space="preserve">
|
||||||
|
<value>Vollständig gelesen und bestätigt</value>
|
||||||
|
</data>
|
||||||
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
||||||
<value>Vollständig signiert</value>
|
<value>Vollständig signiert</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -168,6 +186,9 @@
|
|||||||
<data name="EnvelopeDeleted" xml:space="preserve">
|
<data name="EnvelopeDeleted" xml:space="preserve">
|
||||||
<value>Umschlag Gelöscht</value>
|
<value>Umschlag Gelöscht</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EnvelopePartlyConfirmed" xml:space="preserve">
|
||||||
|
<value>Teil-Bestätigt</value>
|
||||||
|
</data>
|
||||||
<data name="EnvelopePartlySigned" xml:space="preserve">
|
<data name="EnvelopePartlySigned" xml:space="preserve">
|
||||||
<value>Teil-Signiert</value>
|
<value>Teil-Signiert</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -180,6 +201,9 @@
|
|||||||
<data name="EnvelopeReportCreated" xml:space="preserve">
|
<data name="EnvelopeReportCreated" xml:space="preserve">
|
||||||
<value>Signierungszertifikat erstellt</value>
|
<value>Signierungszertifikat erstellt</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EnvelopeReportCreatedRaC" xml:space="preserve">
|
||||||
|
<value>Lesebestätigungszertifikat erstellt</value>
|
||||||
|
</data>
|
||||||
<data name="EnvelopeSaved" xml:space="preserve">
|
<data name="EnvelopeSaved" xml:space="preserve">
|
||||||
<value>Gespeichert</value>
|
<value>Gespeichert</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -196,7 +220,7 @@
|
|||||||
<value>Abschlussemail versendet</value>
|
<value>Abschlussemail versendet</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MessageConfirmationSent" xml:space="preserve">
|
<data name="MessageConfirmationSent" xml:space="preserve">
|
||||||
<value>Signaturbestätigung versendet</value>
|
<value>Abschlussbestätigung versendet</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MessageDeletionSent" xml:space="preserve">
|
<data name="MessageDeletionSent" xml:space="preserve">
|
||||||
<value>Löschinformation versendet</value>
|
<value>Löschinformation versendet</value>
|
||||||
@@ -207,6 +231,9 @@
|
|||||||
<data name="No" xml:space="preserve">
|
<data name="No" xml:space="preserve">
|
||||||
<value>Nein</value>
|
<value>Nein</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PartlyConfirmed" xml:space="preserve">
|
||||||
|
<value>Teil-Bestätigt</value>
|
||||||
|
</data>
|
||||||
<data name="PartlySigned" xml:space="preserve">
|
<data name="PartlySigned" xml:space="preserve">
|
||||||
<value>Teil-Signiert</value>
|
<value>Teil-Signiert</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -214,7 +241,7 @@
|
|||||||
<value>Qualifizierte Signatur</value>
|
<value>Qualifizierte Signatur</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ReadAndSign" xml:space="preserve">
|
<data name="ReadAndSign" xml:space="preserve">
|
||||||
<value>Arbeitsanweisung</value>
|
<value>Lesebestätigung</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ResetTOTPUser" xml:space="preserve">
|
<data name="ResetTOTPUser" xml:space="preserve">
|
||||||
<value>Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren!</value>
|
<value>Wollen Sie die 2-Faktor Definition für diesen Empfänger zurücksetzen. Der Empfänger muss sich dann neu identifizieren!</value>
|
||||||
@@ -229,7 +256,7 @@
|
|||||||
<value>Signatur</value>
|
<value>Signatur</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SignatureConfirmed" xml:space="preserve">
|
<data name="SignatureConfirmed" xml:space="preserve">
|
||||||
<value>Signatur bestätigt</value>
|
<value>Abschluss bestätigt</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Signed" xml:space="preserve">
|
<data name="Signed" xml:space="preserve">
|
||||||
<value>Signiert</value>
|
<value>Signiert</value>
|
||||||
@@ -237,6 +264,9 @@
|
|||||||
<data name="Success_FormClose" xml:space="preserve">
|
<data name="Success_FormClose" xml:space="preserve">
|
||||||
<value>Erfolgreich! Dialog wird geschlossen.</value>
|
<value>Erfolgreich! Dialog wird geschlossen.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Unconfirmed" xml:space="preserve">
|
||||||
|
<value>Unbestätigt</value>
|
||||||
|
</data>
|
||||||
<data name="Unsigned" xml:space="preserve">
|
<data name="Unsigned" xml:space="preserve">
|
||||||
<value>Unsigniert</value>
|
<value>Unsigniert</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Public Class FieldEditorController
|
|||||||
Inherits BaseController
|
Inherits BaseController
|
||||||
|
|
||||||
Private ReadOnly Document As Document
|
Private ReadOnly Document As Document
|
||||||
Public Property Elements As New List(Of Signature)
|
Public Property Elements As New List(Of DocReceiverElement)
|
||||||
|
|
||||||
Public Class ElementInfo
|
Public Class ElementInfo
|
||||||
Public ReceiverId As Integer
|
Public ReceiverId As Integer
|
||||||
@@ -36,7 +36,7 @@ Public Class FieldEditorController
|
|||||||
}
|
}
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Private Function GetElementByPosition(pAnnotation As AnnotationStickyNote, pPage As Integer, pReceiverId As Integer) As Signature
|
Private Function GetElementByPosition(pAnnotation As AnnotationStickyNote, pPage As Integer, pReceiverId As Integer) As DocReceiverElement
|
||||||
Dim oElement = Elements.
|
Dim oElement = Elements.
|
||||||
Where(Function(e)
|
Where(Function(e)
|
||||||
Return e.Left = CSng(Math.Round(pAnnotation.Left, 5)) And
|
Return e.Left = CSng(Math.Round(pAnnotation.Left, 5)) And
|
||||||
@@ -47,12 +47,12 @@ Public Class FieldEditorController
|
|||||||
Return oElement
|
Return oElement
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Private Function GetElementByGuid(pGuid As Integer) As Signature
|
Private Function GetElementByGuid(pGuid As Integer) As DocReceiverElement
|
||||||
Dim oElement = Elements.Where(Function(e) pGuid = e.Id).SingleOrDefault()
|
Dim oElement = Elements.Where(Function(e) pGuid = e.Id).SingleOrDefault()
|
||||||
Return oElement
|
Return oElement
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function GetElement(pAnnotation As AnnotationStickyNote) As Signature
|
Public Function GetElement(pAnnotation As AnnotationStickyNote) As DocReceiverElement
|
||||||
Dim oInfo = GetElementInfo(pAnnotation.Tag)
|
Dim oInfo = GetElementInfo(pAnnotation.Tag)
|
||||||
|
|
||||||
If oInfo.Guid = -1 Then
|
If oInfo.Guid = -1 Then
|
||||||
@@ -85,7 +85,7 @@ Public Class FieldEditorController
|
|||||||
Else
|
Else
|
||||||
Dim oInfo = GetElementInfo(pAnnotation.Tag)
|
Dim oInfo = GetElementInfo(pAnnotation.Tag)
|
||||||
|
|
||||||
Elements.Add(New Signature() With {
|
Elements.Add(New DocReceiverElement() With {
|
||||||
.ElementType = Constants.ElementType.Signature,
|
.ElementType = Constants.ElementType.Signature,
|
||||||
.Height = oAnnotationHeight,
|
.Height = oAnnotationHeight,
|
||||||
.Width = oAnnotationWidth,
|
.Width = oAnnotationWidth,
|
||||||
@@ -98,7 +98,7 @@ Public Class FieldEditorController
|
|||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
Public Function ClearElements(pPage As Integer, pReceiverId As Integer) As IEnumerable(Of Signature)
|
Public Function ClearElements(pPage As Integer, pReceiverId As Integer) As IEnumerable(Of DocReceiverElement)
|
||||||
Return Elements.
|
Return Elements.
|
||||||
Where(Function(e) e.Page <> pPage And e.ReceiverId <> pReceiverId).
|
Where(Function(e) e.Page <> pPage And e.ReceiverId <> pReceiverId).
|
||||||
ToList()
|
ToList()
|
||||||
@@ -120,7 +120,7 @@ Public Class FieldEditorController
|
|||||||
All(Function(pResult) pResult = True)
|
All(Function(pResult) pResult = True)
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function SaveElement(pElement As Signature) As Boolean
|
Public Function SaveElement(pElement As DocReceiverElement) As Boolean
|
||||||
Try
|
Try
|
||||||
If pElement.Id > 0 Then
|
If pElement.Id > 0 Then
|
||||||
Return ElementModel.Update(pElement)
|
Return ElementModel.Update(pElement)
|
||||||
@@ -134,11 +134,11 @@ Public Class FieldEditorController
|
|||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function DeleteElement(pElement As Signature) As Boolean
|
Public Function DeleteElement(pElement As DocReceiverElement) As Boolean
|
||||||
Try
|
Try
|
||||||
' Element aus Datenbank löschen
|
' Element aus Datenbank löschen
|
||||||
If ElementModel.DeleteElement(pElement) Then
|
If ElementModel.DeleteElement(pElement) Then
|
||||||
Dim oElement = New List(Of Signature)() From {pElement}
|
Dim oElement = New List(Of DocReceiverElement)() From {pElement}
|
||||||
Elements = Elements.Except(oElement).ToList()
|
Elements = Elements.Except(oElement).ToList()
|
||||||
Return True
|
Return True
|
||||||
Else
|
Else
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ Partial Public Class frmFieldEditor
|
|||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
Private Sub LoadAnnotation(pElement As Signature, pReceiverId As Integer)
|
Private Sub LoadAnnotation(pElement As DocReceiverElement, pReceiverId As Integer)
|
||||||
Dim oAnnotation As AnnotationStickyNote = Manager.AddStickyNoteAnnot(0, 0, 0, 0, "SIGNATUR")
|
Dim oAnnotation As AnnotationStickyNote = Manager.AddStickyNoteAnnot(0, 0, 0, 0, "SIGNATUR")
|
||||||
Dim oPage = pElement.Page
|
Dim oPage = pElement.Page
|
||||||
Dim oReceiver = Receivers.Where(Function(r) r.Id = pReceiverId).Single()
|
Dim oReceiver = Receivers.Where(Function(r) r.Id = pReceiverId).Single()
|
||||||
|
|||||||
@@ -74,14 +74,14 @@ namespace EnvelopeGenerator.Infrastructure
|
|||||||
services.AddSQLExecutor<Envelope>();
|
services.AddSQLExecutor<Envelope>();
|
||||||
services.AddSQLExecutor<Receiver>();
|
services.AddSQLExecutor<Receiver>();
|
||||||
services.AddSQLExecutor<Document>();
|
services.AddSQLExecutor<Document>();
|
||||||
services.AddSQLExecutor<Signature>();
|
services.AddSQLExecutor<DocReceiverElement>();
|
||||||
services.AddSQLExecutor<DocumentStatus>();
|
services.AddSQLExecutor<DocumentStatus>();
|
||||||
|
|
||||||
SetDapperTypeMap<Envelope>();
|
SetDapperTypeMap<Envelope>();
|
||||||
SetDapperTypeMap<User>();
|
SetDapperTypeMap<User>();
|
||||||
SetDapperTypeMap<Receiver>();
|
SetDapperTypeMap<Receiver>();
|
||||||
SetDapperTypeMap<Document>();
|
SetDapperTypeMap<Document>();
|
||||||
SetDapperTypeMap<Signature>();
|
SetDapperTypeMap<DocReceiverElement>();
|
||||||
SetDapperTypeMap<DocumentStatus>();
|
SetDapperTypeMap<DocumentStatus>();
|
||||||
|
|
||||||
services.AddScoped<IEnvelopeExecutor, EnvelopeExecutor>();
|
services.AddScoped<IEnvelopeExecutor, EnvelopeExecutor>();
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ public abstract class EGDbContextBase : DbContext
|
|||||||
|
|
||||||
public DbSet<Envelope> Envelopes { get; set; }
|
public DbSet<Envelope> Envelopes { get; set; }
|
||||||
|
|
||||||
public DbSet<Signature> DocumentReceiverElements { get; set; }
|
public DbSet<DocReceiverElement> DocumentReceiverElements { get; set; }
|
||||||
|
|
||||||
|
public DbSet<ElementAnnotation> DocumentReceiverElementAnnotations { get; set; }
|
||||||
|
|
||||||
public DbSet<DocumentStatus> DocumentStatus { get; set; }
|
public DbSet<DocumentStatus> DocumentStatus { get; set; }
|
||||||
|
|
||||||
@@ -152,7 +154,7 @@ public abstract class EGDbContextBase : DbContext
|
|||||||
#endregion EnvelopeDocument
|
#endregion EnvelopeDocument
|
||||||
|
|
||||||
#region DocumentReceiverElement
|
#region DocumentReceiverElement
|
||||||
modelBuilder.Entity<Signature>()
|
modelBuilder.Entity<DocReceiverElement>()
|
||||||
.HasOne(dre => dre.Document)
|
.HasOne(dre => dre.Document)
|
||||||
.WithMany(ed => ed.Elements)
|
.WithMany(ed => ed.Elements)
|
||||||
.HasForeignKey(dre => dre.DocumentId);
|
.HasForeignKey(dre => dre.DocumentId);
|
||||||
@@ -194,7 +196,7 @@ public abstract class EGDbContextBase : DbContext
|
|||||||
#endregion DocumentStatus
|
#endregion DocumentStatus
|
||||||
|
|
||||||
#region Annotation
|
#region Annotation
|
||||||
modelBuilder.Entity<Signature>()
|
modelBuilder.Entity<DocReceiverElement>()
|
||||||
.HasMany(signature => signature.Annotations)
|
.HasMany(signature => signature.Annotations)
|
||||||
.WithOne(annot => annot.Element)
|
.WithOne(annot => annot.Element)
|
||||||
.HasForeignKey(annot => annot.ElementId);
|
.HasForeignKey(annot => annot.ElementId);
|
||||||
@@ -215,7 +217,7 @@ public abstract class EGDbContextBase : DbContext
|
|||||||
|
|
||||||
// TODO: call add trigger methods with attributes and reflection
|
// TODO: call add trigger methods with attributes and reflection
|
||||||
AddTrigger<Config>();
|
AddTrigger<Config>();
|
||||||
AddTrigger<Signature>();
|
AddTrigger<DocReceiverElement>();
|
||||||
AddTrigger<DocumentStatus>();
|
AddTrigger<DocumentStatus>();
|
||||||
AddTrigger<EmailTemplate>();
|
AddTrigger<EmailTemplate>();
|
||||||
AddTrigger<Envelope>();
|
AddTrigger<Envelope>();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.6.0" />
|
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.6.0" />
|
||||||
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.6.1" />
|
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.6.1" />
|
||||||
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
||||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
|
||||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
|
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
|
||||||
<PackageReference Include="QuestPDF" Version="2025.7.1" />
|
<PackageReference Include="QuestPDF" Version="2025.7.1" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|||||||
namespace EnvelopeGenerator.Infrastructure.Repositories;
|
namespace EnvelopeGenerator.Infrastructure.Repositories;
|
||||||
|
|
||||||
[Obsolete("Use IRepository")]
|
[Obsolete("Use IRepository")]
|
||||||
public class DocumentReceiverElementRepository : CRUDRepository<Signature, int, EGDbContext>, IDocumentReceiverElementRepository
|
public class DocumentReceiverElementRepository : CRUDRepository<DocReceiverElement, int, EGDbContext>, IDocumentReceiverElementRepository
|
||||||
{
|
{
|
||||||
public DocumentReceiverElementRepository(EGDbContext dbContext) : base(dbContext, dbContext.DocumentReceiverElements)
|
public DocumentReceiverElementRepository(EGDbContext dbContext) : base(dbContext, dbContext.DocumentReceiverElements)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />
|
|
||||||
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
|
||||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
|
|
||||||
<PackageReference Include="Quartz" Version="3.9.0" />
|
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.3" />
|
|
||||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
|
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
|
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
|
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using Microsoft.Data.SqlClient;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
|
||||||
using Quartz;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Jobs.APIBackendJobs;
|
|
||||||
|
|
||||||
public class APIEnvelopeJob(ILogger<APIEnvelopeJob>? logger = null) : IJob
|
|
||||||
{
|
|
||||||
private readonly ILogger<APIEnvelopeJob> _logger = logger ?? NullLogger<APIEnvelopeJob>.Instance;
|
|
||||||
|
|
||||||
public async Task Execute(IJobExecutionContext context)
|
|
||||||
{
|
|
||||||
var jobId = context.JobDetail.Key.ToString();
|
|
||||||
_logger.LogDebug("API Envelopes - Starting job {JobId}", jobId);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var connectionString = context.MergedJobDataMap.GetString(Value.DATABASE);
|
|
||||||
if (string.IsNullOrWhiteSpace(connectionString))
|
|
||||||
{
|
|
||||||
_logger.LogWarning("API Envelopes - Connection string missing");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await using var connection = new SqlConnection(connectionString);
|
|
||||||
await connection.OpenAsync(context.CancellationToken);
|
|
||||||
|
|
||||||
await ProcessInvitationsAsync(connection, context.CancellationToken);
|
|
||||||
await ProcessWithdrawnAsync(connection, context.CancellationToken);
|
|
||||||
|
|
||||||
_logger.LogDebug("API Envelopes - Completed job {JobId} successfully", jobId);
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "API Envelopes job failed");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_logger.LogDebug("API Envelopes execution for {JobId} ended", jobId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ProcessInvitationsAsync(SqlConnection connection, System.Threading.CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
const string sql = "SELECT GUID FROM TBSIG_ENVELOPE WHERE SOURCE = 'API' AND STATUS = 1003 ORDER BY GUID";
|
|
||||||
var envelopeIds = new List<int>();
|
|
||||||
|
|
||||||
await using (var command = new SqlCommand(sql, connection))
|
|
||||||
await using (var reader = await command.ExecuteReaderAsync(cancellationToken))
|
|
||||||
{
|
|
||||||
while (await reader.ReadAsync(cancellationToken))
|
|
||||||
{
|
|
||||||
if (reader[0] is int id)
|
|
||||||
{
|
|
||||||
envelopeIds.Add(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (envelopeIds.Count == 0)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("SendInvMail - No envelopes found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation("SendInvMail - Found {Count} envelopes", envelopeIds.Count);
|
|
||||||
var total = envelopeIds.Count;
|
|
||||||
var current = 1;
|
|
||||||
|
|
||||||
foreach (var id in envelopeIds)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("SendInvMail - Processing Envelope {EnvelopeId} ({Current}/{Total})", id, current, total);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Placeholder for invitation email sending logic.
|
|
||||||
_logger.LogDebug("SendInvMail - Marking envelope {EnvelopeId} as queued", id);
|
|
||||||
const string updateSql = "UPDATE TBSIG_ENVELOPE SET CURRENT_WORK_APP = @App WHERE GUID = @Id";
|
|
||||||
await using var updateCommand = new SqlCommand(updateSql, connection);
|
|
||||||
updateCommand.Parameters.AddWithValue("@App", "signFLOW_API_EnvJob_InvMail");
|
|
||||||
updateCommand.Parameters.AddWithValue("@Id", id);
|
|
||||||
await updateCommand.ExecuteNonQueryAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "SendInvMail - Unhandled exception while working envelope {EnvelopeId}", id);
|
|
||||||
}
|
|
||||||
|
|
||||||
current++;
|
|
||||||
_logger.LogInformation("SendInvMail - Envelope finalized");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ProcessWithdrawnAsync(SqlConnection connection, System.Threading.CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
const string sql = @"SELECT ENV.GUID, REJ.COMMENT AS REJECTION_REASON FROM
|
|
||||||
(SELECT * FROM TBSIG_ENVELOPE WHERE STATUS = 1009 AND SOURCE = 'API') ENV INNER JOIN
|
|
||||||
(SELECT MAX(GUID) GUID, ENVELOPE_ID, MAX(ADDED_WHEN) ADDED_WHEN, MAX(ACTION_DATE) ACTION_DATE, COMMENT FROM TBSIG_ENVELOPE_HISTORY WHERE STATUS = 1009 GROUP BY ENVELOPE_ID, COMMENT ) REJ ON ENV.GUID = REJ.ENVELOPE_ID LEFT JOIN
|
|
||||||
(SELECT * FROM TBSIG_ENVELOPE_HISTORY WHERE STATUS = 3004 ) M_Send ON ENV.GUID = M_Send.ENVELOPE_ID
|
|
||||||
WHERE M_Send.GUID IS NULL";
|
|
||||||
|
|
||||||
var withdrawn = new List<(int EnvelopeId, string Reason)>();
|
|
||||||
await using (var command = new SqlCommand(sql, connection))
|
|
||||||
await using (var reader = await command.ExecuteReaderAsync(cancellationToken))
|
|
||||||
{
|
|
||||||
while (await reader.ReadAsync(cancellationToken))
|
|
||||||
{
|
|
||||||
var id = reader.GetInt32(0);
|
|
||||||
var reason = reader.IsDBNull(1) ? string.Empty : reader.GetString(1);
|
|
||||||
withdrawn.Add((id, reason));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (withdrawn.Count == 0)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("WithdrawnEnv - No envelopes found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation("WithdrawnEnv - Found {Count} envelopes", withdrawn.Count);
|
|
||||||
var total = withdrawn.Count;
|
|
||||||
var current = 1;
|
|
||||||
|
|
||||||
foreach (var (envelopeId, reason) in withdrawn)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("WithdrawnEnv - Processing Envelope {EnvelopeId} ({Current}/{Total})", envelopeId, current, total);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Log withdrawn mail trigger placeholder
|
|
||||||
const string insertHistory = "INSERT INTO TBSIG_ENVELOPE_HISTORY (ENVELOPE_ID, STATUS, USER_REFERENCE, ADDED_WHEN, ACTION_DATE, COMMENT) VALUES (@EnvelopeId, @Status, @UserReference, GETDATE(), GETDATE(), @Comment)";
|
|
||||||
await using var insertCommand = new SqlCommand(insertHistory, connection);
|
|
||||||
insertCommand.Parameters.AddWithValue("@EnvelopeId", envelopeId);
|
|
||||||
insertCommand.Parameters.AddWithValue("@Status", 3004);
|
|
||||||
insertCommand.Parameters.AddWithValue("@UserReference", "API");
|
|
||||||
insertCommand.Parameters.AddWithValue("@Comment", reason ?? string.Empty);
|
|
||||||
await insertCommand.ExecuteNonQueryAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "WithdrawnEnv - Unhandled exception while working envelope {EnvelopeId}", envelopeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
current++;
|
|
||||||
_logger.LogInformation("WithdrawnEnv - Envelope finalized");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Jobs;
|
|
||||||
|
|
||||||
public static class DataRowExtensions
|
|
||||||
{
|
|
||||||
public static T? GetValueOrDefault<T>(this DataRow row, string columnName, T? defaultValue = default)
|
|
||||||
{
|
|
||||||
if (!row.Table.Columns.Contains(columnName))
|
|
||||||
{
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var value = row[columnName];
|
|
||||||
if (value == DBNull.Value)
|
|
||||||
{
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return (T)Convert.ChangeType(value, typeof(T));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Jobs.FinalizeDocument;
|
|
||||||
|
|
||||||
public static class FinalizeDocumentExceptions
|
|
||||||
{
|
|
||||||
public class MergeDocumentException : ApplicationException
|
|
||||||
{
|
|
||||||
public MergeDocumentException(string message) : base(message) { }
|
|
||||||
public MergeDocumentException(string message, Exception innerException) : base(message, innerException) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BurnAnnotationException : ApplicationException
|
|
||||||
{
|
|
||||||
public BurnAnnotationException(string message) : base(message) { }
|
|
||||||
public BurnAnnotationException(string message, Exception innerException) : base(message, innerException) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CreateReportException : ApplicationException
|
|
||||||
{
|
|
||||||
public CreateReportException(string message) : base(message) { }
|
|
||||||
public CreateReportException(string message, Exception innerException) : base(message, innerException) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ExportDocumentException : ApplicationException
|
|
||||||
{
|
|
||||||
public ExportDocumentException(string message) : base(message) { }
|
|
||||||
public ExportDocumentException(string message, Exception innerException) : base(message, innerException) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user