Compare commits
14 Commits
bugfix/dev
...
8655d84ed5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8655d84ed5 | ||
|
|
caae986b2d | ||
|
|
af28844714 | ||
|
|
8257ed573d | ||
|
|
93488ba83e | ||
|
|
4aa889f178 | ||
|
|
60d7043164 | ||
|
|
7aa9853756 | ||
|
|
0a544cfe85 | ||
|
|
4f3c66b4f7 | ||
|
|
7271a92d32 | ||
|
|
c7275ad966 | ||
|
|
bf8115259a | ||
|
|
590ab9bf02 |
@@ -1,424 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,17 +0,0 @@
|
||||
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,14 +1,12 @@
|
||||
using DigitalData.Core.Abstraction.Application.DTO;
|
||||
using DigitalData.Core.Exceptions;
|
||||
using EnvelopeGenerator.API.Extensions;
|
||||
using EnvelopeGenerator.Application.Common.Dto;
|
||||
using EnvelopeGenerator.Application.Common.Extensions;
|
||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||
using EnvelopeGenerator.Application.Common.Notifications.DocSigned;
|
||||
using EnvelopeGenerator.Application.Common.Notifications.RemoveSignature;
|
||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
||||
using EnvelopeGenerator.Application.Histories.Queries;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using EnvelopeGenerator.API.Extensions;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
@@ -61,8 +59,8 @@ public class AnnotationController : ControllerBase
|
||||
[Obsolete("PSPDF Kit will no longer be used.")]
|
||||
public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation? psPdfKitAnnotation = null, CancellationToken cancel = default)
|
||||
{
|
||||
var signature = User.ReceiverSignature();
|
||||
var uuid = User.EnvelopeUuid();
|
||||
var signature = User.GetReceiverSignatureOfReceiver();
|
||||
var uuid = User.GetEnvelopeUuidOfReceiver();
|
||||
|
||||
var envelopeReceiver = await _mediator.ReadEnvelopeReceiverAsync(uuid, signature, cancel).ThrowIfNull(Exceptions.NotFound);
|
||||
|
||||
@@ -74,24 +72,12 @@ public class AnnotationController : ControllerBase
|
||||
else if (await _mediator.AnyHistoryAsync(uuid, new[] { EnvelopeStatus.EnvelopeRejected, EnvelopeStatus.DocumentRejected }, cancel))
|
||||
return Problem(statusCode: StatusCodes.Status423Locked);
|
||||
|
||||
var envelopeReceiverDto = await _mediator.ReadEnvelopeReceiverAsync(uuid, signature, cancel);
|
||||
var docSignedNotification = envelopeReceiverDto is not null
|
||||
? new DocSignedNotification { EnvelopeReceiver = envelopeReceiverDto, PsPdfKitAnnotation = psPdfKitAnnotation }
|
||||
: throw new NotFoundException("Envelope receiver is not found.");
|
||||
var docSignedNotification = await _mediator
|
||||
.ReadEnvelopeReceiverAsync(uuid, signature, cancel)
|
||||
.ToDocSignedNotification(psPdfKitAnnotation)
|
||||
?? throw new NotFoundException("Envelope receiver is not found.");
|
||||
|
||||
try
|
||||
{
|
||||
await _mediator.Publish(docSignedNotification, cancel);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await _mediator.Publish(new RemoveSignatureNotification()
|
||||
{
|
||||
EnvelopeId = docSignedNotification.EnvelopeReceiver.EnvelopeId,
|
||||
ReceiverId = docSignedNotification.EnvelopeReceiver.ReceiverId
|
||||
}, cancel);
|
||||
throw;
|
||||
}
|
||||
await _mediator.PublishSafely(docSignedNotification, cancel);
|
||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
|
||||
return Ok();
|
||||
@@ -106,9 +92,9 @@ public class AnnotationController : ControllerBase
|
||||
[Obsolete("Use MediatR")]
|
||||
public async Task<IActionResult> Reject([FromBody] string? reason = null)
|
||||
{
|
||||
var signature = User.ReceiverSignature();
|
||||
var uuid = User.EnvelopeUuid();
|
||||
var mail = User.ReceiverMail();
|
||||
var signature = User.GetReceiverSignatureOfReceiver();
|
||||
var uuid = User.GetEnvelopeUuidOfReceiver();
|
||||
var mail = User.GetReceiverMailOfReceiver();
|
||||
|
||||
var envRcvRes = await _envelopeReceiverService.ReadByUuidSignatureAsync(uuid: uuid, signature: signature);
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using DigitalData.Auth.Claims;
|
||||
using EnvelopeGenerator.API.Controllers.Interfaces;
|
||||
using EnvelopeGenerator.API.Models;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
@@ -74,44 +73,4 @@ public partial class AuthController(IOptions<AuthTokenKeys> authTokenKeyOptions,
|
||||
=> role is not null && !User.IsInRole(role)
|
||||
? Unauthorized()
|
||||
: 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();
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
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,4 +1,3 @@
|
||||
using DigitalData.Auth.Claims;
|
||||
using EnvelopeGenerator.API.Controllers.Interfaces;
|
||||
using EnvelopeGenerator.API.Extensions;
|
||||
using EnvelopeGenerator.Application.Documents.Queries;
|
||||
@@ -15,9 +14,10 @@ namespace EnvelopeGenerator.API.Controllers;
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="DocumentController"/> class.
|
||||
/// </remarks>
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class DocumentController(IMediator mediator, IAuthorizationService authService, ILogger<DocumentController> logger) : ControllerBase, IAuthController
|
||||
public class DocumentController(IMediator mediator, IAuthorizationService authService) : ControllerBase, IAuthController
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
@@ -51,7 +51,7 @@ public class DocumentController(IMediator mediator, IAuthorizationService authSe
|
||||
if (query is not null)
|
||||
return BadRequest("Query parameters are not allowed for receiver role.");
|
||||
|
||||
var envelopeId = User.EnvelopeId();
|
||||
var envelopeId = User.GetEnvelopeIdOfReceiver();
|
||||
var receiverDoc = await mediator.Send(new ReadDocumentQuery { EnvelopeId = envelopeId }, cancel);
|
||||
return receiverDoc.ByteData is byte[] receiverDocByte
|
||||
? File(receiverDocByte, "application/octet-stream")
|
||||
@@ -60,25 +60,4 @@ public class DocumentController(IMediator mediator, IAuthorizationService authSe
|
||||
|
||||
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="403">Der Benutzer hat keine Berechtigung, auf die Ressource zuzugreifen.</response>
|
||||
/// <response code="500">Ein unerwarteter Fehler ist aufgetreten.</response>
|
||||
[Authorize(AuthenticationSchemes = AuthScheme.Sender)]
|
||||
[Authorize]
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetAsync([FromQuery] ReadEnvelopeQuery envelope)
|
||||
{
|
||||
@@ -98,7 +98,7 @@ public class EnvelopeController : ControllerBase
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> CreateAsync([FromBody] CreateEnvelopeCommand command)
|
||||
{
|
||||
var res = await _mediator.Send(command.WithAuth(User.GetId()));
|
||||
var res = await _mediator.Send(command.Authorize(User.GetId()));
|
||||
|
||||
if (res is null)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,6 @@ using EnvelopeGenerator.Application.Common.SQL;
|
||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
||||
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
||||
using EnvelopeGenerator.API.Extensions;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
|
||||
namespace EnvelopeGenerator.API.Controllers;
|
||||
|
||||
@@ -74,24 +73,6 @@ public class EnvelopeReceiverController : ControllerBase
|
||||
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>
|
||||
/// Ruft den Namen des zuletzt verwendeten Empfängers basierend auf der angegebenen E-Mail-Adresse ab.
|
||||
/// </summary>
|
||||
@@ -200,7 +181,7 @@ public class EnvelopeReceiverController : ControllerBase
|
||||
SELECT @OUT_SUCCESS as [@OUT_SUCCESS];";
|
||||
|
||||
foreach (var rcv in res.SentReceiver)
|
||||
foreach (var sign in request.Receivers.Where(r => r.EmailAddress == rcv.EmailAddress).FirstOrDefault()?.DocReceiverElements ?? Enumerable.Empty<Application.EnvelopeReceivers.Commands.DocReceiverElementCreateDto>())
|
||||
foreach (var sign in request.Receivers.Where(r => r.EmailAddress == rcv.EmailAddress).FirstOrDefault()?.Signatures ?? Enumerable.Empty<Application.EnvelopeReceivers.Commands.Signature>())
|
||||
{
|
||||
using SqlConnection conn = new(_cnnStr);
|
||||
conn.Open();
|
||||
|
||||
@@ -41,14 +41,14 @@ public class ReadOnlyController : ControllerBase
|
||||
[Obsolete("Use MediatR")]
|
||||
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
|
||||
{
|
||||
var authReceiverMail = User.ReceiverMail();
|
||||
var authReceiverMail = User.GetReceiverMailOfReceiver();
|
||||
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));
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var envelopeId = User.EnvelopeId();
|
||||
var envelopeId = User.GetEnvelopeIdOfReceiver();
|
||||
|
||||
createDto.AddedWho = authReceiverMail;
|
||||
createDto.EnvelopeId = envelopeId;
|
||||
|
||||
407
EnvelopeGenerator.API/Controllers/ReceiverAuthController.cs
Normal file
407
EnvelopeGenerator.API/Controllers/ReceiverAuthController.cs
Normal file
@@ -0,0 +1,407 @@
|
||||
using EnvelopeGenerator.API.Models;
|
||||
using EnvelopeGenerator.Application.Common.Extensions;
|
||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using EnvelopeGenerator.API.Extensions;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using OtpNet;
|
||||
|
||||
namespace EnvelopeGenerator.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// REST-API für den Empfänger-Authentifizierungs-Flow.
|
||||
///
|
||||
/// Entspricht der Logik in EnvelopeGenerator.Web.Controllers.EnvelopeController
|
||||
/// (Main + LogInEnvelope), aber gibt JSON statt Views zurück.
|
||||
///
|
||||
/// Der Blazor-Client (ReceiverUI) ruft diese Endpunkte auf.
|
||||
///
|
||||
/// FLOW:
|
||||
/// 1. Client ruft GET /api/receiverauth/{key}/status → Prüft Status
|
||||
/// 2. Client ruft POST /api/receiverauth/{key}/access-code → Sendet AccessCode
|
||||
/// 3. Client ruft POST /api/receiverauth/{key}/tfa → Sendet TFA-Code
|
||||
///
|
||||
/// Nach erfolgreicher Authentifizierung wird ein Cookie gesetzt (SignInEnvelopeAsync).
|
||||
/// Danach kann der Client die Dokument-Daten über die bestehenden Envelope-Endpunkte laden.
|
||||
/// </summary>
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class ReceiverAuthController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<ReceiverAuthController> _logger;
|
||||
private readonly IMediator _mediator;
|
||||
private readonly IEnvelopeReceiverService _envRcvService;
|
||||
private readonly IEnvelopeHistoryService _historyService;
|
||||
private readonly IAuthenticator _authenticator;
|
||||
private readonly IReceiverService _rcvService;
|
||||
private readonly IEnvelopeSmsHandler _envSmsHandler;
|
||||
|
||||
public ReceiverAuthController(
|
||||
ILogger<ReceiverAuthController> logger,
|
||||
IMediator mediator,
|
||||
IEnvelopeReceiverService envRcvService,
|
||||
IEnvelopeHistoryService historyService,
|
||||
IAuthenticator authenticator,
|
||||
IReceiverService rcvService,
|
||||
IEnvelopeSmsHandler envSmsHandler)
|
||||
{
|
||||
_logger = logger;
|
||||
_mediator = mediator;
|
||||
_envRcvService = envRcvService;
|
||||
_historyService = historyService;
|
||||
_authenticator = authenticator;
|
||||
_rcvService = rcvService;
|
||||
_envSmsHandler = envSmsHandler;
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// ENDPUNKT 1: STATUS PRÜFEN
|
||||
// Entspricht: Web.EnvelopeController.Main()
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
/// <summary>
|
||||
/// Prüft den aktuellen Status eines Umschlags für den Empfänger.
|
||||
/// Entscheidet ob: NotFound, Rejected, Signed, AccessCode nötig, oder direkt anzeigen.
|
||||
/// </summary>
|
||||
/// <param name="key">Der EnvelopeReceiver-Key aus der URL (Base64-kodiert)</param>
|
||||
/// <param name="cancel">Cancellation-Token</param>
|
||||
/// <returns>ReceiverAuthResponse mit dem aktuellen Status</returns>
|
||||
[HttpGet("{key}/status")]
|
||||
public async Task<IActionResult> GetStatus([FromRoute] string key, CancellationToken cancel)
|
||||
{
|
||||
try
|
||||
{
|
||||
// ── Key dekodieren ──
|
||||
if (!key.TryDecode(out var decoded))
|
||||
return NotFound(new ReceiverAuthResponse { Status = "not_found" });
|
||||
|
||||
// ── ReadOnly-Links ──
|
||||
if (decoded.GetEncodeType() == EncodeType.EnvelopeReceiverReadOnly)
|
||||
{
|
||||
return Ok(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "show_document",
|
||||
ReadOnly = true
|
||||
});
|
||||
}
|
||||
|
||||
// ── EnvelopeReceiver laden (Light-Query: ohne Documents/Elements) ──
|
||||
var er = await _mediator.Send(
|
||||
new ReadEnvelopeReceiverLightQuery { Key = key }, cancel);
|
||||
if (er is null)
|
||||
return NotFound(new ReceiverAuthResponse { Status = "not_found" });
|
||||
|
||||
// ── Abgelehnt? ──
|
||||
var rejRcvrs = await _historyService.ReadRejectingReceivers(er.Envelope!.Id);
|
||||
if (rejRcvrs.Any())
|
||||
{
|
||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
return Ok(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "rejected",
|
||||
Title = er.Envelope.Title,
|
||||
SenderEmail = er.Envelope.User?.Email
|
||||
});
|
||||
}
|
||||
|
||||
// ── Bereits signiert? ──
|
||||
if (await _historyService.IsSigned(
|
||||
envelopeId: er.Envelope.Id,
|
||||
userReference: er.Receiver!.EmailAddress))
|
||||
{
|
||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
return Ok(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "already_signed",
|
||||
Title = er.Envelope.Title,
|
||||
SenderEmail = er.Envelope.User?.Email
|
||||
});
|
||||
}
|
||||
|
||||
// ── Kein AccessCode nötig? → Direkt SignIn ──
|
||||
if (!er.Envelope.UseAccessCode)
|
||||
{
|
||||
(string? uuid, string? signature) = decoded.ParseEnvelopeReceiverId();
|
||||
var erSecretRes = await _envRcvService.ReadWithSecretByUuidSignatureAsync(
|
||||
uuid: uuid!, signature: signature!);
|
||||
|
||||
if (erSecretRes.IsFailed)
|
||||
return NotFound(new ReceiverAuthResponse { Status = "not_found" });
|
||||
|
||||
await HttpContext.SignInEnvelopeAsync(erSecretRes.Data, Role.ReceiverFull);
|
||||
|
||||
return Ok(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "show_document",
|
||||
Title = er.Envelope.Title,
|
||||
Message = er.Envelope.Message,
|
||||
SenderEmail = er.Envelope.User?.Email,
|
||||
ReadOnly = er.Envelope.ReadOnly
|
||||
});
|
||||
}
|
||||
|
||||
// ── AccessCode nötig ──
|
||||
// HINWEIS: Die E-Mail mit dem AccessCode wird NICHT hier gesendet.
|
||||
// Das passiert bereits im Web-Projekt, wenn der Link generiert wird.
|
||||
// Der Blazor-Flow übernimmt erst NACH dem E-Mail-Versand.
|
||||
bool accessCodeAlreadyRequested = await _historyService.AccessCodeAlreadyRequested(
|
||||
envelopeId: er.Envelope.Id,
|
||||
userReference: er.Receiver.EmailAddress);
|
||||
|
||||
if (!accessCodeAlreadyRequested)
|
||||
{
|
||||
// AccessCode wurde noch nie angefordert — das bedeutet der Empfänger
|
||||
// kommt zum ersten Mal. Wir zeichnen es auf, aber die E-Mail
|
||||
// wurde bereits vom Web-Projekt gesendet.
|
||||
await _historyService.RecordAsync(
|
||||
er.EnvelopeId, er.Receiver.EmailAddress, EnvelopeStatus.AccessCodeRequested);
|
||||
}
|
||||
|
||||
// ── Prüfe ob der Nutzer bereits eingeloggt ist ──
|
||||
if (User.IsInRole(Role.ReceiverFull))
|
||||
{
|
||||
return Ok(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "show_document",
|
||||
Title = er.Envelope.Title,
|
||||
Message = er.Envelope.Message,
|
||||
SenderEmail = er.Envelope.User?.Email,
|
||||
ReadOnly = er.Envelope.ReadOnly
|
||||
});
|
||||
}
|
||||
|
||||
return Ok(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "requires_access_code",
|
||||
Title = er.Envelope.Title,
|
||||
SenderEmail = er.Envelope.User?.Email,
|
||||
TfaEnabled = er.Envelope.TFAEnabled,
|
||||
HasPhoneNumber = er.HasPhoneNumber
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error checking status for key {Key}", key);
|
||||
return StatusCode(500, new ReceiverAuthResponse
|
||||
{
|
||||
Status = "error",
|
||||
ErrorMessage = "Ein unerwarteter Fehler ist aufgetreten."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// ENDPUNKT 2: ACCESS-CODE PRÜFEN
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
/// <summary>
|
||||
/// Prüft den eingegebenen Zugangscode.
|
||||
/// Bei Erfolg: SignIn oder TFA-Weiterleitung.
|
||||
/// Bei Fehler: Fehlermeldung zurückgeben.
|
||||
/// </summary>
|
||||
[HttpPost("{key}/access-code")]
|
||||
public async Task<IActionResult> SubmitAccessCode(
|
||||
[FromRoute] string key,
|
||||
[FromBody] AccessCodeRequest request,
|
||||
CancellationToken cancel)
|
||||
{
|
||||
try
|
||||
{
|
||||
// ── Key dekodieren + Daten laden ──
|
||||
(string? uuid, string? signature) = key.DecodeEnvelopeReceiverId();
|
||||
if (uuid is null || signature is null)
|
||||
return NotFound(new ReceiverAuthResponse { Status = "not_found" });
|
||||
|
||||
var erSecretRes = await _envRcvService.ReadWithSecretByUuidSignatureAsync(
|
||||
uuid: uuid, signature: signature);
|
||||
|
||||
if (erSecretRes.IsFailed)
|
||||
return NotFound(new ReceiverAuthResponse { Status = "not_found" });
|
||||
|
||||
var erSecret = erSecretRes.Data;
|
||||
|
||||
// ── AccessCode prüfen ──
|
||||
if (erSecret.AccessCode != request.AccessCode)
|
||||
{
|
||||
await _historyService.RecordAsync(
|
||||
erSecret.EnvelopeId,
|
||||
erSecret.Receiver!.EmailAddress,
|
||||
EnvelopeStatus.AccessCodeIncorrect);
|
||||
|
||||
return Unauthorized(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "requires_access_code",
|
||||
Title = erSecret.Envelope!.Title,
|
||||
SenderEmail = erSecret.Envelope.User?.Email,
|
||||
TfaEnabled = erSecret.Envelope.TFAEnabled,
|
||||
HasPhoneNumber = erSecret.HasPhoneNumber,
|
||||
ErrorMessage = "Falscher Zugangscode."
|
||||
});
|
||||
}
|
||||
|
||||
// ── AccessCode korrekt ──
|
||||
await _historyService.RecordAsync(
|
||||
erSecret.EnvelopeId,
|
||||
erSecret.Receiver!.EmailAddress,
|
||||
EnvelopeStatus.AccessCodeCorrect);
|
||||
|
||||
// ── TFA erforderlich? ──
|
||||
if (erSecret.Envelope!.TFAEnabled)
|
||||
{
|
||||
var rcv = erSecret.Receiver;
|
||||
if (rcv.TotpSecretkey is null)
|
||||
{
|
||||
rcv.TotpSecretkey = _authenticator.GenerateTotpSecretKey();
|
||||
await _rcvService.UpdateAsync(rcv);
|
||||
}
|
||||
|
||||
await HttpContext.SignInEnvelopeAsync(erSecret, Role.ReceiverTFA);
|
||||
|
||||
if (request.PreferSms)
|
||||
{
|
||||
var (smsRes, expiration) = await _envSmsHandler.SendTotpAsync(erSecret);
|
||||
|
||||
return Ok(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "requires_tfa",
|
||||
TfaType = "sms",
|
||||
TfaExpiration = expiration,
|
||||
Title = erSecret.Envelope.Title,
|
||||
SenderEmail = erSecret.Envelope.User?.Email,
|
||||
HasPhoneNumber = erSecret.HasPhoneNumber
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return Ok(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "requires_tfa",
|
||||
TfaType = "authenticator",
|
||||
Title = erSecret.Envelope.Title,
|
||||
SenderEmail = erSecret.Envelope.User?.Email,
|
||||
HasPhoneNumber = erSecret.HasPhoneNumber
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Kein TFA → Direkt SignIn ──
|
||||
await HttpContext.SignInEnvelopeAsync(erSecret, Role.ReceiverFull);
|
||||
|
||||
return Ok(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "show_document",
|
||||
Title = erSecret.Envelope.Title,
|
||||
Message = erSecret.Envelope.Message,
|
||||
SenderEmail = erSecret.Envelope.User?.Email,
|
||||
ReadOnly = erSecret.Envelope.ReadOnly
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error submitting access code for key {Key}", key);
|
||||
return StatusCode(500, new ReceiverAuthResponse
|
||||
{
|
||||
Status = "error",
|
||||
ErrorMessage = "Ein unerwarteter Fehler ist aufgetreten."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// ENDPUNKT 3: TFA-CODE PRÜFEN
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
/// <summary>
|
||||
/// Prüft den TFA-Code (SMS oder Authenticator).
|
||||
/// Setzt voraus, dass der Nutzer bereits mit ReceiverTFA-Rolle eingeloggt ist.
|
||||
/// </summary>
|
||||
[HttpPost("{key}/tfa")]
|
||||
public async Task<IActionResult> SubmitTfaCode(
|
||||
[FromRoute] string key,
|
||||
[FromBody] TfaCodeRequest request,
|
||||
CancellationToken cancel)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!User.IsInRole(Role.ReceiverTFA))
|
||||
return Unauthorized(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "requires_access_code",
|
||||
ErrorMessage = "Bitte zuerst den Zugangscode eingeben."
|
||||
});
|
||||
|
||||
(string? uuid, string? signature) = key.DecodeEnvelopeReceiverId();
|
||||
if (uuid is null || signature is null)
|
||||
return NotFound(new ReceiverAuthResponse { Status = "not_found" });
|
||||
|
||||
var erSecretRes = await _envRcvService.ReadWithSecretByUuidSignatureAsync(
|
||||
uuid: uuid, signature: signature);
|
||||
|
||||
if (erSecretRes.IsFailed)
|
||||
return NotFound(new ReceiverAuthResponse { Status = "not_found" });
|
||||
|
||||
var erSecret = erSecretRes.Data;
|
||||
|
||||
if (erSecret.Receiver!.TotpSecretkey is null)
|
||||
{
|
||||
_logger.LogError("TotpSecretkey is null for receiver {Signature}", signature);
|
||||
return StatusCode(500, new ReceiverAuthResponse
|
||||
{
|
||||
Status = "error",
|
||||
ErrorMessage = "TFA-Konfiguration fehlt."
|
||||
});
|
||||
}
|
||||
|
||||
bool codeValid;
|
||||
|
||||
if (request.Type == "sms")
|
||||
{
|
||||
codeValid = _envSmsHandler.VerifyTotp(request.Code, erSecret.Receiver.TotpSecretkey);
|
||||
}
|
||||
else
|
||||
{
|
||||
codeValid = _authenticator.VerifyTotp(
|
||||
request.Code,
|
||||
erSecret.Receiver.TotpSecretkey,
|
||||
window: VerificationWindow.RfcSpecifiedNetworkDelay);
|
||||
}
|
||||
|
||||
if (!codeValid)
|
||||
{
|
||||
return Unauthorized(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "requires_tfa",
|
||||
TfaType = request.Type,
|
||||
Title = erSecret.Envelope!.Title,
|
||||
SenderEmail = erSecret.Envelope.User?.Email,
|
||||
HasPhoneNumber = erSecret.HasPhoneNumber,
|
||||
ErrorMessage = "Falscher Code."
|
||||
});
|
||||
}
|
||||
|
||||
await HttpContext.SignInEnvelopeAsync(erSecret, Role.ReceiverFull);
|
||||
|
||||
return Ok(new ReceiverAuthResponse
|
||||
{
|
||||
Status = "show_document",
|
||||
Title = erSecret.Envelope!.Title,
|
||||
Message = erSecret.Envelope.Message,
|
||||
SenderEmail = erSecret.Envelope.User?.Email,
|
||||
ReadOnly = erSecret.Envelope.ReadOnly
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error submitting TFA code for key {Key}", key);
|
||||
return StatusCode(500, new ReceiverAuthResponse
|
||||
{
|
||||
Status = "error",
|
||||
ErrorMessage = "Ein unerwarteter Fehler ist aufgetreten."
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
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,12 +16,6 @@ public sealed class AuthProxyDocumentFilter : IDocumentFilter
|
||||
/// <param name="swaggerDoc"></param>
|
||||
/// <param name="context"></param>
|
||||
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";
|
||||
|
||||
@@ -73,51 +67,4 @@ 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
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
17
EnvelopeGenerator.API/EnvelopeClaimTypes.cs
Normal file
17
EnvelopeGenerator.API/EnvelopeClaimTypes.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
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">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>net9.0</TargetFrameworks>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
@@ -10,9 +10,9 @@
|
||||
<Authors>Digital Data GmbH</Authors>
|
||||
<Company>Digital Data GmbH</Company>
|
||||
<Product>EnvelopeGenerator.GeneratorAPI</Product>
|
||||
<Version>1.4.0</Version>
|
||||
<FileVersion>1.4.0</FileVersion>
|
||||
<AssemblyVersion>1.4.0</AssemblyVersion>
|
||||
<Version>1.2.3</Version>
|
||||
<FileVersion>1.2.3</FileVersion>
|
||||
<AssemblyVersion>1.2.3</AssemblyVersion>
|
||||
<PackageOutputPath>Copyright © 2025 Digital Data GmbH. All rights reserved.</PackageOutputPath>
|
||||
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
@@ -30,17 +30,11 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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.Core.API" Version="2.2.1" />
|
||||
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="8.0.11" Condition="'$(TargetFramework)' == 'net8.0'" />
|
||||
<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.AspNetCore.OpenApi" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
|
||||
<PackageReference Include="NLog" Version="5.2.5" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.0" />
|
||||
@@ -77,7 +71,6 @@
|
||||
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
|
||||
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
|
||||
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using DigitalData.Auth.Claims;
|
||||
using Microsoft.IdentityModel.JsonWebTokens;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
|
||||
namespace EnvelopeGenerator.API.Extensions;
|
||||
|
||||
@@ -9,14 +11,7 @@ namespace EnvelopeGenerator.API.Extensions;
|
||||
/// </summary>
|
||||
public static class ReceiverClaimExtensions
|
||||
{
|
||||
/// <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)
|
||||
private static string GetRequiredClaimOfReceiver(this ClaimsPrincipal user, string claimType)
|
||||
{
|
||||
var value = user.FindFirstValue(claimType);
|
||||
if (value is not null)
|
||||
@@ -32,65 +27,75 @@ public static class ReceiverClaimExtensions
|
||||
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>
|
||||
/// Gets the authenticated envelope UUID from the claims.
|
||||
/// </summary>
|
||||
public static string EnvelopeUuid(this ClaimsPrincipal user)
|
||||
=> user.GetRequiredClaimValue(EnvelopeClaimNames.EnvelopeUuid);
|
||||
public static string GetEnvelopeUuidOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.NameIdentifier);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authenticated receiver signature from the claims.
|
||||
/// </summary>
|
||||
public static string ReceiverSignature(this ClaimsPrincipal user)
|
||||
=> user.GetRequiredClaimValue(EnvelopeClaimNames.ReceiverSignature);
|
||||
public static string GetReceiverSignatureOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Hash);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authenticated receiver display name from the claims.
|
||||
/// </summary>
|
||||
public static string GetReceiverNameOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authenticated receiver email address from the claims.
|
||||
/// </summary>
|
||||
public static string ReceiverMail(this ClaimsPrincipal user)
|
||||
=> user.GetRequiredClaimValue(JwtRegisteredClaimNames.Email);
|
||||
public static string GetReceiverMailOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Email);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authenticated envelope title from the claims.
|
||||
/// </summary>
|
||||
public static string GetEnvelopeTitleOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(EnvelopeClaimTypes.Title);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authenticated envelope identifier from the claims.
|
||||
/// </summary>
|
||||
public static int EnvelopeId(this ClaimsPrincipal user)
|
||||
public static int GetEnvelopeIdOfReceiver(this ClaimsPrincipal user)
|
||||
{
|
||||
var envIdStr = user.GetRequiredClaimValue(EnvelopeClaimNames.EnvelopeId);
|
||||
if (int.TryParse(envIdStr, out var envId))
|
||||
return envId;
|
||||
else
|
||||
throw new InvalidOperationException($"Claim '{EnvelopeClaimNames.EnvelopeId}' is not a valid integer.");
|
||||
var envIdStr = user.GetRequiredClaimOfReceiver(EnvelopeClaimTypes.Id);
|
||||
if (!int.TryParse(envIdStr, out var envId))
|
||||
{
|
||||
throw new InvalidOperationException($"Claim '{EnvelopeClaimTypes.Id}' is not a valid integer.");
|
||||
}
|
||||
|
||||
return envId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authenticated receiver identifier from the claims.
|
||||
/// Signs in an envelope receiver using cookie authentication and attaches envelope claims.
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
public static int ReceiverId(this ClaimsPrincipal user)
|
||||
/// <param name="context">The current HTTP context.</param>
|
||||
/// <param name="envelopeReceiver">Envelope receiver DTO to extract claims from.</param>
|
||||
/// <param name="receiverRole">Role to attach to the authentication ticket.</param>
|
||||
public static async Task SignInEnvelopeAsync(this HttpContext context, EnvelopeReceiverDto envelopeReceiver, string receiverRole)
|
||||
{
|
||||
var rcvIdStr = user.GetRequiredClaimValue(EnvelopeClaimNames.ReceiverId);
|
||||
if (int.TryParse(rcvIdStr, out var rcvId))
|
||||
return rcvId;
|
||||
else
|
||||
throw new InvalidOperationException($"Claim '{EnvelopeClaimNames.ReceiverId}' is not a valid integer.");
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new(ClaimTypes.NameIdentifier, envelopeReceiver.Envelope!.Uuid),
|
||||
new(ClaimTypes.Hash, envelopeReceiver.Receiver!.Signature),
|
||||
new(ClaimTypes.Name, envelopeReceiver.Name ?? string.Empty),
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
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);
|
||||
78
EnvelopeGenerator.API/Models/ReceiverAuthResponse.cs
Normal file
78
EnvelopeGenerator.API/Models/ReceiverAuthResponse.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
namespace EnvelopeGenerator.API.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Einheitliche Antwort des ReceiverAuthControllers.
|
||||
///
|
||||
/// WARUM ein einziges Response-Objekt für alle Endpunkte?
|
||||
/// - Der Client braucht nur ein Format zu verstehen
|
||||
/// - Der Status-String bestimmt, welche Felder relevant sind
|
||||
/// - Entspricht dem, was der Web-Controller bisher über ViewData verteilt hat
|
||||
///
|
||||
/// Status-Werte und was sie bedeuten:
|
||||
/// - "requires_access_code" → AccessCode-Eingabe zeigen
|
||||
/// - "requires_tfa" → TFA-Code-Eingabe zeigen (nach AccessCode)
|
||||
/// - "show_document" → Dokument laden und anzeigen
|
||||
/// - "already_signed" → Info-Seite "Bereits unterschrieben"
|
||||
/// - "rejected" → Info-Seite "Abgelehnt"
|
||||
/// - "not_found" → Fehler-Seite "Nicht gefunden"
|
||||
/// - "expired" → Fehler-Seite "Link abgelaufen"
|
||||
/// </summary>
|
||||
public class ReceiverAuthResponse
|
||||
{
|
||||
/// <summary>Aktueller Status des Empfänger-Flows</summary>
|
||||
public required string Status { get; init; }
|
||||
|
||||
/// <summary>Titel des Umschlags (z.B. "Vertragsdokument")</summary>
|
||||
public string? Title { get; init; }
|
||||
|
||||
/// <summary>Nachricht des Absenders</summary>
|
||||
public string? Message { get; init; }
|
||||
|
||||
/// <summary>E-Mail des Absenders (für Rückfragen-Hinweis)</summary>
|
||||
public string? SenderEmail { get; init; }
|
||||
|
||||
/// <summary>Name des Empfängers</summary>
|
||||
public string? ReceiverName { get; init; }
|
||||
|
||||
/// <summary>Ob TFA für diesen Umschlag aktiviert ist</summary>
|
||||
public bool TfaEnabled { get; init; }
|
||||
|
||||
/// <summary>Ob der Empfänger eine Telefonnummer hat (für SMS-TFA)</summary>
|
||||
public bool HasPhoneNumber { get; init; }
|
||||
|
||||
/// <summary>Ob das Dokument nur gelesen werden soll (ReadAndConfirm)</summary>
|
||||
public bool ReadOnly { get; init; }
|
||||
|
||||
/// <summary>TFA-Typ: "sms" oder "authenticator" (wenn Status = "requires_tfa")</summary>
|
||||
public string? TfaType { get; init; }
|
||||
|
||||
/// <summary>Ablaufzeit des SMS-Codes (für Countdown-Timer)</summary>
|
||||
public DateTime? TfaExpiration { get; init; }
|
||||
|
||||
/// <summary>Fehlermeldung (z.B. "Falscher Zugangscode")</summary>
|
||||
public string? ErrorMessage { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request-Body für POST /api/receiverauth/{key}/access-code
|
||||
/// </summary>
|
||||
public class AccessCodeRequest
|
||||
{
|
||||
/// <summary>Der vom Empfänger eingegebene Zugangscode</summary>
|
||||
public required string AccessCode { get; init; }
|
||||
|
||||
/// <summary>Ob SMS statt Authenticator bevorzugt wird</summary>
|
||||
public bool PreferSms { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request-Body für POST /api/receiverauth/{key}/tfa
|
||||
/// </summary>
|
||||
public class TfaCodeRequest
|
||||
{
|
||||
/// <summary>Der eingegebene TFA-Code (6-stellig)</summary>
|
||||
public required string Code { get; init; }
|
||||
|
||||
/// <summary>"sms" oder "authenticator"</summary>
|
||||
public required string Type { get; init; }
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
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,11 +17,8 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using DigitalData.Core.Abstractions.Security.Extensions;
|
||||
using EnvelopeGenerator.API.Middleware;
|
||||
using EnvelopeGenerator.API.Options;
|
||||
using NLog.Web;
|
||||
using NLog;
|
||||
using DigitalData.Auth.Claims;
|
||||
using EnvelopeGenerator.API;
|
||||
|
||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||
logger.Info("Logging initialized!");
|
||||
@@ -113,11 +110,9 @@ try
|
||||
|
||||
options.DocumentFilter<EnvelopeGenerator.API.Documentation.AuthProxyDocumentFilter>();
|
||||
});
|
||||
#if NET9_0_OR_GREATER
|
||||
builder.Services.AddOpenApi();
|
||||
#endif
|
||||
|
||||
//Add EF Core dbcontext
|
||||
//AddEF Core dbcontext
|
||||
var useDbMigration = Environment.GetEnvironmentVariable("MIGRATION_TEST_MODE") == true.ToString() || config.GetValue<bool>("UseDbMigration");
|
||||
var cnnStrName = useDbMigration ? "DbMigrationTest" : "Default";
|
||||
var connStr = config.GetConnectionString(cnnStrName)
|
||||
@@ -136,7 +131,7 @@ try
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(AuthScheme.Sender, opt =>
|
||||
.AddJwtBearer(opt =>
|
||||
{
|
||||
opt.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
@@ -168,61 +163,6 @@ try
|
||||
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
|
||||
@@ -238,16 +178,14 @@ try
|
||||
});
|
||||
|
||||
builder.Services.AddAuthorizationBuilder()
|
||||
.AddPolicy(AuthPolicy.SenderOrReceiver, policy => policy.RequireRole(Role.Sender, Role.Receiver.Full))
|
||||
|
||||
.AddPolicy(AuthPolicy.Sender, policy => policy
|
||||
.RequireRole(Role.Sender)
|
||||
.AddAuthenticationSchemes(AuthScheme.Sender))
|
||||
.AddPolicy(AuthPolicy.Receiver, policy => policy
|
||||
.AddAuthenticationSchemes(AuthScheme.Receiver)
|
||||
.RequireAuthenticatedUser()
|
||||
.RequireRole(Role.Receiver.Full, "receiver"))
|
||||
.AddPolicy(AuthPolicy.ReceiverTFA, policy => policy.RequireRole(Role.Receiver.TFA));
|
||||
.AddPolicy(AuthPolicy.SenderOrReceiver, policy =>
|
||||
policy.RequireRole(Role.Sender, Role.Receiver.Full))
|
||||
.AddPolicy(AuthPolicy.Sender, policy =>
|
||||
policy.RequireRole(Role.Sender))
|
||||
.AddPolicy(AuthPolicy.Receiver, policy =>
|
||||
policy.RequireRole(Role.Receiver.Full))
|
||||
.AddPolicy(AuthPolicy.ReceiverTFA, policy =>
|
||||
policy.RequireRole(Role.Receiver.TFA));
|
||||
|
||||
// User manager
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
@@ -261,20 +199,6 @@ try
|
||||
// Localizer
|
||||
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
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
builder.Services
|
||||
@@ -300,9 +224,7 @@ try
|
||||
|
||||
app.UseMiddleware<ExceptionHandlingMiddleware>();
|
||||
|
||||
#if NET9_0_OR_GREATER
|
||||
app.MapOpenApi();
|
||||
#endif
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment() || (app.IsDevOrDiP() && config.GetValue<bool>("UseSwagger")))
|
||||
@@ -336,10 +258,8 @@ try
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
// Catch-all YARP proxy — only forward requests that are not swagger/scalar/openapi paths.
|
||||
app.MapReverseProxy();
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
<?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,21 +1,10 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"ReverseProxy": {
|
||||
"Clusters": {
|
||||
"receiver-ui": {
|
||||
"Destinations": {
|
||||
"primary": {
|
||||
"Address": "https://localhost:52936"
|
||||
}
|
||||
}
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"AuthClientParams": {
|
||||
"Url": "http://172.24.12.39:9090/auth-hub",
|
||||
"PublicKeys": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"UseSwagger": true,
|
||||
"UseDbMigration": false,
|
||||
"UseDbMigration": true,
|
||||
"DiPMode": true,
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
@@ -174,14 +174,6 @@
|
||||
"Receiver": [],
|
||||
"EmailTemplate": [ "TBSIG_EMAIL_TEMPLATE_AFT_UPD" ]
|
||||
},
|
||||
"Cache": {
|
||||
"SignatureCacheExpiration": null,
|
||||
"SqlServer": {
|
||||
"ConnectionString": null,
|
||||
"SchemaName": "dbo",
|
||||
"TableName": "TBDD_CACHE"
|
||||
}
|
||||
},
|
||||
"MainPageTitle": null,
|
||||
"AnnotationParams": {
|
||||
"Background": {
|
||||
|
||||
@@ -1,177 +1,18 @@
|
||||
{
|
||||
"ReverseProxy": {
|
||||
"Routes": {
|
||||
"receiver-ui-root": {
|
||||
"ClusterId": "receiver-ui",
|
||||
"Order": 300,
|
||||
"Match": {
|
||||
"Path": "/",
|
||||
"Methods": [ "GET", "HEAD" ]
|
||||
},
|
||||
"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" ]
|
||||
"ReverseProxy": {
|
||||
"Routes": {
|
||||
"auth-login": {
|
||||
"ClusterId": "auth-hub",
|
||||
"Match": {
|
||||
"Path": "/api/auth",
|
||||
"Methods": [ "POST" ]
|
||||
},
|
||||
"Transforms": [
|
||||
{ "PathSet": "/api/auth/sign-flow" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"receiver-ui": {
|
||||
"Destinations": {
|
||||
"primary": {
|
||||
"Address": "https://localhost:52936"
|
||||
}
|
||||
}
|
||||
},
|
||||
"auth-hub": {
|
||||
"Destinations": {
|
||||
"primary": {
|
||||
@@ -182,4 +23,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,77 +8,42 @@ public record AnnotationCreateDto
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public long Id { get; set; }
|
||||
public int ElementId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("Not required for DevExpress")]
|
||||
public int ElementId { get; init; } = -1;
|
||||
public string Name { get; init; } = null!;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("Not required for DevExpress")]
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string Value { get; init; } = null!;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("Not required for DevExpress")]
|
||||
public string Value { get; init; } = string.Empty;
|
||||
public string Type { get; init; } = null!;
|
||||
|
||||
/// <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; }
|
||||
|
||||
/// <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>
|
||||
public double? Y { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("Not required for DevExpress")]
|
||||
public double? Width { get; init; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("Not required for DevExpress")]
|
||||
public double? Height { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Added to eliminate the need for SignatureDto in DevExpress
|
||||
/// </summary>
|
||||
public int? Page { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -31,5 +31,5 @@ public class DocumentDto
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of elements associated with the document for receiver interactions, if any.
|
||||
/// </summary>
|
||||
public IEnumerable<DocReceiverElementDto>? Elements { get; set; }
|
||||
public IEnumerable<SignatureDto>? Elements { get; set; }
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
|
||||
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.Entities;
|
||||
using EnvelopeGenerator.Domain.Interfaces;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||
|
||||
@@ -14,7 +10,7 @@ namespace EnvelopeGenerator.Application.Common.Dto;
|
||||
///
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public record EnvelopeDto : IEnvelope
|
||||
public record EnvelopeDto
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
@@ -78,12 +74,10 @@ public record EnvelopeDto : IEnvelope
|
||||
/// </summary>
|
||||
public int? EnvelopeTypeId { get; set; }
|
||||
|
||||
// TODO: use ReadAndConfirm property name
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("Use EnvelopeExtensions.IsReadAndConfirm extension metot instead.")]
|
||||
public bool ReadOnly => this.IsReadAndConfirm();
|
||||
public bool ReadOnly => EnvelopeTypeId == 2;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@@ -129,9 +123,4 @@ public record EnvelopeDto : IEnvelope
|
||||
///
|
||||
/// </summary>
|
||||
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
|
||||
CreateMap<Config, ConfigDto>();
|
||||
CreateMap<DocReceiverElement, DocReceiverElementDto>();
|
||||
CreateMap<Signature, SignatureDto>();
|
||||
CreateMap<DocumentStatus, DocumentStatusDto>();
|
||||
CreateMap<EmailTemplate, EmailTemplateDto>();
|
||||
CreateMap<Envelope, EnvelopeDto>().ForMember(dest => dest.Receivers, opt => opt.MapFrom(src => src.EnvelopeReceivers.Select(er => er.Receiver)));
|
||||
CreateMap<Envelope, EnvelopeDto>();
|
||||
CreateMap<Document, DocumentDto>();
|
||||
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));
|
||||
@@ -39,10 +39,7 @@ public class MappingProfile : Profile
|
||||
|
||||
// DTO to Entity mappings
|
||||
CreateMap<ConfigDto, Config>();
|
||||
CreateMap<DocReceiverElementDto, DocReceiverElement>();
|
||||
CreateMap<Signature, DocReceiverElement>()
|
||||
.ForMember(dest => dest.Ink, opt => opt.MapFrom(src => src.DataUrl.MapDataUrlToRequiredBytes()))
|
||||
.MapChangedWhen();
|
||||
CreateMap<SignatureDto, Signature>();
|
||||
CreateMap<DocumentStatusDto, DocumentStatus>();
|
||||
CreateMap<EmailTemplateDto, EmailTemplate>();
|
||||
CreateMap<EnvelopeDto, Envelope>();
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
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);
|
||||
@@ -1,65 +0,0 @@
|
||||
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,7 +1,5 @@
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using EnvelopeGenerator.Domain.Interfaces;
|
||||
using EnvelopeGenerator.Domain.Interfaces;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||
|
||||
@@ -9,7 +7,7 @@ namespace EnvelopeGenerator.Application.Common.Dto;
|
||||
/// Data Transfer Object representing a positioned element assigned to a document receiver.
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public class DocReceiverElementDto : IDocReceiverElement
|
||||
public class SignatureDto : ISignature
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier of the element.
|
||||
@@ -95,34 +93,4 @@ public class DocReceiverElementDto : IDocReceiverElement
|
||||
/// Gets or sets the left position of the element (in layout terms).
|
||||
/// </summary>
|
||||
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; }
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using AutoMapper;
|
||||
using EnvelopeGenerator.Domain.Interfaces.Auditing;
|
||||
using System;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
||||
|
||||
@@ -14,29 +13,12 @@ public static class AutoMapperAuditingExtensions
|
||||
/// </summary>
|
||||
public static IMappingExpression<TSource, TDestination> MapAddedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
||||
where TDestination : IHasAddedWhen
|
||||
=> expression.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.Now));
|
||||
=> expression.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.UtcNow));
|
||||
|
||||
/// <summary>
|
||||
/// Maps <see cref="IHasChangedWhen.ChangedWhen"/> to the current UTC time.
|
||||
/// </summary>
|
||||
public static IMappingExpression<TSource, TDestination> MapChangedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
||||
where TDestination : IHasChangedWhen
|
||||
=> 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);
|
||||
}
|
||||
=> expression.ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(_ => DateTime.UtcNow));
|
||||
}
|
||||
@@ -6,6 +6,6 @@ namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("Use IRepository")]
|
||||
public interface IDocumentReceiverElementRepository : ICRUDRepository<DocReceiverElement, int>
|
||||
public interface IDocumentReceiverElementRepository : ICRUDRepository<Signature, int>
|
||||
{
|
||||
}
|
||||
@@ -8,6 +8,6 @@ namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("Use MediatR")]
|
||||
public interface IDocumentReceiverElementService : IBasicCRUDService<DocReceiverElementDto, DocReceiverElement, int>
|
||||
public interface IDocumentReceiverElementService : IBasicCRUDService<SignatureDto, Signature, int>
|
||||
{
|
||||
}
|
||||
@@ -1,37 +1,88 @@
|
||||
using EnvelopeGenerator.Application.Common.Dto;
|
||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||
using EnvelopeGenerator.Application.Common.Extensions;
|
||||
using EnvelopeGenerator.Application.Common.Notifications.RemoveSignature;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using MediatR;
|
||||
using System.Dynamic;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned;
|
||||
|
||||
/// <summary>
|
||||
/// Notification raised when a document is signed by a receiver.
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
||||
public record DocSignedNotification : INotification, ISendMailNotification
|
||||
/// <param name="Instant"></param>
|
||||
/// <param name="Structured"></param>
|
||||
public record PsPdfKitAnnotation(ExpandoObject Instant, IEnumerable<AnnotationCreateDto> Structured);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="Original"></param>
|
||||
public record DocSignedNotification(EnvelopeReceiverDto Original) : EnvelopeReceiverDto(Original), INotification, ISendMailNotification
|
||||
{
|
||||
/// <summary>
|
||||
/// The envelope receiver information.
|
||||
///
|
||||
/// </summary>
|
||||
public required EnvelopeReceiverDto EnvelopeReceiver { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The PSPDFKit annotation data.
|
||||
/// </summary>
|
||||
[Obsolete("The PSPDFKit library is deprecated.")]
|
||||
public PsPdfKitAnnotation? PsPdfKitAnnotation { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the email template type.
|
||||
///
|
||||
/// </summary>
|
||||
public EmailTemplateType TemplateType => EmailTemplateType.DocumentSigned;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the email address of the receiver.
|
||||
///
|
||||
/// </summary>
|
||||
public string EmailAddress => EnvelopeReceiver.Receiver?.EmailAddress
|
||||
public string EmailAddress => Receiver?.EmailAddress
|
||||
?? throw new InvalidOperationException($"Receiver is null." +
|
||||
$"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,6 +1,5 @@
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using EnvelopeGenerator.Application.Common.Dto;
|
||||
using MediatR;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
||||
@@ -8,7 +7,6 @@ namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("The PSPDFKit library is deprecated.")]
|
||||
public class AnnotationHandler : INotificationHandler<DocSignedNotification>
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using EnvelopeGenerator.Application.DocStatus.Commands;
|
||||
using EnvelopeGenerator.Application.Common.Dto;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using MediatR;
|
||||
using System.Text.Json;
|
||||
@@ -9,7 +8,6 @@ namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
||||
public class DocStatusHandler : INotificationHandler<DocSignedNotification>
|
||||
{
|
||||
private const string BlankAnnotationJson = "{}";
|
||||
@@ -31,13 +29,15 @@ public class DocStatusHandler : INotificationHandler<DocSignedNotification>
|
||||
/// <param name="notification"></param>
|
||||
/// <param name="cancel"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
||||
public Task Handle(DocSignedNotification notification, CancellationToken cancel) => _sender.Send(new CreateDocStatusCommand()
|
||||
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
|
||||
{
|
||||
EnvelopeId = notification.EnvelopeReceiver.EnvelopeId,
|
||||
ReceiverId = notification.EnvelopeReceiver.ReceiverId,
|
||||
Value = notification.PsPdfKitAnnotation is PsPdfKitAnnotation annot
|
||||
? JsonSerializer.Serialize(annot.Instant, Format.Json.ForAnnotations)
|
||||
await _sender.Send(new SaveDocStatusCommand()
|
||||
{
|
||||
Envelope = new() { Id = notification.EnvelopeId },
|
||||
Receiver = new() { Id = notification.ReceiverId},
|
||||
Value = notification.PsPdfKitAnnotation is PsPdfKitAnnotation annot
|
||||
? JsonSerializer.Serialize(annot.Instant, Format.Json.ForAnnotations)
|
||||
: BlankAnnotationJson
|
||||
}, cancel);
|
||||
}, cancel);
|
||||
}
|
||||
}
|
||||
@@ -29,13 +29,13 @@ public class HistoryHandler : INotificationHandler<DocSignedNotification>
|
||||
/// <returns></returns>
|
||||
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
|
||||
{
|
||||
if (notification.EnvelopeReceiver.Receiver is null)
|
||||
if (notification.Receiver is null)
|
||||
throw new InvalidOperationException($"Receiver information is missing in the notification. DocSignedNotification:\n {notification.ToJson(Format.Json.ForDiagnostics)}");
|
||||
|
||||
await _sender.Send(new CreateHistoryCommand()
|
||||
{
|
||||
EnvelopeId = notification.EnvelopeReceiver.EnvelopeId,
|
||||
UserReference = notification.EnvelopeReceiver.Receiver.EmailAddress,
|
||||
EnvelopeId = notification.EnvelopeId,
|
||||
UserReference = notification.Receiver.EmailAddress,
|
||||
Status = EnvelopeStatus.DocumentSigned,
|
||||
}, cancel);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
||||
using EnvelopeGenerator.Application.Common.Configurations;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using Microsoft.Extensions.Options;
|
||||
using EnvelopeGenerator.Domain.Interfaces;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
||||
|
||||
@@ -31,7 +30,7 @@ public class SendSignedMailHandler : SendMailHandler<DocSignedNotification>
|
||||
protected override void ConfigureEmailOut(DocSignedNotification notification, EmailOut emailOut)
|
||||
{
|
||||
emailOut.ReferenceString = notification.EmailAddress;
|
||||
emailOut.ReferenceId = notification.EnvelopeReceiver.ReceiverId;
|
||||
emailOut.ReferenceId = notification.ReceiverId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -42,29 +41,10 @@ public class SendSignedMailHandler : SendMailHandler<DocSignedNotification>
|
||||
{
|
||||
var placeHolders = new Dictionary<string, string>()
|
||||
{
|
||||
{ "[NAME_RECEIVER]", notification.EnvelopeReceiver.Name ?? string.Empty },
|
||||
{ "[DOCUMENT_TITLE]", notification.EnvelopeReceiver.Envelope?.Title ?? string.Empty },
|
||||
{ "[NAME_RECEIVER]", notification.Name ?? string.Empty },
|
||||
{ "[DOCUMENT_TITLE]", notification.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;
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ public abstract class SendMailHandler<TNotification> : INotificationHandler<TNot
|
||||
EmailAddress = notification.EmailAddress,
|
||||
EmailBody = temp.Body,
|
||||
EmailSubj = temp.Subject,
|
||||
AddedWhen = DateTime.Now,
|
||||
AddedWhen = DateTime.UtcNow,
|
||||
AddedWho = DispatcherParams.AddedWho,
|
||||
SendingProfile = DispatcherParams.SendingProfile,
|
||||
ReminderTypeId = DispatcherParams.ReminderTypeId,
|
||||
|
||||
@@ -7,9 +7,6 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using QRCoder;
|
||||
using System.Reflection;
|
||||
using MediatR;
|
||||
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
||||
using EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
||||
|
||||
namespace EnvelopeGenerator.Application;
|
||||
|
||||
@@ -59,22 +56,6 @@ public static class DependencyInjection
|
||||
services.AddMediatR(cfg =>
|
||||
{
|
||||
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;
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
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,
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
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,22 +8,12 @@ namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public record CreateDocStatusCommand : IRequest<DocumentStatus>
|
||||
public record CreateDocStatusCommand : ModifyDocStatusCommandBase, IRequest<DocumentStatus>
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets timestamp when this record was added. Returns the StatusChangedWhen value.
|
||||
/// </summary>
|
||||
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; }
|
||||
public DateTime AddedWhen => StatusChangedWhen;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
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,41 +1,14 @@
|
||||
using EnvelopeGenerator.Application.Common.Commands;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using System.Linq.Expressions;
|
||||
using EnvelopeGenerator.Domain;
|
||||
|
||||
namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="Value"></param>
|
||||
public record DocStatusUpdateDto(string? Value);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public record UpdateDocStatusCommand : UpdateCommand<DocStatusUpdateDto, DocumentStatus>
|
||||
public record UpdateDocStatusCommand : ModifyDocStatusCommandBase
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// Gets timestamp when this record was added. Returns the StatusChangedWhen value.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
public DateTime? ChangedWhen => StatusChangedWhen;
|
||||
}
|
||||
@@ -18,16 +18,11 @@ public class MappingProfile : Profile
|
||||
CreateMap<CreateDocStatusCommand, DocumentStatus>()
|
||||
.ForMember(dest => dest.Envelope, 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();
|
||||
|
||||
CreateMap<UpdateDocStatusCommand, DocumentStatus>()
|
||||
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.StatusChangedWhen, opt => opt.MapFrom(src => DateTime.Now))
|
||||
.MapChangedWhen();
|
||||
}
|
||||
}
|
||||
@@ -53,17 +53,14 @@ public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, Docum
|
||||
/// </exception>
|
||||
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)
|
||||
{
|
||||
var doc = await docQuery.Where(d => d.Id == query.Id).FirstOrDefaultAsync(cancel);
|
||||
|
||||
var doc = await _repo.Query.Where(d => d.Id == query.Id).FirstOrDefaultAsync(cancel);
|
||||
return _mapper.Map<DocumentDto>(doc);
|
||||
}
|
||||
else if (query.EnvelopeId is not null)
|
||||
{
|
||||
var doc = await docQuery.Where(d => d.EnvelopeId == query.EnvelopeId).FirstOrDefaultAsync(cancel);
|
||||
var doc = await _repo.Query.Where(d => d.EnvelopeId == query.EnvelopeId).FirstOrDefaultAsync(cancel);
|
||||
return _mapper.Map<DocumentDto>(doc);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
|
||||
<PackageReference Include="CommandDotNet">
|
||||
<Version>7.0.5</Version>
|
||||
</PackageReference>
|
||||
@@ -88,6 +88,7 @@
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
|
||||
<PackageReference Include="CommandDotNet">
|
||||
<Version>8.1.1</Version>
|
||||
</PackageReference>
|
||||
@@ -95,6 +96,7 @@
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
|
||||
<PackageReference Include="CommandDotNet">
|
||||
<Version>8.1.1</Version>
|
||||
</PackageReference>
|
||||
|
||||
@@ -29,7 +29,7 @@ public record CreateEnvelopeReceiverCommand : CreateEnvelopeCommand, IRequest<Cr
|
||||
/// <param name="X">X-Position</param>
|
||||
/// <param name="Y">Y-Position</param>
|
||||
/// <param name="Page">Seite, auf der sie sich befindet</param>
|
||||
public record DocReceiverElementCreateDto([Required] double X, [Required] double Y, [Required] int Page);
|
||||
public record Signature([Required] double X, [Required] double Y, [Required] int Page);
|
||||
|
||||
/// <summary>
|
||||
/// DTO für Empfänger, die erstellt oder abgerufen werden sollen.
|
||||
@@ -41,7 +41,7 @@ public class ReceiverGetOrCreateCommand
|
||||
/// Unterschriften auf Dokumenten.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public List<DocReceiverElementCreateDto> DocReceiverElements { get; init; } = new();
|
||||
public List<Signature> Signatures { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Der Name, mit dem der Empfänger angesprochen werden soll.
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using AutoMapper;
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||
using EnvelopeGenerator.Application.Common.Extensions;
|
||||
using EnvelopeGenerator.Application.Common.Query;
|
||||
using EnvelopeGenerator.Application.Envelopes.Queries;
|
||||
using EnvelopeGenerator.Application.Receivers.Queries;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// Leichte Variante von <see cref="ReadEnvelopeReceiverQuery"/>:
|
||||
/// Lädt NUR Envelope (mit Histories + User) und Receiver.
|
||||
/// Lädt NICHT Documents/Elements.
|
||||
///
|
||||
/// Verwendung: Status-Prüfungen wo keine Dokument-Daten benötigt werden
|
||||
/// (z.B. ReceiverAuthController.GetStatus).
|
||||
/// </summary>
|
||||
public record ReadEnvelopeReceiverLightQuery
|
||||
: EnvelopeReceiverQueryBase<ReadEnvelopeQuery, ReadReceiverQuery>, IRequest<EnvelopeReceiverDto?>;
|
||||
|
||||
/// <summary>
|
||||
/// Handler für <see cref="ReadEnvelopeReceiverLightQuery"/>.
|
||||
/// </summary>
|
||||
public class ReadEnvelopeReceiverLightQueryHandler
|
||||
: IRequestHandler<ReadEnvelopeReceiverLightQuery, EnvelopeReceiverDto?>
|
||||
{
|
||||
private readonly IRepository<EnvelopeReceiver> _repo;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
/// <summary>
|
||||
/// Konstruktor.
|
||||
/// </summary>
|
||||
/// <param name="repo">EnvelopeReceiver-Repository</param>
|
||||
/// <param name="mapper">AutoMapper</param>
|
||||
public ReadEnvelopeReceiverLightQueryHandler(IRepository<EnvelopeReceiver> repo, IMapper mapper)
|
||||
{
|
||||
_repo = repo;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lädt einen EnvelopeReceiver ohne Documents/Elements.
|
||||
/// </summary>
|
||||
public async Task<EnvelopeReceiverDto?> Handle(ReadEnvelopeReceiverLightQuery request, CancellationToken cancel)
|
||||
{
|
||||
var envRcv = await _repo.Query
|
||||
.Where(request, notnull: false)
|
||||
.AsNoTracking()
|
||||
.Include(er => er.Envelope).ThenInclude(e => e!.Histories)
|
||||
.Include(er => er.Envelope).ThenInclude(e => e!.User)
|
||||
.Include(er => er.Receiver)
|
||||
.FirstOrDefaultAsync(cancel);
|
||||
|
||||
return envRcv is null ? null : _mapper.Map<EnvelopeReceiverDto>(envRcv);
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
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>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
public CreateEnvelopeCommand WithAuth(int userId)
|
||||
public bool Authorize(int userId)
|
||||
{
|
||||
UserId = userId;
|
||||
return this;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -22,7 +22,7 @@ public record ReadEnvelopeQuery : EnvelopeQueryBase, IRequest<IEnumerable<Envelo
|
||||
/// <summary>
|
||||
/// Optionaler Benutzerfilter; wenn gesetzt, werden nur Umschläge des Benutzers geladen.
|
||||
/// </summary>
|
||||
internal int? UserId { get; init; }
|
||||
public int? UserId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Setzt den Benutzerkontext für die Abfrage.
|
||||
@@ -133,7 +133,7 @@ public class ReadEnvelopeQueryHandler : IRequestHandler<ReadEnvelopeQuery, IEnum
|
||||
}
|
||||
|
||||
var envelopes = await query
|
||||
.Include(e => e.EnvelopeReceivers).ThenInclude(er => er.Receiver)
|
||||
.Include(e => e.Documents)
|
||||
.ToListAsync(cancel);
|
||||
|
||||
return _mapper.Map<IEnumerable<EnvelopeDto>>(envelopes);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using AutoMapper;
|
||||
using EnvelopeGenerator.Application.Common.Extensions;
|
||||
using EnvelopeGenerator.Application.Histories.Commands;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
|
||||
@@ -18,7 +17,6 @@ public class MappingProfile: Profile
|
||||
CreateMap<CreateHistoryCommand, History>()
|
||||
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Sender, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
|
||||
.MapAddedWhen();
|
||||
.ForMember(dest => dest.Receiver, opt => opt.Ignore());
|
||||
}
|
||||
}
|
||||
@@ -190,13 +190,6 @@ public static class Extensions
|
||||
/// <returns></returns>
|
||||
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>
|
||||
@@ -211,13 +204,6 @@ public static class Extensions
|
||||
/// <returns></returns>
|
||||
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>
|
||||
@@ -253,13 +239,6 @@ public static class Extensions
|
||||
/// <returns></returns>
|
||||
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>
|
||||
@@ -288,13 +267,6 @@ public static class Extensions
|
||||
/// <returns></returns>
|
||||
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>
|
||||
@@ -323,13 +295,6 @@ public static class Extensions
|
||||
/// <returns></returns>
|
||||
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>
|
||||
|
||||
@@ -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>
|
||||
</data>
|
||||
<data name="RejectionInfo2_ext" xml:space="preserve">
|
||||
<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>
|
||||
<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>
|
||||
</data>
|
||||
<data name="RejectionReasonQ" xml:space="preserve">
|
||||
<value>Bitte geben Sie einen Grund an:</value>
|
||||
@@ -447,34 +447,4 @@
|
||||
<data name="DocumentReset" xml:space="preserve">
|
||||
<value>Dokument wurde zurückgesetzt.</value>
|
||||
</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>
|
||||
@@ -447,34 +447,4 @@
|
||||
<data name="DocumentReset" xml:space="preserve">
|
||||
<value>Document has been reset.</value>
|
||||
</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>
|
||||
@@ -447,34 +447,4 @@
|
||||
<data name="DocumentReset" xml:space="preserve">
|
||||
<value>Le document a été réinitialisé.</value>
|
||||
</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>
|
||||
@@ -1,9 +1,9 @@
|
||||
using AutoMapper;
|
||||
using DigitalData.Core.Application;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using EnvelopeGenerator.Application.Common.Dto;
|
||||
using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Services;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace EnvelopeGenerator.Application.Services;
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("Use MediatR")]
|
||||
public class DocumentReceiverElementService : BasicCRUDService<IDocumentReceiverElementRepository, DocReceiverElementDto, DocReceiverElement, int>, IDocumentReceiverElementService
|
||||
public class DocumentReceiverElementService : BasicCRUDService<IDocumentReceiverElementRepository, SignatureDto, Signature, int>, IDocumentReceiverElementService
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
|
||||
@@ -12,7 +12,6 @@ using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
||||
using EnvelopeGenerator.Application.Common.Extensions;
|
||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||
using MediatR;
|
||||
using EnvelopeGenerator.Domain.Interfaces;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Services;
|
||||
|
||||
@@ -50,33 +49,14 @@ public class EnvelopeMailService : EmailOutService, IEnvelopeMailService
|
||||
_sender = sender;
|
||||
}
|
||||
|
||||
private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? er = null)
|
||||
private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? envelopeReceiverDto = 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)
|
||||
_placeholders["[DOCUMENT_ACCESS_CODE]"] = accessCode;
|
||||
|
||||
if (er?.Envelope is not null && er.Receiver is not null)
|
||||
if (envelopeReceiverDto?.Envelope is not null && envelopeReceiverDto.Receiver is not null)
|
||||
{
|
||||
var erId = (er.Envelope.Uuid, er.Receiver.Signature).ToEnvelopeKey();
|
||||
var erId = (envelopeReceiverDto.Envelope.Uuid, envelopeReceiverDto.Receiver.Signature).ToEnvelopeKey();
|
||||
var sigHost = await _configService.ReadDefaultSignatureHost();
|
||||
var linkToDoc = $"{sigHost}/EnvelopeKey/{erId}";
|
||||
_placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
|
||||
@@ -86,8 +66,7 @@ public class EnvelopeMailService : EmailOutService, IEnvelopeMailService
|
||||
return _placeholders;
|
||||
}
|
||||
|
||||
// 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)
|
||||
private async Task<Dictionary<string, string>> CreatePlaceholders(EnvelopeReceiverReadOnlyDto? readOnlyDto = null)
|
||||
{
|
||||
if (readOnlyDto?.Envelope is not null && readOnlyDto.Receiver is not null)
|
||||
{
|
||||
@@ -145,7 +124,7 @@ public class EnvelopeMailService : EmailOutService, IEnvelopeMailService
|
||||
return acResult.ToFail<int>().Notice(LogLevel.Error, "Therefore, access code cannot be sent");
|
||||
var accessCode = acResult.Data;
|
||||
|
||||
var placeholders = await CreatePlaceholders(accessCode: accessCode, er: dto);
|
||||
var placeholders = await CreatePlaceholders(accessCode: accessCode, envelopeReceiverDto: dto);
|
||||
|
||||
// Add optional place holders.
|
||||
if (optionalPlaceholders is not null)
|
||||
|
||||
@@ -487,6 +487,10 @@
|
||||
<Project>{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8}</Project>
|
||||
<Name>EnvelopeGenerator.CommonServices</Name>
|
||||
</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">
|
||||
<Project>{4f32a98d-e6f0-4a09-bd97-1cf26107e837}</Project>
|
||||
<Name>EnvelopeGenerator.Domain</Name>
|
||||
|
||||
12
EnvelopeGenerator.BBTests/frmFinalizePDF.Designer.vb
generated
12
EnvelopeGenerator.BBTests/frmFinalizePDF.Designer.vb
generated
@@ -27,7 +27,6 @@ Partial Class frmFinalizePDF
|
||||
Me.Label2 = New System.Windows.Forms.Label()
|
||||
Me.Button1 = 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.txtEnvelope = New System.Windows.Forms.TextBox()
|
||||
Me.SuspendLayout()
|
||||
@@ -76,15 +75,6 @@ Partial Class frmFinalizePDF
|
||||
Me.Button2.Text = "Merge Json"
|
||||
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
|
||||
'
|
||||
Me.txtResult.Location = New System.Drawing.Point(333, 12)
|
||||
@@ -107,7 +97,6 @@ Partial Class frmFinalizePDF
|
||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||
Me.ClientSize = New System.Drawing.Size(800, 450)
|
||||
Me.Controls.Add(Me.txtResult)
|
||||
Me.Controls.Add(Me.Button3)
|
||||
Me.Controls.Add(Me.Button2)
|
||||
Me.Controls.Add(Me.Button1)
|
||||
Me.Controls.Add(Me.Label2)
|
||||
@@ -127,6 +116,5 @@ Partial Class frmFinalizePDF
|
||||
Friend WithEvents Label2 As Label
|
||||
Friend WithEvents Button1 As Button
|
||||
Friend WithEvents Button2 As Button
|
||||
Friend WithEvents Button3 As Button
|
||||
Friend WithEvents txtResult As TextBox
|
||||
End Class
|
||||
|
||||
@@ -7,12 +7,11 @@ Imports GdPicture14
|
||||
Imports Newtonsoft.Json.Linq
|
||||
Imports EnvelopeGenerator.Infrastructure
|
||||
Imports Microsoft.EntityFrameworkCore
|
||||
Imports System.Text
|
||||
Imports DigitalData.Core.Abstractions
|
||||
Imports DigitalData.Core.Abstraction.Application.Repository
|
||||
Imports EnvelopeGenerator.Domain.Entities
|
||||
|
||||
Public Class frmFinalizePDF
|
||||
Private Const CONNECTIONSTRING = "Server=sDD-VMP04-SQL17\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=+bk8oAbbQP1AzoHtvZUbd+Mbok2f8Fl4miEx1qssJ5yEaEWoQJ9prg4L14fURpPnqi1WMNs9fE4=;" + "Encrypt=True;TrustServerCertificate=True;"
|
||||
Private Const CONNECTIONSTRING = "Server=sDD-VMP04-SQL17\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=+bk8oAbbQP1AzoHtvZUbd+Mbok2f8Fl4miEx1qssJ5yEaEWoQJ9prg4L14fURpPnqi1WMNs9fE4=;"
|
||||
|
||||
Private Database As MSSQLServer
|
||||
Private LogConfig As LogConfig
|
||||
@@ -94,118 +93,56 @@ Public Class frmFinalizePDF
|
||||
End Function
|
||||
|
||||
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
|
||||
Dim oTable = LoadAnnotationDataForEnvelope()
|
||||
Dim oJsonList = oTable.Rows.
|
||||
Cast(Of DataRow).
|
||||
Select(Function(r As DataRow) r.Item("VALUE").ToString()).
|
||||
ToList()
|
||||
|
||||
Dim envelopeId As Integer = CInt(txtEnvelope.Text)
|
||||
Dim oBuffer As Byte() = ReadEnvelope(envelopeId)
|
||||
Dim oNewBuffer = PDFBurner.BurnAnnotsToPDF(oBuffer, oJsonList, envelopeId)
|
||||
Dim desktopPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
|
||||
Dim oNewPath = Path.Combine(desktopPath, $"E{txtEnvelope.Text}R{txtReceiver.Text}.burned.pdf")
|
||||
|
||||
File.WriteAllBytes(oNewPath, oNewBuffer)
|
||||
|
||||
Process.Start(oNewPath)
|
||||
End Sub
|
||||
|
||||
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
|
||||
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 oJsonList = oTable.Rows.
|
||||
Cast(Of DataRow).
|
||||
Select(Function(r As DataRow) r.Item("VALUE").ToString()).
|
||||
ToList()
|
||||
log.AppendLine($"Annotation JSON count: {oJsonList.Count}")
|
||||
|
||||
' 2. Load document bytes (same as Service)
|
||||
Dim envelopeId As Integer = CInt(txtEnvelope.Text)
|
||||
Dim oBuffer As Byte() = ReadEnvelope(envelopeId)
|
||||
log.AppendLine($"Document bytes: {oBuffer.Length}")
|
||||
|
||||
' 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()
|
||||
|
||||
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}")
|
||||
Dim oNewPath = Path.Combine(desktopPath, $"E{txtEnvelope.Text}R{txtReceiver.Text}.burned.pdf")
|
||||
|
||||
File.WriteAllBytes(oNewPath, oNewBuffer)
|
||||
|
||||
txtResult.Text = log.ToString()
|
||||
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
|
||||
|
||||
End Sub
|
||||
|
||||
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
|
||||
Try
|
||||
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()
|
||||
|
||||
|
||||
Catch ex As Exception
|
||||
txtResult.Text = $"ERROR: {ex.Message}{vbCrLf}{vbCrLf}{ex.ToString()}"
|
||||
MsgBox(ex.Message, MsgBoxStyle.Critical)
|
||||
End Try
|
||||
End Sub
|
||||
End Class
|
||||
@@ -15,13 +15,13 @@
|
||||
<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.Framework" version="3.2.0" targetFramework="net462" />
|
||||
<package id="EntityFramework" version="6.4.4" targetFramework="net462" />
|
||||
<package id="EntityFramework" version="6.5.1" 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="GdPicture" 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.Bcl.AsyncInterfaces" version="8.0.0" targetFramework="net462" />
|
||||
<package id="Microsoft.Bcl.AsyncInterfaces" version="9.0.0" targetFramework="net462" />
|
||||
<package id="Microsoft.Bcl.Cryptography" version="9.0.0" targetFramework="net462" />
|
||||
<package id="Microsoft.Bcl.HashCode" version="1.1.1" targetFramework="net462" />
|
||||
<package id="Microsoft.CSharp" version="4.7.0" targetFramework="net462" />
|
||||
@@ -62,10 +62,10 @@
|
||||
<package id="protobuf-net.Core" version="3.2.46" 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="System.Buffers" version="4.6.0" targetFramework="net462" />
|
||||
<package id="System.Buffers" version="4.6.1" targetFramework="net462" />
|
||||
<package id="System.ClientModel" version="1.8.0" targetFramework="net462" />
|
||||
<package id="System.CodeDom" version="8.0.0" targetFramework="net462" />
|
||||
<package id="System.Collections.Immutable" version="8.0.0" targetFramework="net462" />
|
||||
<package id="System.CodeDom" version="9.0.0" targetFramework="net462" />
|
||||
<package id="System.Collections.Immutable" version="9.0.0" targetFramework="net462" />
|
||||
<package id="System.ComponentModel.Annotations" version="4.7.0" targetFramework="net462" />
|
||||
<package id="System.Data.Common" version="4.3.0" targetFramework="net462" />
|
||||
<package id="System.Data.Odbc" version="6.0.1" targetFramework="net462" />
|
||||
@@ -74,23 +74,23 @@
|
||||
<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.IO.FileSystem.AccessControl" version="5.0.0" targetFramework="net462" />
|
||||
<package id="System.IO.Packaging" version="8.0.1" targetFramework="net462" />
|
||||
<package id="System.IO.Packaging" version="9.0.0" targetFramework="net462" />
|
||||
<package id="System.IO.Pipelines" version="9.0.0" targetFramework="net462" />
|
||||
<package id="System.Management" version="8.0.0" targetFramework="net462" />
|
||||
<package id="System.Memory" version="4.6.0" targetFramework="net462" />
|
||||
<package id="System.Management" version="9.0.0" targetFramework="net462" />
|
||||
<package id="System.Memory" version="4.6.3" targetFramework="net462" />
|
||||
<package id="System.Memory.Data" version="8.0.1" targetFramework="net462" />
|
||||
<package id="System.Numerics.Vectors" version="4.6.0" targetFramework="net462" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.0" targetFramework="net462" />
|
||||
<package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net462" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" 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.Cng" version="5.0.0" targetFramework="net462" />
|
||||
<package id="System.Security.Cryptography.Pkcs" version="8.0.1" targetFramework="net462" />
|
||||
<package id="System.Security.Cryptography.Pkcs" version="9.0.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.Principal.Windows" version="5.0.0" targetFramework="net462" />
|
||||
<package id="System.Text.Encodings.Web" version="8.0.0" targetFramework="net462" />
|
||||
<package id="System.Text.Json" version="8.0.6" targetFramework="net462" />
|
||||
<package id="System.Text.Encodings.Web" version="9.0.0" targetFramework="net462" />
|
||||
<package id="System.Text.Json" version="9.0.0" targetFramework="net462" />
|
||||
<package id="System.Text.RegularExpressions" version="4.3.1" targetFramework="net462" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net462" />
|
||||
<package id="System.ValueTuple" version="4.5.0" targetFramework="net462" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.6.0" targetFramework="net462" />
|
||||
<package id="System.ValueTuple" version="4.6.1" targetFramework="net462" />
|
||||
</packages>
|
||||
@@ -15,7 +15,6 @@ Imports DigitalData.Core.Abstraction.Application
|
||||
Imports EnvelopeGenerator.Infrastructure
|
||||
Imports Microsoft.EntityFrameworkCore
|
||||
Imports DigitalData.Core.Abstractions
|
||||
Imports EnvelopeGenerator.Domain.Interfaces
|
||||
|
||||
Namespace Jobs
|
||||
Public Class FinalizeDocumentJob
|
||||
@@ -231,29 +230,6 @@ Namespace Jobs
|
||||
|
||||
Return Task.FromResult(True)
|
||||
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)
|
||||
Dim SqlCom As SqlCommand
|
||||
Dim imageData As Byte()
|
||||
@@ -351,7 +327,7 @@ Namespace Jobs
|
||||
Logger.Warn($"No SendFinalEmailToCreator - oMailToCreator [{oMailToCreator}] <> [{FinalEmailType.No}] ")
|
||||
End If
|
||||
|
||||
If oMailToReceivers <> FinalEmailType.No And pEnvelope.IsReadAndSign() Then
|
||||
If oMailToReceivers <> FinalEmailType.No Then
|
||||
Logger.Debug("Sending emails to receivers..")
|
||||
SendFinalEmailToReceivers(pEnvelope) ', pAttachment
|
||||
Else
|
||||
@@ -442,18 +418,7 @@ Namespace Jobs
|
||||
End Try
|
||||
End If
|
||||
|
||||
#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)
|
||||
Return PDFBurner.BurnAnnotsToPDF(oInputDocumentBuffer, oAnnotations, pEnvelopeData.EnvelopeId)
|
||||
End Function
|
||||
|
||||
Private Function GetEnvelopeData(pEnvelopeId As Integer) As EnvelopeData
|
||||
|
||||
@@ -46,7 +46,7 @@ Namespace Jobs.FinalizeDocument
|
||||
Return pSourceBuffer
|
||||
End If
|
||||
|
||||
Dim sigRepo = scope.ServiceProvider.Repository(Of DocReceiverElement)()
|
||||
Dim sigRepo = scope.ServiceProvider.Repository(Of Signature)()
|
||||
Dim elements = sigRepo _
|
||||
.Where(Function(sig) sig.Document.EnvelopeId = envelopeId) _
|
||||
.Include(Function(sig) sig.Annotations) _
|
||||
@@ -58,7 +58,7 @@ Namespace Jobs.FinalizeDocument
|
||||
End Using
|
||||
End Function
|
||||
|
||||
Public Function BurnElementAnnotsToPDF(pSourceBuffer As Byte(), elements As List(Of DocReceiverElement)) As Byte()
|
||||
Public Function BurnElementAnnotsToPDF(pSourceBuffer As Byte(), elements As List(Of Signature)) As Byte()
|
||||
' Add background
|
||||
Using doc As Pdf(Of MemoryStream, MemoryStream) = Pdf.FromMemory(pSourceBuffer)
|
||||
'TODO: take the length from the largest y
|
||||
@@ -85,37 +85,16 @@ Namespace Jobs.FinalizeDocument
|
||||
|
||||
'Add annotations
|
||||
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 frame = elementAnnotations.FirstOrDefault(Function(a) a.Name = "frame")
|
||||
Dim frame = element.Annotations.FirstOrDefault(Function(a) a.Name = "frame")
|
||||
Dim frameY = element.Top - 0.5 - margin
|
||||
Dim frameYShift As Double = 0
|
||||
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
|
||||
Dim frameYShift = frame.Y - frameY * inchFactor
|
||||
Dim frameXShift = frame.X - frameX * inchFactor
|
||||
|
||||
For Each annot In element.Annotations
|
||||
Dim yOffsetofFF As Double = If(yOffsetsOfFF.TryGetValue(annot.Name, yOffsetofFF), yOffsetofFF, 0)
|
||||
Dim y = frameY + yOffsetofFF
|
||||
|
||||
If annot.Type = AnnotationType.FormField Then
|
||||
|
||||
@@ -56,7 +56,7 @@ Public Class PDFMerger
|
||||
|
||||
' Convert to PDF/A
|
||||
oMergedPDF.ConvertToPDFA(oFinalStream, PDFAConformanceLevel, ALLOW_VECTORIZATION, ALLOW_RASTERIZATION)
|
||||
oStatus = oMergedPDF.GetStat()
|
||||
oStatus = oDocumentPDF.GetStat()
|
||||
If oStatus <> GdPictureStatus.OK Then
|
||||
Throw New MergeDocumentException($"Document could not be converted to PDF/A: {oStatus}")
|
||||
End If
|
||||
|
||||
@@ -10,8 +10,8 @@ Public Class ElementModel
|
||||
MyBase.New(pState)
|
||||
End Sub
|
||||
|
||||
Private Function ToElement(pRow As DataRow) As DocReceiverElement
|
||||
Return New DocReceiverElement() With {
|
||||
Private Function ToElement(pRow As DataRow) As Signature
|
||||
Return New Signature() With {
|
||||
.Id = pRow.ItemEx("GUID", 0),
|
||||
.DocumentId = pRow.ItemEx("DOCUMENT_ID", 0),
|
||||
.ReceiverId = pRow.ItemEx("RECEIVER_ID", 0),
|
||||
@@ -24,7 +24,7 @@ Public Class ElementModel
|
||||
}
|
||||
End Function
|
||||
|
||||
Private Function ToElements(pTable As DataTable) As List(Of DocReceiverElement)
|
||||
Private Function ToElements(pTable As DataTable) As List(Of Signature)
|
||||
Return pTable?.Rows.Cast(Of DataRow).
|
||||
Select(AddressOf ToElement).
|
||||
ToList()
|
||||
@@ -80,7 +80,7 @@ Public Class ElementModel
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Public Function List(pDocumentId As Integer) As List(Of DocReceiverElement)
|
||||
Public Function List(pDocumentId As Integer) As List(Of Signature)
|
||||
Try
|
||||
Dim oSql = $"SELECT * FROM [dbo].[TBSIG_DOCUMENT_RECEIVER_ELEMENT] WHERE DOCUMENT_ID = {pDocumentId} ORDER BY PAGE ASC"
|
||||
Dim oTable = Database.GetDatatable(oSql)
|
||||
@@ -93,7 +93,7 @@ Public Class ElementModel
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Public Function List(pDocumentId As Integer, pReceiverId As Integer) As List(Of DocReceiverElement)
|
||||
Public Function List(pDocumentId As Integer, pReceiverId As Integer) As List(Of Signature)
|
||||
Try
|
||||
Dim oReceiverConstraint = ""
|
||||
If pReceiverId > 0 Then
|
||||
@@ -111,7 +111,7 @@ Public Class ElementModel
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Public Function Insert(pElement As DocReceiverElement) As Boolean
|
||||
Public Function Insert(pElement As Signature) As Boolean
|
||||
Try
|
||||
Dim oSql = "INSERT INTO [dbo].[TBSIG_DOCUMENT_RECEIVER_ELEMENT]
|
||||
([DOCUMENT_ID]
|
||||
@@ -161,7 +161,7 @@ Public Class ElementModel
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Public Function Update(pElement As DocReceiverElement) As Boolean
|
||||
Public Function Update(pElement As Signature) As Boolean
|
||||
Try
|
||||
Dim oSql = "UPDATE [dbo].[TBSIG_DOCUMENT_RECEIVER_ELEMENT]
|
||||
SET [POSITION_X] = @POSITION_X
|
||||
@@ -185,7 +185,7 @@ Public Class ElementModel
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Private Function GetElementId(pElement As DocReceiverElement) As Integer
|
||||
Private Function GetElementId(pElement As Signature) As Integer
|
||||
Try
|
||||
Return Database.GetScalarValue($"SELECT MAX(GUID) FROM TBSIG_DOCUMENT_RECEIVER_ELEMENT
|
||||
WHERE DOCUMENT_ID = {pElement.DocumentId} AND RECEIVER_ID = {pElement.ReceiverId}")
|
||||
@@ -196,7 +196,7 @@ Public Class ElementModel
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Public Function DeleteElement(pElement As DocReceiverElement) As Boolean
|
||||
Public Function DeleteElement(pElement As Signature) As Boolean
|
||||
Try
|
||||
Dim oSql = $"DELETE FROM TBSIG_DOCUMENT_RECEIVER_ELEMENT WHERE GUID = {pElement.Id}"
|
||||
Return Database.ExecuteNonQuery(oSql)
|
||||
|
||||
@@ -270,26 +270,12 @@ Public Class ReceiverModel
|
||||
Private Function GetSignedDate(pEmailAddress As String, pEnvelopeId As Integer) As Date
|
||||
Try
|
||||
Dim oStatusInt As Integer = EnvelopeStatus.DocumentSigned
|
||||
Dim value = Database.GetScalarValue($"SELECT ACTION_DATE FROM [DD_ECM].[dbo].[TBSIG_ENVELOPE_HISTORY] WHERE ENVELOPE_ID = {pEnvelopeId}
|
||||
Return Database.GetScalarValue($"SELECT ACTION_DATE FROM [DD_ECM].[dbo].[TBSIG_ENVELOPE_HISTORY] WHERE ENVELOPE_ID = {pEnvelopeId}
|
||||
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
|
||||
Logger.Error(ex)
|
||||
Return DateTime.MinValue
|
||||
Return Nothing
|
||||
End Try
|
||||
End Function
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'------------------------------------------------------------------------------
|
||||
' <auto-generated>
|
||||
' This code was generated by a tool.
|
||||
' Runtime Version:4.0.30319.42000
|
||||
' Dieser Code wurde von einem Tool generiert.
|
||||
' Laufzeitversion:4.0.30319.42000
|
||||
'
|
||||
' Changes to this file may cause incorrect behavior and will be lost if
|
||||
' the code is regenerated.
|
||||
' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
|
||||
' der Code erneut generiert wird.
|
||||
' </auto-generated>
|
||||
'------------------------------------------------------------------------------
|
||||
|
||||
@@ -15,12 +15,12 @@ Imports System
|
||||
|
||||
Namespace My.Resources
|
||||
|
||||
'This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
'class via a tool like ResGen or Visual Studio.
|
||||
'To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
'with the /str option, or rebuild your VS project.
|
||||
'Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
|
||||
'-Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
|
||||
'Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
|
||||
'mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
|
||||
'''<summary>
|
||||
''' A strongly-typed resource class, for looking up localized strings, etc.
|
||||
''' Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
|
||||
'''</summary>
|
||||
<Global.System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0"), _
|
||||
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
|
||||
@@ -37,7 +37,7 @@ Namespace My.Resources
|
||||
End Sub
|
||||
|
||||
'''<summary>
|
||||
''' Returns the cached ResourceManager instance used by this class.
|
||||
''' Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
|
||||
'''</summary>
|
||||
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
|
||||
Public Shared ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager
|
||||
@@ -51,8 +51,8 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Overrides the current thread's CurrentUICulture property for all
|
||||
''' resource lookups using this strongly typed resource class.
|
||||
''' Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
|
||||
''' Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
|
||||
'''</summary>
|
||||
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
|
||||
Public Shared Property Culture() As Global.System.Globalization.CultureInfo
|
||||
@@ -65,7 +65,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Zugriffscode korrekt eingegeben.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Zugriffscode korrekt eingegeben ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property AccessCodeCorrect() As String
|
||||
Get
|
||||
@@ -74,7 +74,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Zugriffscode falsch eingegeben.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Zugriffscode falsch eingegeben ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property AccessCodeIncorrect() As String
|
||||
Get
|
||||
@@ -83,7 +83,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Zugriffscode angefordert.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Zugriffscode angefordert ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property AccessCodeRequested() As String
|
||||
Get
|
||||
@@ -92,7 +92,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Fortgeschrittene Elektronische Signatur.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Fortgeschrittene Elektronische Signatur ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property AdvancedElectronicSignature() As String
|
||||
Get
|
||||
@@ -101,7 +101,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Abgeschlossen.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Abgeschlossen ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property Completed() As String
|
||||
Get
|
||||
@@ -110,16 +110,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 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.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Vollständig Signiert ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property CompletelySigned() As String
|
||||
Get
|
||||
@@ -128,25 +119,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 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.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Vertrag ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property Contract() As String
|
||||
Get
|
||||
@@ -155,7 +128,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Erstellt.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Erstellt ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property Created() As String
|
||||
Get
|
||||
@@ -164,16 +137,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 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.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Dokument Rotation geändert ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property DocumentMod_Rotation() As String
|
||||
Get
|
||||
@@ -182,7 +146,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Dokument geöffnet.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Dokument geöffnet ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property DocumentOpened() As String
|
||||
Get
|
||||
@@ -191,7 +155,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Unterzeichnung abgelehnt.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Unterzeichnung abgelehnt ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property DocumentRejected() As String
|
||||
Get
|
||||
@@ -200,16 +164,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 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.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Dokument unterzeichnet ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property DocumentSigned() As String
|
||||
Get
|
||||
@@ -218,7 +173,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Entwurf.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Entwurf ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property Draft() As String
|
||||
Get
|
||||
@@ -227,7 +182,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Archiviert.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Archiviert ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property EnvelopeArchived() As String
|
||||
Get
|
||||
@@ -236,16 +191,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 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.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Vollständig signiert ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property EnvelopeCompletelySigned() As String
|
||||
Get
|
||||
@@ -254,7 +200,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Umschlag Erstellt.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Umschlag Erstellt ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property EnvelopeCreated() As String
|
||||
Get
|
||||
@@ -263,7 +209,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Umschlag Gelöscht.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Umschlag Gelöscht ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property EnvelopeDeleted() As String
|
||||
Get
|
||||
@@ -272,16 +218,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 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.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Teil-Signiert ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property EnvelopePartlySigned() As String
|
||||
Get
|
||||
@@ -290,7 +227,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Umschlag in Queue.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Umschlag in Queue ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property EnvelopeQueued() As String
|
||||
Get
|
||||
@@ -299,7 +236,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Umschlag abgelehnt.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Umschlag abgelehnt ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property EnvelopeRejected() As String
|
||||
Get
|
||||
@@ -308,7 +245,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Signierungszertifikat erstellt.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Signierungszertifikat erstellt ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property EnvelopeReportCreated() As String
|
||||
Get
|
||||
@@ -317,16 +254,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 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.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Gespeichert ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property EnvelopeSaved() As String
|
||||
Get
|
||||
@@ -335,7 +263,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Gesendet.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Gesendet ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property EnvelopeSent() As String
|
||||
Get
|
||||
@@ -344,7 +272,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Umschlag zurückgezogen.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Umschlag zurückgezogen ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property EnvelopeWithdrawn() As String
|
||||
Get
|
||||
@@ -353,7 +281,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Zugriffscode versendet.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Zugriffscode versendet ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property MessageAccessCodeSent() As String
|
||||
Get
|
||||
@@ -362,7 +290,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Abschlussemail versendet.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Abschlussemail versendet ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property MessageCompletionSent() As String
|
||||
Get
|
||||
@@ -371,7 +299,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Signaturbestätigung versendet.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Signaturbestätigung versendet ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property MessageConfirmationSent() As String
|
||||
Get
|
||||
@@ -380,16 +308,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 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.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Löschinformation versendet ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property MessageDeletionSent() As String
|
||||
Get
|
||||
@@ -398,7 +317,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Dokumentenlink versendet.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Dokumentenlink versendet ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property MessageInvitationSent() As String
|
||||
Get
|
||||
@@ -407,7 +326,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Nein.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Nein ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property No() As String
|
||||
Get
|
||||
@@ -416,16 +335,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 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.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Teil-Signiert ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property PartlySigned() As String
|
||||
Get
|
||||
@@ -434,7 +344,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Qualifizierte Signatur.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Qualifizierte Signatur ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property QualifiedSignature() As String
|
||||
Get
|
||||
@@ -443,7 +353,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Lesebestätigung.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Arbeitsanweisung ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property ReadAndSign() As String
|
||||
Get
|
||||
@@ -452,7 +362,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' 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!.
|
||||
''' 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.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property ResetTOTPUser() As String
|
||||
Get
|
||||
@@ -461,7 +371,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Gespeichert.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Gespeichert ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property Saved() As String
|
||||
Get
|
||||
@@ -470,7 +380,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Gesendet.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Gesendet ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property Sent() As String
|
||||
Get
|
||||
@@ -479,7 +389,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Signatur.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Signatur ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property Signature() As String
|
||||
Get
|
||||
@@ -488,7 +398,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Abschluss bestätigt.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Signatur bestätigt ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property SignatureConfirmed() As String
|
||||
Get
|
||||
@@ -497,7 +407,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Signiert.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Signiert ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property Signed() As String
|
||||
Get
|
||||
@@ -506,7 +416,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Erfolgreich! Dialog wird geschlossen..
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Erfolgreich! Dialog wird geschlossen. ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property Success_FormClose() As String
|
||||
Get
|
||||
@@ -515,7 +425,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Unsigniert.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Unsigniert ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property Unsigned() As String
|
||||
Get
|
||||
@@ -524,7 +434,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Ja.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Ja ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property Yes() As String
|
||||
Get
|
||||
@@ -533,7 +443,7 @@ Namespace My.Resources
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to Ja, mit Anhang.
|
||||
''' Sucht eine lokalisierte Zeichenfolge, die Ja, mit Anhang ähnelt.
|
||||
'''</summary>
|
||||
Public Shared ReadOnly Property YesWithAttachment() As String
|
||||
Get
|
||||
|
||||
@@ -132,27 +132,12 @@
|
||||
<data name="Completed" xml:space="preserve">
|
||||
<value>Completed</value>
|
||||
</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">
|
||||
<value>Contract</value>
|
||||
</data>
|
||||
<data name="Created" xml:space="preserve">
|
||||
<value>Created</value>
|
||||
</data>
|
||||
<data name="DocumentConfirmed" xml:space="preserve">
|
||||
<value>Document read and confirmed</value>
|
||||
</data>
|
||||
<data name="DocumentMod_Rotation" xml:space="preserve">
|
||||
<value>Document rotation adapted</value>
|
||||
</data>
|
||||
@@ -162,9 +147,6 @@
|
||||
<data name="DocumentRejected" xml:space="preserve">
|
||||
<value>Signing rejected</value>
|
||||
</data>
|
||||
<data name="DocumentRejectedRaC" xml:space="preserve">
|
||||
<value>Read confirmation rejected</value>
|
||||
</data>
|
||||
<data name="DocumentSigned" xml:space="preserve">
|
||||
<value>Document signed</value>
|
||||
</data>
|
||||
@@ -174,9 +156,6 @@
|
||||
<data name="EnvelopeArchived" xml:space="preserve">
|
||||
<value>Archived</value>
|
||||
</data>
|
||||
<data name="EnvelopeCompletelyConfirmed" xml:space="preserve">
|
||||
<value>Completely confirmed</value>
|
||||
</data>
|
||||
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
||||
<value>Completely signed</value>
|
||||
</data>
|
||||
@@ -186,11 +165,8 @@
|
||||
<data name="EnvelopeDeleted" xml:space="preserve">
|
||||
<value>Envelope Deleted</value>
|
||||
</data>
|
||||
<data name="EnvelopePartlyConfirmed" xml:space="preserve">
|
||||
<value>Partially confirmed</value>
|
||||
</data>
|
||||
<data name="EnvelopePartlySigned" xml:space="preserve">
|
||||
<value>Partially signed</value>
|
||||
<value>Partly signed</value>
|
||||
</data>
|
||||
<data name="EnvelopeQueued" xml:space="preserve">
|
||||
<value>Envelope Queued</value>
|
||||
@@ -201,9 +177,6 @@
|
||||
<data name="EnvelopeReportCreated" xml:space="preserve">
|
||||
<value>Signature certificate created</value>
|
||||
</data>
|
||||
<data name="EnvelopeReportCreatedRaC" xml:space="preserve">
|
||||
<value>Read Confirmation Certificate Created</value>
|
||||
</data>
|
||||
<data name="EnvelopeSaved" xml:space="preserve">
|
||||
<value>Saved</value>
|
||||
</data>
|
||||
@@ -222,9 +195,6 @@
|
||||
<data name="MessageConfirmationSent" xml:space="preserve">
|
||||
<value>Confirmation Sent</value>
|
||||
</data>
|
||||
<data name="MessageConfirmationSentRaC" xml:space="preserve">
|
||||
<value>Read Confirmation Sent</value>
|
||||
</data>
|
||||
<data name="MessageDeletionSent" xml:space="preserve">
|
||||
<value>Deletion Notice Sent</value>
|
||||
</data>
|
||||
@@ -234,12 +204,6 @@
|
||||
<data name="No" xml:space="preserve">
|
||||
<value>No</value>
|
||||
</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">
|
||||
<value>Qualified Signature</value>
|
||||
</data>
|
||||
@@ -259,7 +223,7 @@
|
||||
<value>Signature</value>
|
||||
</data>
|
||||
<data name="SignatureConfirmed" xml:space="preserve">
|
||||
<value>Finalization confirmed</value>
|
||||
<value>Signature confirmed</value>
|
||||
</data>
|
||||
<data name="Signed" xml:space="preserve">
|
||||
<value>Signed</value>
|
||||
|
||||
@@ -132,17 +132,8 @@
|
||||
<data name="Completed" xml:space="preserve">
|
||||
<value>Abgeschlossen</value>
|
||||
</data>
|
||||
<data name="CompletelyConfirmed" xml:space="preserve">
|
||||
<value>Vollständig bestätigt</value>
|
||||
</data>
|
||||
<data name="CompletelySigned" xml:space="preserve">
|
||||
<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>
|
||||
<value>Vollständig Signiert</value>
|
||||
</data>
|
||||
<data name="Contract" xml:space="preserve">
|
||||
<value>Vertrag</value>
|
||||
@@ -150,9 +141,6 @@
|
||||
<data name="Created" xml:space="preserve">
|
||||
<value>Erstellt</value>
|
||||
</data>
|
||||
<data name="DocumentConfirmed" xml:space="preserve">
|
||||
<value>Dokument gelesen und bestätigt</value>
|
||||
</data>
|
||||
<data name="DocumentMod_Rotation" xml:space="preserve">
|
||||
<value>Dokument Rotation geändert</value>
|
||||
</data>
|
||||
@@ -162,9 +150,6 @@
|
||||
<data name="DocumentRejected" xml:space="preserve">
|
||||
<value>Unterzeichnung abgelehnt</value>
|
||||
</data>
|
||||
<data name="DocumentRejectedRaC" xml:space="preserve">
|
||||
<value>Lesebestätigung abgelehnt</value>
|
||||
</data>
|
||||
<data name="DocumentSigned" xml:space="preserve">
|
||||
<value>Dokument unterzeichnet</value>
|
||||
</data>
|
||||
@@ -174,9 +159,6 @@
|
||||
<data name="EnvelopeArchived" xml:space="preserve">
|
||||
<value>Archiviert</value>
|
||||
</data>
|
||||
<data name="EnvelopeCompletelyConfirmed" xml:space="preserve">
|
||||
<value>Vollständig bestätigt</value>
|
||||
</data>
|
||||
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
||||
<value>Vollständig signiert</value>
|
||||
</data>
|
||||
@@ -186,9 +168,6 @@
|
||||
<data name="EnvelopeDeleted" xml:space="preserve">
|
||||
<value>Umschlag Gelöscht</value>
|
||||
</data>
|
||||
<data name="EnvelopePartlyConfirmed" xml:space="preserve">
|
||||
<value>Teil-Bestätigt</value>
|
||||
</data>
|
||||
<data name="EnvelopePartlySigned" xml:space="preserve">
|
||||
<value>Teil-Signiert</value>
|
||||
</data>
|
||||
@@ -201,9 +180,6 @@
|
||||
<data name="EnvelopeReportCreated" xml:space="preserve">
|
||||
<value>Signierungszertifikat erstellt</value>
|
||||
</data>
|
||||
<data name="EnvelopeReportCreatedRaC" xml:space="preserve">
|
||||
<value>Lesebestätigungszertifikat erstellt</value>
|
||||
</data>
|
||||
<data name="EnvelopeSaved" xml:space="preserve">
|
||||
<value>Gespeichert</value>
|
||||
</data>
|
||||
@@ -222,9 +198,6 @@
|
||||
<data name="MessageConfirmationSent" xml:space="preserve">
|
||||
<value>Signaturbestätigung versendet</value>
|
||||
</data>
|
||||
<data name="MessageConfirmationSentRaC" xml:space="preserve">
|
||||
<value>Lesebestätigung versendet</value>
|
||||
</data>
|
||||
<data name="MessageDeletionSent" xml:space="preserve">
|
||||
<value>Löschinformation versendet</value>
|
||||
</data>
|
||||
@@ -234,9 +207,6 @@
|
||||
<data name="No" xml:space="preserve">
|
||||
<value>Nein</value>
|
||||
</data>
|
||||
<data name="PartlyConfirmed" xml:space="preserve">
|
||||
<value>Teil-Bestätigt</value>
|
||||
</data>
|
||||
<data name="PartlySigned" xml:space="preserve">
|
||||
<value>Teil-Signiert</value>
|
||||
</data>
|
||||
@@ -244,7 +214,7 @@
|
||||
<value>Qualifizierte Signatur</value>
|
||||
</data>
|
||||
<data name="ReadAndSign" xml:space="preserve">
|
||||
<value>Lesebestätigung</value>
|
||||
<value>Arbeitsanweisung</value>
|
||||
</data>
|
||||
<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>
|
||||
@@ -259,7 +229,7 @@
|
||||
<value>Signatur</value>
|
||||
</data>
|
||||
<data name="SignatureConfirmed" xml:space="preserve">
|
||||
<value>Abschluss bestätigt</value>
|
||||
<value>Signatur bestätigt</value>
|
||||
</data>
|
||||
<data name="Signed" xml:space="preserve">
|
||||
<value>Signiert</value>
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
<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>
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace EnvelopeGenerator.Domain.Constants
|
||||
{
|
||||
public enum SenderAppType
|
||||
{
|
||||
LegacyFormApp = 0,
|
||||
ReceiverUIBlazorApp = 1
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace EnvelopeGenerator.Domain.Entities
|
||||
public Document()
|
||||
{
|
||||
#if NETFRAMEWORK
|
||||
Elements = Enumerable.Empty<DocReceiverElement>().ToList();
|
||||
Elements = Enumerable.Empty<Signature>().ToList();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace EnvelopeGenerator.Domain.Entities
|
||||
FileNameOriginal { get; set; }
|
||||
#endregion
|
||||
|
||||
public virtual List<DocReceiverElement>
|
||||
public virtual List<Signature>
|
||||
#if nullable
|
||||
?
|
||||
#endif
|
||||
|
||||
@@ -35,9 +35,6 @@ namespace EnvelopeGenerator.Domain.Entities
|
||||
[Column("STATUS")]
|
||||
public Constants.DocumentStatus Status { get; set; }
|
||||
|
||||
[Column("STATUS_CHANGED_WHEN", TypeName = "datetime")]
|
||||
public DateTime? StatusChangedWhen { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column("ADDED_WHEN", TypeName = "datetime")]
|
||||
public DateTime AddedWhen { get; set; }
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace EnvelopeGenerator.Domain.Entities
|
||||
ChangedWho { get; set; }
|
||||
|
||||
[ForeignKey("ElementId")]
|
||||
public virtual DocReceiverElement
|
||||
public virtual Signature
|
||||
#if nullable
|
||||
?
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.ComponentModel.DataAnnotations.Schema;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using Newtonsoft.Json;
|
||||
using EnvelopeGenerator.Domain.Interfaces.Auditing;
|
||||
using EnvelopeGenerator.Domain.Interfaces;
|
||||
#if NETFRAMEWORK
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -14,7 +13,7 @@ using System.Linq;
|
||||
namespace EnvelopeGenerator.Domain.Entities
|
||||
{
|
||||
[Table("TBSIG_ENVELOPE", Schema = "dbo")]
|
||||
public class Envelope : IHasAddedWhen, IHasChangedWhen, IEnvelope
|
||||
public class Envelope : IHasAddedWhen, IHasChangedWhen
|
||||
{
|
||||
public Envelope()
|
||||
{
|
||||
@@ -94,7 +93,7 @@ namespace EnvelopeGenerator.Domain.Entities
|
||||
public string Language { get; set; }
|
||||
|
||||
[Column("SEND_REMINDER_EMAILS")]
|
||||
public bool? SendReminderEmails { get; set; }
|
||||
public bool SendReminderEmails { get; set; }
|
||||
|
||||
[Column("FIRST_REMINDER_DAYS")]
|
||||
public int? FirstReminderDays { get; set; }
|
||||
@@ -107,14 +106,13 @@ namespace EnvelopeGenerator.Domain.Entities
|
||||
|
||||
[JsonIgnore]
|
||||
[NotMapped]
|
||||
[Obsolete("Use EnvelopeGenerator.Domain.Interfaces.EnvelopeExtensions.IsReadAndConfirm extension method instead.")]
|
||||
public bool ReadOnly => this.IsReadAndConfirm();
|
||||
public bool ReadOnly => EnvelopeTypeId == 2;
|
||||
|
||||
[Column("CERTIFICATION_TYPE")]
|
||||
public int? CertificationType { get; set; }
|
||||
|
||||
[Column("USE_ACCESS_CODE")]
|
||||
public bool? UseAccessCode { get; set; }
|
||||
public bool UseAccessCode { get; set; }
|
||||
|
||||
[Column("FINAL_EMAIL_TO_CREATOR")]
|
||||
public int? FinalEmailToCreator { get; set; }
|
||||
@@ -132,7 +130,7 @@ namespace EnvelopeGenerator.Domain.Entities
|
||||
public User User { get; set; }
|
||||
|
||||
[Column("TFA_ENABLED")]
|
||||
public bool? TfaEnabled { get; set; }
|
||||
public bool TfaEnabled { get; set; }
|
||||
#if NETFRAMEWORK
|
||||
= false;
|
||||
#endif
|
||||
|
||||
@@ -35,11 +35,8 @@ namespace EnvelopeGenerator.Domain.Entities
|
||||
public DateTime AddedWhen { get; set; }
|
||||
|
||||
[Column("ACTION_DATE", TypeName = "datetime")]
|
||||
public DateTime? ActionDate { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public DateTime? ChangedWhen { get => ActionDate; set => ActionDate = value; }
|
||||
|
||||
public DateTime? ChangedWhen { get; set; }
|
||||
|
||||
[Column("COMMENT", TypeName = "nvarchar(max)")]
|
||||
public string
|
||||
#if nullable
|
||||
|
||||
@@ -11,9 +11,9 @@ using System.Collections.Generic;
|
||||
namespace EnvelopeGenerator.Domain.Entities
|
||||
{
|
||||
[Table("TBSIG_DOCUMENT_RECEIVER_ELEMENT", Schema = "dbo")]
|
||||
public class DocReceiverElement : IDocReceiverElement, IHasReceiver, IHasAddedWhen, IUpdateAuditable
|
||||
public class Signature : ISignature, IHasReceiver, IHasAddedWhen, IUpdateAuditable
|
||||
{
|
||||
public DocReceiverElement()
|
||||
public Signature()
|
||||
{
|
||||
// TODO: * Check the Form App and remove the default value
|
||||
#if NETFRAMEWORK
|
||||
@@ -126,33 +126,5 @@ namespace EnvelopeGenerator.Domain.Entities
|
||||
[NotMapped]
|
||||
public double Left => Math.Round(X, 5);
|
||||
#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; }
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace EnvelopeGenerator.Domain.Interfaces
|
||||
{
|
||||
public interface IDocReceiverElement
|
||||
public interface ISignature
|
||||
{
|
||||
int Page { get; set; }
|
||||
|
||||
200
EnvelopeGenerator.Domain/Resources/Model.Designer.cs
generated
200
EnvelopeGenerator.Domain/Resources/Model.Designer.cs
generated
@@ -1,10 +1,10 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
// Dieser Code wurde von einem Tool generiert.
|
||||
// Laufzeitversion:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
|
||||
// der Code erneut generiert wird.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -13,12 +13,12 @@ namespace My.Resources {
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
|
||||
// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
|
||||
// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
|
||||
// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
@@ -33,7 +33,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
@@ -47,8 +47,8 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
|
||||
/// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
@@ -61,7 +61,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Zugriffscode korrekt eingegeben.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Zugriffscode korrekt eingegeben ähnelt.
|
||||
/// </summary>
|
||||
public static string AccessCodeCorrect {
|
||||
get {
|
||||
@@ -70,7 +70,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Zugriffscode falsch eingegeben.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Zugriffscode falsch eingegeben ähnelt.
|
||||
/// </summary>
|
||||
public static string AccessCodeIncorrect {
|
||||
get {
|
||||
@@ -79,7 +79,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Zugriffscode angefordert.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Zugriffscode angefordert ähnelt.
|
||||
/// </summary>
|
||||
public static string AccessCodeRequested {
|
||||
get {
|
||||
@@ -88,7 +88,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Fortgeschrittene Elektronische Signatur.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Fortgeschrittene Elektronische Signatur ähnelt.
|
||||
/// </summary>
|
||||
public static string AdvancedElectronicSignature {
|
||||
get {
|
||||
@@ -97,7 +97,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Abgeschlossen.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Abgeschlossen ähnelt.
|
||||
/// </summary>
|
||||
public static string Completed {
|
||||
get {
|
||||
@@ -106,16 +106,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Vollständig Signiert ähnelt.
|
||||
/// </summary>
|
||||
public static string CompletelySigned {
|
||||
get {
|
||||
@@ -124,25 +115,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Vertrag ähnelt.
|
||||
/// </summary>
|
||||
public static string Contract {
|
||||
get {
|
||||
@@ -151,7 +124,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Erstellt.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Erstellt ähnelt.
|
||||
/// </summary>
|
||||
public static string Created {
|
||||
get {
|
||||
@@ -160,16 +133,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Dokument Rotation geändert ähnelt.
|
||||
/// </summary>
|
||||
public static string DocumentMod_Rotation {
|
||||
get {
|
||||
@@ -178,7 +142,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Dokument geöffnet.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Dokument geöffnet ähnelt.
|
||||
/// </summary>
|
||||
public static string DocumentOpened {
|
||||
get {
|
||||
@@ -187,7 +151,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Unterzeichnung abgelehnt.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Unterzeichnung abgelehnt ähnelt.
|
||||
/// </summary>
|
||||
public static string DocumentRejected {
|
||||
get {
|
||||
@@ -196,16 +160,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Dokument unterzeichnet ähnelt.
|
||||
/// </summary>
|
||||
public static string DocumentSigned {
|
||||
get {
|
||||
@@ -214,7 +169,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Entwurf.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Entwurf ähnelt.
|
||||
/// </summary>
|
||||
public static string Draft {
|
||||
get {
|
||||
@@ -223,7 +178,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Archiviert.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Archiviert ähnelt.
|
||||
/// </summary>
|
||||
public static string EnvelopeArchived {
|
||||
get {
|
||||
@@ -232,16 +187,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Vollständig signiert ähnelt.
|
||||
/// </summary>
|
||||
public static string EnvelopeCompletelySigned {
|
||||
get {
|
||||
@@ -250,7 +196,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Umschlag Erstellt.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Umschlag Erstellt ähnelt.
|
||||
/// </summary>
|
||||
public static string EnvelopeCreated {
|
||||
get {
|
||||
@@ -259,7 +205,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Umschlag Gelöscht.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Umschlag Gelöscht ähnelt.
|
||||
/// </summary>
|
||||
public static string EnvelopeDeleted {
|
||||
get {
|
||||
@@ -268,16 +214,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Teil-Signiert ähnelt.
|
||||
/// </summary>
|
||||
public static string EnvelopePartlySigned {
|
||||
get {
|
||||
@@ -286,7 +223,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Umschlag in Queue.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Umschlag in Queue ähnelt.
|
||||
/// </summary>
|
||||
public static string EnvelopeQueued {
|
||||
get {
|
||||
@@ -295,7 +232,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Umschlag abgelehnt.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Umschlag abgelehnt ähnelt.
|
||||
/// </summary>
|
||||
public static string EnvelopeRejected {
|
||||
get {
|
||||
@@ -304,7 +241,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Signierungszertifikat erstellt.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Signierungszertifikat erstellt ähnelt.
|
||||
/// </summary>
|
||||
public static string EnvelopeReportCreated {
|
||||
get {
|
||||
@@ -313,16 +250,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Gespeichert ähnelt.
|
||||
/// </summary>
|
||||
public static string EnvelopeSaved {
|
||||
get {
|
||||
@@ -331,7 +259,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Gesendet.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Gesendet ähnelt.
|
||||
/// </summary>
|
||||
public static string EnvelopeSent {
|
||||
get {
|
||||
@@ -340,7 +268,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Umschlag zurückgezogen.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Umschlag zurückgezogen ähnelt.
|
||||
/// </summary>
|
||||
public static string EnvelopeWithdrawn {
|
||||
get {
|
||||
@@ -349,7 +277,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Zugriffscode versendet.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Zugriffscode versendet ähnelt.
|
||||
/// </summary>
|
||||
public static string MessageAccessCodeSent {
|
||||
get {
|
||||
@@ -358,7 +286,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Abschlussemail versendet.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Abschlussemail versendet ähnelt.
|
||||
/// </summary>
|
||||
public static string MessageCompletionSent {
|
||||
get {
|
||||
@@ -367,7 +295,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Abschlussbestätigung versendet.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Signaturbestätigung versendet ähnelt.
|
||||
/// </summary>
|
||||
public static string MessageConfirmationSent {
|
||||
get {
|
||||
@@ -376,7 +304,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Löschinformation versendet.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Löschinformation versendet ähnelt.
|
||||
/// </summary>
|
||||
public static string MessageDeletionSent {
|
||||
get {
|
||||
@@ -385,7 +313,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Dokumentenlink versendet.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Dokumentenlink versendet ähnelt.
|
||||
/// </summary>
|
||||
public static string MessageInvitationSent {
|
||||
get {
|
||||
@@ -394,7 +322,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Nein.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Nein ähnelt.
|
||||
/// </summary>
|
||||
public static string No {
|
||||
get {
|
||||
@@ -403,16 +331,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Teil-Signiert ähnelt.
|
||||
/// </summary>
|
||||
public static string PartlySigned {
|
||||
get {
|
||||
@@ -421,7 +340,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Qualifizierte Signatur.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Qualifizierte Signatur ähnelt.
|
||||
/// </summary>
|
||||
public static string QualifiedSignature {
|
||||
get {
|
||||
@@ -430,7 +349,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Lesebestätigung.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Arbeitsanweisung ähnelt.
|
||||
/// </summary>
|
||||
public static string ReadAndSign {
|
||||
get {
|
||||
@@ -439,7 +358,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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!.
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public static string ResetTOTPUser {
|
||||
get {
|
||||
@@ -448,7 +367,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Gespeichert.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Gespeichert ähnelt.
|
||||
/// </summary>
|
||||
public static string Saved {
|
||||
get {
|
||||
@@ -457,7 +376,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Gesendet.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Gesendet ähnelt.
|
||||
/// </summary>
|
||||
public static string Sent {
|
||||
get {
|
||||
@@ -466,7 +385,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Signatur.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Signatur ähnelt.
|
||||
/// </summary>
|
||||
public static string Signature {
|
||||
get {
|
||||
@@ -475,7 +394,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Abschluss bestätigt.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Signatur bestätigt ähnelt.
|
||||
/// </summary>
|
||||
public static string SignatureConfirmed {
|
||||
get {
|
||||
@@ -484,7 +403,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Signiert.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Signiert ähnelt.
|
||||
/// </summary>
|
||||
public static string Signed {
|
||||
get {
|
||||
@@ -493,7 +412,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Erfolgreich! Dialog wird geschlossen..
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Erfolgreich! Dialog wird geschlossen. ähnelt.
|
||||
/// </summary>
|
||||
public static string Success_FormClose {
|
||||
get {
|
||||
@@ -502,16 +421,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Unsigniert ähnelt.
|
||||
/// </summary>
|
||||
public static string Unsigned {
|
||||
get {
|
||||
@@ -520,7 +430,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Ja.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Ja ähnelt.
|
||||
/// </summary>
|
||||
public static string Yes {
|
||||
get {
|
||||
@@ -529,7 +439,7 @@ namespace My.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Ja, mit Anhang.
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Ja, mit Anhang ähnelt.
|
||||
/// </summary>
|
||||
public static string YesWithAttachment {
|
||||
get {
|
||||
|
||||
@@ -132,27 +132,15 @@
|
||||
<data name="Completed" xml:space="preserve">
|
||||
<value>Completed</value>
|
||||
</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">
|
||||
<value>Contract</value>
|
||||
</data>
|
||||
<data name="Created" xml:space="preserve">
|
||||
<value>Created</value>
|
||||
</data>
|
||||
<data name="DocumentConfirmed" xml:space="preserve">
|
||||
<value>Document read and signed</value>
|
||||
</data>
|
||||
<data name="DocumentMod_Rotation" xml:space="preserve">
|
||||
<value>Document rotation adapted</value>
|
||||
</data>
|
||||
@@ -162,9 +150,6 @@
|
||||
<data name="DocumentRejected" xml:space="preserve">
|
||||
<value>Signing rejected</value>
|
||||
</data>
|
||||
<data name="DocumentRejectedRaC" xml:space="preserve">
|
||||
<value>Read confirmation rejected</value>
|
||||
</data>
|
||||
<data name="DocumentSigned" xml:space="preserve">
|
||||
<value>Document signed</value>
|
||||
</data>
|
||||
@@ -174,9 +159,6 @@
|
||||
<data name="EnvelopeArchived" xml:space="preserve">
|
||||
<value>Archived</value>
|
||||
</data>
|
||||
<data name="EnvelopeCompletelyConfirmed" xml:space="preserve">
|
||||
<value>Completely confirmed</value>
|
||||
</data>
|
||||
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
||||
<value>Completely signed</value>
|
||||
</data>
|
||||
@@ -186,11 +168,8 @@
|
||||
<data name="EnvelopeDeleted" xml:space="preserve">
|
||||
<value>Envelope Deleted</value>
|
||||
</data>
|
||||
<data name="EnvelopePartlyConfirmed" xml:space="preserve">
|
||||
<value>Partially confirmed</value>
|
||||
</data>
|
||||
<data name="EnvelopePartlySigned" xml:space="preserve">
|
||||
<value>Partially signed</value>
|
||||
<value>Partly signed</value>
|
||||
</data>
|
||||
<data name="EnvelopeQueued" xml:space="preserve">
|
||||
<value>Envelope Queued</value>
|
||||
@@ -201,9 +180,6 @@
|
||||
<data name="EnvelopeReportCreated" xml:space="preserve">
|
||||
<value>Signature certificate created</value>
|
||||
</data>
|
||||
<data name="EnvelopeReportCreatedRaC" xml:space="preserve">
|
||||
<value>Read confirmartion certificate created</value>
|
||||
</data>
|
||||
<data name="EnvelopeSaved" xml:space="preserve">
|
||||
<value>Saved</value>
|
||||
</data>
|
||||
@@ -220,7 +196,7 @@
|
||||
<value>Final email sent</value>
|
||||
</data>
|
||||
<data name="MessageConfirmationSent" xml:space="preserve">
|
||||
<value>Finalization Confirmation Sent</value>
|
||||
<value>Confirmation Sent</value>
|
||||
</data>
|
||||
<data name="MessageDeletionSent" xml:space="preserve">
|
||||
<value>Deletion Notice Sent</value>
|
||||
@@ -231,12 +207,6 @@
|
||||
<data name="No" xml:space="preserve">
|
||||
<value>No</value>
|
||||
</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">
|
||||
<value>Qualified Signature</value>
|
||||
</data>
|
||||
@@ -256,7 +226,7 @@
|
||||
<value>Signature</value>
|
||||
</data>
|
||||
<data name="SignatureConfirmed" xml:space="preserve">
|
||||
<value>Finalization confirmed</value>
|
||||
<value>Signature confirmed</value>
|
||||
</data>
|
||||
<data name="Signed" xml:space="preserve">
|
||||
<value>Signed</value>
|
||||
@@ -264,9 +234,6 @@
|
||||
<data name="Success_FormClose" xml:space="preserve">
|
||||
<value>Successful! Dialog is closed.successful! Dialog is closed.</value>
|
||||
</data>
|
||||
<data name="Unconfirmed" xml:space="preserve">
|
||||
<value>Unconfirmed</value>
|
||||
</data>
|
||||
<data name="Unsigned" xml:space="preserve">
|
||||
<value>Unsigned</value>
|
||||
</data>
|
||||
|
||||
@@ -132,17 +132,8 @@
|
||||
<data name="Completed" xml:space="preserve">
|
||||
<value>Abgeschlossen</value>
|
||||
</data>
|
||||
<data name="CompletelyConfirmed" xml:space="preserve">
|
||||
<value>Vollständig bestätigt</value>
|
||||
</data>
|
||||
<data name="CompletelySigned" xml:space="preserve">
|
||||
<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>
|
||||
<value>Vollständig Signiert</value>
|
||||
</data>
|
||||
<data name="Contract" xml:space="preserve">
|
||||
<value>Vertrag</value>
|
||||
@@ -150,9 +141,6 @@
|
||||
<data name="Created" xml:space="preserve">
|
||||
<value>Erstellt</value>
|
||||
</data>
|
||||
<data name="DocumentConfirmed" xml:space="preserve">
|
||||
<value>Dokument gelesen und bestätigt</value>
|
||||
</data>
|
||||
<data name="DocumentMod_Rotation" xml:space="preserve">
|
||||
<value>Dokument Rotation geändert</value>
|
||||
</data>
|
||||
@@ -162,9 +150,6 @@
|
||||
<data name="DocumentRejected" xml:space="preserve">
|
||||
<value>Unterzeichnung abgelehnt</value>
|
||||
</data>
|
||||
<data name="DocumentRejectedRaC" xml:space="preserve">
|
||||
<value>Lesebestätigung abgelehnt</value>
|
||||
</data>
|
||||
<data name="DocumentSigned" xml:space="preserve">
|
||||
<value>Dokument unterzeichnet</value>
|
||||
</data>
|
||||
@@ -174,9 +159,6 @@
|
||||
<data name="EnvelopeArchived" xml:space="preserve">
|
||||
<value>Archiviert</value>
|
||||
</data>
|
||||
<data name="EnvelopeCompletelyConfirmed" xml:space="preserve">
|
||||
<value>Vollständig gelesen und bestätigt</value>
|
||||
</data>
|
||||
<data name="EnvelopeCompletelySigned" xml:space="preserve">
|
||||
<value>Vollständig signiert</value>
|
||||
</data>
|
||||
@@ -186,9 +168,6 @@
|
||||
<data name="EnvelopeDeleted" xml:space="preserve">
|
||||
<value>Umschlag Gelöscht</value>
|
||||
</data>
|
||||
<data name="EnvelopePartlyConfirmed" xml:space="preserve">
|
||||
<value>Teil-Bestätigt</value>
|
||||
</data>
|
||||
<data name="EnvelopePartlySigned" xml:space="preserve">
|
||||
<value>Teil-Signiert</value>
|
||||
</data>
|
||||
@@ -201,9 +180,6 @@
|
||||
<data name="EnvelopeReportCreated" xml:space="preserve">
|
||||
<value>Signierungszertifikat erstellt</value>
|
||||
</data>
|
||||
<data name="EnvelopeReportCreatedRaC" xml:space="preserve">
|
||||
<value>Lesebestätigungszertifikat erstellt</value>
|
||||
</data>
|
||||
<data name="EnvelopeSaved" xml:space="preserve">
|
||||
<value>Gespeichert</value>
|
||||
</data>
|
||||
@@ -220,7 +196,7 @@
|
||||
<value>Abschlussemail versendet</value>
|
||||
</data>
|
||||
<data name="MessageConfirmationSent" xml:space="preserve">
|
||||
<value>Abschlussbestätigung versendet</value>
|
||||
<value>Signaturbestätigung versendet</value>
|
||||
</data>
|
||||
<data name="MessageDeletionSent" xml:space="preserve">
|
||||
<value>Löschinformation versendet</value>
|
||||
@@ -231,9 +207,6 @@
|
||||
<data name="No" xml:space="preserve">
|
||||
<value>Nein</value>
|
||||
</data>
|
||||
<data name="PartlyConfirmed" xml:space="preserve">
|
||||
<value>Teil-Bestätigt</value>
|
||||
</data>
|
||||
<data name="PartlySigned" xml:space="preserve">
|
||||
<value>Teil-Signiert</value>
|
||||
</data>
|
||||
@@ -241,7 +214,7 @@
|
||||
<value>Qualifizierte Signatur</value>
|
||||
</data>
|
||||
<data name="ReadAndSign" xml:space="preserve">
|
||||
<value>Lesebestätigung</value>
|
||||
<value>Arbeitsanweisung</value>
|
||||
</data>
|
||||
<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>
|
||||
@@ -256,7 +229,7 @@
|
||||
<value>Signatur</value>
|
||||
</data>
|
||||
<data name="SignatureConfirmed" xml:space="preserve">
|
||||
<value>Abschluss bestätigt</value>
|
||||
<value>Signatur bestätigt</value>
|
||||
</data>
|
||||
<data name="Signed" xml:space="preserve">
|
||||
<value>Signiert</value>
|
||||
@@ -264,9 +237,6 @@
|
||||
<data name="Success_FormClose" xml:space="preserve">
|
||||
<value>Erfolgreich! Dialog wird geschlossen.</value>
|
||||
</data>
|
||||
<data name="Unconfirmed" xml:space="preserve">
|
||||
<value>Unbestätigt</value>
|
||||
</data>
|
||||
<data name="Unsigned" xml:space="preserve">
|
||||
<value>Unsigniert</value>
|
||||
</data>
|
||||
|
||||
@@ -8,7 +8,7 @@ Public Class FieldEditorController
|
||||
Inherits BaseController
|
||||
|
||||
Private ReadOnly Document As Document
|
||||
Public Property Elements As New List(Of DocReceiverElement)
|
||||
Public Property Elements As New List(Of Signature)
|
||||
|
||||
Public Class ElementInfo
|
||||
Public ReceiverId As Integer
|
||||
@@ -36,7 +36,7 @@ Public Class FieldEditorController
|
||||
}
|
||||
End Function
|
||||
|
||||
Private Function GetElementByPosition(pAnnotation As AnnotationStickyNote, pPage As Integer, pReceiverId As Integer) As DocReceiverElement
|
||||
Private Function GetElementByPosition(pAnnotation As AnnotationStickyNote, pPage As Integer, pReceiverId As Integer) As Signature
|
||||
Dim oElement = Elements.
|
||||
Where(Function(e)
|
||||
Return e.Left = CSng(Math.Round(pAnnotation.Left, 5)) And
|
||||
@@ -47,12 +47,12 @@ Public Class FieldEditorController
|
||||
Return oElement
|
||||
End Function
|
||||
|
||||
Private Function GetElementByGuid(pGuid As Integer) As DocReceiverElement
|
||||
Private Function GetElementByGuid(pGuid As Integer) As Signature
|
||||
Dim oElement = Elements.Where(Function(e) pGuid = e.Id).SingleOrDefault()
|
||||
Return oElement
|
||||
End Function
|
||||
|
||||
Public Function GetElement(pAnnotation As AnnotationStickyNote) As DocReceiverElement
|
||||
Public Function GetElement(pAnnotation As AnnotationStickyNote) As Signature
|
||||
Dim oInfo = GetElementInfo(pAnnotation.Tag)
|
||||
|
||||
If oInfo.Guid = -1 Then
|
||||
@@ -85,7 +85,7 @@ Public Class FieldEditorController
|
||||
Else
|
||||
Dim oInfo = GetElementInfo(pAnnotation.Tag)
|
||||
|
||||
Elements.Add(New DocReceiverElement() With {
|
||||
Elements.Add(New Signature() With {
|
||||
.ElementType = Constants.ElementType.Signature,
|
||||
.Height = oAnnotationHeight,
|
||||
.Width = oAnnotationWidth,
|
||||
@@ -98,7 +98,7 @@ Public Class FieldEditorController
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Public Function ClearElements(pPage As Integer, pReceiverId As Integer) As IEnumerable(Of DocReceiverElement)
|
||||
Public Function ClearElements(pPage As Integer, pReceiverId As Integer) As IEnumerable(Of Signature)
|
||||
Return Elements.
|
||||
Where(Function(e) e.Page <> pPage And e.ReceiverId <> pReceiverId).
|
||||
ToList()
|
||||
@@ -120,7 +120,7 @@ Public Class FieldEditorController
|
||||
All(Function(pResult) pResult = True)
|
||||
End Function
|
||||
|
||||
Public Function SaveElement(pElement As DocReceiverElement) As Boolean
|
||||
Public Function SaveElement(pElement As Signature) As Boolean
|
||||
Try
|
||||
If pElement.Id > 0 Then
|
||||
Return ElementModel.Update(pElement)
|
||||
@@ -134,11 +134,11 @@ Public Class FieldEditorController
|
||||
End Try
|
||||
End Function
|
||||
|
||||
Public Function DeleteElement(pElement As DocReceiverElement) As Boolean
|
||||
Public Function DeleteElement(pElement As Signature) As Boolean
|
||||
Try
|
||||
' Element aus Datenbank löschen
|
||||
If ElementModel.DeleteElement(pElement) Then
|
||||
Dim oElement = New List(Of DocReceiverElement)() From {pElement}
|
||||
Dim oElement = New List(Of Signature)() From {pElement}
|
||||
Elements = Elements.Except(oElement).ToList()
|
||||
Return True
|
||||
Else
|
||||
|
||||
@@ -280,7 +280,7 @@ Partial Public Class frmFieldEditor
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Private Sub LoadAnnotation(pElement As DocReceiverElement, pReceiverId As Integer)
|
||||
Private Sub LoadAnnotation(pElement As Signature, pReceiverId As Integer)
|
||||
Dim oAnnotation As AnnotationStickyNote = Manager.AddStickyNoteAnnot(0, 0, 0, 0, "SIGNATUR")
|
||||
Dim oPage = pElement.Page
|
||||
Dim oReceiver = Receivers.Where(Function(r) r.Id = pReceiverId).Single()
|
||||
|
||||
@@ -74,14 +74,14 @@ namespace EnvelopeGenerator.Infrastructure
|
||||
services.AddSQLExecutor<Envelope>();
|
||||
services.AddSQLExecutor<Receiver>();
|
||||
services.AddSQLExecutor<Document>();
|
||||
services.AddSQLExecutor<DocReceiverElement>();
|
||||
services.AddSQLExecutor<Signature>();
|
||||
services.AddSQLExecutor<DocumentStatus>();
|
||||
|
||||
SetDapperTypeMap<Envelope>();
|
||||
SetDapperTypeMap<User>();
|
||||
SetDapperTypeMap<Receiver>();
|
||||
SetDapperTypeMap<Document>();
|
||||
SetDapperTypeMap<DocReceiverElement>();
|
||||
SetDapperTypeMap<Signature>();
|
||||
SetDapperTypeMap<DocumentStatus>();
|
||||
|
||||
services.AddScoped<IEnvelopeExecutor, EnvelopeExecutor>();
|
||||
|
||||
@@ -45,9 +45,7 @@ public abstract class EGDbContextBase : DbContext
|
||||
|
||||
public DbSet<Envelope> Envelopes { get; set; }
|
||||
|
||||
public DbSet<DocReceiverElement> DocumentReceiverElements { get; set; }
|
||||
|
||||
public DbSet<ElementAnnotation> DocumentReceiverElementAnnotations { get; set; }
|
||||
public DbSet<Signature> DocumentReceiverElements { get; set; }
|
||||
|
||||
public DbSet<DocumentStatus> DocumentStatus { get; set; }
|
||||
|
||||
@@ -154,7 +152,7 @@ public abstract class EGDbContextBase : DbContext
|
||||
#endregion EnvelopeDocument
|
||||
|
||||
#region DocumentReceiverElement
|
||||
modelBuilder.Entity<DocReceiverElement>()
|
||||
modelBuilder.Entity<Signature>()
|
||||
.HasOne(dre => dre.Document)
|
||||
.WithMany(ed => ed.Elements)
|
||||
.HasForeignKey(dre => dre.DocumentId);
|
||||
@@ -196,7 +194,7 @@ public abstract class EGDbContextBase : DbContext
|
||||
#endregion DocumentStatus
|
||||
|
||||
#region Annotation
|
||||
modelBuilder.Entity<DocReceiverElement>()
|
||||
modelBuilder.Entity<Signature>()
|
||||
.HasMany(signature => signature.Annotations)
|
||||
.WithOne(annot => annot.Element)
|
||||
.HasForeignKey(annot => annot.ElementId);
|
||||
@@ -217,7 +215,7 @@ public abstract class EGDbContextBase : DbContext
|
||||
|
||||
// TODO: call add trigger methods with attributes and reflection
|
||||
AddTrigger<Config>();
|
||||
AddTrigger<DocReceiverElement>();
|
||||
AddTrigger<Signature>();
|
||||
AddTrigger<DocumentStatus>();
|
||||
AddTrigger<EmailTemplate>();
|
||||
AddTrigger<Envelope>();
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.6.0" />
|
||||
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.6.1" />
|
||||
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
|
||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
|
||||
<PackageReference Include="QuestPDF" Version="2025.7.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
||||
|
||||
@@ -6,7 +6,7 @@ using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
||||
namespace EnvelopeGenerator.Infrastructure.Repositories;
|
||||
|
||||
[Obsolete("Use IRepository")]
|
||||
public class DocumentReceiverElementRepository : CRUDRepository<DocReceiverElement, int, EGDbContext>, IDocumentReceiverElementRepository
|
||||
public class DocumentReceiverElementRepository : CRUDRepository<Signature, int, EGDbContext>, IDocumentReceiverElementRepository
|
||||
{
|
||||
public DocumentReceiverElementRepository(EGDbContext dbContext) : base(dbContext, dbContext.DocumentReceiverElements)
|
||||
{
|
||||
|
||||
26
EnvelopeGenerator.Jobs/EnvelopeGenerator.Jobs.csproj
Normal file
26
EnvelopeGenerator.Jobs/EnvelopeGenerator.Jobs.csproj
Normal file
@@ -0,0 +1,26 @@
|
||||
<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>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user