Introduced `ESignParams` for configurable e-signature settings, including `ApplicationName` and `DefaultReason`. Updated `DocReceiverElementDto` and `PdfSignOptionsDto` to support new properties. Registered `ESignParams` in DI container. Refactored `SignPdfByElementCommand` to include new fields and improved error handling. Added `SignPdfByEnvelopeReceiverCommand` to enable signing documents for envelope receivers. Replaced DevExpress with PdfSharp for PDF rendering in `ReceiverSignedPage.razor`, improving layout and flexibility. Enhanced `PdfSignatureController` with a new endpoint for signing documents by envelope key. Added `DataIntegrityException` for better error handling. Updated `appsettings.json` with `ESignParams` configuration.
133 lines
4.6 KiB
C#
133 lines
4.6 KiB
C#
using DigitalData.Core.Exceptions;
|
|
using EnvelopeGenerator.Application.Common.Configurations;
|
|
using EnvelopeGenerator.Application.Common.Dto;
|
|
using EnvelopeGenerator.Application.Common.Enums;
|
|
using EnvelopeGenerator.Application.Common.Interfaces.Doc;
|
|
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
using EnvelopeGenerator.Application.DocReceiverElements.Queries;
|
|
using MediatR;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace EnvelopeGenerator.Application.PdfSigning;
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public record SignPdfByElementCommand : IRequest<Stream>
|
|
{
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public DocReceiverElementDto? Element { get; set; }
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public int? ElementId { get; init; }
|
|
|
|
/// <summary>
|
|
/// Custom document
|
|
/// </summary>
|
|
public Stream? Document { get; set; }
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public string? Phone { get; set; }
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public string? Reason { get; set; }
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public PdfSignatureCertificationLevel CertificationLevel { get; init; } = PdfSignatureCertificationLevel.NoChangesAllowed;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="signService"></param>
|
|
/// <param name="imgRenderer"></param>
|
|
/// <param name="options"></param>
|
|
public class SignPdfCommandHandler(ISender sender, IPdfDigitalSignatureService signService, IPdfPageImageRenderer imgRenderer, IOptionsMonitor<ESignParams> options) : IRequestHandler<SignPdfByElementCommand, Stream>
|
|
{
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="request"></param>
|
|
/// <param name="cancellationToken"></param>
|
|
/// <returns></returns>
|
|
public async Task<Stream> Handle(SignPdfByElementCommand request, CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
// ensure element
|
|
if (request.Element is null)
|
|
{
|
|
var elements = await sender.Send(new ReadDocReceiverElementQuery()
|
|
{
|
|
Id = request.ElementId!,
|
|
IncludeDocument = request.Document is null
|
|
}, cancellationToken);
|
|
|
|
request.Element = elements.FirstOrDefault() ?? throw new NotFoundException($"Element with ID {request.ElementId} not found."); ;
|
|
}
|
|
|
|
// ensure document
|
|
if(request.Document is null)
|
|
{
|
|
if (request.Element?.Document?.ByteData is not { Length: > 0 })
|
|
throw new NotFoundException("Document data is missing or empty.");
|
|
|
|
request.Document = new MemoryStream(request.Element.Document.ByteData);
|
|
}
|
|
|
|
ValidateAfterDataIsComplete(request);
|
|
|
|
// create signature image from element
|
|
var img = imgRenderer.ComposeSignaturePreviewPng(request.Element);
|
|
|
|
// sign pdf
|
|
var outputPdf = new MemoryStream();
|
|
|
|
await signService.SignPdfAsync(
|
|
inputPdf: request.Document,
|
|
outputPdf: outputPdf,
|
|
options: new PdfSignOptionsDto
|
|
{
|
|
Name = request.Element.FullName!,
|
|
Location = request.Element.Position!,
|
|
Phone = request.Phone,
|
|
Reason = request.Reason ?? options.CurrentValue.DefaultReason,
|
|
Email = request.Element.Receiver!.EmailAddress,
|
|
ApplicationName = options.CurrentValue.ApplicationName,
|
|
DocumentElementId = request.Element.Id,
|
|
|
|
CertificationLevel = request.CertificationLevel,
|
|
Appearance = new PdfSignatureAppearanceDto(
|
|
PageNumber: request.Element.Page,
|
|
Left: request.Element.Left,
|
|
Bottom: request.Element.Y,
|
|
Width: request.Element.Width,
|
|
Height: request.Element.Height,
|
|
CoordinateUnit: PdfSignatureCoordinateUnit.Inch,
|
|
CoordinateOrigin: PdfSignatureCoordinateOrigin.TopLeft)
|
|
},
|
|
image: img,
|
|
cancellationToken: cancellationToken);
|
|
|
|
return outputPdf;
|
|
}
|
|
|
|
private static void ValidateAfterDataIsComplete(SignPdfByElementCommand request)
|
|
{
|
|
if(request.Element!.FullName is null)
|
|
throw new BadRequestException($"Document is not signed. FullName is required. Element ID: {request.Element.Id}");
|
|
else if(request.Element.Position is null)
|
|
throw new BadRequestException($"Document is not signed. Position is required. Element ID: {request.Element.Id}");
|
|
}
|
|
} |