This commit is contained in:
SchreiberM 2024-03-15 11:47:30 +01:00
commit a3c66ecd19
36 changed files with 537 additions and 329 deletions

View File

@ -0,0 +1,14 @@
using DigitalData.Core.Contracts.Application;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
namespace EnvelopeGenerator.Application.Contracts
{
public interface IConfigService : IBasicCRUDService<IConfigRepository, ConfigDto, Config, int>
{
Task<IServiceResult<ConfigDto>> ReadFirstAsync();
async Task<IServiceResult<ConfigDto>> ReadDefaultAsync() => await ReadFirstAsync();
}
}

View File

@ -0,0 +1,11 @@
using DigitalData.Core.Contracts.Application;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
namespace EnvelopeGenerator.Application.Contracts
{
public interface IEnvelopeDocumentService : IBasicCRUDService<IEnvelopeDocumentRepository, EnvelopeDocumentDto, EnvelopeDocument, int>
{
}
}

View File

@ -0,0 +1,15 @@
namespace EnvelopeGenerator.Application.DTOs
{
public record ConfigDto
{
public string DocumentPath { get; init; }
public int SendingProfile { get; init; }
public string SignatureHost { get; init; }
public string ExternalProgramName { get; init; }
public string ExportPath { get; init; }
public string DocumentPathDmz { get; init; }
public string ExportPathDmz { get; init; }
public string SignedMailPath { get; init; }
public string DocumentPathMoveAftsend { get; init; }
}
}

View File

@ -0,0 +1,12 @@
namespace EnvelopeGenerator.Application.DTOs
{
public record EnvelopeDocumentDto
{
public int Guid { get; init; }
public int EnvelopeId { get; init; }
public string Filename { get; init; }
public string Filepath { get; init; }
public DateTime AddedWhen { get; init; }
public string FilenameOriginal { get; init; }
}
}

View File

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="DigitalData.Core.Application">
<HintPath>..\..\WebCoreModules\DigitalData.Core.Application\bin\Debug\net7.0\DigitalData.Core.Application.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.Contracts">
<HintPath>..\..\WebCoreModules\DigitalData.Core.Application\bin\Debug\net7.0\DigitalData.Core.Contracts.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,18 @@
using AutoMapper;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.MappingProfiles
{
public class BasicDtoMappingProfile : Profile
{
public BasicDtoMappingProfile()
{
CreateMap<Config, ConfigDto>();
CreateMap<EnvelopeDocument, EnvelopeDocumentDto>();
CreateMap<ConfigDto, Config>();
CreateMap<EnvelopeDocumentDto, EnvelopeDocument>();
}
}
}

View File

@ -0,0 +1,30 @@
using AutoMapper;
using DigitalData.Core.Application;
using DigitalData.Core.Contracts.Application;
using DigitalData.Core.Contracts.CultureServices;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
namespace EnvelopeGenerator.Application.Services
{
public class ConfigService : BasicCRUDService<IConfigRepository, ConfigDto, Config, int>, IConfigService
{
public ConfigService(IConfigRepository repository, IKeyTranslationService translationService, IMapper mapper) : base(repository, translationService, mapper)
{
}
public async Task<IServiceResult<ConfigDto>> ReadFirstAsync()
{
var config = await _repository.ReadFirstAsync();
if (config is null)
return Failed<ConfigDto>("There is no configuration in DB.");
return Successful(_mapper.MapOrThrow<ConfigDto>(config));
}
public async Task<IServiceResult<ConfigDto>> ReadDefaultAsync() => await ReadFirstAsync();
}
}

View File

@ -0,0 +1,18 @@
using AutoMapper;
using DigitalData.Core.Application;
using DigitalData.Core.Contracts.Application;
using DigitalData.Core.Contracts.CultureServices;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
namespace EnvelopeGenerator.Application.Services
{
public class EnvelopeDocumentService : BasicCRUDService<IEnvelopeDocumentRepository, EnvelopeDocumentDto, EnvelopeDocument, int>, IEnvelopeDocumentService
{
public EnvelopeDocumentService(IEnvelopeDocumentRepository repository, IKeyTranslationService translationService, IMapper mapper) : base(repository, translationService, mapper)
{
}
}
}

View File

@ -19,10 +19,4 @@ Public Class EnvelopeDocument
Public Property Filepath As String Public Property Filepath As String
Public Property PageCount As Integer Public Property PageCount As Integer
End Class
Public ReadOnly Property PageCountTranslated As String
Get
Return $"{PageCount} Seiten"
End Get
End Property
End Class

View File

@ -45,7 +45,7 @@ Public Class DocumentModel
Public Function List(pEnvelopeId As Integer) As IEnumerable(Of EnvelopeDocument) Public Function List(pEnvelopeId As Integer) As IEnumerable(Of EnvelopeDocument)
Try Try
Dim oSql = $"SELECT FROM [dbo].[TBSIG_ENVELOPE_DOCUMENT] WHERE ENVELOPE_ID = {pEnvelopeId}" Dim oSql = $"SELECT * FROM [dbo].[TBSIG_ENVELOPE_DOCUMENT] WHERE ENVELOPE_ID = {pEnvelopeId}"
Dim oTable = Database.GetDatatable(oSql) Dim oTable = Database.GetDatatable(oSql)
Return oTable?.Rows.Cast(Of DataRow). Return oTable?.Rows.Cast(Of DataRow).

View File

@ -0,0 +1,48 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EnvelopeGenerator.Domain.Entities
{
[Table("TBSIG_CONFIG", Schema = "dbo")]
public class Config
{
[Column("DOCUMENT_PATH", TypeName = "nvarchar(256)")]
public string DocumentPath { get; set; }
[Column("SENDING_PROFILE", TypeName = "int")]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.None)] // Assuming SENDING_PROFILE is manually entered or controlled by the application logic
[DefaultValue(0)] // This sets the default value for SENDING_PROFILE
public int SendingProfile { get; set; }
[Column("SIGNATURE_HOST", TypeName = "nvarchar(128)")]
public string SignatureHost { get; set; }
[Column("EXTERNAL_PROGRAM_NAME", TypeName = "nvarchar(30)")]
public string ExternalProgramName { get; set; }
[Column("EXPORT_PATH", TypeName = "nvarchar(256)")]
public string ExportPath { get; set; }
[Column("DOCUMENT_PATH_DMZ", TypeName = "nvarchar(512)")]
[Required]
[DefaultValue("")] // This sets the default value for DOCUMENT_PATH_DMZ
public string DocumentPathDmz { get; set; }
[Column("EXPORT_PATH_DMZ", TypeName = "nvarchar(512)")]
[Required]
[DefaultValue("")] // This sets the default value for EXPORT_PATH_DMZ
public string ExportPathDmz { get; set; }
[Column("SIGNED_MAIL_PATH", TypeName = "nvarchar(512)")]
[Required]
[DefaultValue("")] // This sets the default value for SIGNED_MAIL_PATH
public string SignedMailPath { get; set; }
[Column("DOCUMENT_PATH_MOVE_AFTSEND", TypeName = "nvarchar(512)")]
[Required]
[DefaultValue("")] // This sets the default value for DOCUMENT_PATH_MOVE_AFTSEND
public string DocumentPathMoveAftsend { get; set; }
}
}

View File

@ -0,0 +1,33 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EnvelopeGenerator.Domain.Entities
{
[Table("TBSIG_ENVELOPE_DOCUMENT", Schema = "dbo")]
public class EnvelopeDocument
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("GUID", TypeName = "int")]
public int Guid { get; set; }
[Column("ENVELOPE_ID", TypeName = "int")]
[Required]
public int EnvelopeId { get; set; }
[Column("FILENAME", TypeName = "nvarchar(256)")]
[Required]
public string Filename { get; set; }
[Column("FILEPATH", TypeName = "nvarchar(256)")]
[Required]
public string Filepath { get; set; }
[Column("ADDED_WHEN", TypeName = "datetime")]
[Required]
public DateTime AddedWhen { get; set; }
[Column("FILENAME_ORIGINAL", TypeName = "nvarchar(256)")]
public string FilenameOriginal { get; set; }
}
}

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,11 @@
using DigitalData.Core.Contracts.Application;
using DigitalData.Core.Contracts.Infrastructure;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Infrastructure.Contracts
{
public interface IConfigRepository : ICRUDRepository<Config, int>
{
Task<Config?> ReadFirstAsync();
}
}

View File

@ -0,0 +1,9 @@
using DigitalData.Core.Contracts.Infrastructure;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Infrastructure.Contracts
{
public interface IEnvelopeDocumentRepository : ICRUDRepository<EnvelopeDocument, int>
{
}
}

View File

@ -0,0 +1,20 @@
using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
namespace DigitalData.UserManager.Infrastructure.Repositories
{
public class EGDbContext : DbContext
{
public EGDbContext(DbContextOptions<EGDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Config>().HasNoKey();
modelBuilder.Entity<EnvelopeDocument>();
base.OnModelCreating(modelBuilder);
}
}
}

View File

@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.16" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.15" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="DigitalData.Core.Contracts">
<HintPath>..\..\WebCoreModules\DigitalData.Core.Infrastructure\bin\Debug\net7.0\DigitalData.Core.Contracts.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.Infrastructure">
<HintPath>..\..\WebCoreModules\DigitalData.Core.Infrastructure\bin\Debug\net7.0\DigitalData.Core.Infrastructure.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,21 @@
using DigitalData.Core.Infrastructure;
using DigitalData.UserManager.Infrastructure.Repositories;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Infrastructure.Repositories
{
public class ConfigRepository : CRUDRepository<Config, int, EGDbContext>, IConfigRepository
{
public ConfigRepository(EGDbContext dbContext) : base(dbContext)
{
}
public async Task<Config?> ReadFirstAsync()
{
var configs = await _dbSet.ToListAsync();
return configs.Count > 0 ? configs[0] : default;
}
}
}

View File

@ -0,0 +1,14 @@
using DigitalData.Core.Infrastructure;
using DigitalData.UserManager.Infrastructure.Repositories;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
namespace EnvelopeGenerator.Infrastructure.Repositories
{
public class EnvelopeDocumentRepository : CRUDRepository<EnvelopeDocument, int, EGDbContext>, IEnvelopeDocumentRepository
{
public EnvelopeDocumentRepository(EGDbContext dbContext) : base(dbContext)
{
}
}
}

View File

@ -1,192 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using DigitalData.Modules.Base;
using DigitalData.Modules.Logging;
using GdPicture14;
using Newtonsoft.Json;
namespace EnvelopeGenerator.Web.Classes
{
public partial class PDFBurner : BaseClass
{
private readonly string LicenseKey;
private readonly AnnotationManager Manager;
private readonly LicenseManager LicenseManager;
private const string ANNOTATION_TYPE_IMAGE = "pspdfkit/image";
private const string ANNOTATION_TYPE_INK = "pspdfkit/ink";
public PDFBurner(LogConfig pLogConfig, string pGDPictureLicenseKey) : base(pLogConfig)
{
LicenseKey = pGDPictureLicenseKey;
LicenseManager = new LicenseManager();
LicenseManager.RegisterKEY(pGDPictureLicenseKey);
Manager = new AnnotationManager();
}
public bool BurnInstantJSONAnnotationsToPDF(string pSourcePath, List<string> pInstantJSONList, string pDestinationPath)
{
if (Manager.InitFromFile(pSourcePath) != GdPictureStatus.OK)
{
Logger.Warn("Could not open file [{0}] for burning.", pSourcePath);
return false;
}
foreach (var oJSON in pInstantJSONList)
{
if (AddInstantJSONAnnotationToPDF(oJSON) == false)
{
Logger.Warn("Adding Annotation failed. Exiting");
return false;
}
}
try
{
Manager.BurnAnnotationsToPage(RemoveInitialAnnots: true, VectorMode: true);
Manager.SaveDocumentToPDF(pDestinationPath);
Manager.Close();
return true;
}
catch (Exception ex)
{
Logger.Warn("Could not burn and save annotations to file [{0}]!", pDestinationPath);
Logger.Error(ex);
return false;
}
}
private bool AddInstantJSONAnnotationToPDF(string pInstantJSON)
{
try
{
var oAnnotationData = JsonConvert.DeserializeObject<AnnotationData>(pInstantJSON);
foreach (Annotation oAnnotation in oAnnotationData.annotations)
{
switch (oAnnotation.type)
{
case ANNOTATION_TYPE_IMAGE:
{
AddImageAnnotation(oAnnotation, oAnnotationData.attachments);
break;
}
case ANNOTATION_TYPE_INK:
{
AddInkAnnotation(oAnnotation);
break;
}
}
}
return true;
}
catch (Exception ex)
{
Logger.Warn("Could not create annotation from InstantJSON");
Logger.Error(ex);
return false;
}
}
private bool AddImageAnnotation(Annotation pAnnotation, Dictionary<string, Attachment> pAttachments)
{
try
{
var oAttachment = pAttachments.Where(a => (a.Key ?? "") == (pAnnotation.imageAttachmentId ?? "")).SingleOrDefault();
// Convert pixels to Inches
var oBounds = pAnnotation.bbox.Select(ToInches).ToList();
float oX = oBounds[0];
float oY = oBounds[1];
float oWidth = oBounds[2];
float oHeight = oBounds[3];
Manager.AddEmbeddedImageAnnotFromBase64(oAttachment.Value.binary, oX, oY, oWidth, oHeight);
return true;
}
catch (Exception ex)
{
Logger.Warn("Could not add image annotation!");
Logger.Error(ex);
return false;
}
}
private bool AddInkAnnotation(Annotation pAnnotation)
{
try
{
var oSegments = pAnnotation.lines.points;
var oColor = ColorTranslator.FromHtml(pAnnotation.strokeColor);
Manager.SelectPage(pAnnotation.pageIndex);
foreach (List<List<float>> oSegment in oSegments)
{
var oPoints = oSegment.Select(ToPointF).ToArray();
Manager.AddFreeHandAnnot(oColor, oPoints);
}
return true;
}
catch (Exception ex)
{
Logger.Warn("Could not add image annotation!");
Logger.Error(ex);
return false;
}
}
private PointF ToPointF(List<float> pPoints)
{
var oPoints = pPoints.Select(ToInches).ToList();
return new PointF(oPoints[0], oPoints[1]);
}
private float ToInches(float pValue)
{
return pValue / 72f;
}
internal partial class AnnotationData
{
public List<Annotation> annotations { get; set; }
public Dictionary<string, Attachment> attachments { get; set; }
}
internal partial class Annotation
{
public string id { get; set; }
public List<float> bbox { get; set; }
public string type { get; set; }
public bool isSignature { get; set; }
public string imageAttachmentId { get; set; }
public Lines lines { get; set; }
public int pageIndex { get; set; }
public string strokeColor { get; set; }
}
internal partial class Lines
{
public List<List<List<float>>> points { get; set; }
}
internal partial class Attachment
{
public string binary { get; set; }
public string contentType { get; set; }
}
}
}

View File

@ -1,12 +0,0 @@
namespace EnvelopeGenerator.Web
{
public class Constants
{
public enum ErrorType
{
None,
ServerError,
FilesystemError
}
}
}

View File

@ -2,7 +2,6 @@
using EnvelopeGenerator.Common; using EnvelopeGenerator.Common;
using EnvelopeGenerator.Web.Services; using EnvelopeGenerator.Web.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static EnvelopeGenerator.Web.Constants;
namespace EnvelopeGenerator.Web.Controllers namespace EnvelopeGenerator.Web.Controllers
{ {
@ -27,7 +26,7 @@ namespace EnvelopeGenerator.Web.Controllers
return Problem( return Problem(
statusCode: 500, statusCode: 500,
detail: e.Message, detail: e.Message,
type: ErrorType.ServerError.ToString()); type: "ServerError");
} }
} }
} }

View File

@ -0,0 +1,15 @@
using DigitalData.Core.API;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
namespace EnvelopeGenerator.Web.Controllers
{
public class ConfigTestController : CRUDControllerBase<ConfigTestController, IConfigService, IConfigRepository, ConfigDto, ConfigDto, ConfigDto, Config, int>
{
public ConfigTestController(ILogger<ConfigTestController> logger, IConfigService service) : base(logger, service)
{
}
}
}

View File

@ -2,6 +2,7 @@
using EnvelopeGenerator.Common; using EnvelopeGenerator.Common;
using EnvelopeGenerator.Web.Services; using EnvelopeGenerator.Web.Services;
using static EnvelopeGenerator.Common.Constants; using static EnvelopeGenerator.Common.Constants;
using EnvelopeGenerator.Application.Contracts;
namespace EnvelopeGenerator.Web.Controllers namespace EnvelopeGenerator.Web.Controllers
{ {
@ -9,16 +10,18 @@ namespace EnvelopeGenerator.Web.Controllers
{ {
private readonly EnvelopeService envelopeService; private readonly EnvelopeService envelopeService;
private readonly ActionService? actionService; private readonly ActionService? actionService;
private readonly IEnvelopeDocumentService _envDocService;
public DocumentController(DatabaseService database, LoggingService logging, EnvelopeService envelope) : base(database, logging) public DocumentController(DatabaseService database, LoggingService logging, EnvelopeService envelope, IEnvelopeDocumentService envDocService) : base(database, logging)
{ {
envelopeService = envelope; envelopeService = envelope;
actionService = database.Services?.actionService; actionService = database.Services?.actionService;
_envDocService = envDocService;
} }
[HttpGet] [HttpGet]
[Route("api/document/{envelopeKey}")] [Route("api/document/{envelopeKey}")]
public async Task<IActionResult> Get(string envelopeKey) public async Task<IActionResult> Get([FromRoute] string envelopeKey, [FromQuery] int index)
{ {
try try
{ {
@ -26,11 +29,11 @@ namespace EnvelopeGenerator.Web.Controllers
// Validate Envelope Key and load envelope // Validate Envelope Key and load envelope
envelopeService.EnsureValidEnvelopeKey(envelopeKey); envelopeService.EnsureValidEnvelopeKey(envelopeKey);
EnvelopeResponse response = envelopeService.LoadEnvelope(envelopeKey); EnvelopeResponse response = await envelopeService.LoadEnvelope(envelopeKey);
// Load document info // Load document info
var Request = ControllerContext.HttpContext.Request; var Request = ControllerContext.HttpContext.Request;
var document = envelopeService.GetDocument(Request, envelopeKey); var document = await envelopeService.GetDocument(Request, envelopeKey);
// Load the document from disk // Load the document from disk
var bytes = await envelopeService.GetDocumentContents(document); var bytes = await envelopeService.GetDocumentContents(document);
@ -46,7 +49,7 @@ namespace EnvelopeGenerator.Web.Controllers
[HttpPost] [HttpPost]
[Route("api/document/{envelopeKey}")] [Route("api/document/{envelopeKey}")]
public IActionResult Open(string envelopeKey) public async Task<IActionResult> Open(string envelopeKey)
{ {
try try
{ {
@ -54,7 +57,7 @@ namespace EnvelopeGenerator.Web.Controllers
// Validate Envelope Key and load envelope // Validate Envelope Key and load envelope
envelopeService.EnsureValidEnvelopeKey(envelopeKey); envelopeService.EnsureValidEnvelopeKey(envelopeKey);
EnvelopeResponse response = envelopeService.LoadEnvelope(envelopeKey); EnvelopeResponse response = await envelopeService.LoadEnvelope(envelopeKey);
actionService.OpenEnvelope(response.Envelope, response.Receiver); actionService.OpenEnvelope(response.Envelope, response.Receiver);

View File

@ -17,7 +17,7 @@ namespace EnvelopeGenerator.Web.Controllers
[HttpGet] [HttpGet]
[Route("api/envelope/{envelopeKey}")] [Route("api/envelope/{envelopeKey}")]
public IActionResult Get(string envelopeKey) public async Task<IActionResult> Get(string envelopeKey)
{ {
try try
{ {
@ -25,7 +25,7 @@ namespace EnvelopeGenerator.Web.Controllers
// Validate Envelope Key and load envelope // Validate Envelope Key and load envelope
envelopeService.EnsureValidEnvelopeKey(envelopeKey); envelopeService.EnsureValidEnvelopeKey(envelopeKey);
EnvelopeResponse response = 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)
{ {
@ -51,7 +51,7 @@ namespace EnvelopeGenerator.Web.Controllers
// Validate Envelope Key and load envelope // Validate Envelope Key and load envelope
envelopeService.EnsureValidEnvelopeKey(envelopeKey); envelopeService.EnsureValidEnvelopeKey(envelopeKey);
EnvelopeResponse response = envelopeService.LoadEnvelope(envelopeKey); EnvelopeResponse response = await envelopeService.LoadEnvelope(envelopeKey);
// Again check if receiver has already signed // Again check if receiver has already signed
if (envelopeService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id) == true) if (envelopeService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id) == true)

View File

@ -0,0 +1,15 @@
using DigitalData.Core.API;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
namespace EnvelopeGenerator.Web.Controllers
{
public class EnvelopeDocumentTestController : CRUDControllerBase<EnvelopeDocumentTestController, IEnvelopeDocumentService, IEnvelopeDocumentRepository, EnvelopeDocumentDto, EnvelopeDocumentDto, EnvelopeDocumentDto, EnvelopeDocument, int>
{
public EnvelopeDocumentTestController(ILogger<EnvelopeDocumentTestController> logger, IEnvelopeDocumentService service) : base(logger, service)
{
}
}
}

View File

@ -70,11 +70,10 @@ namespace EnvelopeGenerator.Web.Controllers
} }
[HttpGet] [HttpGet]
[Route("/EnvelopeKey/{EnvelopeReceiverId}")] [Route("/EnvelopeKey/{envelopeReceiverId}")]
public IActionResult ShowEnvelope() public async Task<IActionResult> ShowEnvelope([FromRoute] string envelopeReceiverId)
{ {
string envelopeKey = (string)HttpContext.Request.RouteValues["EnvelopeReceiverId"]; EnvelopeResponse response = await _envelopeService.LoadEnvelope(envelopeReceiverId);
EnvelopeResponse response = _envelopeService.LoadEnvelope(envelopeKey);
if (response.Envelope.UseAccessCode) if (response.Envelope.UseAccessCode)
{ {
@ -87,61 +86,46 @@ namespace EnvelopeGenerator.Web.Controllers
bool result = database.Services.emailService.SendDocumentAccessCodeReceivedEmail(response.Envelope, response.Receiver); bool result = database.Services.emailService.SendDocumentAccessCodeReceivedEmail(response.Envelope, response.Receiver);
} }
return Redirect($"/EnvelopeKey/{envelopeKey}/Locked"); return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked");
} }
else else
{ {
ViewData["EnvelopeKey"] = envelopeKey; ViewData["EnvelopeKey"] = envelopeReceiverId;
return View(); return View();
} }
} }
[HttpPost] [HttpPost]
[Route("/EnvelopeKey/{EnvelopeReceiverId}/Locked")] [Route("/EnvelopeKey/{envelopeReceiverId}/Locked")]
public IActionResult ShowEnvelopePost() public async Task<IActionResult> ShowEnvelopePost([FromRoute] string envelopeReceiverId, [FromForm] string access_code)
{ {
string envelopeKey = (string)HttpContext.Request.RouteValues["EnvelopeReceiverId"]; EnvelopeResponse response = await _envelopeService.LoadEnvelope(envelopeReceiverId);
StringValues accessCodeFromForm = HttpContext.Request.Form["access_code"];
if (accessCodeFromForm.Count == 0)
{
return Redirect($"/EnvelopeKey/{envelopeKey}/Locked");
}
if (accessCodeFromForm.Count > 1)
{
return Redirect($"/EnvelopeKey/{envelopeKey}/Locked");
}
EnvelopeResponse response = _envelopeService.LoadEnvelope(envelopeKey);
string accessCode = response.Receiver.AccessCode; string accessCode = response.Receiver.AccessCode;
if (string.IsNullOrEmpty(accessCodeFromForm[0])) if (string.IsNullOrEmpty(access_code))
{ {
return Redirect($"/EnvelopeKey/{envelopeKey}/Locked"); return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked");
} }
if (accessCode.Equals(accessCodeFromForm[0], StringComparison.Ordinal)) if (accessCode.Equals(access_code, StringComparison.Ordinal))
{ {
bool actionResult = database.Services.actionService.EnterCorrectAccessCode(response.Envelope, response.Receiver); bool actionResult = database.Services.actionService.EnterCorrectAccessCode(response.Envelope, response.Receiver);
ViewData["EnvelopeKey"] = envelopeKey; ViewData["EnvelopeKey"] = envelopeReceiverId;
return View("ShowEnvelope"); return View("ShowEnvelope");
} }
else else
{ {
bool actionResult = database.Services.actionService.EnterIncorrectAccessCode(response.Envelope, response.Receiver); bool actionResult = database.Services.actionService.EnterIncorrectAccessCode(response.Envelope, response.Receiver);
return Redirect($"/EnvelopeKey/{envelopeKey}/Locked"); return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked");
} }
} }
[HttpGet] [HttpGet]
[Route("/EnvelopeKey/{EnvelopeReceiverId}/Locked")] [Route("/EnvelopeKey/{envelopeReceiverId}/Locked")]
public IActionResult EnvelopeLocked() public IActionResult EnvelopeLocked([FromRoute] string envelopeReceiverId)
{ {
ViewData["EnvelopeKey"] = HttpContext.Request.RouteValues["EnvelopeReceiverId"]; ViewData["EnvelopeKey"] = envelopeReceiverId;
return View(); return View();
} }

View File

@ -7,24 +7,50 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.16" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.15" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.2.5" /> <PackageReference Include="NLog" Version="5.2.5" />
<PackageReference Include="Quartz" Version="3.8.0" /> <PackageReference Include="Quartz" Version="3.8.0" />
<PackageReference Include="Quartz.AspNetCore" Version="3.8.0" /> <PackageReference Include="Quartz.AspNetCore" Version="3.8.0" />
<PackageReference Include="Quartz.Plugins" Version="3.8.0" /> <PackageReference Include="Quartz.Plugins" Version="3.8.0" />
<PackageReference Include="Quartz.Serialization.Json" Version="3.8.0" /> <PackageReference Include="Quartz.Serialization.Json" Version="3.8.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" /> <PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
<PackageReference Include="System.Drawing.Common" Version="7.0.0" /> <PackageReference Include="System.Drawing.Common" Version="7.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.Common\EnvelopeGenerator.Common.vbproj" /> <ProjectReference Include="..\EnvelopeGenerator.Common\EnvelopeGenerator.Common.vbproj" />
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="DevExpress.Data.v21.2"> <Reference Include="DevExpress.Data.v21.2">
<HintPath>D:\ProgramFiles\DevExpress 21.2\Components\Bin\Framework\DevExpress.Data.v21.2.dll</HintPath> <HintPath>D:\ProgramFiles\DevExpress 21.2\Components\Bin\Framework\DevExpress.Data.v21.2.dll</HintPath>
</Reference> </Reference>
<Reference Include="DigitalData.Core.API">
<HintPath>..\..\WebCoreModules\DigitalData.Core.API\bin\Debug\net7.0\DigitalData.Core.API.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.Application">
<HintPath>..\..\WebCoreModules\DigitalData.Core.Application\bin\Debug\net7.0\DigitalData.Core.Application.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.Contracts">
<HintPath>..\..\WebCoreModules\DigitalData.Core.Contracts\bin\Debug\net7.0\DigitalData.Core.Contracts.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.CultureServices">
<HintPath>..\..\WebCoreModules\DigitalData.Core.CultureServices\bin\Debug\net7.0\DigitalData.Core.CultureServices.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Core.Infrastructure">
<HintPath>..\..\WebCoreModules\DigitalData.Core.Infrastructure\bin\Debug\net7.0\DigitalData.Core.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Modules.Base"> <Reference Include="DigitalData.Modules.Base">
<HintPath>..\..\DDModules\Base\bin\Debug\DigitalData.Modules.Base.dll</HintPath> <HintPath>..\..\DDModules\Base\bin\Debug\DigitalData.Modules.Base.dll</HintPath>
</Reference> </Reference>

View File

@ -1,6 +1,12 @@
using DigitalData.Modules.Logging; using DigitalData.Core.CultureServices;
using EnvelopeGenerator.Common; using DigitalData.UserManager.Infrastructure.Repositories;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.MappingProfiles;
using EnvelopeGenerator.Application.Services;
using EnvelopeGenerator.Infrastructure.Contracts;
using EnvelopeGenerator.Infrastructure.Repositories;
using EnvelopeGenerator.Web.Services; using EnvelopeGenerator.Web.Services;
using Microsoft.EntityFrameworkCore;
using Quartz; using Quartz;
namespace EnvelopeGenerator.Web namespace EnvelopeGenerator.Web
@ -13,10 +19,10 @@ namespace EnvelopeGenerator.Web
// Add base services // Add base services
builder.Services.AddSingleton<LoggingService>(); builder.Services.AddSingleton<LoggingService>();
builder.Services.AddTransient<DatabaseService>(); builder.Services.AddScoped<DatabaseService>();
// Add higher order services // Add higher order services
builder.Services.AddSingleton<EnvelopeService>(); builder.Services.AddScoped<EnvelopeService>();
// Add services to the container. // Add services to the container.
builder.Services.AddControllersWithViews().AddJsonOptions(q => builder.Services.AddControllersWithViews().AddJsonOptions(q =>
@ -25,6 +31,26 @@ namespace EnvelopeGenerator.Web
q.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles; q.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles;
}); });
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddKeyTranslationService();
//AddEF Core dbcontext
var connStr = builder.Configuration["Config:ConnectionString"];
builder.Services.AddDbContext<EGDbContext>(options =>
options.UseSqlServer(connStr));
//Inject CRUD Service and repositories
builder.Services.AddScoped<IConfigRepository, ConfigRepository>();
builder.Services.AddScoped<IEnvelopeDocumentRepository, EnvelopeDocumentRepository>();
builder.Services.AddScoped<IConfigService, ConfigService>();
builder.Services.AddScoped<IEnvelopeDocumentService, EnvelopeDocumentService>();
//Auto mapping profiles
builder.Services.AddAutoMapper(typeof(BasicDtoMappingProfile).Assembly);
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
@ -35,6 +61,9 @@ namespace EnvelopeGenerator.Web
app.UseHsts(); app.UseHsts();
} }
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseStaticFiles(); app.UseStaticFiles();

View File

@ -23,6 +23,16 @@
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
},
"swagger": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7202;http://localhost:5009",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
} }
} }
} }

View File

@ -1,44 +0,0 @@
using DigitalData.Modules.Base;
using DigitalData.Modules.Logging;
using EnvelopeGenerator.Common;
using Quartz;
using System;
using System.Collections.Specialized;
using System.ComponentModel.DataAnnotations;
namespace EnvelopeGenerator.Web
{
public class Scheduler : BaseClass
{
private const string DATABASE = "DATABASE";
private const string LOGCONFIG = "LOGCONFIG";
private string ConnectionString;
public Scheduler(LogConfig logConfig, string connectionString) : base(logConfig)
{
this.ConnectionString = connectionString;
}
public void ScheduleJob<TJob>(IServiceCollectionQuartzConfigurator q, string name, int interval) where TJob : IJob
{
var jobKey = new JobKey(name);
var jobData = new JobDataMap
{
{ DATABASE, ConnectionString },
{ LOGCONFIG, LogConfig }
};
q.AddJob<TJob>(opts => opts
.WithIdentity(jobKey)
.UsingJobData(jobData));
q.AddTrigger(opts => opts
.ForJob(jobKey)
.WithIdentity($"{name}-trigger")
.WithSimpleSchedule(s => s
.RepeatForever()
.WithIntervalInMinutes(interval)));
}
}
}

View File

@ -1,8 +1,7 @@
using EnvelopeGenerator.Common; using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Common;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using System.Reflection.Metadata;
using System.Text; using System.Text;
using static EnvelopeGenerator.Common.Constants;
namespace EnvelopeGenerator.Web.Services namespace EnvelopeGenerator.Web.Services
{ {
@ -14,7 +13,9 @@ namespace EnvelopeGenerator.Web.Services
private readonly DocumentStatusModel documentStatusModel; private readonly DocumentStatusModel documentStatusModel;
public EnvelopeService(IConfiguration Config, LoggingService Logging, DatabaseService database) : base(Config, Logging) private IConfigService _configService;
public EnvelopeService(IConfiguration Config, LoggingService Logging, DatabaseService database, IConfigService configService) : base(Config, Logging)
{ {
logger = Logging.LogConfig.GetLogger(); logger = Logging.LogConfig.GetLogger();
@ -27,6 +28,8 @@ namespace EnvelopeGenerator.Web.Services
envelopeModel = database.Models.envelopeModel; envelopeModel = database.Models.envelopeModel;
historyModel = database.Models.historyModel; historyModel = database.Models.historyModel;
documentStatusModel = database.Models.documentStatusModel; documentStatusModel = database.Models.documentStatusModel;
_configService = configService;
} }
public void EnsureValidEnvelopeKey(string envelopeKey) public void EnsureValidEnvelopeKey(string envelopeKey)
@ -47,7 +50,7 @@ namespace EnvelopeGenerator.Web.Services
throw new ArgumentNullException("ReceiverSignature"); throw new ArgumentNullException("ReceiverSignature");
} }
public EnvelopeResponse LoadEnvelope(string pEnvelopeKey) public async Task<EnvelopeResponse> LoadEnvelope(string pEnvelopeKey)
{ {
logger.Debug("Loading Envelope by Key [{0}]", pEnvelopeKey); logger.Debug("Loading Envelope by Key [{0}]", pEnvelopeKey);
@ -87,7 +90,30 @@ namespace EnvelopeGenerator.Web.Services
logger.Debug("Loading documents for receiver [{0}]", receiver.Email); logger.Debug("Loading documents for receiver [{0}]", receiver.Email);
envelope.Documents = FilterElementsByReceiver(envelope, receiverId); // filter elements by receiver
envelope.Documents = envelope.Documents.Select((document) =>
{
document.Elements = document.Elements.Where((e) => e.ReceiverId == receiverId).ToList();
return document;
}).ToList();
//if documenet_path_dmz is existing in config, replace the path with it
var configResult = await _configService.ReadDefaultAsync();
if (configResult.IsSuccess && configResult.Data is not null)
{
var config = configResult.Data;
if(config.DocumentPathDmz is not null && config.DocumentPathDmz != string.Empty)
foreach(var doc in envelope.Documents)
{
doc.Filepath = doc.Filepath.Replace(config.DocumentPath , config.DocumentPathDmz);
}
}
else
{
logger.Error(configResult.Messages);
throw new InvalidOperationException(String.Join(". ", configResult.Messages));
}
return new() return new()
{ {
@ -96,18 +122,6 @@ namespace EnvelopeGenerator.Web.Services
}; };
} }
private static List<EnvelopeDocument> FilterElementsByReceiver(Envelope envelope, int receiverId)
{
return envelope.Documents.
Select((document) =>
{
var elements = document.Elements.Where((e) => e.ReceiverId == receiverId);
document.Elements = elements.ToList();
return document;
}).
ToList();
}
public List<Envelope> LoadEnvelopes() public List<Envelope> LoadEnvelopes()
{ {
var receivers = receiverModel.ListReceivers(); var receivers = receiverModel.ListReceivers();
@ -176,9 +190,9 @@ namespace EnvelopeGenerator.Web.Services
return documentIndex; return documentIndex;
} }
public EnvelopeDocument GetDocument(HttpRequest request, string envelopeKey) public async Task<EnvelopeDocument> GetDocument(HttpRequest request, string envelopeKey)
{ {
EnvelopeResponse response = LoadEnvelope(envelopeKey); EnvelopeResponse response = await LoadEnvelope(envelopeKey);
int documentId = EnsureValidDocumentIndex(request); int documentId = EnsureValidDocumentIndex(request);
logger.Debug("Loading document for Id [{0}]", documentId); logger.Debug("Loading document for Id [{0}]", documentId);

View File

@ -7,10 +7,11 @@
} }
}, },
"Config": { "Config": {
"ConnectionString": "Server=sDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;", "ConnectionString": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;",
//preivous connection string without Encrypt=false and TrustServerCertificate=True -> "Server=sDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;"
"LogPath": "E:\\EnvelopeGenerator\\Logs", "LogPath": "E:\\EnvelopeGenerator\\Logs",
"LogDebug": true, "LogDebug": true,
"LogJson": true, "LogJson": true,
"AdminPassword": "dd" "AdminPassword": "dd"
} }
} }

View File

@ -1,9 +1,17 @@
{ {
"DetailedErrors": true,
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Information", "Default": "Information",
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"AllowedHosts": "*" "Config": {
} "ConnectionString": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;",
//preivous connection string without Encrypt=false and TrustServerCertificate=True -> "Server=sDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;"
"LogPath": "E:\\EnvelopeGenerator\\Logs",
"LogDebug": true,
"LogJson": true,
"AdminPassword": "dd"
}
}

View File

@ -28,7 +28,7 @@
* @param {any} documentId * @param {any} documentId
*/ */
async getDocument(envelopeKey, documentId) { async getDocument(envelopeKey, documentId) {
console.log("getDocument") console.log("getDocument", `/api/document/${envelopeKey}?index=${documentId}`)
return this.getRequest(`/api/document/${envelopeKey}?index=${documentId}`) return this.getRequest(`/api/document/${envelopeKey}?index=${documentId}`)
.then(this.wrapBinaryResponse.bind(this)) .then(this.wrapBinaryResponse.bind(this))
} }

View File

@ -13,6 +13,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnvelopeGenerator.Web", "En
EndProject EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EnvelopeGenerator.Service", "EnvelopeGenerator.Service\EnvelopeGenerator.Service.vbproj", "{83ED2617-B398-4859-8F59-B38F8807E83E}" Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EnvelopeGenerator.Service", "EnvelopeGenerator.Service\EnvelopeGenerator.Service.vbproj", "{83ED2617-B398-4859-8F59-B38F8807E83E}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Domain", "EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj", "{4F32A98D-E6F0-4A09-BD97-1CF26107E837}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Infrastructure", "EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj", "{63E32615-0ECA-42DC-96E3-91037324B7C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Application", "EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj", "{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -39,6 +45,18 @@ Global
{83ED2617-B398-4859-8F59-B38F8807E83E}.Debug|Any CPU.Build.0 = Debug|Any CPU {83ED2617-B398-4859-8F59-B38F8807E83E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83ED2617-B398-4859-8F59-B38F8807E83E}.Release|Any CPU.ActiveCfg = Release|Any CPU {83ED2617-B398-4859-8F59-B38F8807E83E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83ED2617-B398-4859-8F59-B38F8807E83E}.Release|Any CPU.Build.0 = Release|Any CPU {83ED2617-B398-4859-8F59-B38F8807E83E}.Release|Any CPU.Build.0 = Release|Any CPU
{4F32A98D-E6F0-4A09-BD97-1CF26107E837}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F32A98D-E6F0-4A09-BD97-1CF26107E837}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F32A98D-E6F0-4A09-BD97-1CF26107E837}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F32A98D-E6F0-4A09-BD97-1CF26107E837}.Release|Any CPU.Build.0 = Release|Any CPU
{63E32615-0ECA-42DC-96E3-91037324B7C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63E32615-0ECA-42DC-96E3-91037324B7C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63E32615-0ECA-42DC-96E3-91037324B7C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63E32615-0ECA-42DC-96E3-91037324B7C7}.Release|Any CPU.Build.0 = Release|Any CPU
{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5A9984F8-51A2-4558-A415-EC5FEED7CF7D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE