diff --git a/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeHistoryService.cs b/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeHistoryService.cs index 04f7ed9e..9ef1bb4e 100644 --- a/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeHistoryService.cs +++ b/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeHistoryService.cs @@ -7,7 +7,7 @@ using static EnvelopeGenerator.Common.Constants; namespace EnvelopeGenerator.Application.Contracts.Services; -public interface IEnvelopeHistoryService : ICRUDService +public interface IEnvelopeHistoryService : ICRUDService { Task CountAsync(int? envelopeId = null, string? userReference = null, int? status = null); diff --git a/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeReceiverReadOnlyService.cs b/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeReceiverReadOnlyService.cs index 2e4cee14..22e27811 100644 --- a/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeReceiverReadOnlyService.cs +++ b/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeReceiverReadOnlyService.cs @@ -4,6 +4,6 @@ using EnvelopeGenerator.Domain.Entities; namespace EnvelopeGenerator.Application.Contracts.Services; -public interface IEnvelopeReceiverReadOnlyService : ICRUDService +public interface IEnvelopeReceiverReadOnlyService : ICRUDService { } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeReceiverService.cs b/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeReceiverService.cs index 0df0f2e9..cc384954 100644 --- a/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeReceiverService.cs +++ b/EnvelopeGenerator.Application/Contracts/Services/IEnvelopeReceiverService.cs @@ -1,4 +1,5 @@ -using DigitalData.Core.Abstractions.Application; +using CommandDotNet; +using DigitalData.Core.Abstractions.Application; using DigitalData.Core.DTO; using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver; using EnvelopeGenerator.Application.DTOs.Messaging; @@ -25,6 +26,7 @@ public interface IEnvelopeReceiverService : IBasicCRUDService> VerifyAccessCodeAsync(string uuid, string signature, string accessCode); + [Command("verify-access-code-async-by-id")] Task> VerifyAccessCodeAsync(string envelopeReceiverId, string accessCode); Task> IsExisting(string envelopeReceiverId); diff --git a/EnvelopeGenerator.Application/Contracts/Services/IReceiverService.cs b/EnvelopeGenerator.Application/Contracts/Services/IReceiverService.cs index 70702642..f56a7958 100644 --- a/EnvelopeGenerator.Application/Contracts/Services/IReceiverService.cs +++ b/EnvelopeGenerator.Application/Contracts/Services/IReceiverService.cs @@ -6,7 +6,7 @@ using EnvelopeGenerator.Domain.Entities; namespace EnvelopeGenerator.Application.Contracts.Services; -public interface IReceiverService : ICRUDService +public interface IReceiverService : ICRUDService { Task> ReadByAsync(string? emailAddress = null, string? signature = null); diff --git a/EnvelopeGenerator.Application/DTOs/MappingProfile.cs b/EnvelopeGenerator.Application/DTOs/MappingProfile.cs new file mode 100644 index 00000000..3dd3797a --- /dev/null +++ b/EnvelopeGenerator.Application/DTOs/MappingProfile.cs @@ -0,0 +1,70 @@ +using AutoMapper; +using EnvelopeGenerator.Application.DTOs.EnvelopeHistory; +using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver; +using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly; +using EnvelopeGenerator.Application.DTOs.Messaging; +using EnvelopeGenerator.Application.DTOs.Receiver; +using EnvelopeGenerator.Application.Extensions; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.DTOs; + +/// +/// Represents the AutoMapper profile configuration for mapping between +/// domain entities and data transfer objects (DTOs) used within the EnvelopeGenerator application. +/// +public class MappingProfile : Profile +{ + /// + /// Initializes a new instance of the class. + /// Configures the mappings between entities and DTOs used throughout the application. + /// + public MappingProfile() + { + // Entity to DTO mappings + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + + // DTO to Entity mappings + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore()); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + + // Messaging mappings + // for GTX messaging + CreateMap() + .ConstructUsing(gtxRes => gtxRes.Ok() + ? new SmsResponse() { Ok = true } + : new SmsResponse() { Ok = false, Errors = gtxRes }); + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Extensions/DIExtensions.cs b/EnvelopeGenerator.Application/DependencyInjection.cs similarity index 76% rename from EnvelopeGenerator.Application/Extensions/DIExtensions.cs rename to EnvelopeGenerator.Application/DependencyInjection.cs index 37579d98..0d96155c 100644 --- a/EnvelopeGenerator.Application/Extensions/DIExtensions.cs +++ b/EnvelopeGenerator.Application/DependencyInjection.cs @@ -1,6 +1,4 @@ -using DigitalData.UserManager.Application.MappingProfiles; -using EnvelopeGenerator.Application.MappingProfiles; -using EnvelopeGenerator.Application.Configurations; +using EnvelopeGenerator.Application.Configurations; using EnvelopeGenerator.Application.Services; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -8,11 +6,21 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using DigitalData.Core.Client; using QRCoder; using EnvelopeGenerator.Application.Contracts.Services; +using System.Reflection; -namespace EnvelopeGenerator.Application.Extensions; +namespace EnvelopeGenerator.Application; -public static class DIExtensions +/// +/// Extensions method for dependency injection +/// +public static class DependencyInjection { + /// + /// Adds all required services for envelope generator application + /// + /// + /// + /// public static IServiceCollection AddEnvelopeGeneratorServices(this IServiceCollection services, IConfiguration config) { //Inject CRUD Service and repositoriesad @@ -32,8 +40,8 @@ public static class DIExtensions services.TryAddScoped(); //Auto mapping profiles - services.AddAutoMapper(typeof(BasicDtoMappingProfile).Assembly); - services.AddAutoMapper(typeof(UserMappingProfile).Assembly); + services.AddAutoMapper(Assembly.GetExecutingAssembly()); + services.AddAutoMapper(typeof(DigitalData.UserManager.Application.DIExtensions)); services.Configure(config.GetSection(nameof(DispatcherParams))); services.Configure(config.GetSection(nameof(MailParams))); @@ -47,6 +55,11 @@ public static class DIExtensions services.TryAddSingleton(); services.TryAddSingleton(); + services.AddMediatR(cfg => + { + cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()); + }); + return services; } } \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Documents/Queries/Read/ReadDocumentMappingProfile.cs b/EnvelopeGenerator.Application/Documents/Queries/Read/ReadDocumentMappingProfile.cs new file mode 100644 index 00000000..ac9c9b18 --- /dev/null +++ b/EnvelopeGenerator.Application/Documents/Queries/Read/ReadDocumentMappingProfile.cs @@ -0,0 +1,18 @@ +using AutoMapper; +using EnvelopeGenerator.Domain.Entities; + +namespace EnvelopeGenerator.Application.Documents.Queries.Read; + +/// +/// +/// +public class ReadDocumentMappingProfile : Profile +{ + /// + /// + /// + public ReadDocumentMappingProfile() + { + CreateMap(); + } +} diff --git a/EnvelopeGenerator.Application/Documents/Queries/Read/ReadDocumentQuery.cs b/EnvelopeGenerator.Application/Documents/Queries/Read/ReadDocumentQuery.cs new file mode 100644 index 00000000..be3fc0c6 --- /dev/null +++ b/EnvelopeGenerator.Application/Documents/Queries/Read/ReadDocumentQuery.cs @@ -0,0 +1,12 @@ +using MediatR; + +namespace EnvelopeGenerator.Application.Documents.Queries.Read; + +/// +/// Represents a query to read a document based on its unique identifier or associated envelope identifier. +/// +/// The unique identifier of the document. Optional. +/// The identifier of the envelope associated with the document. Optional. +public record ReadDocumentQuery(int? Id = null, int? EnvelopeId = null) : IRequest +{ +} diff --git a/EnvelopeGenerator.Application/Documents/Queries/Read/ReadDocumentQueryHandler.cs b/EnvelopeGenerator.Application/Documents/Queries/Read/ReadDocumentQueryHandler.cs new file mode 100644 index 00000000..f27cfa1a --- /dev/null +++ b/EnvelopeGenerator.Application/Documents/Queries/Read/ReadDocumentQueryHandler.cs @@ -0,0 +1,47 @@ +using DigitalData.Core.Abstractions.Infrastructure; +using EnvelopeGenerator.Domain.Entities; +using MediatR; + +namespace EnvelopeGenerator.Application.Documents.Queries.Read; + +/// +/// Handles queries for reading data based on either the document ID or the envelope ID. +/// +public class ReadDocumentQueryHandler : IRequestHandler +{ + /// + /// Repository for accessing entities. + /// + private readonly IRepository _repo; + + /// + /// Initializes a new instance of the class. + /// + /// The repository used to access entities. + public ReadDocumentQueryHandler(IRepository envelopeDocumentRepository) + { + _repo = envelopeDocumentRepository; + } + + /// + /// Handles the and returns a based on the provided identifiers. + /// + /// The query containing the document ID or envelope ID to search for. + /// A token to monitor for cancellation requests. + /// + /// A if a matching document is found; otherwise, null. + /// + /// + /// Thrown when neither nor is provided. + /// + public async Task Handle(ReadDocumentQuery query, CancellationToken cancellationToken) + { + if (query.Id is not null) + return await _repo.ReadOrDefaultAsync(d => d.Id == query.Id); + else if (query.EnvelopeId is not null) + return await _repo.ReadOrDefaultAsync(d => d.EnvelopeId == query.EnvelopeId); + + throw new InvalidOperationException( + $"Invalid {nameof(ReadDocumentQuery)}: either {nameof(query.Id)} or {nameof(query.EnvelopeId)} must be provided."); + } +} diff --git a/EnvelopeGenerator.Application/Documents/Queries/Read/ReadDocumentResponse.cs b/EnvelopeGenerator.Application/Documents/Queries/Read/ReadDocumentResponse.cs new file mode 100644 index 00000000..e5d1b287 --- /dev/null +++ b/EnvelopeGenerator.Application/Documents/Queries/Read/ReadDocumentResponse.cs @@ -0,0 +1,27 @@ +namespace EnvelopeGenerator.Application.Documents.Queries.Read; + +/// +/// Represents the response for reading a document. +/// +public class ReadDocumentResponse +{ + /// + /// The unique identifier of the document. + /// + public int Guid { get; init; } + + /// + /// The identifier of the associated envelope. + /// + public int EnvelopeId { get; init; } + + /// + /// The date and time when the document was added. + /// + public DateTime AddedWhen { get; init; } + + /// + /// The binary data of the document, if available. + /// + public byte[]? ByteData { get; init; } +} diff --git a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj index 3fe00e17..f032eed8 100644 --- a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj +++ b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj @@ -13,19 +13,18 @@ - - - + + - - - + + + + - - + @@ -60,4 +59,25 @@ + + + + 7.0.5 + + + + + + + 8.1.1 + + + + + + + 8.1.1 + + + diff --git a/EnvelopeGenerator.Application/MappingProfiles/BasicDtoMappingProfile.cs b/EnvelopeGenerator.Application/MappingProfiles/BasicDtoMappingProfile.cs deleted file mode 100644 index ae14252e..00000000 --- a/EnvelopeGenerator.Application/MappingProfiles/BasicDtoMappingProfile.cs +++ /dev/null @@ -1,64 +0,0 @@ -using AutoMapper; -using EnvelopeGenerator.Application.DTOs; -using EnvelopeGenerator.Application.DTOs.EnvelopeHistory; -using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver; -using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly; -using EnvelopeGenerator.Application.DTOs.Messaging; -using EnvelopeGenerator.Application.DTOs.Receiver; -using EnvelopeGenerator.Application.Extensions; -using EnvelopeGenerator.Domain.Entities; - -namespace EnvelopeGenerator.Application.MappingProfiles -{ - public class BasicDtoMappingProfile : Profile - { - public BasicDtoMappingProfile() - { - // Entity to DTO mappings - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - - // DTO to Entity mappings - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore()); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - - // Messaging mappings - // for GTX messaging - CreateMap() - .ConstructUsing(gtxRes => gtxRes.Ok() - ? new SmsResponse() { Ok = true } - : new SmsResponse() { Ok = false, Errors = gtxRes }); - } - } -} \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Services/EnvelopeHistoryService.cs b/EnvelopeGenerator.Application/Services/EnvelopeHistoryService.cs index a5d97c3b..9d0bc453 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeHistoryService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeHistoryService.cs @@ -10,7 +10,7 @@ using EnvelopeGenerator.Application.Contracts.Services; namespace EnvelopeGenerator.Application.Services; -public class EnvelopeHistoryService : CRUDService, IEnvelopeHistoryService +public class EnvelopeHistoryService : CRUDService, IEnvelopeHistoryService { public EnvelopeHistoryService(IEnvelopeHistoryRepository repository, IMapper mapper) : base(repository, mapper) diff --git a/EnvelopeGenerator.Application/Services/EnvelopeReceiverReadOnlyService.cs b/EnvelopeGenerator.Application/Services/EnvelopeReceiverReadOnlyService.cs index aa4d11da..74746ee5 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeReceiverReadOnlyService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeReceiverReadOnlyService.cs @@ -7,7 +7,7 @@ using EnvelopeGenerator.Application.Contracts.Repositories; namespace EnvelopeGenerator.Application.Services; -public class EnvelopeReceiverReadOnlyService : CRUDService, IEnvelopeReceiverReadOnlyService +public class EnvelopeReceiverReadOnlyService : CRUDService, IEnvelopeReceiverReadOnlyService { public EnvelopeReceiverReadOnlyService(IEnvelopeReceiverReadOnlyRepository repository, IMapper mapper) : base(repository, mapper) { diff --git a/EnvelopeGenerator.Application/Services/ReceiverService.cs b/EnvelopeGenerator.Application/Services/ReceiverService.cs index 98b6a58b..2b587daa 100644 --- a/EnvelopeGenerator.Application/Services/ReceiverService.cs +++ b/EnvelopeGenerator.Application/Services/ReceiverService.cs @@ -10,7 +10,7 @@ using EnvelopeGenerator.Application.Contracts.Services; namespace EnvelopeGenerator.Application.Services; -public class ReceiverService : CRUDService, IReceiverService +public class ReceiverService : CRUDService, IReceiverService { public ReceiverService(IReceiverRepository repository, IMapper mapper) : base(repository, mapper) diff --git a/EnvelopeGenerator.Domain/Entities/EnvelopeDocument.cs b/EnvelopeGenerator.Domain/Entities/EnvelopeDocument.cs index 97723f0a..22aa3b2b 100644 --- a/EnvelopeGenerator.Domain/Entities/EnvelopeDocument.cs +++ b/EnvelopeGenerator.Domain/Entities/EnvelopeDocument.cs @@ -2,27 +2,26 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace EnvelopeGenerator.Domain.Entities +namespace EnvelopeGenerator.Domain.Entities; + +[Table("TBSIG_ENVELOPE_DOCUMENT", Schema = "dbo")] +public class EnvelopeDocument : IUnique { - [Table("TBSIG_ENVELOPE_DOCUMENT", Schema = "dbo")] - public class EnvelopeDocument : IUnique - { - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - [Column("GUID")] - public int Id { get; set; } + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [Column("GUID")] + public int Id { get; set; } - [Required] - [Column("ENVELOPE_ID")] - public int EnvelopeId { get; set; } + [Required] + [Column("ENVELOPE_ID")] + public int EnvelopeId { get; set; } - [Required] - [Column("ADDED_WHEN", TypeName = "datetime")] - public required DateTime AddedWhen { get; set; } + [Required] + [Column("ADDED_WHEN", TypeName = "datetime")] + public required DateTime AddedWhen { get; set; } - [Column("BYTE_DATA", TypeName = "varbinary(max)")] - public byte[]? ByteData { get; init; } + [Column("BYTE_DATA", TypeName = "varbinary(max)")] + public byte[]? ByteData { get; init; } - public IEnumerable? Elements { get; set; } - } + public IEnumerable? Elements { get; set; } } \ No newline at end of file diff --git a/EnvelopeGenerator.Domain/EnvelopeGenerator.Domain.csproj b/EnvelopeGenerator.Domain/EnvelopeGenerator.Domain.csproj index 7827d6d6..8a430d70 100644 --- a/EnvelopeGenerator.Domain/EnvelopeGenerator.Domain.csproj +++ b/EnvelopeGenerator.Domain/EnvelopeGenerator.Domain.csproj @@ -1,4 +1,4 @@ - + net7.0;net8.0;net9.0 @@ -7,9 +7,9 @@ - - - + + + diff --git a/EnvelopeGenerator.Form/Controllers/EnvelopeEditorController.vb b/EnvelopeGenerator.Form/Controllers/EnvelopeEditorController.vb index 0f3de13d..62ac97bd 100644 --- a/EnvelopeGenerator.Form/Controllers/EnvelopeEditorController.vb +++ b/EnvelopeGenerator.Form/Controllers/EnvelopeEditorController.vb @@ -159,7 +159,11 @@ Public Class EnvelopeEditorController Public Async Function CreateDocument(pDocumentFilePath As String) As Threading.Tasks.Task(Of EnvelopeDocument) Try - Dim oFileInfo = New FileInfo(pDocumentFilePath) + Dim oFixedPath = FixPageRotation.FixPageRotation(pDocumentFilePath) + If oFixedPath <> pDocumentFilePath Then + Logger.Info("PageRotation has been reseted to 0.") + End If + Dim oFileInfo = New FileInfo(oFixedPath) Dim oTempFiles As New TempFiles(State.LogConfig) Dim oTempFilePath = Path.Combine(oTempFiles._TempPath, Guid.NewGuid().ToString + oFileInfo.Extension) @@ -175,7 +179,7 @@ Public Class EnvelopeEditorController .FileNameOriginal = oFileInfo.Name, .Thumbnail = Thumbnail.GetThumbnailFromPDFFile(oTempFilePath), .PageCount = Thumbnail.GetPageCount(oTempFilePath), - .Byte_Data = ReadFile(pDocumentFilePath) + .Byte_Data = ReadFile(oFixedPath) } Return oDocument diff --git a/EnvelopeGenerator.Form/EnvelopeGenerator.Form.vbproj b/EnvelopeGenerator.Form/EnvelopeGenerator.Form.vbproj index d499ac6c..67de8a81 100644 --- a/EnvelopeGenerator.Form/EnvelopeGenerator.Form.vbproj +++ b/EnvelopeGenerator.Form/EnvelopeGenerator.Form.vbproj @@ -368,6 +368,7 @@ Form + diff --git a/EnvelopeGenerator.Form/Helper/FixPageRotation.vb b/EnvelopeGenerator.Form/Helper/FixPageRotation.vb new file mode 100644 index 00000000..58c7a8c9 --- /dev/null +++ b/EnvelopeGenerator.Form/Helper/FixPageRotation.vb @@ -0,0 +1,48 @@ +Imports System.IO +Imports GdPicture14 + +Public Class FixPageRotation + ''' + ''' Checks if there are any rotations in the document. If so, normalizes the page rotation to 0 without affecting its visual appearance. + ''' Creates and uses a new document with the corrected properties. + ''' Fixes the issue of annotations being rotated to match the page's rotation. + ''' + ''' + ''' + Public Shared Function FixPageRotation(pFilePath As String) As String + + Dim oFolder As String = Path.GetDirectoryName(pFilePath) + Dim oChanged As Boolean = False + + Using gdpicturePDF As New GdPicturePDF() + + Dim status As GdPictureStatus = gdpicturePDF.LoadFromFile(pFilePath, True) + If status = GdPictureStatus.OK Then + + Dim count As Integer = gdpicturePDF.GetPageCount() + For i As Integer = 1 To count + If gdpicturePDF.SelectPage(i) = GdPictureStatus.OK Then + Dim rotation As Integer = gdpicturePDF.GetPageRotation() + If rotation <> 0 Then + gdpicturePDF.NormalizePage() + oChanged = True + End If + End If + Next + + End If + + If oChanged Then + Dim newFilesPath As String = Path.Combine(oFolder, "RotationFixed_" & Path.GetFileName(pFilePath)) + If gdpicturePDF.SaveToFile(newFilesPath) = GdPictureStatus.OK Then + Return newFilesPath + End If + End If + + End Using + + Return pFilePath + + End Function + +End Class diff --git a/EnvelopeGenerator.GeneratorAPI/EnvelopeGenerator.GeneratorAPI.csproj b/EnvelopeGenerator.GeneratorAPI/EnvelopeGenerator.GeneratorAPI.csproj index e20cd48a..e76957a6 100644 --- a/EnvelopeGenerator.GeneratorAPI/EnvelopeGenerator.GeneratorAPI.csproj +++ b/EnvelopeGenerator.GeneratorAPI/EnvelopeGenerator.GeneratorAPI.csproj @@ -19,16 +19,35 @@ - - + + + + + + + + + + + + + + + + + + + + + diff --git a/EnvelopeGenerator.GeneratorAPI/Program.cs b/EnvelopeGenerator.GeneratorAPI/Program.cs index 1ff1f7c4..77ee0bab 100644 --- a/EnvelopeGenerator.GeneratorAPI/Program.cs +++ b/EnvelopeGenerator.GeneratorAPI/Program.cs @@ -1,7 +1,6 @@ using DigitalData.Core.API; using DigitalData.Core.Application; using DigitalData.UserManager.Application; -using EnvelopeGenerator.Application.Extensions; using EnvelopeGenerator.Infrastructure; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Localization; @@ -9,6 +8,8 @@ using Microsoft.EntityFrameworkCore; using System.Globalization; using Scalar.AspNetCore; using Microsoft.OpenApi.Models; +using DigitalData.UserManager.DependencyInjection; +using EnvelopeGenerator.Application; var builder = WebApplication.CreateBuilder(args); @@ -101,7 +102,7 @@ builder.Services.AddUserManager(); // LDAP builder.ConfigureBySection(); -builder.Services.AddDirectorySearchService(); +builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions")); // Localizer builder.Services.AddCookieBasedLocalizer() ; diff --git a/EnvelopeGenerator.Infrastructure/DIExtensions.cs b/EnvelopeGenerator.Infrastructure/DIExtensions.cs index 0f6b7485..c457d264 100644 --- a/EnvelopeGenerator.Infrastructure/DIExtensions.cs +++ b/EnvelopeGenerator.Infrastructure/DIExtensions.cs @@ -3,6 +3,9 @@ using EnvelopeGenerator.Infrastructure.Repositories; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.EntityFrameworkCore; +using DigitalData.Core.Infrastructure; +using EnvelopeGenerator.Domain.Entities; +using DigitalData.Core.Infrastructure.AutoMapper; namespace EnvelopeGenerator.Infrastructure; @@ -30,13 +33,11 @@ public static class DIExtensions services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); - services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); - services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); @@ -44,6 +45,20 @@ public static class DIExtensions services.TryAddScoped(); services.TryAddScoped(); + services.AddDbRepository(context => context.Configs).UseAutoMapper(); + services.AddDbRepository(context => context.DocumentReceiverElements).UseAutoMapper(); + services.AddDbRepository(context => context.EnvelopeDocument).UseAutoMapper(); + services.AddDbRepository(context => context.DocumentStatus).UseAutoMapper(); + services.AddDbRepository(context => context.EmailTemplate).UseAutoMapper(); + services.AddDbRepository(context => context.Envelopes).UseAutoMapper(); + services.AddDbRepository(context => context.EnvelopeCertificates).UseAutoMapper(); + services.AddDbRepository(context => context.EnvelopeHistories).UseAutoMapper(); + services.AddDbRepository(context => context.EnvelopeReceivers).UseAutoMapper(); + services.AddDbRepository(context => context.EnvelopeTypes).UseAutoMapper(); + services.AddDbRepository(context => context.Receivers).UseAutoMapper(); + services.AddDbRepository(context => context.UserReceivers).UseAutoMapper(); + services.AddDbRepository(context => context.EnvelopeReceiverReadOnlys).UseAutoMapper(); + return services; } } diff --git a/EnvelopeGenerator.Infrastructure/EGDbContext.cs b/EnvelopeGenerator.Infrastructure/EGDbContext.cs index 7772a47a..89bf900f 100644 --- a/EnvelopeGenerator.Infrastructure/EGDbContext.cs +++ b/EnvelopeGenerator.Infrastructure/EGDbContext.cs @@ -57,6 +57,8 @@ public class EGDbContext : DbContext, IUserManagerDbContext, IMailDbContext public DbSet EnvelopeReceiverReadOnlys { get; set; } + public DbSet ClientUsers { get; set; } + private readonly DbTriggerParams _triggers; private readonly ILogger _logger; diff --git a/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj b/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj index 9f46ea48..f18a5f72 100644 --- a/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj +++ b/EnvelopeGenerator.Infrastructure/EnvelopeGenerator.Infrastructure.csproj @@ -7,21 +7,31 @@ - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + + + + + + + + + + + + + + + + + + diff --git a/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeDocumentRepository.cs b/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeDocumentRepository.cs index 153510f7..2f15f10f 100644 --- a/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeDocumentRepository.cs +++ b/EnvelopeGenerator.Infrastructure/Repositories/EnvelopeDocumentRepository.cs @@ -1,5 +1,4 @@ using DigitalData.Core.Infrastructure; -using DigitalData.UserManager.Infrastructure.Repositories; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Application.Contracts.Repositories; diff --git a/EnvelopeGenerator.Terminal/CommandManager.cs b/EnvelopeGenerator.Terminal/CommandManager.cs new file mode 100644 index 00000000..3ba0f76a --- /dev/null +++ b/EnvelopeGenerator.Terminal/CommandManager.cs @@ -0,0 +1,42 @@ +using CommandDotNet; +using EnvelopeGenerator.Application.Contracts.Services; +using EnvelopeGenerator.Application.Documents.Queries.Read; +using MediatR; +using System.Reflection; +using System.Text.Json; + +namespace EnvelopeGenerator.Terminal; + +public class CommandManager +{ + private static JsonSerializerOptions Options = new () + { + WriteIndented = true + }; + private readonly IEnvelopeReceiverService _envelopeReceiverService; + private readonly IMediator _mediator; + + public CommandManager(IEnvelopeReceiverService envelopeReceiverService, IMediator mediator) + { + _envelopeReceiverService = envelopeReceiverService; + _mediator = mediator; + } + + [DefaultCommand] + public void Execute([Option(Description = "print envelope generator termianal version.")] bool version) + { + if(version) + Console.WriteLine($"v{Assembly.GetExecutingAssembly().GetName().Version}"); + } + + [Subcommand] + public IEnvelopeReceiverService EnvelopeReceiver => _envelopeReceiverService; + + [Command] + public async Task ReadDocument(IConsole console, int? id = null, int? envelopeId = null) + { + ReadDocumentQuery query = new(id, envelopeId); + var document = await _mediator.Send(query); + console.WriteLine(JsonSerializer.Serialize(document, Options)); + } +} diff --git a/EnvelopeGenerator.Terminal/DependencyInjection.cs b/EnvelopeGenerator.Terminal/DependencyInjection.cs new file mode 100644 index 00000000..ad011146 --- /dev/null +++ b/EnvelopeGenerator.Terminal/DependencyInjection.cs @@ -0,0 +1,55 @@ +using CommandDotNet.NameCasing; +using CommandDotNet; +using Microsoft.Extensions.DependencyInjection; +using CommandDotNet.IoC.MicrosoftDependencyInjection; +using EnvelopeGenerator.Infrastructure; +using Microsoft.Extensions.Configuration; +using Microsoft.EntityFrameworkCore; +using EnvelopeGenerator.Application.Contracts.Services; +using EnvelopeGenerator.Application.Services; +using Microsoft.Extensions.Hosting; +using EnvelopeGenerator.Application; + +namespace EnvelopeGenerator.Terminal; + +public static class DependencyInjection +{ + public static IServiceCollection AddCommandManagerRunner(this IServiceCollection services, IConfiguration configuration, Case @case = Case.KebabCase, string connectionStringKeyName = "Default") + { + var connStr = configuration.GetConnectionString(connectionStringKeyName) + ?? throw new InvalidOperationException("There is no default connection string in appsettings.json."); + + services.AddDistributedSqlServerCache(options => + { + options.ConnectionString = connStr; + options.SchemaName = "dbo"; + options.TableName = "TBDD_CACHE"; + }); + + // Add envelope generator services + services.AddEnvelopeGeneratorRepositories(options => options.UseSqlServer(connStr)); + + return services + .AddSingleton() + .AddEnvelopeGeneratorRepositories() + .AddEnvelopeGeneratorServices(configuration) + .AddSingleton(sp => + { + var runner = new AppRunner(); + runner.UseMicrosoftDependencyInjection(sp); + runner.UseNameCasing(@case); + return runner; + }) + .AddScoped() + .AddMemoryCache() + .AddLocalization(); + } + + public static Task RunCommandManagerRunner(this IServiceProvider provider, string[] args) + { + var runner = provider.GetRequiredService>(); + return runner.RunAsync(args); + } + + public static Task RunCommandManagerRunner(this IHost host, string[] args) => host.Services.RunCommandManagerRunner(args); +} diff --git a/EnvelopeGenerator.Terminal/EnvelopeGenerator.Terminal.csproj b/EnvelopeGenerator.Terminal/EnvelopeGenerator.Terminal.csproj new file mode 100644 index 00000000..175074cf --- /dev/null +++ b/EnvelopeGenerator.Terminal/EnvelopeGenerator.Terminal.csproj @@ -0,0 +1,38 @@ + + + + Exe + net7.0 + enable + enable + + + + + PreserveNewest + true + PreserveNewest + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/EnvelopeGenerator.Terminal/Program.cs b/EnvelopeGenerator.Terminal/Program.cs new file mode 100644 index 00000000..2aed708a --- /dev/null +++ b/EnvelopeGenerator.Terminal/Program.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Hosting; + +namespace EnvelopeGenerator.Terminal; + +public class Program +{ + static async Task Main(string[] args) + { + var builder = Host.CreateApplicationBuilder(args); + + var config = builder.Configuration; + + builder.Services.AddCommandManagerRunner(config); + + var app = builder.Build(); + + return await app.RunCommandManagerRunner(args); + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Terminal/appsettings.json b/EnvelopeGenerator.Terminal/appsettings.json new file mode 100644 index 00000000..136c48a8 --- /dev/null +++ b/EnvelopeGenerator.Terminal/appsettings.json @@ -0,0 +1,208 @@ +{ + "DiPMode": false, //Please be careful when enabling Development in Production (DiP) mode. It allows Swagger and test controllers to be enabled in a production environment. + "EnableSwagger": true, + "EnableTestControllers": true, + "DetailedErrors": true, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "Microsoft.AspNetCore.Hosting.Diagnostics": "Warning" + } + }, + "ConnectionStrings": { + "Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;" + }, + "PSPDFKitLicenseKey": "SXCtGGY9XA-31OGUXQK-r7c6AkdLGPm2ljuyDr1qu0kkhLvydg-Do-fxpNUF4Rq3fS_xAnZRNFRHbXpE6sQ2BMcCSVTcXVJO6tPviexjpiT-HnrDEySlUERJnnvh-tmeOWprxS6BySPnSILkmaVQtUfOIUS-cUbvvEYHTvQBKbSF8di4XHQFyfv49ihr51axm3NVV3AXwh2EiKL5C5XdqBZ4sQ4O7vXBjM2zvxdPxlxdcNYmiU83uAzw7B83O_jubPzya4CdUHh_YH7Nlp2gP56MeG1Sw2JhMtfG3Rj14Sg4ctaeL9p6AEWca5dDjJ2li5tFIV2fQSsw6A_cowLu0gtMm5i8IfJXeIcQbMC2-0wGv1oe9hZYJvFMdzhTM_FiejM0agemxt3lJyzuyP8zbBSOgp7Si6A85krLWPZptyZBTG7pp7IHboUHfPMxCXqi-zMsqewOJtQBE2mjntU-lPryKnssOpMPfswwQX7QSkJYV5EMqNmEhQX6mEkp2wcqFzMC7bJQew1aO4pOpvChUaMvb1vgRek0HxLag0nwQYX2YrYGh7F_xXJs-8HNwJe8H0-eW4x4faayCgM5rB5772CCCsD9ThZcvXFrjNHHLGJ8WuBUFm6LArvSfFQdii_7j-_sqHMpeKZt26NFgivj1A==", + "Content-Security-Policy": [ // The first format parameter {0} will be replaced by the nonce value. + "default-src 'self'", + "script-src 'self' 'nonce-{0}' 'unsafe-eval'", + "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com:*", + "img-src 'self' data: https: blob:", + "font-src 'self' https://fonts.gstatic.com:*", + "connect-src 'self' https://nominatim.openstreetmap.org:* http://localhost:* https://localhost:* ws://localhost:* wss://localhost:* blob:", + "frame-src 'self'", + "media-src 'self'", + "object-src 'self'" + ], + "AllowedOrigins": [ "https://localhost:7202", "https://digitale.unterschrift.wisag.de/" ], + "NLog": { + "throwConfigExceptions": true, + "variables": { + "logDirectory": "E:\\LogFiles\\Digital Data\\signFlow", + "logFileNamePrefix": "${shortdate}-ECM.EnvelopeGenerator.Web" + }, + "targets": { + "infoLogs": { + "type": "File", + "fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log", + "maxArchiveDays": 30 + }, + "errorLogs": { + "type": "File", + "fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log", + "maxArchiveDays": 30 + }, + "criticalLogs": { + "type": "File", + "fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log", + "maxArchiveDays": 30 + } + }, + // Trace, Debug, Info, Warn, Error and *Fatal* + "rules": [ + { + "logger": "*", + "minLevel": "Info", + "maxLevel": "Warn", + "writeTo": "infoLogs" + }, + { + "logger": "*", + "level": "Error", + "writeTo": "errorLogs" + }, + { + "logger": "*", + "level": "Fatal", + "writeTo": "criticalLogs" + } + ] + }, + "ContactLink": { + "Label": "Kontakt", + "Href": "https://digitaldata.works/", + "HrefLang": "de", + "Target": "_blank", + "Title": "Digital Data GmbH" + }, + /* Resx naming format is -> Resource.language.resx (eg: Resource.de_DE.resx). + To add a new language, first you should write the required resx file. + first is the default culture name. */ + "Cultures": [ + { + "Language": "de-DE", + "FIClass": "fi-de" + }, + { + "Language": "en-US", + "FIClass": "fi-us" + } + ], + "DisableMultiLanguage": false, + "Regexes": [ + { + "Pattern": "/^\\p{L}+(?:([\\ \\-\\']|(\\.\\ ))\\p{L}+)*$/u", + "Name": "City", + "Platforms": [ ".NET" ] + }, + { + "Pattern": "/^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$/", + "Name": "City", + "Platforms": [ "javascript" ] + } + ], + "CustomImages": { + "App": { + "Src": "/img/DD_signFLOW_LOGO.png", + "Classes": { + "Main": "signFlow-logo" + } + }, + "Company": { + "Src": "/img/digital_data.svg", + "Classes": { + "Show": "dd-show-logo", + "Locked": "dd-locked-logo" + } + } + }, + "DispatcherParams": { + "SendingProfile": 1, + "AddedWho": "DDEnvelopGenerator", + "ReminderTypeId": 202377, + "EmailAttmt1": "" + }, + "MailParams": { + "Placeholders": { + "[NAME_PORTAL]": "signFlow", + "[SIGNATURE_TYPE]": "signieren", + "[REASON]": "" + } + }, + "GtxMessagingParams": { + "Uri": "https://rest.gtx-messaging.net", + "Path": "smsc/sendsms/f566f7e5-bdf2-4a9a-bf52-ed88215a432e/json", + "Headers": {}, + "QueryParams": { + "from": "signFlow" + } + }, + "TFARegParams": { + "TimeLimit": "00:30:00" + }, + "DbTriggerParams": { + "Envelope": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ], + "EnvelopeHistory": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ], + "EmailOut": [ "TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD" ], + "EnvelopeReceiverReadOnly": [ "TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD" ], + "Receiver": [] + }, + "MainPageTitle": null, + "AnnotationParams": { + "DefaultAnnotation": { + "Width": 1, + "Height": 0.5, + "MarginTop": 1 + }, + "Annotations": [ + { + "Name": "Signature", + "MarginTop": 0 + }, + { + "Name": "PositionLabel", + "VerBoundAnnotName": "Signature", + "WidthRatio": 1.2, + "HeightRatio": 0.5, + "MarginTopRatio": 0.22 + }, + { + "Name": "Position", + "VerBoundAnnotName": "PositionLabel", + "WidthRatio": 1.2, + "HeightRatio": 0.5, + "MarginTopRatio": -0.05 + }, + { + "Name": "CityLabel", + "VerBoundAnnotName": "Position", + "WidthRatio": 1.2, + "HeightRatio": 0.5, + "MarginTopRatio": 0.05 + }, + { + "Name": "City", + "VerBoundAnnotName": "CityLabel", + "WidthRatio": 1.2, + "HeightRatio": 0.5, + "MarginTopRatio": -0.05 + }, + { + "Name": "DateLabel", + "VerBoundAnnotName": "City", + "WidthRatio": 1.55, + "HeightRatio": 0.5, + "MarginTopRatio": 0.05 + }, + { + "Name": "Date", + "VerBoundAnnotName": "DateLabel", + "WidthRatio": 1.55, + "HeightRatio": 0.5, + "MarginTopRatio": -0.1 + } + ] + } +} diff --git a/EnvelopeGenerator.Tests.Application/EnvelopeGenerator.Tests.Application.csproj b/EnvelopeGenerator.Tests.Application/EnvelopeGenerator.Tests.Application.csproj index b9626bf8..c6e028c7 100644 --- a/EnvelopeGenerator.Tests.Application/EnvelopeGenerator.Tests.Application.csproj +++ b/EnvelopeGenerator.Tests.Application/EnvelopeGenerator.Tests.Application.csproj @@ -23,15 +23,18 @@ + + + - + - + diff --git a/EnvelopeGenerator.Tests.Application/Mock.cs b/EnvelopeGenerator.Tests.Application/Mock.cs index a9cc42d6..10bc7ea6 100644 --- a/EnvelopeGenerator.Tests.Application/Mock.cs +++ b/EnvelopeGenerator.Tests.Application/Mock.cs @@ -1,10 +1,10 @@ using Microsoft.Extensions.Hosting; -using EnvelopeGenerator.Application.Extensions; using EnvelopeGenerator.Infrastructure; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using EnvelopeGenerator.Application.Services; using Microsoft.EntityFrameworkCore; +using EnvelopeGenerator.Application; namespace EnvelopeGenerator.Tests.Application; diff --git a/EnvelopeGenerator.Web/Controllers/Test/TestControllerBase.cs b/EnvelopeGenerator.Web/Controllers/Test/TestControllerBase.cs index b92ae934..9e81ac1f 100644 --- a/EnvelopeGenerator.Web/Controllers/Test/TestControllerBase.cs +++ b/EnvelopeGenerator.Web/Controllers/Test/TestControllerBase.cs @@ -8,7 +8,7 @@ namespace EnvelopeGenerator.Web.Controllers.Test [ApiController] [Route("api/test/[controller]")] public class TestControllerBase : BasicCRUDControllerBase - where TCRUDService : ICRUDService + where TCRUDService : ICRUDService where TDto : class, IUnique where TEntity : class, IUnique { public TestControllerBase(ILogger logger, TCRUDService service) : base(logger, service) diff --git a/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj b/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj index 7f65cc86..7b50de3b 100644 --- a/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj +++ b/EnvelopeGenerator.Web/EnvelopeGenerator.Web.csproj @@ -1,7 +1,7 @@  - net7.0;net8.0;net9.0 + net7.0 enable enable EnvelopeGenerator.Web @@ -2101,15 +2101,16 @@ - - - + + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -2131,7 +2132,6 @@ - diff --git a/EnvelopeGenerator.Web/Program.cs b/EnvelopeGenerator.Web/Program.cs index 96fcec96..8a2f7fc1 100644 --- a/EnvelopeGenerator.Web/Program.cs +++ b/EnvelopeGenerator.Web/Program.cs @@ -15,7 +15,6 @@ using DigitalData.EmailProfilerDispatcher; using EnvelopeGenerator.Infrastructure; using EnvelopeGenerator.Web.Sanitizers; using EnvelopeGenerator.Application.Contracts.Services; -using EnvelopeGenerator.Application.Extensions; var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); logger.Info("Logging initialized!"); diff --git a/EnvelopeGenerator.sln b/EnvelopeGenerator.sln index 0275e352..4c8aa8c9 100644 --- a/EnvelopeGenerator.sln +++ b/EnvelopeGenerator.sln @@ -35,6 +35,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "infrastructure", "infrastru EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "presentation", "presentation", "{E3C758DC-914D-4B7E-8457-0813F1FDB0CB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Terminal", "EnvelopeGenerator.Terminal\EnvelopeGenerator.Terminal.csproj", "{A9F9B431-BB9B-49B8-9E2C-0703634A653A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -85,6 +87,10 @@ Global {A4D0DD1A-67BC-4E1A-AD29-BC4BC0D41399}.Debug|Any CPU.Build.0 = Debug|Any CPU {A4D0DD1A-67BC-4E1A-AD29-BC4BC0D41399}.Release|Any CPU.ActiveCfg = Release|Any CPU {A4D0DD1A-67BC-4E1A-AD29-BC4BC0D41399}.Release|Any CPU.Build.0 = Release|Any CPU + {A9F9B431-BB9B-49B8-9E2C-0703634A653A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9F9B431-BB9B-49B8-9E2C-0703634A653A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9F9B431-BB9B-49B8-9E2C-0703634A653A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9F9B431-BB9B-49B8-9E2C-0703634A653A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -104,6 +110,7 @@ Global {A4D0DD1A-67BC-4E1A-AD29-BC4BC0D41399} = {0CBC2432-A561-4440-89BC-671B66A24146} {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {134D4164-B291-4E19-99B9-E4FA3AFAB62C} {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} = {134D4164-B291-4E19-99B9-E4FA3AFAB62C} + {A9F9B431-BB9B-49B8-9E2C-0703634A653A} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {73E60370-756D-45AD-A19A-C40A02DACCC7}