diff --git a/EnvelopeGenerator.Application/Contracts/IConfigService.cs b/EnvelopeGenerator.Application/Contracts/IConfigService.cs new file mode 100644 index 00000000..c3ca102a --- /dev/null +++ b/EnvelopeGenerator.Application/Contracts/IConfigService.cs @@ -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 + { + Task> ReadFirstAsync(); + + async Task> ReadDefaultAsync() => await ReadFirstAsync(); + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Contracts/IEnvelopeDocumentService.cs b/EnvelopeGenerator.Application/Contracts/IEnvelopeDocumentService.cs new file mode 100644 index 00000000..39c93f5f --- /dev/null +++ b/EnvelopeGenerator.Application/Contracts/IEnvelopeDocumentService.cs @@ -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 + { + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/DTOs/ConfigDto.cs b/EnvelopeGenerator.Application/DTOs/ConfigDto.cs new file mode 100644 index 00000000..b728de31 --- /dev/null +++ b/EnvelopeGenerator.Application/DTOs/ConfigDto.cs @@ -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; } + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/DTOs/EnvelopeDocumentDto.cs b/EnvelopeGenerator.Application/DTOs/EnvelopeDocumentDto.cs new file mode 100644 index 00000000..c5ea7402 --- /dev/null +++ b/EnvelopeGenerator.Application/DTOs/EnvelopeDocumentDto.cs @@ -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; } + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj new file mode 100644 index 00000000..65f46232 --- /dev/null +++ b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj @@ -0,0 +1,26 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + ..\..\WebCoreModules\DigitalData.Core.Application\bin\Debug\net7.0\DigitalData.Core.Application.dll + + + ..\..\WebCoreModules\DigitalData.Core.Application\bin\Debug\net7.0\DigitalData.Core.Contracts.dll + + + + diff --git a/EnvelopeGenerator.Application/MappingProfiles/BasicDtoMappingProfile.cs b/EnvelopeGenerator.Application/MappingProfiles/BasicDtoMappingProfile.cs new file mode 100644 index 00000000..2a4dc6d3 --- /dev/null +++ b/EnvelopeGenerator.Application/MappingProfiles/BasicDtoMappingProfile.cs @@ -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(); + CreateMap(); + + CreateMap(); + CreateMap(); + } + } +} diff --git a/EnvelopeGenerator.Application/Services/ConfigService.cs b/EnvelopeGenerator.Application/Services/ConfigService.cs new file mode 100644 index 00000000..67dcf11b --- /dev/null +++ b/EnvelopeGenerator.Application/Services/ConfigService.cs @@ -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, IConfigService + { + public ConfigService(IConfigRepository repository, IKeyTranslationService translationService, IMapper mapper) : base(repository, translationService, mapper) + { + } + + public async Task> ReadFirstAsync() + { + var config = await _repository.ReadFirstAsync(); + + if (config is null) + return Failed("There is no configuration in DB."); + + return Successful(_mapper.MapOrThrow(config)); + } + + public async Task> ReadDefaultAsync() => await ReadFirstAsync(); + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Services/EnvelopeDocumentService.cs b/EnvelopeGenerator.Application/Services/EnvelopeDocumentService.cs new file mode 100644 index 00000000..048e1589 --- /dev/null +++ b/EnvelopeGenerator.Application/Services/EnvelopeDocumentService.cs @@ -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, IEnvelopeDocumentService + { + public EnvelopeDocumentService(IEnvelopeDocumentRepository repository, IKeyTranslationService translationService, IMapper mapper) : base(repository, translationService, mapper) + { + } + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Common/Entities/EnvelopeDocument.vb b/EnvelopeGenerator.Common/Entities/EnvelopeDocument.vb index 967a81f1..077e88a5 100644 --- a/EnvelopeGenerator.Common/Entities/EnvelopeDocument.vb +++ b/EnvelopeGenerator.Common/Entities/EnvelopeDocument.vb @@ -19,10 +19,4 @@ Public Class EnvelopeDocument Public Property Filepath As String Public Property PageCount As Integer - - Public ReadOnly Property PageCountTranslated As String - Get - Return $"{PageCount} Seiten" - End Get - End Property -End Class +End Class \ No newline at end of file diff --git a/EnvelopeGenerator.Common/Models/DocumentModel.vb b/EnvelopeGenerator.Common/Models/DocumentModel.vb index b1c30643..68b78672 100644 --- a/EnvelopeGenerator.Common/Models/DocumentModel.vb +++ b/EnvelopeGenerator.Common/Models/DocumentModel.vb @@ -45,7 +45,7 @@ Public Class DocumentModel Public Function List(pEnvelopeId As Integer) As IEnumerable(Of EnvelopeDocument) 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) Return oTable?.Rows.Cast(Of DataRow). diff --git a/EnvelopeGenerator.Domain/Entities/Config.cs b/EnvelopeGenerator.Domain/Entities/Config.cs new file mode 100644 index 00000000..4c31ae74 --- /dev/null +++ b/EnvelopeGenerator.Domain/Entities/Config.cs @@ -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; } + } +} diff --git a/EnvelopeGenerator.Domain/Entities/EnvelopeDocument.cs b/EnvelopeGenerator.Domain/Entities/EnvelopeDocument.cs new file mode 100644 index 00000000..a70efdc4 --- /dev/null +++ b/EnvelopeGenerator.Domain/Entities/EnvelopeDocument.cs @@ -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; } + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Domain/EnvelopeGenerator.Domain.csproj b/EnvelopeGenerator.Domain/EnvelopeGenerator.Domain.csproj new file mode 100644 index 00000000..cfadb03d --- /dev/null +++ b/EnvelopeGenerator.Domain/EnvelopeGenerator.Domain.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/EnvelopeGenerator.Infrastructure/Contracts/IConfigRepository.cs b/EnvelopeGenerator.Infrastructure/Contracts/IConfigRepository.cs new file mode 100644 index 00000000..a4fb4812 --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/Contracts/IConfigRepository.cs @@ -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 + { + Task ReadFirstAsync(); + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeDocumentRepository.cs b/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeDocumentRepository.cs new file mode 100644 index 00000000..a91b0098 --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/Contracts/IEnvelopeDocumentRepository.cs @@ -0,0 +1,9 @@ +using DigitalData.Core.Contracts.Infrastructure; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Infrastructure.Contracts +{ + public interface IEnvelopeDocumentRepository : ICRUDRepository + { + } +} diff --git a/EnvelopeGenerator.Infrastructure/EGDbContext.cs b/EnvelopeGenerator.Infrastructure/EGDbContext.cs new file mode 100644 index 00000000..e361faba --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/EGDbContext.cs @@ -0,0 +1,20 @@ +using EnvelopeGenerator.Domain.Entities; +using Microsoft.EntityFrameworkCore; + +namespace DigitalData.UserManager.Infrastructure.Repositories +{ + public class EGDbContext : DbContext + { + public EGDbContext(DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasNoKey(); + modelBuilder.Entity(); + + base.OnModelCreating(modelBuilder); + } + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj b/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj new file mode 100644 index 00000000..208721a6 --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj @@ -0,0 +1,31 @@ + + + + net7.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + ..\..\WebCoreModules\DigitalData.Core.Infrastructure\bin\Debug\net7.0\DigitalData.Core.Contracts.dll + + + ..\..\WebCoreModules\DigitalData.Core.Infrastructure\bin\Debug\net7.0\DigitalData.Core.Infrastructure.dll + + + + diff --git a/EnvelopeGenerator.Infrastructure/Repositories/ConfigRepository.cs b/EnvelopeGenerator.Infrastructure/Repositories/ConfigRepository.cs new file mode 100644 index 00000000..6dc89ebb --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/Repositories/ConfigRepository.cs @@ -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, IConfigRepository + { + public ConfigRepository(EGDbContext dbContext) : base(dbContext) + { + } + + public async Task ReadFirstAsync() + { + var configs = await _dbSet.ToListAsync(); + return configs.Count > 0 ? configs[0] : default; + } + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeDocumentRepository.cs b/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeDocumentRepository.cs new file mode 100644 index 00000000..57fa4eee --- /dev/null +++ b/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeDocumentRepository.cs @@ -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, IEnvelopeDocumentRepository + { + public EnvelopeDocumentRepository(EGDbContext dbContext) : base(dbContext) + { + } + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Classes/PDFBurner.cs b/EnvelopeGenerator.Web/Classes/PDFBurner.cs deleted file mode 100644 index b4d28b33..00000000 --- a/EnvelopeGenerator.Web/Classes/PDFBurner.cs +++ /dev/null @@ -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 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(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 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> 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 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 annotations { get; set; } - public Dictionary attachments { get; set; } - } - - internal partial class Annotation - { - public string id { get; set; } - public List 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>> points { get; set; } - } - - internal partial class Attachment - { - public string binary { get; set; } - public string contentType { get; set; } - } -} -} diff --git a/EnvelopeGenerator.Web/Constants.cs b/EnvelopeGenerator.Web/Constants.cs deleted file mode 100644 index 7f9e5cf2..00000000 --- a/EnvelopeGenerator.Web/Constants.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace EnvelopeGenerator.Web -{ - public class Constants - { - public enum ErrorType - { - None, - ServerError, - FilesystemError - } - } -} diff --git a/EnvelopeGenerator.Web/Controllers/BaseController.cs b/EnvelopeGenerator.Web/Controllers/BaseController.cs index 5a88e0c0..bd66ea78 100644 --- a/EnvelopeGenerator.Web/Controllers/BaseController.cs +++ b/EnvelopeGenerator.Web/Controllers/BaseController.cs @@ -2,7 +2,6 @@ using EnvelopeGenerator.Common; using EnvelopeGenerator.Web.Services; using Microsoft.AspNetCore.Mvc; -using static EnvelopeGenerator.Web.Constants; namespace EnvelopeGenerator.Web.Controllers { @@ -27,7 +26,7 @@ namespace EnvelopeGenerator.Web.Controllers return Problem( statusCode: 500, detail: e.Message, - type: ErrorType.ServerError.ToString()); + type: "ServerError"); } } } diff --git a/EnvelopeGenerator.Web/Controllers/ConfigTestController.cs b/EnvelopeGenerator.Web/Controllers/ConfigTestController.cs new file mode 100644 index 00000000..8ef3a656 --- /dev/null +++ b/EnvelopeGenerator.Web/Controllers/ConfigTestController.cs @@ -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 + { + public ConfigTestController(ILogger logger, IConfigService service) : base(logger, service) + { + } + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Controllers/DocumentController.cs b/EnvelopeGenerator.Web/Controllers/DocumentController.cs index 98f7b17e..d0ce6871 100644 --- a/EnvelopeGenerator.Web/Controllers/DocumentController.cs +++ b/EnvelopeGenerator.Web/Controllers/DocumentController.cs @@ -2,6 +2,7 @@ using EnvelopeGenerator.Common; using EnvelopeGenerator.Web.Services; using static EnvelopeGenerator.Common.Constants; +using EnvelopeGenerator.Application.Contracts; namespace EnvelopeGenerator.Web.Controllers { @@ -9,16 +10,18 @@ namespace EnvelopeGenerator.Web.Controllers { private readonly EnvelopeService envelopeService; 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; actionService = database.Services?.actionService; + _envDocService = envDocService; } [HttpGet] [Route("api/document/{envelopeKey}")] - public async Task Get(string envelopeKey) + public async Task Get([FromRoute] string envelopeKey, [FromQuery] int index) { try { @@ -26,11 +29,11 @@ namespace EnvelopeGenerator.Web.Controllers // Validate Envelope Key and load envelope envelopeService.EnsureValidEnvelopeKey(envelopeKey); - EnvelopeResponse response = envelopeService.LoadEnvelope(envelopeKey); + EnvelopeResponse response = await envelopeService.LoadEnvelope(envelopeKey); // Load document info var Request = ControllerContext.HttpContext.Request; - var document = envelopeService.GetDocument(Request, envelopeKey); + var document = await envelopeService.GetDocument(Request, envelopeKey); // Load the document from disk var bytes = await envelopeService.GetDocumentContents(document); @@ -46,7 +49,7 @@ namespace EnvelopeGenerator.Web.Controllers [HttpPost] [Route("api/document/{envelopeKey}")] - public IActionResult Open(string envelopeKey) + public async Task Open(string envelopeKey) { try { @@ -54,7 +57,7 @@ namespace EnvelopeGenerator.Web.Controllers // Validate Envelope Key and load envelope envelopeService.EnsureValidEnvelopeKey(envelopeKey); - EnvelopeResponse response = envelopeService.LoadEnvelope(envelopeKey); + EnvelopeResponse response = await envelopeService.LoadEnvelope(envelopeKey); actionService.OpenEnvelope(response.Envelope, response.Receiver); diff --git a/EnvelopeGenerator.Web/Controllers/EnvelopeController.cs b/EnvelopeGenerator.Web/Controllers/EnvelopeController.cs index 5c945e5f..5b87d9d9 100644 --- a/EnvelopeGenerator.Web/Controllers/EnvelopeController.cs +++ b/EnvelopeGenerator.Web/Controllers/EnvelopeController.cs @@ -17,7 +17,7 @@ namespace EnvelopeGenerator.Web.Controllers [HttpGet] [Route("api/envelope/{envelopeKey}")] - public IActionResult Get(string envelopeKey) + public async Task Get(string envelopeKey) { try { @@ -25,7 +25,7 @@ namespace EnvelopeGenerator.Web.Controllers // Validate Envelope Key and load envelope envelopeService.EnsureValidEnvelopeKey(envelopeKey); - EnvelopeResponse response = envelopeService.LoadEnvelope(envelopeKey); + EnvelopeResponse response = await envelopeService.LoadEnvelope(envelopeKey); if (envelopeService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id) == true) { @@ -51,7 +51,7 @@ namespace EnvelopeGenerator.Web.Controllers // Validate Envelope Key and load envelope envelopeService.EnsureValidEnvelopeKey(envelopeKey); - EnvelopeResponse response = envelopeService.LoadEnvelope(envelopeKey); + EnvelopeResponse response = await envelopeService.LoadEnvelope(envelopeKey); // Again check if receiver has already signed if (envelopeService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id) == true) diff --git a/EnvelopeGenerator.Web/Controllers/EnvelopeDocumentTestController.cs b/EnvelopeGenerator.Web/Controllers/EnvelopeDocumentTestController.cs new file mode 100644 index 00000000..c191dbf2 --- /dev/null +++ b/EnvelopeGenerator.Web/Controllers/EnvelopeDocumentTestController.cs @@ -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 + { + public EnvelopeDocumentTestController(ILogger logger, IEnvelopeDocumentService service) : base(logger, service) + { + } + } +} diff --git a/EnvelopeGenerator.Web/Controllers/HomeController.cs b/EnvelopeGenerator.Web/Controllers/HomeController.cs index ed1da50e..7f6e1d59 100644 --- a/EnvelopeGenerator.Web/Controllers/HomeController.cs +++ b/EnvelopeGenerator.Web/Controllers/HomeController.cs @@ -70,11 +70,10 @@ namespace EnvelopeGenerator.Web.Controllers } [HttpGet] - [Route("/EnvelopeKey/{EnvelopeReceiverId}")] - public IActionResult ShowEnvelope() + [Route("/EnvelopeKey/{envelopeReceiverId}")] + public async Task ShowEnvelope([FromRoute] string envelopeReceiverId) { - string envelopeKey = (string)HttpContext.Request.RouteValues["EnvelopeReceiverId"]; - EnvelopeResponse response = _envelopeService.LoadEnvelope(envelopeKey); + EnvelopeResponse response = await _envelopeService.LoadEnvelope(envelopeReceiverId); if (response.Envelope.UseAccessCode) { @@ -87,61 +86,46 @@ namespace EnvelopeGenerator.Web.Controllers bool result = database.Services.emailService.SendDocumentAccessCodeReceivedEmail(response.Envelope, response.Receiver); } - return Redirect($"/EnvelopeKey/{envelopeKey}/Locked"); + return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked"); } else { - ViewData["EnvelopeKey"] = envelopeKey; + ViewData["EnvelopeKey"] = envelopeReceiverId; return View(); } } [HttpPost] - [Route("/EnvelopeKey/{EnvelopeReceiverId}/Locked")] - public IActionResult ShowEnvelopePost() + [Route("/EnvelopeKey/{envelopeReceiverId}/Locked")] + public async Task ShowEnvelopePost([FromRoute] string envelopeReceiverId, [FromForm] string access_code) { - string envelopeKey = (string)HttpContext.Request.RouteValues["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); + EnvelopeResponse response = await _envelopeService.LoadEnvelope(envelopeReceiverId); 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); - ViewData["EnvelopeKey"] = envelopeKey; + ViewData["EnvelopeKey"] = envelopeReceiverId; return View("ShowEnvelope"); } else { bool actionResult = database.Services.actionService.EnterIncorrectAccessCode(response.Envelope, response.Receiver); - return Redirect($"/EnvelopeKey/{envelopeKey}/Locked"); + return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked"); } } [HttpGet] - [Route("/EnvelopeKey/{EnvelopeReceiverId}/Locked")] - public IActionResult EnvelopeLocked() + [Route("/EnvelopeKey/{envelopeReceiverId}/Locked")] + public IActionResult EnvelopeLocked([FromRoute] string envelopeReceiverId) { - ViewData["EnvelopeKey"] = HttpContext.Request.RouteValues["EnvelopeReceiverId"]; - + ViewData["EnvelopeKey"] = envelopeReceiverId; return View(); } diff --git a/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj b/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj index e0250ce0..7945b33d 100644 --- a/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj +++ b/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj @@ -7,24 +7,50 @@ + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + D:\ProgramFiles\DevExpress 21.2\Components\Bin\Framework\DevExpress.Data.v21.2.dll + + ..\..\WebCoreModules\DigitalData.Core.API\bin\Debug\net7.0\DigitalData.Core.API.dll + + + ..\..\WebCoreModules\DigitalData.Core.Application\bin\Debug\net7.0\DigitalData.Core.Application.dll + + + ..\..\WebCoreModules\DigitalData.Core.Contracts\bin\Debug\net7.0\DigitalData.Core.Contracts.dll + + + ..\..\WebCoreModules\DigitalData.Core.CultureServices\bin\Debug\net7.0\DigitalData.Core.CultureServices.dll + + + ..\..\WebCoreModules\DigitalData.Core.Infrastructure\bin\Debug\net7.0\DigitalData.Core.Infrastructure.dll + ..\..\DDModules\Base\bin\Debug\DigitalData.Modules.Base.dll diff --git a/EnvelopeGenerator.Web/Program.cs b/EnvelopeGenerator.Web/Program.cs index d9e87321..b272d19c 100644 --- a/EnvelopeGenerator.Web/Program.cs +++ b/EnvelopeGenerator.Web/Program.cs @@ -1,6 +1,12 @@ -using DigitalData.Modules.Logging; -using EnvelopeGenerator.Common; +using DigitalData.Core.CultureServices; +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 Microsoft.EntityFrameworkCore; using Quartz; namespace EnvelopeGenerator.Web @@ -13,10 +19,10 @@ namespace EnvelopeGenerator.Web // Add base services builder.Services.AddSingleton(); - builder.Services.AddTransient(); + builder.Services.AddScoped(); // Add higher order services - builder.Services.AddSingleton(); + builder.Services.AddScoped(); // Add services to the container. builder.Services.AddControllersWithViews().AddJsonOptions(q => @@ -25,6 +31,26 @@ namespace EnvelopeGenerator.Web 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(options => + options.UseSqlServer(connStr)); + + //Inject CRUD Service and repositories + builder.Services.AddScoped(); + builder.Services.AddScoped(); + + builder.Services.AddScoped(); + builder.Services.AddScoped(); + + //Auto mapping profiles + builder.Services.AddAutoMapper(typeof(BasicDtoMappingProfile).Assembly); + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -35,6 +61,9 @@ namespace EnvelopeGenerator.Web app.UseHsts(); } + app.UseSwagger(); + app.UseSwaggerUI(); + app.UseHttpsRedirection(); app.UseStaticFiles(); diff --git a/EnvelopeGenerator.Web/Properties/launchSettings.json b/EnvelopeGenerator.Web/Properties/launchSettings.json index 9ca225f4..bbb353e2 100644 --- a/EnvelopeGenerator.Web/Properties/launchSettings.json +++ b/EnvelopeGenerator.Web/Properties/launchSettings.json @@ -23,6 +23,16 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } + }, + "swagger": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7202;http://localhost:5009", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } } } } diff --git a/EnvelopeGenerator.Web/Scheduler.cs b/EnvelopeGenerator.Web/Scheduler.cs deleted file mode 100644 index 7c46cecb..00000000 --- a/EnvelopeGenerator.Web/Scheduler.cs +++ /dev/null @@ -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(IServiceCollectionQuartzConfigurator q, string name, int interval) where TJob : IJob - { - var jobKey = new JobKey(name); - var jobData = new JobDataMap - { - { DATABASE, ConnectionString }, - { LOGCONFIG, LogConfig } - }; - - q.AddJob(opts => opts - .WithIdentity(jobKey) - .UsingJobData(jobData)); - - q.AddTrigger(opts => opts - .ForJob(jobKey) - .WithIdentity($"{name}-trigger") - .WithSimpleSchedule(s => s - .RepeatForever() - .WithIntervalInMinutes(interval))); - } - } -} diff --git a/EnvelopeGenerator.Web/Services/EnvelopeService.cs b/EnvelopeGenerator.Web/Services/EnvelopeService.cs index 7df4e06c..a27049e6 100644 --- a/EnvelopeGenerator.Web/Services/EnvelopeService.cs +++ b/EnvelopeGenerator.Web/Services/EnvelopeService.cs @@ -1,8 +1,7 @@ -using EnvelopeGenerator.Common; +using EnvelopeGenerator.Application.Contracts; +using EnvelopeGenerator.Common; using Microsoft.Extensions.Primitives; -using System.Reflection.Metadata; using System.Text; -using static EnvelopeGenerator.Common.Constants; namespace EnvelopeGenerator.Web.Services { @@ -14,7 +13,9 @@ namespace EnvelopeGenerator.Web.Services 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(); @@ -27,6 +28,8 @@ namespace EnvelopeGenerator.Web.Services envelopeModel = database.Models.envelopeModel; historyModel = database.Models.historyModel; documentStatusModel = database.Models.documentStatusModel; + + _configService = configService; } public void EnsureValidEnvelopeKey(string envelopeKey) @@ -47,7 +50,7 @@ namespace EnvelopeGenerator.Web.Services throw new ArgumentNullException("ReceiverSignature"); } - public EnvelopeResponse LoadEnvelope(string pEnvelopeKey) + public async Task LoadEnvelope(string 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); - 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() { @@ -96,18 +122,6 @@ namespace EnvelopeGenerator.Web.Services }; } - private static List 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 LoadEnvelopes() { var receivers = receiverModel.ListReceivers(); @@ -176,9 +190,9 @@ namespace EnvelopeGenerator.Web.Services return documentIndex; } - public EnvelopeDocument GetDocument(HttpRequest request, string envelopeKey) + public async Task GetDocument(HttpRequest request, string envelopeKey) { - EnvelopeResponse response = LoadEnvelope(envelopeKey); + EnvelopeResponse response = await LoadEnvelope(envelopeKey); int documentId = EnsureValidDocumentIndex(request); logger.Debug("Loading document for Id [{0}]", documentId); diff --git a/EnvelopeGenerator.Web/appsettings.Development.json b/EnvelopeGenerator.Web/appsettings.Development.json index aefe0330..83b8aace 100644 --- a/EnvelopeGenerator.Web/appsettings.Development.json +++ b/EnvelopeGenerator.Web/appsettings.Development.json @@ -7,10 +7,11 @@ } }, "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", "LogDebug": true, "LogJson": true, - "AdminPassword": "dd" + "AdminPassword": "dd" } -} +} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/appsettings.json b/EnvelopeGenerator.Web/appsettings.json index 10f68b8c..83b8aace 100644 --- a/EnvelopeGenerator.Web/appsettings.json +++ b/EnvelopeGenerator.Web/appsettings.json @@ -1,9 +1,17 @@ { + "DetailedErrors": true, "Logging": { "LogLevel": { "Default": "Information", "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" + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/wwwroot/js/network.js b/EnvelopeGenerator.Web/wwwroot/js/network.js index fccdff38..e9bfc28d 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/network.js +++ b/EnvelopeGenerator.Web/wwwroot/js/network.js @@ -28,7 +28,7 @@ * @param {any} documentId */ async getDocument(envelopeKey, documentId) { - console.log("getDocument") + console.log("getDocument", `/api/document/${envelopeKey}?index=${documentId}`) return this.getRequest(`/api/document/${envelopeKey}?index=${documentId}`) .then(this.wrapBinaryResponse.bind(this)) } diff --git a/EnvelopeGenerator.sln b/EnvelopeGenerator.sln index 28071dbb..e8b12685 100644 --- a/EnvelopeGenerator.sln +++ b/EnvelopeGenerator.sln @@ -13,6 +13,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnvelopeGenerator.Web", "En EndProject Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EnvelopeGenerator.Service", "EnvelopeGenerator.Service\EnvelopeGenerator.Service.vbproj", "{83ED2617-B398-4859-8F59-B38F8807E83E}" 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 GlobalSection(SolutionConfigurationPlatforms) = preSolution 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}.Release|Any CPU.ActiveCfg = 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 GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE