Compare commits

...

6 Commits

Author SHA1 Message Date
Developer 02
6941a3db8d Optimize data handling by embedding EnvelopeResponse in Razor Page, eliminating additional requests 2024-04-08 17:05:01 +02:00
Developer 02
1584fd6f1c feat: Begrüßung und Dokumentdetails auf Umschlagseite aktualisiert 2024-04-08 16:39:02 +02:00
Developer 02
2512de0f26 Signaturprüfung zur Filterung der Umschlagempfänger hinzugefügt 2024-04-08 16:22:17 +02:00
Developer 02
db83eb90ee Sicherheitsverbesserung: VerifyAccessCode implementiert und Verifizierungscode aus DTO entfernt
Die VerifyAccessCode-Methode wurde zur Validierung von Zugangscodes hinzugefügt und der Verifizierungscode aus Sicherheitsgründen aus dem DTO entfernt.
2024-04-08 12:54:26 +02:00
Developer 02
501d48961e Refaktorisierung der DecodeEnvelopeReceiverId-Methode in EnvelopeGeneratorExtensions 2024-04-08 10:23:54 +02:00
Developer 02
ab713a23ac Feature-Update: Erweiterung der Envelope- und DocumentReceiverElement-Abfragen
Die Methoden `ReadAllWithAsync` und `ReadByUuidAsync` in `EnvelopeRepository` wurden erweitert, um optional DocumentReceiverElemente und Signaturdetails einzubeziehen.
2024-04-05 16:54:29 +02:00
29 changed files with 239 additions and 131 deletions

View File

@@ -7,5 +7,6 @@ namespace EnvelopeGenerator.Application.Contracts
{ {
public interface IEnvelopeReceiverService : IBasicCRUDService<IEnvelopeReceiverRepository, EnvelopeReceiverDto, EnvelopeReceiver, int> public interface IEnvelopeReceiverService : IBasicCRUDService<IEnvelopeReceiverRepository, EnvelopeReceiverDto, EnvelopeReceiver, int>
{ {
Task<IServiceMessage> VerifyAccessCode(string envelopeUuid, string accessCode);
} }
} }

View File

@@ -7,8 +7,8 @@ namespace EnvelopeGenerator.Application.Contracts
{ {
public interface IEnvelopeService : IBasicCRUDService<IEnvelopeRepository, EnvelopeDto, Envelope, int> public interface IEnvelopeService : IBasicCRUDService<IEnvelopeRepository, EnvelopeDto, Envelope, int>
{ {
Task<IServiceResult<IEnumerable<EnvelopeDto>>> ReadAllWithAsync(bool documents = false, bool receivers = false, bool history = false); Task<IServiceResult<IEnumerable<EnvelopeDto>>> ReadAllWithAsync(bool documents = false, bool receivers = false, bool history = false, bool documentReceiverElement = false);
Task<IServiceResult<EnvelopeDto>> ReadByUuidAsync(string uuid, bool withDocuments = false, bool withReceivers = false, bool withHistory = false); Task<IServiceResult<EnvelopeDto>> ReadByUuidAsync(string uuid, string? signature = null, bool withDocuments = false, bool withReceivers = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withAll = false);
} }
} }

View File

@@ -10,6 +10,6 @@ namespace EnvelopeGenerator.Application.DTOs
string Filepath, string Filepath,
DateTime AddedWhen, DateTime AddedWhen,
string FilenameOriginal, string FilenameOriginal,
ICollection<DocumentReceiverElement>? Elements IEnumerable<DocumentReceiverElement>? Elements
); );
} }

View File

@@ -32,7 +32,7 @@ namespace EnvelopeGenerator.Application.DTOs
bool IsAlreadySent, bool IsAlreadySent,
string? StatusTranslated, string? StatusTranslated,
string? ContractTypeTranslated, string? ContractTypeTranslated,
ICollection<EnvelopeDocument>? Documents, IEnumerable<EnvelopeDocumentDto>? Documents,
ICollection<EnvelopeReceiver>? Receivers, IEnumerable<EnvelopeReceiverDto>? Receivers,
ICollection<EnvelopeHistory>? History); IEnumerable<EnvelopeHistoryDto>? History);
} }

View File

@@ -1,4 +1,6 @@
namespace EnvelopeGenerator.Application.DTOs using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.DTOs
{ {
public record EnvelopeReceiverDto( public record EnvelopeReceiverDto(
int EnvelopeId, int EnvelopeId,
@@ -8,7 +10,8 @@
string JobTitle, string JobTitle,
string CompanyName, string CompanyName,
string PrivateMessage, string PrivateMessage,
string AccessCode,
DateTime AddedWhen, DateTime AddedWhen,
DateTime? ChangedWhen); DateTime? ChangedWhen,
Envelope? Envelope,
Receiver? Receiver);
} }

View File

@@ -0,0 +1,17 @@
namespace EnvelopeGenerator.Application.Services
{
public static class EnvelopeGeneratorExtensions
{
public static (string EnvelopeUuid, string ReceiverSignature) DecodeEnvelopeReceiverId(this string envelopeReceiverId)
{
byte[] bytes = Convert.FromBase64String(envelopeReceiverId);
string decodedString = System.Text.Encoding.UTF8.GetString(bytes);
string[] parts = decodedString.Split(new string[] { "::" }, StringSplitOptions.None);
if (parts.Length > 1)
return (EnvelopeUuid: parts[0], ReceiverSignature: parts[1]);
else
return (string.Empty, string.Empty);
}
}
}

View File

@@ -1,10 +1,12 @@
using AutoMapper; using AutoMapper;
using DigitalData.Core.Application; using DigitalData.Core.Application;
using DigitalData.Core.Contracts.Application;
using DigitalData.Core.Contracts.CultureServices; using DigitalData.Core.Contracts.CultureServices;
using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts; using EnvelopeGenerator.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Application.Services namespace EnvelopeGenerator.Application.Services
{ {
@@ -14,5 +16,11 @@ namespace EnvelopeGenerator.Application.Services
: base(repository, translationService, mapper) : base(repository, translationService, mapper)
{ {
} }
public async Task<IServiceMessage> VerifyAccessCode(string envelopeUuid, string accessCode)
{
var envelopeAccessCode = await _repository.ReadAccessCodeByEnvelopeUuid(envelopeUuid);
return CreateMessage(isSuccess: accessCode == envelopeAccessCode) ;
}
} }
} }

View File

@@ -16,16 +16,16 @@ namespace EnvelopeGenerator.Application.Services
{ {
} }
public async Task<IServiceResult<IEnumerable<EnvelopeDto>>> ReadAllWithAsync(bool documents = false, bool receivers = false, bool history = false) public async Task<IServiceResult<IEnumerable<EnvelopeDto>>> ReadAllWithAsync(bool documents = false, bool receivers = false, bool history = false, bool documentReceiverElement = false)
{ {
var envelopes = await _repository.ReadAllWithAsync(documents: documents, receivers: receivers, history: history); var envelopes = await _repository.ReadAllWithAsync(documents: documents, receivers: receivers, history: history, documentReceiverElement: documentReceiverElement);
var readDto = _mapper.MapOrThrow<IEnumerable<EnvelopeDto>>(envelopes); var readDto = _mapper.MapOrThrow<IEnumerable<EnvelopeDto>>(envelopes);
return Successful(readDto); return Successful(readDto);
} }
public async Task<IServiceResult<EnvelopeDto>> ReadByUuidAsync(string uuid, bool withDocuments = false, bool withReceivers = false, bool withHistory = false) public async Task<IServiceResult<EnvelopeDto>> ReadByUuidAsync(string uuid, string? signature = null, bool withDocuments = false, bool withReceivers = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withAll = false)
{ {
var envelope = await _repository.ReadByUuidAsync(uuid: uuid, withDocuments: withDocuments, withReceivers: withReceivers, withHistory: withHistory); var envelope = await _repository.ReadByUuidAsync(uuid: uuid, signature: signature, withDocuments: withDocuments, withReceivers: withReceivers, withHistory: withHistory, withDocumentReceiverElement: withDocumentReceiverElement, withAll:withAll);
if (envelope is null) if (envelope is null)
return Failed<EnvelopeDto>(); return Failed<EnvelopeDto>();

View File

@@ -109,10 +109,10 @@ namespace EnvelopeGenerator.Domain.Entities
} }
} }
public ICollection<EnvelopeDocument>? Documents { get; set; } public IEnumerable<EnvelopeDocument>? Documents { get; set; }
public ICollection<EnvelopeReceiver>? Receivers { get; set; } public IEnumerable<EnvelopeReceiver>? Receivers { get; set; }
public ICollection<EnvelopeHistory>? History { get; set; } public IEnumerable<EnvelopeHistory>? History { get; set; }
} }
} }

View File

@@ -30,6 +30,6 @@ namespace EnvelopeGenerator.Domain.Entities
[Column("FILENAME_ORIGINAL", TypeName = "nvarchar(256)")] [Column("FILENAME_ORIGINAL", TypeName = "nvarchar(256)")]
public string FilenameOriginal { get; set; } public string FilenameOriginal { get; set; }
public ICollection<DocumentReceiverElement>? Elements { get; set; } public IEnumerable<DocumentReceiverElement>? Elements { get; set; }
} }
} }

View File

@@ -6,9 +6,11 @@ namespace EnvelopeGenerator.Domain.Entities
[Table("TBSIG_ENVELOPE_RECEIVER", Schema = "dbo")] [Table("TBSIG_ENVELOPE_RECEIVER", Schema = "dbo")]
public class EnvelopeReceiver public class EnvelopeReceiver
{ {
[Key]
[Column("ENVELOPE_ID")] [Column("ENVELOPE_ID")]
public int EnvelopeId { get; set; } public int EnvelopeId { get; set; }
[Key]
[Column("RECEIVER_ID")] [Column("RECEIVER_ID")]
public int ReceiverId { get; set; } public int ReceiverId { get; set; }
@@ -37,5 +39,11 @@ namespace EnvelopeGenerator.Domain.Entities
[Column("CHANGED_WHEN", TypeName = "datetime")] [Column("CHANGED_WHEN", TypeName = "datetime")]
public DateTime? ChangedWhen { get; set; } public DateTime? ChangedWhen { get; set; }
[ForeignKey("EnvelopeId")]
public Envelope? Envelope { get; set; }
[ForeignKey("ReceiverId")]
public Receiver? Receiver { get; set; }
} }
} }

View File

@@ -22,5 +22,7 @@ namespace EnvelopeGenerator.Domain.Entities
[Required] [Required]
[Column("ADDED_WHEN", TypeName = "datetime")] [Column("ADDED_WHEN", TypeName = "datetime")]
public DateTime AddedWhen { get; set; } public DateTime AddedWhen { get; set; }
public IEnumerable<EnvelopeReceiver>? EnvelopeReceivers { get; set; }
} }
} }

View File

@@ -5,5 +5,6 @@ namespace EnvelopeGenerator.Infrastructure.Contracts
{ {
public interface IEnvelopeReceiverRepository : ICRUDRepository<EnvelopeReceiver, int> public interface IEnvelopeReceiverRepository : ICRUDRepository<EnvelopeReceiver, int>
{ {
Task<string?> ReadAccessCodeByEnvelopeUuid(string envelopeUuid);
} }
} }

View File

@@ -5,8 +5,8 @@ namespace EnvelopeGenerator.Infrastructure.Contracts
{ {
public interface IEnvelopeRepository : ICRUDRepository<Envelope, int> public interface IEnvelopeRepository : ICRUDRepository<Envelope, int>
{ {
Task<IEnumerable<Envelope>> ReadAllWithAsync(bool documents = false, bool receivers = false, bool history = false); Task<IEnumerable<Envelope>> ReadAllWithAsync(bool documents = false, bool receivers = false, bool history = false, bool documentReceiverElement = true);
Task<Envelope?> ReadByUuidAsync(string uuid, bool withDocuments = false, bool withReceivers = false, bool withHistory = false); Task<Envelope?> ReadByUuidAsync(string uuid, string? signature = null, bool withDocuments = false, bool withReceivers = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withAll = false);
} }
} }

View File

@@ -33,16 +33,31 @@ namespace DigitalData.UserManager.Infrastructure.Repositories
.WithOne() .WithOne()
.HasForeignKey(ed => ed.EnvelopeId); .HasForeignKey(ed => ed.EnvelopeId);
modelBuilder.Entity<Envelope>() //modelBuilder.Entity<Envelope>()
.HasMany(e => e.Receivers) // .HasMany(e => e.Receivers)
.WithOne() // .WithOne(er => er.Envelope)
.HasForeignKey(er => er.EnvelopeId); // .HasForeignKey(er => er.EnvelopeId);
modelBuilder.Entity<Envelope>() modelBuilder.Entity<Envelope>()
.HasMany(e => e.History) .HasMany(e => e.History)
.WithOne() .WithOne()
.HasForeignKey(eh => eh.EnvelopeId); .HasForeignKey(eh => eh.EnvelopeId);
modelBuilder.Entity<EnvelopeDocument>()
.HasMany(ed => ed.Elements)
.WithOne(e => e.Document)
.HasForeignKey(e => e.DocumentId);
modelBuilder.Entity<DocumentReceiverElement>()
.HasOne(dre => dre.Document)
.WithMany(ed => ed.Elements)
.HasForeignKey(dre => dre.DocumentId);
//modelBuilder.Entity<Receiver>()
// .HasMany(e => e.EnvelopeReceivers)
// .WithOne(er => er.Receiver)
// .HasForeignKey(er => er.ReceiverId);
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
} }
} }

View File

@@ -12,12 +12,15 @@ namespace EnvelopeGenerator.Infrastructure.Repositories
{ {
} }
public async Task<IEnumerable<Envelope>> ReadAllWithAsync(bool documents = false, bool receivers = false, bool history = false) public async Task<IEnumerable<Envelope>> ReadAllWithAsync(bool documents = false, bool receivers = false, bool history = false, bool documentReceiverElement = false)
{ {
var query = _dbSet.AsQueryable(); var query = _dbSet.AsQueryable();
if (documents) if (documents)
query = query.Include(e => e.Documents); if (documentReceiverElement)
query = query.Include(e => e.Documents!).ThenInclude(d => d.Elements);
else
query = query.Include(e => e.Documents);
if (receivers) if (receivers)
query = query.Include(e => e.Receivers); query = query.Include(e => e.Receivers);
@@ -28,17 +31,23 @@ namespace EnvelopeGenerator.Infrastructure.Repositories
return await query.ToListAsync(); return await query.ToListAsync();
} }
public async Task<Envelope?> ReadByUuidAsync(string uuid, bool withDocuments = false, bool withReceivers = false, bool withHistory = false) public async Task<Envelope?> ReadByUuidAsync(string uuid, string? signature = null, bool withDocuments = false, bool withReceivers = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withAll = false)
{ {
var query = _dbSet.Where(e => e.Uuid == uuid); var query = _dbSet.Where(e => e.Uuid == uuid);
if (withDocuments) if (signature is not null)
query = query.Include(e => e.Documents); query = query.Where(e => e.Receivers != null && e.Receivers.Any(er => er.Receiver != null && er.Receiver.Signature == signature));
if (withReceivers) if (withAll || withDocuments)
query = query.Include(e => e.Receivers); if (withDocumentReceiverElement)
query = query.Include(e => e.Documents!).ThenInclude(d => d.Elements);
else
query = query.Include(e => e.Documents);
if (withHistory) if (withAll || withReceivers)
query = query.Include(e => e.Receivers!).ThenInclude(er => er.Receiver);
if (withAll || withHistory)
query = query.Include(e => e.History); query = query.Include(e => e.History);
return await query.FirstOrDefaultAsync(); return await query.FirstOrDefaultAsync();

View File

@@ -2,6 +2,7 @@
using DigitalData.UserManager.Infrastructure.Repositories; using DigitalData.UserManager.Infrastructure.Repositories;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts; using EnvelopeGenerator.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Infrastructure.Repositories namespace EnvelopeGenerator.Infrastructure.Repositories
{ {
@@ -10,5 +11,15 @@ namespace EnvelopeGenerator.Infrastructure.Repositories
public EnvelopeReceiverRepository(EGDbContext dbContext) : base(dbContext) public EnvelopeReceiverRepository(EGDbContext dbContext) : base(dbContext)
{ {
} }
public async Task<string?> ReadAccessCodeByEnvelopeUuid(string envelopeUuid)
{
var accessCode = await _dbSet
.Where(er => er.Envelope != null && er.Envelope.Uuid == envelopeUuid)
.Select(er => er.AccessCode)
.FirstOrDefaultAsync();
return accessCode;
}
} }
} }

View File

@@ -20,25 +20,13 @@ namespace EnvelopeGenerator.Web.Controllers
} }
[HttpGet("api/envelope/{envelopeKey}")] [HttpGet("api/envelope/{envelopeKey}")]
public async Task<IActionResult> Get(string envelopeKey) public async Task<IActionResult> Get([FromRoute] string envelopeKey)
{ {
//_logger.LogInformation($"Loading Envelope by Key [{envelopeKey}]");
//Tuple<string, string> result = Helpers.DecodeEnvelopeReceiverId(envelopeKey);
//var envelopeUuid = result.Item1;
//var receiverSignature = result.Item2;
////var receiverId = receiverModel.GetReceiverIdBySignature(receiverSignature);
////_logger.LogInformation("Resolved receiver signature to receiverId [{0}]", receiverId);
//var envlopeServiceResult = await _envelopeService.ReadByUuidAsync(envelopeUuid, withDocuments:true, withReceivers:true, withHistory:true);
//_logger.LogInformation("Loading envelope..");
try try
{ {
// Validate Envelope Key and load envelope // Validate Envelope Key and load envelope
envelopeService.EnsureValidEnvelopeKey(envelopeKey); envelopeService.EnsureValidEnvelopeKey(envelopeKey);
EnvelopeResponse response = await envelopeService.LoadEnvelope(envelopeKey); EnvelopeResponse response = await envelopeService.LoadEnvelope(envelopeKey);
if (envelopeService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id) == true) if (envelopeService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id) == true)

View File

@@ -1,4 +1,5 @@
using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Application.Services; using EnvelopeGenerator.Application.Services;
using EnvelopeGenerator.Common; using EnvelopeGenerator.Common;
using EnvelopeGenerator.Web.Models; using EnvelopeGenerator.Web.Models;
@@ -13,11 +14,13 @@ namespace EnvelopeGenerator.Web.Controllers
{ {
private readonly EnvelopeOldService envelopeOldService; private readonly EnvelopeOldService envelopeOldService;
private readonly IConfiguration _config; private readonly IConfiguration _config;
private readonly IEnvelopeReceiverService _envRcvService;
private readonly IEnvelopeService _envelopeService; private readonly IEnvelopeService _envelopeService;
public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger<HomeController> logger, IConfiguration configuration, IEnvelopeService envelopeService) : base(databaseService, logger) public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger<HomeController> logger, IConfiguration configuration, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeService envelopeService) : base(databaseService, logger)
{ {
this.envelopeOldService = envelopeOldService; this.envelopeOldService = envelopeOldService;
_envRcvService = envelopeReceiverService;
_envelopeService = envelopeService; _envelopeService = envelopeService;
_config = configuration; _config = configuration;
} }
@@ -65,51 +68,28 @@ namespace EnvelopeGenerator.Web.Controllers
} }
[HttpGet("/EnvelopeKey/{envelopeReceiverId}")] [HttpGet("/EnvelopeKey/{envelopeReceiverId}")]
public async Task<IActionResult> ShowEnvelope([FromRoute] string envelopeReceiverId) public IActionResult ShowEnvelope([FromRoute] string envelopeReceiverId) => Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked");
{
EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId);
if (response.Envelope.UseAccessCode)
{
bool accessCodeAlreadyRequested = database.Models.receiverModel.AccessCodeAlreadyRequested(response.Receiver.Email, response.Envelope.Id);
if (!accessCodeAlreadyRequested)
{
// Send email with password
bool actionResult = database.Services.actionService.RequestAccessCode(response.Envelope, response.Receiver);
bool result = database.Services.emailService.SendDocumentAccessCodeReceivedEmail(response.Envelope, response.Receiver);
}
return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked");
}
else
{
ViewData["EnvelopeKey"] = envelopeReceiverId;
return View();
}
}
[HttpPost("/EnvelopeKey/{envelopeReceiverId}/Locked")] [HttpPost("/EnvelopeKey/{envelopeReceiverId}/Locked")]
public async Task<IActionResult> ShowEnvelopePost([FromRoute] string envelopeReceiverId, [FromForm] string access_code) public async Task<IActionResult> ShowEnvelope([FromRoute] string envelopeReceiverId, [FromForm] string access_code)
{ {
var decodedId = envelopeReceiverId.DecodeEnvelopeReceiverId();
var verification = await _envRcvService.VerifyAccessCode(decodedId.EnvelopeUuid, access_code);
EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId); EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId);
string accessCode = response.Receiver.AccessCode;
if (string.IsNullOrEmpty(access_code)) if (verification.IsSuccess)
{ {
return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked"); var envelope = await _envelopeService.ReadByUuidAsync(uuid: decodedId.EnvelopeUuid, signature: decodedId.ReceiverSignature, withAll:true);
} database.Services.actionService.EnterCorrectAccessCode(response.Envelope, response.Receiver); //for history
if (accessCode == access_code)
{
bool actionResult = database.Services.actionService.EnterCorrectAccessCode(response.Envelope, response.Receiver);
ViewData["EnvelopeKey"] = envelopeReceiverId; ViewData["EnvelopeKey"] = envelopeReceiverId;
return View("ShowEnvelope"); ViewData["EnvelopeResponse"] = response;
return View("ShowEnvelope", envelope);
} }
else else
{ {
bool actionResult = database.Services.actionService.EnterIncorrectAccessCode(response.Envelope, response.Receiver); database.Services.actionService.EnterIncorrectAccessCode(response.Envelope, response.Receiver); //for history
return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked"); return Unauthorized();
} }
} }
@@ -117,13 +97,6 @@ namespace EnvelopeGenerator.Web.Controllers
[HttpGet("/EnvelopeKey/{envelopeReceiverId}/Locked")] [HttpGet("/EnvelopeKey/{envelopeReceiverId}/Locked")]
public async Task<IActionResult> EnvelopeLocked([FromRoute] string envelopeReceiverId) public async Task<IActionResult> EnvelopeLocked([FromRoute] string envelopeReceiverId)
{ {
Tuple<string, string> decode = Common.Helpers.DecodeEnvelopeReceiverId(envelopeReceiverId);
var envelopeUuid = decode.Item1;
var envlopeServiceResult = await _envelopeService.ReadByUuidAsync(envelopeUuid, withDocuments: true, withReceivers: true, withHistory: true);
ViewData["Envelope"] = envlopeServiceResult.Data;
ViewData["EnvelopeKey"] = envelopeReceiverId; ViewData["EnvelopeKey"] = envelopeReceiverId;
return View(); return View();
} }

View File

@@ -1,6 +1,7 @@
using DigitalData.Core.API; using DigitalData.Core.API;
using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Application.Services;
using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts; using EnvelopeGenerator.Infrastructure.Contracts;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -24,9 +25,12 @@ namespace EnvelopeGenerator.Web.Controllers
{ {
if(envelopeKey is not null) if(envelopeKey is not null)
{ {
Tuple<string, string> decode = Common.Helpers.DecodeEnvelopeReceiverId(envelopeKey); var decoded = envelopeKey.DecodeEnvelopeReceiverId();
var envelopeUuid = decode.Item1;
var envlopeServiceResult = await _service.ReadByUuidAsync(envelopeUuid, withDocuments: true, withReceivers: true, withHistory: true); var envlopeServiceResult = await _service.ReadByUuidAsync(
uuid: decoded.EnvelopeUuid,
signature: decoded.ReceiverSignature,
withDocuments: withDocuments, withReceivers: withReceivers, withHistory: withHistory);
if (envlopeServiceResult.IsSuccess) if (envlopeServiceResult.IsSuccess)
{ {

View File

@@ -16,8 +16,6 @@
<section class="text-center"> <section class="text-center">
<p>Sie haben das Dokument signiert. Im Anschluss erhalten Sie eine schriftliche Bestätigung.</p> <p>Sie haben das Dokument signiert. Im Anschluss erhalten Sie eine schriftliche Bestätigung.</p>
</section> </section>
</div> </div>
<footer class="container" id="page-footer">&copy; SignFlow 2023-2024 <a href="https://digitaldata.works">Digital Data GmbH</a></footer> <footer class="container" id="page-footer">&copy; SignFlow 2023-2024 <a href="https://digitaldata.works">Digital Data GmbH</a></footer>

View File

@@ -1,28 +1,77 @@
@using EnvelopeGenerator.Application.DTOs; @using DigitalData.Core.Contracts.Application;
@using EnvelopeGenerator.Application.DTOs;
@model IServiceResult<EnvelopeDto>;
@{ @{
ViewData["Title"] = "Dokument unterschreiben"; ViewData["Title"] = "Dokument unterschreiben";
EnvelopeDto? envelopeDto = ViewData["envelope"] as EnvelopeDto;
} }
@if(envelopeDto is not null) @if (Model.IsSuccess && Model.Data is not null)
{ {
var envelope = Model.Data;
var document = envelope.Documents?.FirstOrDefault();
var receiver = envelope.Receivers?.FirstOrDefault();
var receiverName = receiver?.Name ?? string.Empty;
var pages = document?.Elements?.Select(e => e.Page) ?? Array.Empty<int>();
var stPageIndexes = string.Join(pages.Count() > 1 ? ", " : "", pages.Take(pages.Count() - 1))
+ (pages.Count() > 1 ? " und " : "") + pages.LastOrDefault();
<nav class="navbar navbar-light bg-light"> <nav class="navbar navbar-light bg-light">
<div class="container-fluid"> <div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggleExternalContent" aria-controls="navbarToggleExternalContent" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggleExternalContent" aria-controls="navbarToggleExternalContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="navbar-brand">Bitte prüfen Sie diese Dokumente und handeln Sie danach</div> <div class="navbar-brand me-auto ms-5 envelope-message">@($"Hallo {receiverName}, {@envelope.Message}")</div>
<div class="col-1 p-0 m-0 me-3 d-flex">
<img src="~/img/digital_data.svg" alt="...">
</div>
</div> </div>
</nav> </nav>
<div class="collapse" id="navbarToggleExternalContent" data-bs-theme="light"> <div class="collapse show" id="navbarToggleExternalContent" data-bs-theme="light">
<div class="bg-light p-1"> <div class="bg-light p-1">
<h5 class="text-body-emphasis h4">Collapsed content</h5> <div class="card sender-card mb-3">
<span class="text-body-secondary">Toggleable via the navbar brand.</span> <div class="row g-0">
<div class="col-1 p-0 m-0 ps-4 mx-auto">
<img src="~/img/default-user.svg" class="img-fluid p-0 m-0" alt="...">
</div>
<div class="col p-0 m-0">
<div class="card-body p-0 m-0">
<h5 class="card-title p-0 m-0">@($"{envelope.Title}")</h5>
<p class="card-text p-0 m-0">@($"Sie haben {(pages.Count())} Briefe zu unterschreiben. Bitte prüfen Sie die Seiten {stPageIndexes}.")</p>
<p class="card-text p-0 m-0"><small class="text-body-secondary">Erstellt am @envelope.AddedWhen</small></p>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
} }
<script> <script>
const collapseNav = () => {
document.addEventListener('click', function (event) {
var navbarToggle = document.getElementById('navbarToggleExternalContent');
var navbarButton = document.querySelector('[data-bs-target="#navbarToggleExternalContent"]');
var isCollapsed = new bootstrap.Collapse(navbarToggle)._isTransitioning;
if (!navbarToggle.contains(event.target) && !navbarButton.contains(event.target) && !isCollapsed) {
new bootstrap.Collapse(navbarToggle).hide();
}
});
}
@{
var envelopeResponse = ViewData["EnvelopeResponse"];
var settings = new Newtonsoft.Json.JsonSerializerSettings
{
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};
var envelopeResponseJson = Newtonsoft.Json.JsonConvert.SerializeObject(envelopeResponse, settings);
}
var envelopeResponse = @Html.Raw(envelopeResponseJson);
console.log(envelopeResponse);
document.addEventListener("DOMContentLoaded", async () => { document.addEventListener("DOMContentLoaded", async () => {
const app = new App("#app", "@ViewData["EnvelopeKey"]"); const app = new App("#app", "@ViewData["EnvelopeKey"]", envelopeResponse);
await app.init(); await app.init();
}) })
</script> </script>

View File

@@ -11,10 +11,6 @@
<link rel="stylesheet" href="~/EnvelopeGenerator.Web.styles.css" asp-append-version="true" /> <link rel="stylesheet" href="~/EnvelopeGenerator.Web.styles.css" asp-append-version="true" />
</head> </head>
<body> <body>
<main role="main">
@RenderBody()
</main>
<script src="~/lib/jquery/dist/jquery.min.js"></script> <script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script> <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/lib/sweetalert2/sweetalert2.min.js"></script> <script src="~/lib/sweetalert2/sweetalert2.min.js"></script>
@@ -24,6 +20,11 @@
<script src="~/js/app.js" asp-append-version="true"></script> <script src="~/js/app.js" asp-append-version="true"></script>
<script src="~/lib/pspdfkit/pspdfkit.js" asp-append-version="true"></script> <script src="~/lib/pspdfkit/pspdfkit.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false) @await RenderSectionAsync("Scripts", required: false)
<main role="main">
@RenderBody()
</main>
@Html.AntiForgeryToken() @Html.AntiForgeryToken()
</body> </body>

View File

@@ -120,3 +120,19 @@ footer#page-footer a:visited,
footer#page-footer a:focus { footer#page-footer a:focus {
color: #444; color: #444;
} }
.sender-card {
background-color: transparent;
border: none;
}
.sender-card .row {
height: 7vh;
}
.sender-card img{
height: 7vh;
background-color: rgb(209, 207, 207);
border-radius: 50px;
}
.envelope-message {
font-family: 'Roboto', sans-serif;
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="6" r="4" fill="#1C274C"/>
<ellipse opacity="0.5" cx="12" cy="17" rx="7" ry="4" fill="#1C274C"/>
</svg>

After

Width:  |  Height:  |  Size: 343 B

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 850 121.32">
<defs>
<style>
.cls-1 {
fill: #a52431;
stroke-width: 0px;
}
</style>
</defs>
<path class="cls-1" d="M83.01,60.66c0-35.12-12.77-57.47-41.51-57.47H0v114.94h39.91c30.33,0,43.1-20.75,43.1-57.47M49.49,60.66c0,19.16-1.6,28.73-15.96,28.73V31.93c14.37,0,15.96,12.77,15.96,28.73M126.11,3.19h-33.52v114.94h33.52V3.19ZM223.49,97.38v-46.29h-39.91l-1.6,23.95h12.77v9.58c-3.19,4.79-6.39,7.98-12.77,7.98-7.98,0-12.77-11.17-12.77-31.93s4.79-31.93,12.77-31.93c6.86,0,11.33,5.75,12.77,14.37l28.73-6.39C218.7,15.96,207.52,0,181.98,0c-33.52,0-46.29,28.73-46.29,60.66s12.77,60.66,46.29,60.66c17.56,0,31.93-7.98,41.51-23.94M268.18,3.19h-33.52v114.94h33.52V3.19ZM346.4,31.93l-3.19-28.74h-65.45l-3.19,28.74h19.16v86.2h33.52V31.93h19.16ZM427.81,118.13L399.07,3.19h-31.93l-28.73,114.94h35.12l1.6-14.37h15.96l1.6,14.37h35.12ZM387.58,76.62h-8.94l3.51-28.74c.32-3.19.64-6.38.8-9.58h.32c.16,3.19.48,6.38.8,9.58l3.51,28.74ZM493.26,89.4h-25.54V3.19h-33.52v114.94h55.87l3.19-28.74ZM614.58,60.66c0-35.12-12.77-57.47-41.51-57.47h-41.51v114.94h39.91c30.33,0,43.1-20.75,43.1-57.47M581.05,60.66c0,19.16-1.6,28.73-15.96,28.73V31.93c14.37,0,15.96,12.77,15.96,28.73M704.75,118.13l-28.73-114.94h-31.93l-28.74,114.94h35.12l1.6-14.37h15.96l1.6,14.37h35.12ZM664.52,76.62h-8.94l3.51-28.74c.32-3.19.64-6.38.8-9.58h.32c.16,3.19.48,6.38.8,9.58l3.51,28.74ZM768.59,31.93l-3.19-28.74h-65.45l-3.19,28.74h19.16v86.2h33.52V31.93h19.16ZM850,118.13l-28.73-114.94h-31.93l-28.73,114.94h35.12l1.6-14.37h15.96l1.6,14.37h35.12ZM809.77,76.62h-8.94l3.51-28.74c.32-3.19.64-6.38.8-9.58h.32c.16,3.19.48,6.38.8,9.58l3.51,28.74Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -10,7 +10,7 @@ const ActionType = {
} }
class App { class App {
constructor(container, envelopeKey) { constructor(container, envelopeKey, envelopeResponse) {
this.container = container this.container = container
this.envelopeKey = envelopeKey this.envelopeKey = envelopeKey
@@ -22,33 +22,15 @@ class App {
this.currentDocument = null this.currentDocument = null
this.currentReceiver = null this.currentReceiver = null
this.signatureCount = 0 this.signatureCount = 0
this.envelopeResponse = envelopeResponse;
} }
// This function will be called from the ShowEnvelope.razor page // This function will be called from the ShowEnvelope.razor page
// and will trigger loading of the Editor Interface // and will trigger loading of the Editor Interface
async init() { async init() {
// Load the envelope from the database // Load the envelope from the database
console.debug('Loading envelope from database..') this.currentDocument = this.envelopeResponse.envelope.documents[0]
const envelopeResponse = await this.Network.getEnvelope(this.envelopeKey) this.currentReceiver = this.envelopeResponse.receiver
if (envelopeResponse.fatal) {
return Swal.fire({
title: 'Fehler',
text: 'Umschlag konnte nicht geladen werden!',
icon: 'error',
})
}
if (envelopeResponse.error) {
return Swal.fire({
title: 'Warnung',
text: 'Umschlag ist nicht mehr verfügbar.',
icon: 'warning',
})
}
this.currentDocument = envelopeResponse.data.envelope.documents[0]
this.currentReceiver = envelopeResponse.data.receiver
// Load the document from the filestore // Load the document from the filestore
console.debug('Loading document from filestore') console.debug('Loading document from filestore')

View File

@@ -66,6 +66,7 @@
*/ */
getCSRFToken() { getCSRFToken() {
const token = document.getElementsByName('__RequestVerificationToken')[0].value const token = document.getElementsByName('__RequestVerificationToken')[0].value
console.log(token)
return { 'X-XSRF-TOKEN': token } return { 'X-XSRF-TOKEN': token }
} }
@@ -73,7 +74,7 @@
* Creates a GET HTTP request to `url` * Creates a GET HTTP request to `url`
* @param {any} url * @param {any} url
*/ */
getRequest(url) { getRequest(url, body) {
const token = this.getCSRFToken() const token = this.getCSRFToken()
const options = { const options = {
credentials: 'include', credentials: 'include',
@@ -83,6 +84,10 @@
} }
} }
if (body !== undefined) {
options.body = JSON.stringify(body);
}
return fetch(url, options) return fetch(url, options)
} }