Compare commits

...

20 Commits

Author SHA1 Message Date
297ab458c7 Replace Cultures with MultiCulture and update middleware order
Switched configuration and DI from Cultures to MultiCulture.
Adjusted middleware registration so CultureMiddleware runs after ExceptionHandlingMiddleware.
2026-02-12 17:16:47 +01:00
0e270e3182 Update Culture model and DI for non-nullable properties
Refactored Culture model to use required and non-nullable properties.
Removed nullable checks for Info in ShowEnvelope.cshtml.
Changed DI from Cultures to MultiCulture in _ViewImports.cshtml.
2026-02-12 17:16:36 +01:00
bcf55f7906 Update CultureMiddleware to use MultiCulture config
Replaced Cultures with MultiCulture in CultureMiddleware.cs.
Constructor now accepts IOptions<MultiCulture> for culture settings.
2026-02-12 17:15:34 +01:00
489b2d0917 Rename Cultures class to MultiCulture throughout codebase
Refactored all references from Cultures to MultiCulture in controllers and base classes. Updated the class definition in MultiCulture.cs to improve clarity and consistency in culture-related functionality. No functional changes were made.
2026-02-12 17:15:14 +01:00
63fbdc3bd5 Validate culture cookie against supported languages
Changed CultureMiddleware to check if the culture cookie value is in the list of supported languages, not just for null or empty. This ensures that only valid, supported culture values are accepted from the cookie; otherwise, the middleware falls back to Accept-Language or default logic.
2026-02-12 16:51:51 +01:00
aaf1f75aa7 Change Localize endpoint to use route parameter for key
The Localize endpoint in TestLocalizerController now accepts the "key" parameter as a route parameter instead of a query string. The method signature was updated to use [FromRoute], and the route template was modified to include "{key}". This changes the endpoint usage from /localize?key=de_DE to /localize/de_DE.
2026-02-12 16:42:49 +01:00
cdca639e4f Update culture handling and adjust middleware order
Changed GetCulture to return current UI culture if cookie is missing, ensuring a non-null result. Reordered middleware in Program.cs to run ExceptionHandlingMiddleware after CultureMiddleware.
2026-02-12 16:40:51 +01:00
ab57dd4cee Add endpoints for culture detection and improve cookie parsing
Added two endpoints to TestLocalizerController for retrieving culture from Accept-Language and user cookies. Enhanced GetCulture extension to parse and return the actual culture string from cookies. Updated usings for new extension methods.
2026-02-12 16:11:40 +01:00
745e9c7bfe Add GetCultureByAcceptLanguage extension for HttpContext
Introduced a new extension method, GetCultureByAcceptLanguage, in the WebExtensions class. This method parses the Accept-Language header from the HTTP request and returns the first valid CultureInfo, enabling detection of the user's preferred culture from browser settings.
2026-02-11 13:54:27 +01:00
670c8ed87c Improve culture selection using Accept-Language header
When no culture cookie is present, use the browser's Accept-Language
header to determine the user's preferred culture. This culture is
applied and stored in a cookie, enhancing localization by respecting
user preferences instead of always defaulting to the application's
default language.
2026-02-11 13:51:01 +01:00
91eb5d1e64 Make Culture lookup case-insensitive; add GetOrDefault()
Changed Culture indexer to use case-insensitive language matching.
Added GetOrDefault() to return a Culture or the default if not found.
2026-02-11 13:50:02 +01:00
9f59a17f63 Mark CreateAsync as obsolete; update Flag reference
Marked CreateAsync in ReadOnlyController as [Obsolete] to encourage use of MediatR. Simplified Flag.DataIntegrityIssue reference in EnvelopeMailService error handling.
2026-02-11 13:14:07 +01:00
2a0f7f99d6 Refactor EnvelopeMailService to use MediatR ISender
Replaces IAuthenticator with ISender in EnvelopeMailService, updates the constructor accordingly, and removes unused dependencies. Improves code readability and formatting, cleans up unused usings and redundant code, and aligns with the intended MediatR-based architecture. No functional changes to email sending logic.
2026-02-11 12:59:17 +01:00
ec674b6e80 Refactor TestEmailTemplateController to use MediatR
Replaced direct service dependency and base class inheritance with MediatR in TestEmailTemplateController. Updated the GetAll endpoint to use a MediatR query and removed obsolete code related to the previous service-based approach. This aligns the controller with CQRS/MediatR best practices.
2026-02-11 12:45:32 +01:00
10f23170fd Rename email template query methods for consistency
Renamed ReadEmailTemplateById and ReadEmailTemplateByType to ReadEmailTemplateAsync in ReadEmailTemplateQueryExtensions. This improves naming consistency and better reflects their asynchronous behavior. No changes to method signatures or functionality.
2026-02-11 12:08:26 +01:00
0ec823ec9e Add ISender extension methods for reading email templates
Introduced ReadEmailTemplateQueryExtensions with methods to fetch a single EmailTemplateDto by ID or type and language code. Added required using directive for EnvelopeGenerator.Application.Common.Extensions. These extensions streamline querying email templates via ISender.
2026-02-11 12:06:32 +01:00
06884fe809 Refactor Get method in EmailTemplateController
Simplified the Get method to always require a ReadEmailTemplateQuery and CancellationToken, delegating all queries to the mediator. Removed conditional logic for handling null or missing parameters. Updated XML documentation to reflect the new method signature.
2026-02-11 11:58:50 +01:00
c2d5bd65aa Refactor email template query to support multiple results
ReadEmailTemplateQuery now returns a collection of EmailTemplateDto, with optional filtering by Id, Type, and new LangCode property. Query handler updated to return all matching templates instead of a single result, and NotFoundException handling was removed.
2026-02-11 11:57:47 +01:00
cfcd43b0ed Make EmailTemplateDto mutable, add LangCode property
Changed EmailTemplateDto properties from init-only to mutable (get/set), removed 'required' from Name, and added a new LangCode property with a default value. Also updated using directives and added conditional compilation in EmailTemplate.cs for .NET Framework compatibility. No functional changes to EmailTemplate class.
2026-02-11 11:00:18 +01:00
f41199c389 Add LangCode property to EmailTemplate entity
Introduced a required LangCode property to the EmailTemplate entity, mapped to the LANG_CODE column in the database as varchar(5). This supports language-specific email templates.
2026-02-11 10:56:20 +01:00
20 changed files with 334 additions and 232 deletions

View File

@@ -19,20 +19,20 @@ namespace EnvelopeGenerator.API.Controllers;
/// <remarks> /// <remarks>
/// Initialisiert eine neue Instanz der <see cref="EmailTemplateController"/>-Klasse. /// Initialisiert eine neue Instanz der <see cref="EmailTemplateController"/>-Klasse.
/// </remarks> /// </remarks>
/// <param name="mapper"> /// <param name="mediator">
/// <param name="repository"> /// Die Mediator-Instanz, die zum Senden von Befehlen und Abfragen verwendet wird.
/// Die AutoMapper-Instanz, die zum Zuordnen von Objekten verwendet wird.
/// </param> /// </param>
[Route("api/[controller]")] [Route("api/[controller]")]
[ApiController] [ApiController]
[Authorize(Policy = AuthPolicy.Sender)] [Authorize(Policy = AuthPolicy.Sender)]
public class EmailTemplateController(IMapper mapper, IRepository<EmailTemplate> repository, IMediator mediator) : ControllerBase public class EmailTemplateController(IMediator mediator) : ControllerBase
{ {
/// <summary> /// <summary>
/// Ruft E-Mail-Vorlagen basierend auf der angegebenen Abfrage ab. /// Ruft E-Mail-Vorlagen basierend auf der angegebenen Abfrage ab.
/// Gibt alles zurück, wenn keine Id- oder Typ-Informationen eingegeben wurden. /// Gibt alles zurück, wenn keine Id- oder Typ-Informationen eingegeben wurden.
/// </summary> /// </summary>
/// <param name="emailTemplate">Die Abfrageparameter zum Abrufen von E-Mail-Vorlagen.</param> /// <param name="emailTemplate">Die Abfrageparameter zum Abrufen von E-Mail-Vorlagen.</param>
/// <param name="cancel"></param>
/// <returns>Gibt HTTP-Antwort zurück</returns> /// <returns>Gibt HTTP-Antwort zurück</returns>
/// <remarks> /// <remarks>
/// Sample request: /// Sample request:
@@ -43,18 +43,10 @@ public class EmailTemplateController(IMapper mapper, IRepository<EmailTemplate>
/// <response code="401">Wenn der Benutzer nicht authentifiziert ist.</response> /// <response code="401">Wenn der Benutzer nicht authentifiziert ist.</response>
/// <response code="404">Wenn die gesuchte Abfrage nicht gefunden wird.</response> /// <response code="404">Wenn die gesuchte Abfrage nicht gefunden wird.</response>
[HttpGet] [HttpGet]
public async Task<IActionResult> Get([FromQuery] ReadEmailTemplateQuery? emailTemplate = null) public async Task<IActionResult> Get([FromQuery] ReadEmailTemplateQuery emailTemplate, CancellationToken cancel)
{ {
if (emailTemplate is null || (emailTemplate.Id is null && emailTemplate.Type is null)) var result = await mediator.Send(emailTemplate, cancel);
{ return Ok(result);
var temps = await repository.Query.ToListAsync();
return Ok(mapper.Map<IEnumerable<EmailTemplateDto>>(temps));
}
else
{
var temp = await mediator.Send(emailTemplate);
return temp is null ? NotFound() : Ok(temp);
}
} }
/// <summary> /// <summary>

View File

@@ -38,6 +38,7 @@ public class ReadOnlyController : ControllerBase
/// <param name="createDto">Creation payload.</param> /// <param name="createDto">Creation payload.</param>
[HttpPost] [HttpPost]
[Authorize(Policy = AuthPolicy.Receiver)] [Authorize(Policy = AuthPolicy.Receiver)]
[Obsolete("Use MediatR")]
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto) public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
{ {
var authReceiverMail = User.GetReceiverMailOfReceiver(); var authReceiverMail = User.GetReceiverMailOfReceiver();

View File

@@ -13,25 +13,30 @@ public record EmailTemplateDto
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public required string Name { get; init; } public string Name { get; set; } = null!;
/// <summary> /// <summary>
/// Das Datum und die Uhrzeit, wann die Vorlage hinzugefügt wurde. /// Das Datum und die Uhrzeit, wann die Vorlage hinzugefügt wurde.
/// </summary> /// </summary>
public DateTime AddedWhen { get; init; } public DateTime AddedWhen { get; set; }
/// <summary> /// <summary>
/// Der Inhalt (Body) der E-Mail-Vorlage. Kann null sein. /// Der Inhalt (Body) der E-Mail-Vorlage. Kann null sein.
/// </summary> /// </summary>
public string? Body { get; init; } public string? Body { get; set; }
/// <summary> /// <summary>
/// Der Betreff der E-Mail-Vorlage. Kann null sein. /// Der Betreff der E-Mail-Vorlage. Kann null sein.
/// </summary> /// </summary>
public string? Subject { get; init; } public string? Subject { get; set; }
/// <summary>
/// Der Sprachcode der E-Mail-Vorlage.
/// </summary>
public string LangCode { get; set; } = null!;
/// <summary> /// <summary>
/// Das Datum und die Uhrzeit, wann die Vorlage zuletzt geändert wurde. Kann null sein. /// Das Datum und die Uhrzeit, wann die Vorlage zuletzt geändert wurde. Kann null sein.
/// </summary> /// </summary>
public DateTime? ChangedWhen { get; init; } public DateTime? ChangedWhen { get; set; }
}; };

View File

@@ -6,6 +6,7 @@ using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Constants;
using DigitalData.Core.Exceptions; using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Application.Common.Extensions;
namespace EnvelopeGenerator.Application.EmailTemplates.Queries; namespace EnvelopeGenerator.Application.EmailTemplates.Queries;
@@ -13,7 +14,7 @@ namespace EnvelopeGenerator.Application.EmailTemplates.Queries;
/// Stellt eine Abfrage dar, um eine E-Mail-Vorlage zu lesen. /// Stellt eine Abfrage dar, um eine E-Mail-Vorlage zu lesen.
/// Diese Klasse erbt von <see cref="IEmailTemplateQuery"/>. /// Diese Klasse erbt von <see cref="IEmailTemplateQuery"/>.
/// </summary> /// </summary>
public record ReadEmailTemplateQuery : IEmailTemplateQuery, IRequest<EmailTemplateDto> public record ReadEmailTemplateQuery : IEmailTemplateQuery, IRequest<IEnumerable<EmailTemplateDto>>
{ {
/// <summary> /// <summary>
/// Die eindeutige Kennung der E-Mail-Vorlage (optional). /// Die eindeutige Kennung der E-Mail-Vorlage (optional).
@@ -34,12 +35,51 @@ public record ReadEmailTemplateQuery : IEmailTemplateQuery, IRequest<EmailTempla
/// 9 - DocumentRejected_REC_2 (für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird. /// 9 - DocumentRejected_REC_2 (für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird.
/// </summary> /// </summary>
public EmailTemplateType? Type { get; set; } public EmailTemplateType? Type { get; set; }
/// <summary>
///
/// </summary>
public string? LangCode { get; set; }
} }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public class ReadEmailTemplateQueryHandler : IRequestHandler<ReadEmailTemplateQuery, EmailTemplateDto> public static class ReadEmailTemplateQueryExtensions
{
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="id"></param>
/// <param name="langCode"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public static async Task<EmailTemplateDto?> ReadEmailTemplateAsync(this ISender sender, int id, string langCode, CancellationToken cancel = default)
{
var result = await sender.Send(new ReadEmailTemplateQuery { Id = id, LangCode = langCode }, cancel);
return result.FirstOrDefault();
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="type"></param>
/// <param name="langCode"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public static async Task<EmailTemplateDto?> ReadEmailTemplateAsync(this ISender sender, EmailTemplateType type, string langCode, CancellationToken cancel = default)
{
var result = await sender.Send(new ReadEmailTemplateQuery { Type = type, LangCode = langCode }, cancel);
return result.FirstOrDefault();
}
}
/// <summary>
///
/// </summary>
public class ReadEmailTemplateQueryHandler : IRequestHandler<ReadEmailTemplateQuery, IEnumerable<EmailTemplateDto>>
{ {
private readonly IMapper _mapper; private readonly IMapper _mapper;
@@ -65,14 +105,21 @@ public class ReadEmailTemplateQueryHandler : IRequestHandler<ReadEmailTemplateQu
/// <param name="cancel"></param> /// <param name="cancel"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="InvalidOperationException"></exception> /// <exception cref="InvalidOperationException"></exception>
public async Task<EmailTemplateDto> Handle(ReadEmailTemplateQuery request, CancellationToken cancel) public async Task<IEnumerable<EmailTemplateDto>> Handle(ReadEmailTemplateQuery request, CancellationToken cancel)
{ {
var query = request.Id is int id var query = _repo.Query;
? _repo.Query.Where(temp => temp.Id == id)
: _repo.Query.Where(temp => temp.Name == request.Type!.ToString());
var entity = await query.FirstOrDefaultAsync(cancel) ?? throw new NotFoundException(); if (request.Id is int id)
query = query.Where(temp => temp.Id == id);
return _mapper.Map<EmailTemplateDto>(entity); if (request.Type is EmailTemplateType type)
query = query.Where(temp => temp.Name == type.ToString());
if (request.LangCode is string langCode)
query = query.Where(temp => temp.LangCode == langCode);
var entity = await query.ToListAsync(cancel);
return _mapper.Map<IEnumerable<EmailTemplateDto>>(entity);
} }
} }

View File

@@ -4,7 +4,6 @@ using DigitalData.EmailProfilerDispatcher.Abstraction.DTOs.EmailOut;
using DigitalData.EmailProfilerDispatcher.Abstraction.Services; using DigitalData.EmailProfilerDispatcher.Abstraction.Services;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using DigitalData.Core.Abstraction.Application.DTO; using DigitalData.Core.Abstraction.Application.DTO;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.Common.Configurations; using EnvelopeGenerator.Application.Common.Configurations;
@@ -12,6 +11,7 @@ using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly; using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Application.Common.Extensions; using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Interfaces.Services; using EnvelopeGenerator.Application.Common.Interfaces.Services;
using MediatR;
namespace EnvelopeGenerator.Application.Services; namespace EnvelopeGenerator.Application.Services;
@@ -21,170 +21,170 @@ namespace EnvelopeGenerator.Application.Services;
[Obsolete("Use MediatR")] [Obsolete("Use MediatR")]
public class EnvelopeMailService : EmailOutService, IEnvelopeMailService public class EnvelopeMailService : EmailOutService, IEnvelopeMailService
{ {
private readonly IEmailTemplateService _tempService; private readonly IEmailTemplateService _tempService;
private readonly IEnvelopeReceiverService _envRcvService; private readonly IEnvelopeReceiverService _envRcvService;
private readonly DispatcherParams _dConfig; private readonly DispatcherParams _dConfig;
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly Dictionary<string, string> _placeholders; private readonly Dictionary<string, string> _placeholders;
private readonly IAuthenticator _authenticator; private readonly ISender _sender;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="repository"></param> /// <param name="repository"></param>
/// <param name="mapper"></param> /// <param name="mapper"></param>
/// <param name="tempService"></param> /// <param name="tempService"></param>
/// <param name="envelopeReceiverService"></param> /// <param name="envelopeReceiverService"></param>
/// <param name="dispatcherConfigOptions"></param> /// <param name="dispatcherConfigOptions"></param>
/// <param name="configService"></param> /// <param name="configService"></param>
/// <param name="mailConfig"></param> /// <param name="mailConfig"></param>
/// <param name="authenticator"></param> /// <param name="sender"></param>
public EnvelopeMailService(IEmailOutRepository repository, IMapper mapper, IEmailTemplateService tempService, IEnvelopeReceiverService envelopeReceiverService, IOptions<DispatcherParams> dispatcherConfigOptions, IConfigService configService, IOptions<MailParams> mailConfig, IAuthenticator authenticator) : base(repository, mapper) public EnvelopeMailService(IEmailOutRepository repository, IMapper mapper, IEmailTemplateService tempService, IEnvelopeReceiverService envelopeReceiverService, IOptions<DispatcherParams> dispatcherConfigOptions, IConfigService configService, IOptions<MailParams> mailConfig, ISender sender) : base(repository, mapper)
{ {
_tempService = tempService; _tempService = tempService;
_envRcvService = envelopeReceiverService; _envRcvService = envelopeReceiverService;
_dConfig = dispatcherConfigOptions.Value; _dConfig = dispatcherConfigOptions.Value;
_configService = configService; _configService = configService;
_placeholders = new Dictionary<string, string>(mailConfig.Value.Placeholders); _placeholders = new Dictionary<string, string>(mailConfig.Value.Placeholders);
_authenticator = authenticator; _sender = sender;
} }
private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? envelopeReceiverDto = null) private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? envelopeReceiverDto = null)
{
if (accessCode is not null)
_placeholders["[DOCUMENT_ACCESS_CODE]"] = accessCode;
if(envelopeReceiverDto?.Envelope is not null && envelopeReceiverDto.Receiver is not null)
{ {
var erId = (envelopeReceiverDto.Envelope.Uuid, envelopeReceiverDto.Receiver.Signature).ToEnvelopeKey(); if (accessCode is not null)
var sigHost = await _configService.ReadDefaultSignatureHost(); _placeholders["[DOCUMENT_ACCESS_CODE]"] = accessCode;
var linkToDoc = $"{sigHost}/EnvelopeKey/{erId}";
_placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc; if (envelopeReceiverDto?.Envelope is not null && envelopeReceiverDto.Receiver is not null)
_placeholders["[LINK_TO_DOCUMENT_TEXT]"] = linkToDoc[..Math.Min(40, linkToDoc.Length)] + ".."; {
var erId = (envelopeReceiverDto.Envelope.Uuid, envelopeReceiverDto.Receiver.Signature).ToEnvelopeKey();
var sigHost = await _configService.ReadDefaultSignatureHost();
var linkToDoc = $"{sigHost}/EnvelopeKey/{erId}";
_placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
_placeholders["[LINK_TO_DOCUMENT_TEXT]"] = linkToDoc[..Math.Min(40, linkToDoc.Length)] + "..";
}
return _placeholders;
} }
return _placeholders; private async Task<Dictionary<string, string>> CreatePlaceholders(EnvelopeReceiverReadOnlyDto? readOnlyDto = null)
}
private async Task<Dictionary<string, string>> CreatePlaceholders(EnvelopeReceiverReadOnlyDto? readOnlyDto = null)
{
if (readOnlyDto?.Envelope is not null && readOnlyDto.Receiver is not null)
{
_placeholders["[NAME_RECEIVER]"] = await _envRcvService.ReadLastUsedReceiverNameByMailAsync(readOnlyDto.AddedWho).ThenAsync(res => res, (msg, ntc) => string.Empty) ?? string.Empty;
var erReadOnlyId = (readOnlyDto.Id).ToEnvelopeKey();
var sigHost = await _configService.ReadDefaultSignatureHost();
var linkToDoc = $"{sigHost}/EnvelopeKey/{erReadOnlyId}";
_placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
_placeholders["[LINK_TO_DOCUMENT_TEXT]"] = linkToDoc[..Math.Min(40, linkToDoc.Length)] + "..";
}
return _placeholders;
}
/// <summary>
///
/// </summary>
/// <param name="dto"></param>
/// <param name="tempType"></param>
/// <param name="optionalPlaceholders"></param>
/// <returns></returns>
public async Task<DataResult<int>> SendAsync(EnvelopeReceiverDto dto, EmailTemplateType tempType, Dictionary<string, object>? optionalPlaceholders = null)
{
var tempSerResult = await _tempService.ReadByNameAsync(tempType);
if (tempSerResult.IsFailed)
return tempSerResult.ToFail<int>().Notice(LogLevel.Error, DigitalData.Core.Abstraction.Application.DTO.Flag.DataIntegrityIssue, $"The email cannot send because '{tempType}' template cannot found.");
var temp = tempSerResult.Data;
var mail = new EmailOutCreateDto()
{ {
EmailAddress = dto.Receiver!.EmailAddress, if (readOnlyDto?.Envelope is not null && readOnlyDto.Receiver is not null)
EmailSubj = temp.Subject, {
EmailBody = temp.Body, _placeholders["[NAME_RECEIVER]"] = await _envRcvService.ReadLastUsedReceiverNameByMailAsync(readOnlyDto.AddedWho).ThenAsync(res => res, (msg, ntc) => string.Empty) ?? string.Empty;
//email_type = envelope_status, var erReadOnlyId = (readOnlyDto.Id).ToEnvelopeKey();
//message = envelope_message, var sigHost = await _configService.ReadDefaultSignatureHost();
ReferenceId = dto.EnvelopeId, //REFERENCE_ID = ENVELOPE_ID var linkToDoc = $"{sigHost}/EnvelopeKey/{erReadOnlyId}";
ReferenceString = dto!.Envelope!.Uuid, //REFERENCE_STRING = ENVELOPE_UUID _placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
//receiver_name = receiver.name, _placeholders["[LINK_TO_DOCUMENT_TEXT]"] = linkToDoc[..Math.Min(40, linkToDoc.Length)] + "..";
//receiver_access_code = receiver.access_code, }
//sender_adress = envelope.user.email,
//sender_name = envelope.user.full_name,
//envelope_title = envelope.title,
ReminderTypeId = _dConfig.ReminderTypeId,
SendingProfile = _dConfig.SendingProfile,
EntityId = null,
WfId = (int) EnvelopeStatus.MessageAccessCodeSent,
WfReference = null,
AddedWho = _dConfig.AddedWho,
EmailAttmt1 = _dConfig.EmailAttmt1
};
//get acccess code return _placeholders;
var acResult = await _envRcvService.ReadAccessCodeByIdAsync(envelopeId: dto.EnvelopeId, receiverId: dto.ReceiverId); }
if (acResult.IsFailed)
return acResult.ToFail<int>().Notice(LogLevel.Error, "Therefore, access code cannot be sent");
var accessCode = acResult.Data;
var placeholders = await CreatePlaceholders(accessCode: accessCode, envelopeReceiverDto: dto);
// Add optional place holders. /// <summary>
if (optionalPlaceholders is not null) ///
foreach (var oph in optionalPlaceholders) /// </summary>
placeholders[oph.Key] = oph.Value.ToString() ?? "NULL"; /// <param name="dto"></param>
/// <param name="tempType"></param>
/// <param name="optionalPlaceholders"></param>
/// <returns></returns>
public async Task<DataResult<int>> SendAsync(EnvelopeReceiverDto dto, EmailTemplateType tempType, Dictionary<string, object>? optionalPlaceholders = null)
{
var tempSerResult = await _tempService.ReadByNameAsync(tempType);
if (tempSerResult.IsFailed)
return tempSerResult.ToFail<int>().Notice(LogLevel.Error, Flag.DataIntegrityIssue, $"The email cannot send because '{tempType}' template cannot found.");
var temp = tempSerResult.Data;
//TODO: remove the requirement to add the models using reflections var mail = new EmailOutCreateDto()
return await CreateWithTemplateAsync(createDto: mail,placeholders: placeholders, {
dto, dto.Envelope.User!, dto.Envelope); EmailAddress = dto.Receiver!.EmailAddress,
} EmailSubj = temp.Subject,
EmailBody = temp.Body,
//email_type = envelope_status,
//message = envelope_message,
ReferenceId = dto.EnvelopeId, //REFERENCE_ID = ENVELOPE_ID
ReferenceString = dto!.Envelope!.Uuid, //REFERENCE_STRING = ENVELOPE_UUID
//receiver_name = receiver.name,
//receiver_access_code = receiver.access_code,
//sender_adress = envelope.user.email,
//sender_name = envelope.user.full_name,
//envelope_title = envelope.title,
ReminderTypeId = _dConfig.ReminderTypeId,
SendingProfile = _dConfig.SendingProfile,
EntityId = null,
WfId = (int)EnvelopeStatus.MessageAccessCodeSent,
WfReference = null,
AddedWho = _dConfig.AddedWho,
EmailAttmt1 = _dConfig.EmailAttmt1
};
/// <summary> //get acccess code
/// var acResult = await _envRcvService.ReadAccessCodeByIdAsync(envelopeId: dto.EnvelopeId, receiverId: dto.ReceiverId);
/// </summary> if (acResult.IsFailed)
/// <param name="dto"></param> return acResult.ToFail<int>().Notice(LogLevel.Error, "Therefore, access code cannot be sent");
/// <param name="optionalPlaceholders"></param> var accessCode = acResult.Data;
/// <returns></returns>
public async Task<DataResult<int>> SendAsync(EnvelopeReceiverReadOnlyDto dto, Dictionary<string, object>? optionalPlaceholders = null)
{
var tempSerResult = await _tempService.ReadByNameAsync(EmailTemplateType.DocumentShared);
if (tempSerResult.IsFailed)
return tempSerResult.ToFail<int>().Notice(LogLevel.Error, Flag.DataIntegrityIssue, $"The email cannot send because '{EmailTemplateType.DocumentShared}' template cannot found.");
var temp = tempSerResult.Data;
var mail = new EmailOutCreateDto() var placeholders = await CreatePlaceholders(accessCode: accessCode, envelopeReceiverDto: dto);
{
EmailAddress = dto.ReceiverMail,
EmailSubj = temp.Subject,
EmailBody = temp.Body,
//TODO: remove int casting when all
ReferenceId = (int) dto.EnvelopeId, //REFERENCE_ID = ENVELOPE_ID
ReferenceString = dto.Envelope!.Uuid, //REFERENCE_STRING = ENVELOPE_UUID
//receiver_name = receiver.name,
//receiver_access_code = receiver.access_code,
//sender_adress = envelope.user.email,
//sender_name = envelope.user.full_name,
//envelope_title = envelope.title,
ReminderTypeId = _dConfig.ReminderTypeId,
SendingProfile = _dConfig.SendingProfile,
EntityId = null,
WfId = (int)EnvelopeStatus.EnvelopeShared,
WfReference = null,
AddedWho = _dConfig.AddedWho,
EmailAttmt1 = _dConfig.EmailAttmt1
};
var placeholders = await CreatePlaceholders(readOnlyDto: dto); // Add optional place holders.
if (optionalPlaceholders is not null)
foreach (var oph in optionalPlaceholders)
placeholders[oph.Key] = oph.Value.ToString() ?? "NULL";
// Add optional place holders. //TODO: remove the requirement to add the models using reflections
if (optionalPlaceholders is not null) return await CreateWithTemplateAsync(createDto: mail, placeholders: placeholders,
foreach (var oph in optionalPlaceholders) dto, dto.Envelope.User!, dto.Envelope);
placeholders[oph.Key] = oph.Value.ToString() ?? "NULL"; }
return await CreateWithTemplateAsync(createDto: mail, placeholders: placeholders, dto.Envelope); /// <summary>
} ///
/// </summary>
/// <param name="dto"></param>
/// <param name="optionalPlaceholders"></param>
/// <returns></returns>
public async Task<DataResult<int>> SendAsync(EnvelopeReceiverReadOnlyDto dto, Dictionary<string, object>? optionalPlaceholders = null)
{
var tempSerResult = await _tempService.ReadByNameAsync(EmailTemplateType.DocumentShared);
if (tempSerResult.IsFailed)
return tempSerResult.ToFail<int>().Notice(LogLevel.Error, Flag.DataIntegrityIssue, $"The email cannot send because '{EmailTemplateType.DocumentShared}' template cannot found.");
var temp = tempSerResult.Data;
var mail = new EmailOutCreateDto()
{
EmailAddress = dto.ReceiverMail,
EmailSubj = temp.Subject,
EmailBody = temp.Body,
//TODO: remove int casting when all
ReferenceId = (int)dto.EnvelopeId, //REFERENCE_ID = ENVELOPE_ID
ReferenceString = dto.Envelope!.Uuid, //REFERENCE_STRING = ENVELOPE_UUID
//receiver_name = receiver.name,
//receiver_access_code = receiver.access_code,
//sender_adress = envelope.user.email,
//sender_name = envelope.user.full_name,
//envelope_title = envelope.title,
ReminderTypeId = _dConfig.ReminderTypeId,
SendingProfile = _dConfig.SendingProfile,
EntityId = null,
WfId = (int)EnvelopeStatus.EnvelopeShared,
WfReference = null,
AddedWho = _dConfig.AddedWho,
EmailAttmt1 = _dConfig.EmailAttmt1
};
var placeholders = await CreatePlaceholders(readOnlyDto: dto);
// Add optional place holders.
if (optionalPlaceholders is not null)
foreach (var oph in optionalPlaceholders)
placeholders[oph.Key] = oph.Value.ToString() ?? "NULL";
return await CreateWithTemplateAsync(createDto: mail, placeholders: placeholders, dto.Envelope);
}
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="dto"></param> /// <param name="dto"></param>
/// <returns></returns> /// <returns></returns>
public async Task<DataResult<int>> SendAccessCodeAsync(EnvelopeReceiverDto dto) => await SendAsync(dto: dto, tempType: EmailTemplateType.DocumentAccessCodeReceived); public async Task<DataResult<int>> SendAccessCodeAsync(EnvelopeReceiverDto dto) => await SendAsync(dto: dto, tempType: EmailTemplateType.DocumentAccessCodeReceived);
} }

View File

@@ -1,9 +1,11 @@
using System; using DigitalData.Core.Abstractions.Interfaces;
using DigitalData.Core.Abstractions.Interfaces;
using System.ComponentModel; using System.ComponentModel;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using EnvelopeGenerator.Domain.Interfaces.Auditing; using EnvelopeGenerator.Domain.Interfaces.Auditing;
#if NETFRAMEWORK
using System;
#endif
namespace EnvelopeGenerator.Domain.Entities namespace EnvelopeGenerator.Domain.Entities
{ {
@@ -24,6 +26,10 @@ namespace EnvelopeGenerator.Domain.Entities
[Column("SUBJECT", TypeName = "nvarchar(512)")] [Column("SUBJECT", TypeName = "nvarchar(512)")]
public string Subject { get; set; } public string Subject { get; set; }
[Required]
[Column("LANG_CODE", TypeName = "varchar(5)")]
public string LangCode { get; set; }
[Required] [Required]
[Column("ADDED_WHEN", TypeName = "datetime")] [Column("ADDED_WHEN", TypeName = "datetime")]
[DefaultValue("GETDATE()")] [DefaultValue("GETDATE()")]

View File

@@ -40,7 +40,7 @@ public class EnvelopeController : ViewControllerBase
private readonly IMediator _mediator; private readonly IMediator _mediator;
[Obsolete("Use MediatR")] [Obsolete("Use MediatR")]
public EnvelopeController(ILogger<EnvelopeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration, Cultures cultures, IEnvelopeMailService envelopeMailService, IEnvelopeReceiverReadOnlyService readOnlyService, IAuthenticator authenticator, IReceiverService receiverService, IEnvelopeSmsHandler envelopeSmsService, IMediator mediator) : base(logger, cultures, localizer) public EnvelopeController(ILogger<EnvelopeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration, MultiCulture cultures, IEnvelopeMailService envelopeMailService, IEnvelopeReceiverReadOnlyService readOnlyService, IAuthenticator authenticator, IReceiverService receiverService, IEnvelopeSmsHandler envelopeSmsService, IMediator mediator) : base(logger, cultures, localizer)
{ {
_envRcvService = envelopeReceiverService; _envRcvService = envelopeReceiverService;
_historyService = historyService; _historyService = historyService;

View File

@@ -11,7 +11,7 @@ public class HomeController : ViewControllerBase
{ {
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
public HomeController(IConfiguration configuration, ILogger<HomeController> logger, Cultures cultures, IStringLocalizer<Resource> localizer) : base(logger, cultures, localizer) public HomeController(IConfiguration configuration, ILogger<HomeController> logger, MultiCulture cultures, IStringLocalizer<Resource> localizer) : base(logger, cultures, localizer)
{ {
_configuration = configuration; _configuration = configuration;
} }

View File

@@ -14,9 +14,9 @@ namespace EnvelopeGenerator.Web.Controllers
private readonly IStringLocalizer<Resource> _localizer; private readonly IStringLocalizer<Resource> _localizer;
private readonly Cultures _cultures; private readonly MultiCulture _cultures;
public LocalizationController(IStringLocalizer<Resource> localizer, Cultures cultures, ILogger<LocalizationController> logger) public LocalizationController(IStringLocalizer<Resource> localizer, MultiCulture cultures, ILogger<LocalizationController> logger)
{ {
_localizer = localizer; _localizer = localizer;
_cultures = cultures; _cultures = cultures;

View File

@@ -26,7 +26,7 @@ public class TFARegController : ViewControllerBase
private readonly TFARegParams _params; private readonly TFARegParams _params;
[Obsolete("Use MediatR")] [Obsolete("Use MediatR")]
public TFARegController(ILogger<TFARegController> logger, Cultures cultures, IStringLocalizer<Resource> localizer, IEnvelopeReceiverService erService, IAuthenticator authenticator, IReceiverService receiverService, IOptions<TFARegParams> tfaRegParamsOptions) : base(logger, cultures, localizer) public TFARegController(ILogger<TFARegController> logger, MultiCulture cultures, IStringLocalizer<Resource> localizer, IEnvelopeReceiverService erService, IAuthenticator authenticator, IReceiverService receiverService, IOptions<TFARegParams> tfaRegParamsOptions) : base(logger, cultures, localizer)
{ {
_envRcvService = erService; _envRcvService = erService;
_authenticator = authenticator; _authenticator = authenticator;

View File

@@ -1,34 +1,22 @@
using DigitalData.Core.Abstraction.Application.DTO; using Microsoft.AspNetCore.Mvc;
using EnvelopeGenerator.Domain.Entities; using MediatR;
using Microsoft.AspNetCore.Mvc; using EnvelopeGenerator.Application.EmailTemplates.Queries;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Application.Common.Interfaces.Services;
namespace EnvelopeGenerator.Web.Controllers.Test; namespace EnvelopeGenerator.Web.Controllers.Test;
[Obsolete("Use MediatR")] public class TestEmailTemplateController : ControllerBase
public class TestEmailTemplateController : TestControllerBase<IEmailTemplateService, EmailTemplateDto, EmailTemplate, int>
{ {
public TestEmailTemplateController(ILogger<TestEmailTemplateController> logger, IEmailTemplateService service) : base(logger, service) private readonly IMediator _mediator;
public TestEmailTemplateController(IMediator mediator)
{ {
_mediator = mediator;
} }
[HttpGet] [HttpGet]
[Obsolete("Use MediatR")] public virtual async Task<IActionResult> GetAll([FromQuery] ReadEmailTemplateQuery query, CancellationToken cancel)
public virtual async Task<IActionResult> GetAll([FromQuery] string? tempType = null)
{ {
return tempType is null var res = await _mediator.Send(query, cancel);
? await base.GetAll() return Ok(res);
: await _service.ReadByNameAsync((EmailTemplateType)Enum.Parse(typeof(EmailTemplateType), tempType)).ThenAsync(
Success: Ok,
Fail: IActionResult (messages, notices) =>
{
_logger.LogNotice(notices);
return NotFound(messages);
});
} }
[NonAction]
public override Task<IActionResult> GetAll() => base.GetAll();
} }

View File

@@ -1,9 +1,12 @@
using AngleSharp.Common; using AngleSharp.Common;
using DigitalData.Core.API; using DigitalData.Core.API;
using EnvelopeGenerator.Application.Resources; using EnvelopeGenerator.Application.Resources;
using EnvelopeGenerator.Web.Extensions;
using EnvelopeGenerator.Web.Models; using EnvelopeGenerator.Web.Models;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using System.Globalization;
namespace EnvelopeGenerator.Web.Controllers.Test namespace EnvelopeGenerator.Web.Controllers.Test
{ {
@@ -12,21 +15,30 @@ namespace EnvelopeGenerator.Web.Controllers.Test
public class TestLocalizerController : ControllerBase public class TestLocalizerController : ControllerBase
{ {
private readonly IStringLocalizer _localizer; private readonly IStringLocalizer _localizer;
private readonly Cultures _cultures; private readonly MultiCulture _cultures;
public TestLocalizerController(IStringLocalizer<Resource> localizer, Cultures cultures) public TestLocalizerController(IStringLocalizer<Resource> localizer, MultiCulture cultures)
{ {
_localizer = localizer; _localizer = localizer;
_cultures = cultures; _cultures = cultures;
} }
[HttpGet] [HttpGet("{key}")]
public IActionResult Localize([FromQuery] string key = "de_DE") => Ok(_localizer[key]); public IActionResult Localize([FromRoute] string key) => Ok(_localizer[key]);
[HttpGet("fi-class")] [HttpGet("fi-class")]
public IActionResult GetFIClass(string? lang = null) => lang is null ? Ok(_cultures.FIClasses) : Ok(_cultures[lang]?.FIClass); public IActionResult GetFIClass(string? lang = null) => lang is null ? Ok(_cultures.FIClasses) : Ok(_cultures[lang]?.FIClass);
[HttpGet("culture")] [HttpGet("culture")]
public IActionResult GetCultures(string? lang = null) => lang is null ? Ok(_cultures) : Ok(_cultures[lang]); public IActionResult GetCultures(string? lang = null) => lang is null ? Ok(_cultures) : Ok(_cultures[lang]);
[HttpGet("culture/accept-language")]
public IActionResult GetCultureByAcceptLanguage()
=> HttpContext.GetCultureByAcceptLanguage()?.Name is string culture
? Ok(culture)
: NotFound();
[HttpGet("culture/user")]
public IActionResult GetUserCulture() => Request.Cookies.GetCulture() is string cult ? Ok(cult) : NotFound();
} }
} }

View File

@@ -8,10 +8,10 @@ namespace EnvelopeGenerator.Web.Controllers;
public class ViewControllerBase : Controller public class ViewControllerBase : Controller
{ {
protected readonly ILogger _logger; protected readonly ILogger _logger;
protected readonly Cultures _cultures; protected readonly MultiCulture _cultures;
protected readonly IStringLocalizer<Resource> _localizer; protected readonly IStringLocalizer<Resource> _localizer;
public ViewControllerBase(ILogger logger, Cultures cultures, IStringLocalizer<Resource> localizer) public ViewControllerBase(ILogger logger, MultiCulture cultures, IStringLocalizer<Resource> localizer)
{ {
_logger = logger; _logger = logger;
_cultures = cultures; _cultures = cultures;

View File

@@ -3,8 +3,10 @@ using EnvelopeGenerator.Application.Common.Dto.Receiver;
using EnvelopeGenerator.Web.Models; using EnvelopeGenerator.Web.Models;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Globalization;
using System.Security.Claims; using System.Security.Claims;
namespace EnvelopeGenerator.Web.Extensions; namespace EnvelopeGenerator.Web.Extensions;
@@ -58,8 +60,19 @@ public static class WebExtensions
#endregion #endregion
#region Cookie #region Cookie
public static string? GetCulture(this IRequestCookieCollection cookies) public static string GetCulture(this IRequestCookieCollection cookies)
=> cookies[CookieRequestCultureProvider.DefaultCookieName]; {
var cookieValue = cookies[CookieRequestCultureProvider.DefaultCookieName];
if (!string.IsNullOrEmpty(cookieValue))
{
var culture = CookieRequestCultureProvider.ParseCookieValue(cookieValue)?.Cultures.FirstOrDefault().Value;
if (!string.IsNullOrEmpty(culture))
return culture;
}
return CultureInfo.CurrentUICulture.Name;
}
public static void SetCulture(this IResponseCookies cookies, string culture) public static void SetCulture(this IResponseCookies cookies, string culture)
{ {
@@ -116,4 +129,31 @@ public static class WebExtensions
Body = "Bitte kontaktieren Sie das IT-Team." Body = "Bitte kontaktieren Sie das IT-Team."
}); });
#endregion #endregion
#region HttpContext
public static CultureInfo? GetCultureByAcceptLanguage(this HttpContext context)
{
var acceptLanguage = context.Request.Headers.AcceptLanguage.ToString();
if (string.IsNullOrWhiteSpace(acceptLanguage))
return null;
foreach (var value in acceptLanguage.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
{
var cultureName = value.Split(';', 2)[0];
if (string.IsNullOrWhiteSpace(cultureName))
continue;
try
{
return new CultureInfo(cultureName);
}
catch (CultureNotFoundException)
{
// ignore invalid cultures and continue
}
}
return null;
}
#endregion
} }

View File

@@ -9,9 +9,9 @@ namespace EnvelopeGenerator.Web.Middleware;
public class CultureMiddleware public class CultureMiddleware
{ {
private readonly RequestDelegate _next; private readonly RequestDelegate _next;
private readonly Cultures _cultures; private readonly MultiCulture _cultures;
public CultureMiddleware(RequestDelegate next, IOptions<Cultures> culturesOpt) public CultureMiddleware(RequestDelegate next, IOptions<MultiCulture> culturesOpt)
{ {
_next = next; _next = next;
_cultures = culturesOpt.Value; _cultures = culturesOpt.Value;
@@ -22,11 +22,15 @@ public class CultureMiddleware
var cookieName = CookieRequestCultureProvider.DefaultCookieName; var cookieName = CookieRequestCultureProvider.DefaultCookieName;
var cookieValue = context.Request.Cookies[cookieName]; var cookieValue = context.Request.Cookies[cookieName];
if (string.IsNullOrEmpty(cookieValue)) if (!_cultures.Languages.Contains(cookieValue))
{ {
context.Response.Cookies.SetCulture(_cultures.Default.Language); var requestCulture = context.GetCultureByAcceptLanguage()?.Name;
CultureInfo.CurrentCulture = new CultureInfo(_cultures.Default.Language); var culture = _cultures.GetOrDefault(requestCulture);
CultureInfo.CurrentUICulture = new CultureInfo(_cultures.Default.Language); var cultureInfo = culture.Info ?? new CultureInfo(culture.Language);
context.Response.Cookies.SetCulture(culture.Language);
CultureInfo.CurrentCulture = cultureInfo;
CultureInfo.CurrentUICulture = cultureInfo;
} }
await _next(context); await _next(context);

View File

@@ -4,15 +4,20 @@ namespace EnvelopeGenerator.Web.Models
{ {
public class Culture public class Culture
{ {
private string _language = string.Empty; private string _language = null!;
public string Language { get => _language;
init { public required string Language
{
get => _language;
init
{
_language = value; _language = value;
Info = new(value); Info = new(value);
} }
} }
public string FIClass { get; init; } = string.Empty;
public CultureInfo? Info { get; init; } public string FIClass { get; init; } = null!;
public CultureInfo Info { get; init; } = null!;
} }
} }

View File

@@ -1,13 +1,15 @@
namespace EnvelopeGenerator.Web.Models namespace EnvelopeGenerator.Web.Models
{ {
public class Cultures : List<Culture> public class MultiCulture : List<Culture>
{ {
public IEnumerable<string> Languages => this.Select(c => c.Language); public IEnumerable<string> Languages => this.Select(c => c.Language);
public IEnumerable<string> FIClasses => this.Select(c => c.FIClass); public IEnumerable<string> FIClasses => this.Select(c => c.FIClass);
public Culture Default => this.First(); public Culture Default => this.First();
public Culture? this[string? language] => language is null ? null : this.Where(c => c.Language == language).FirstOrDefault(); public Culture GetOrDefault(string? language) => this[language] ?? Default;
public Culture? this[string? language] => language is null ? null : this.FirstOrDefault(c => string.Equals(c.Language, language, StringComparison.OrdinalIgnoreCase));
} }
} }

View File

@@ -165,8 +165,8 @@ try
}); });
// Register the FlagIconCssClass instance as a singleton // Register the FlagIconCssClass instance as a singleton
builder.Services.Configure<Cultures>(config.GetSection("Cultures")); builder.Services.Configure<MultiCulture>(config.GetSection("Cultures"));
builder.Services.AddSingleton(sp => sp.GetRequiredService<IOptions<Cultures>>().Value); builder.Services.AddSingleton(sp => sp.GetRequiredService<IOptions<MultiCulture>>().Value);
// Register mail services // Register mail services
#pragma warning disable CS0618 // Type or member is obsolete #pragma warning disable CS0618 // Type or member is obsolete
@@ -230,7 +230,7 @@ try
app.UseAuthentication(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
var cultures = app.Services.GetRequiredService<Cultures>(); var cultures = app.Services.GetRequiredService<MultiCulture>();
if (!cultures.Any()) if (!cultures.Any())
throw new InvalidOperationException(@"Languages section is missing in the appsettings. Please configure like following. throw new InvalidOperationException(@"Languages section is missing in the appsettings. Please configure like following.
Language is both a name of the culture and the name of the resx file such as Resource.de-DE.resx Language is both a name of the culture and the name of the resx file such as Resource.de-DE.resx

View File

@@ -86,7 +86,7 @@
<p> <p>
<small class="text-body-secondary"> <small class="text-body-secondary">
@Html.Raw(_localizer.EnvelopeInfo2().Format( @Html.Raw(_localizer.EnvelopeInfo2().Format(
envelope?.AddedWhen.ToString(userCulture?.Info?.DateTimeFormat), envelope?.AddedWhen.ToString(userCulture?.Info.DateTimeFormat),
$"{sender?.Prename} {sender?.Name}", $"{sender?.Prename} {sender?.Name}",
sender?.Email, sender?.Email,
envelope?.Title, envelope?.Title,
@@ -216,6 +216,6 @@
var documentBase64String = Convert.ToBase64String(documentBytes); var documentBase64String = Convert.ToBase64String(documentBytes);
var envelopeKey = ViewData["EnvelopeKey"] as string; var envelopeKey = ViewData["EnvelopeKey"] as string;
@:document.addEventListener("DOMContentLoaded", async () => await new App("@envelopeKey", @Html.Raw(envelopeReceiverJson), B64ToBuff("@Html.Raw(documentBase64String)"), "@ViewData["PSPDFKitLicenseKey"]", "@userCulture?.Info?.TwoLetterISOLanguageName").init()) @:document.addEventListener("DOMContentLoaded", async () => await new App("@envelopeKey", @Html.Raw(envelopeReceiverJson), B64ToBuff("@Html.Raw(documentBase64String)"), "@ViewData["PSPDFKitLicenseKey"]", "@userCulture?.Info.TwoLetterISOLanguageName").init())
} }
</script> </script>

View File

@@ -7,6 +7,6 @@
@using Microsoft.Extensions.Options @using Microsoft.Extensions.Options
@inject IStringLocalizer<Resource> _localizer @inject IStringLocalizer<Resource> _localizer
@inject Microsoft.AspNetCore.Http.IHttpContextAccessor _accessor @inject Microsoft.AspNetCore.Http.IHttpContextAccessor _accessor
@inject Cultures _cultures @inject MultiCulture _cultures
@inject IOptions<CustomImages> _cImgOpt @inject IOptions<CustomImages> _cImgOpt
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers