From 68714c2937b23d140ca0ae0968017f5087fe1e4e Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Wed, 15 May 2024 16:11:26 +0200 Subject: [PATCH] Integrierte Mehrsprachigkeit (Deutsch und Englisch) mit Cookie-basierter Sprachauswahl --- .../EnvelopeGenerator.Application.csproj | 7 +- .../{Resources => }/Resource.cs | 2 +- .../{Resource.resx => Resource.de-DE.resx} | 22 +++- ...esource.de_DE.resx => Resource.en-US.resx} | 25 +++- .../Resources/Resource.en_US.resx | 123 ------------------ .../Services/ConfigService.cs | 1 - .../DocumentReceiverElementService.cs | 1 - .../Services/DocumentStatusService.cs | 1 - .../Services/EmailOutService.cs | 1 - .../Services/EmailTemplateService.cs | 1 - .../Services/EnvelopeCertificateService.cs | 1 - .../Services/EnvelopeDocumentService.cs | 1 - .../Services/EnvelopeHistoryService.cs | 1 - .../Services/EnvelopeReceiverService.cs | 1 - .../Services/EnvelopeService.cs | 1 - .../Services/EnvelopeTypeService.cs | 1 - .../Services/ReceiverService.cs | 1 - .../Services/UserReceiverService.cs | 1 - .../EnvelopeGenerator.Test.vbproj | 4 +- .../Controllers/HomeController.cs | 74 +++++++---- .../Test/TestLocalizerController.cs | 20 ++- EnvelopeGenerator.Web/MessageKey.cs | 12 -- EnvelopeGenerator.Web/Models/Culture.cs | 8 ++ EnvelopeGenerator.Web/Models/Cultures.cs | 15 +++ EnvelopeGenerator.Web/Program.cs | 45 +++++-- .../Views/Home/EnvelopeLocked.cshtml | 83 +++++------- .../Views/Shared/_Layout.cshtml | 1 + .../Views/_ViewImports.cshtml | 7 +- EnvelopeGenerator.Web/WebKey.cs | 20 +++ EnvelopeGenerator.Web/appsettings.json | 14 +- EnvelopeGenerator.Web/wwwroot/css/site.css | 4 + EnvelopeGenerator.Web/wwwroot/js/network.js | 36 +++++ 32 files changed, 282 insertions(+), 253 deletions(-) rename EnvelopeGenerator.Application/{Resources => }/Resource.cs (71%) rename EnvelopeGenerator.Application/Resources/{Resource.resx => Resource.de-DE.resx} (85%) rename EnvelopeGenerator.Application/Resources/{Resource.de_DE.resx => Resource.en-US.resx} (85%) delete mode 100644 EnvelopeGenerator.Application/Resources/Resource.en_US.resx delete mode 100644 EnvelopeGenerator.Web/MessageKey.cs create mode 100644 EnvelopeGenerator.Web/Models/Culture.cs create mode 100644 EnvelopeGenerator.Web/Models/Cultures.cs create mode 100644 EnvelopeGenerator.Web/WebKey.cs diff --git a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj index bc2066ed..06b59406 100644 --- a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj +++ b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj @@ -40,13 +40,10 @@ - + PreserveNewest - - PreserveNewest - - + PreserveNewest diff --git a/EnvelopeGenerator.Application/Resources/Resource.cs b/EnvelopeGenerator.Application/Resource.cs similarity index 71% rename from EnvelopeGenerator.Application/Resources/Resource.cs rename to EnvelopeGenerator.Application/Resource.cs index 8b7e50aa..64b0bb3b 100644 --- a/EnvelopeGenerator.Application/Resources/Resource.cs +++ b/EnvelopeGenerator.Application/Resource.cs @@ -1,4 +1,4 @@ -namespace EnvelopeGenerator.Application.Resources +namespace EnvelopeGenerator.Application { /// /// The place holder class for Resource.*.resx diff --git a/EnvelopeGenerator.Application/Resources/Resource.resx b/EnvelopeGenerator.Application/Resources/Resource.de-DE.resx similarity index 85% rename from EnvelopeGenerator.Application/Resources/Resource.resx rename to EnvelopeGenerator.Application/Resources/Resource.de-DE.resx index 25125a78..6b26b6cd 100644 --- a/EnvelopeGenerator.Application/Resources/Resource.resx +++ b/EnvelopeGenerator.Application/Resources/Resource.de-DE.resx @@ -117,10 +117,28 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + Deutch - + Englisch + + Öffnen + + + Zugriffscode + + + Wir haben Ihnen gerade den Zugriffscode an die hinterlegte Email Adresse gesendet. Dies kann evtl. einige Minuten dauern. + + + Bitte überprüfen Sie Ihr Email Postfach inklusive Spam-Ordner. Sie können auch den Absender bitten, Ihnen den Code auf anderem Wege zukommen zu lassen. + + + Sie haben keinen Zugriffscode erhalten? + + + Dokument erfordert einen Zugriffscode + \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Resources/Resource.de_DE.resx b/EnvelopeGenerator.Application/Resources/Resource.en-US.resx similarity index 85% rename from EnvelopeGenerator.Application/Resources/Resource.de_DE.resx rename to EnvelopeGenerator.Application/Resources/Resource.en-US.resx index cad038d8..226ae4d3 100644 --- a/EnvelopeGenerator.Application/Resources/Resource.de_DE.resx +++ b/EnvelopeGenerator.Application/Resources/Resource.en-US.resx @@ -117,7 +117,28 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Hallo!! + + German + + + English + + + Open + + + Access Code + + + We have just sent you the access code to the email address you provided. This may take a few minutes. + + + Please check your email inbox including your spam folder. Furthermore, you can also ask the sender to send the code by other means. + + + You have not received an access code? + + + Document requires an access code \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Resources/Resource.en_US.resx b/EnvelopeGenerator.Application/Resources/Resource.en_US.resx deleted file mode 100644 index 2ed34433..00000000 --- a/EnvelopeGenerator.Application/Resources/Resource.en_US.resx +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Hello!! - - \ No newline at end of file diff --git a/EnvelopeGenerator.Application/Services/ConfigService.cs b/EnvelopeGenerator.Application/Services/ConfigService.cs index 06cd4e8b..4cb589fd 100644 --- a/EnvelopeGenerator.Application/Services/ConfigService.cs +++ b/EnvelopeGenerator.Application/Services/ConfigService.cs @@ -3,7 +3,6 @@ using DigitalData.Core.Application; using DigitalData.Core.DTO; using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.DTOs; -using EnvelopeGenerator.Application.Resources; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; using Microsoft.Extensions.Localization; diff --git a/EnvelopeGenerator.Application/Services/DocumentReceiverElementService.cs b/EnvelopeGenerator.Application/Services/DocumentReceiverElementService.cs index 6f25514a..6b1fc9bb 100644 --- a/EnvelopeGenerator.Application/Services/DocumentReceiverElementService.cs +++ b/EnvelopeGenerator.Application/Services/DocumentReceiverElementService.cs @@ -2,7 +2,6 @@ using DigitalData.Core.Application; using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.DTOs; -using EnvelopeGenerator.Application.Resources; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; using Microsoft.Extensions.Localization; diff --git a/EnvelopeGenerator.Application/Services/DocumentStatusService.cs b/EnvelopeGenerator.Application/Services/DocumentStatusService.cs index f52d789a..c175700e 100644 --- a/EnvelopeGenerator.Application/Services/DocumentStatusService.cs +++ b/EnvelopeGenerator.Application/Services/DocumentStatusService.cs @@ -5,7 +5,6 @@ using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; using Microsoft.Extensions.Localization; -using EnvelopeGenerator.Application.Resources; namespace EnvelopeGenerator.Application.Services { diff --git a/EnvelopeGenerator.Application/Services/EmailOutService.cs b/EnvelopeGenerator.Application/Services/EmailOutService.cs index 895f2f95..45c29062 100644 --- a/EnvelopeGenerator.Application/Services/EmailOutService.cs +++ b/EnvelopeGenerator.Application/Services/EmailOutService.cs @@ -2,7 +2,6 @@ using DigitalData.Core.Application; using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.DTOs; -using EnvelopeGenerator.Application.Resources; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; using Microsoft.Extensions.Localization; diff --git a/EnvelopeGenerator.Application/Services/EmailTemplateService.cs b/EnvelopeGenerator.Application/Services/EmailTemplateService.cs index 34f133f4..e03d0735 100644 --- a/EnvelopeGenerator.Application/Services/EmailTemplateService.cs +++ b/EnvelopeGenerator.Application/Services/EmailTemplateService.cs @@ -5,7 +5,6 @@ using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; -using EnvelopeGenerator.Application.Resources; namespace EnvelopeGenerator.Application.Services { diff --git a/EnvelopeGenerator.Application/Services/EnvelopeCertificateService.cs b/EnvelopeGenerator.Application/Services/EnvelopeCertificateService.cs index 807ba729..1aeb7bd9 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeCertificateService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeCertificateService.cs @@ -5,7 +5,6 @@ using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; -using EnvelopeGenerator.Application.Resources; namespace EnvelopeGenerator.Application.Services { diff --git a/EnvelopeGenerator.Application/Services/EnvelopeDocumentService.cs b/EnvelopeGenerator.Application/Services/EnvelopeDocumentService.cs index 36e880cc..d9780511 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeDocumentService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeDocumentService.cs @@ -6,7 +6,6 @@ using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; -using EnvelopeGenerator.Application.Resources; namespace EnvelopeGenerator.Application.Services { diff --git a/EnvelopeGenerator.Application/Services/EnvelopeHistoryService.cs b/EnvelopeGenerator.Application/Services/EnvelopeHistoryService.cs index fb8d3c77..560e2eb0 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeHistoryService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeHistoryService.cs @@ -6,7 +6,6 @@ using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; using static EnvelopeGenerator.Common.Constants; -using EnvelopeGenerator.Application.Resources; namespace EnvelopeGenerator.Application.Services { diff --git a/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs b/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs index 95d26ed7..3ab2b3f9 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeReceiverService.cs @@ -7,7 +7,6 @@ using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; -using EnvelopeGenerator.Application.Resources; namespace EnvelopeGenerator.Application.Services { diff --git a/EnvelopeGenerator.Application/Services/EnvelopeService.cs b/EnvelopeGenerator.Application/Services/EnvelopeService.cs index 07d00734..8f746da3 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeService.cs @@ -7,7 +7,6 @@ using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; -using EnvelopeGenerator.Application.Resources; namespace EnvelopeGenerator.Application.Services { diff --git a/EnvelopeGenerator.Application/Services/EnvelopeTypeService.cs b/EnvelopeGenerator.Application/Services/EnvelopeTypeService.cs index c43a0cb8..dd944892 100644 --- a/EnvelopeGenerator.Application/Services/EnvelopeTypeService.cs +++ b/EnvelopeGenerator.Application/Services/EnvelopeTypeService.cs @@ -5,7 +5,6 @@ using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; -using EnvelopeGenerator.Application.Resources; namespace EnvelopeGenerator.Application.Services { diff --git a/EnvelopeGenerator.Application/Services/ReceiverService.cs b/EnvelopeGenerator.Application/Services/ReceiverService.cs index 9fc76b95..e0f375a7 100644 --- a/EnvelopeGenerator.Application/Services/ReceiverService.cs +++ b/EnvelopeGenerator.Application/Services/ReceiverService.cs @@ -5,7 +5,6 @@ using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; -using EnvelopeGenerator.Application.Resources; namespace EnvelopeGenerator.Application.Services { diff --git a/EnvelopeGenerator.Application/Services/UserReceiverService.cs b/EnvelopeGenerator.Application/Services/UserReceiverService.cs index 9fe3ee72..30d7d38d 100644 --- a/EnvelopeGenerator.Application/Services/UserReceiverService.cs +++ b/EnvelopeGenerator.Application/Services/UserReceiverService.cs @@ -5,7 +5,6 @@ using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.DTOs; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.Infrastructure.Contracts; -using EnvelopeGenerator.Application.Resources; namespace EnvelopeGenerator.Application.Services { diff --git a/EnvelopeGenerator.Test/EnvelopeGenerator.Test.vbproj b/EnvelopeGenerator.Test/EnvelopeGenerator.Test.vbproj index fef61ccd..eb2aec88 100644 --- a/EnvelopeGenerator.Test/EnvelopeGenerator.Test.vbproj +++ b/EnvelopeGenerator.Test/EnvelopeGenerator.Test.vbproj @@ -120,7 +120,9 @@ frmFinalizePDF.vb - + + Form + frmReportViewer.vb diff --git a/EnvelopeGenerator.Web/Controllers/HomeController.cs b/EnvelopeGenerator.Web/Controllers/HomeController.cs index 41bcf022..51cb6ec3 100644 --- a/EnvelopeGenerator.Web/Controllers/HomeController.cs +++ b/EnvelopeGenerator.Web/Controllers/HomeController.cs @@ -11,10 +11,10 @@ using DigitalData.Core.API; using EnvelopeGenerator.Application; using Microsoft.Extensions.Localization; using DigitalData.Core.DTO; -using EnvelopeGenerator.Application.Resources; using EnvelopeGenerator.Application.DTOs; using Microsoft.AspNetCore.Localization; using System.Text.Encodings.Web; +using EnvelopeGenerator.Web.Models; namespace EnvelopeGenerator.Web.Controllers { @@ -26,8 +26,9 @@ namespace EnvelopeGenerator.Web.Controllers private readonly IStringLocalizer _localizer; private readonly IConfiguration _configuration; private readonly UrlEncoder _urlEncoder; + private readonly Cultures _cultures; - public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer localizer, IConfiguration configuration, UrlEncoder urlEncoder) : base(databaseService, logger) + public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer localizer, IConfiguration configuration, UrlEncoder urlEncoder, Cultures cultures) : base(databaseService, logger) { this.envelopeOldService = envelopeOldService; _envRcvService = envelopeReceiverService; @@ -35,6 +36,7 @@ namespace EnvelopeGenerator.Web.Controllers _localizer = localizer; _configuration = configuration; _urlEncoder = urlEncoder; + _cultures = cultures; } [HttpGet("EnvelopeKey/{envelopeReceiverId}")] @@ -68,7 +70,7 @@ namespace EnvelopeGenerator.Web.Controllers } catch(Exception ex) { - _logger.LogEnvelopeError(envelopeEeceiverId: envelopeReceiverId, exception:ex, message: _localizer[MessageKey.UnexpectedError]); + _logger.LogEnvelopeError(envelopeEeceiverId: envelopeReceiverId, exception:ex, message: _localizer[WebKey.UnexpectedError]); return this.ViewInnerServiceError(); } } @@ -79,7 +81,6 @@ namespace EnvelopeGenerator.Web.Controllers try { envelopeReceiverId = _urlEncoder.Encode(envelopeReceiverId); - ViewData["Languages"] = _configuration.GetSection("Languages").Get()!; ViewData["UserLanguage"] = UserLanguage; return await _envRcvService.IsExisting(envelopeReceiverId: envelopeReceiverId).ThenAsync( @@ -107,7 +108,7 @@ namespace EnvelopeGenerator.Web.Controllers if(uuid is null || signature is null) { - _logger.LogEnvelopeError(uuid: uuid, signature: signature, message: _localizer[MessageKey.WrongEnvelopeReceiverId]); + _logger.LogEnvelopeError(uuid: uuid, signature: signature, message: _localizer[WebKey.WrongEnvelopeReceiverId]); return Unauthorized(); } @@ -229,50 +230,75 @@ namespace EnvelopeGenerator.Web.Controllers [NonAction] public IActionResult GetLanguage() => Ok(UserLanguage); - [HttpPost("lang")] - public IActionResult SetLanguage([FromForm] string language) + [HttpPost("lang/{language}")] + public IActionResult SetLanguage([FromRoute] string language) { try { - language = _urlEncoder.Encode(language); - var cookieOptions = new CookieOptions() + if (Languages is null) { - Expires = DateTimeOffset.UtcNow.AddYears(1), - Secure = false, - Path = "/", - SameSite = SameSiteMode.Strict, - HttpOnly = true - }; + _logger.LogWarning("There is no language assigned under languages key in appesettings.json"); + return StatusCode(statusCode: StatusCodes.Status500InternalServerError); + } + else if (!language.Contains(language)) + return BadRequest(); - Response.Cookies.Append( - CookieRequestCultureProvider.DefaultCookieName, - CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(language)), - cookieOptions); + language = _urlEncoder.Encode(language); + + UserLanguage = language; return Redirect(Request.Headers["Referer"].ToString()); } catch(Exception ex) { - _logger.LogError(ex, ex.Message); - return this.ViewEnvelopeNotFound(); + _logger.LogError(ex, "{Message}", ex.Message); + return StatusCode(statusCode: StatusCodes.Status500InternalServerError); + } + } + + [HttpGet("lang")] + public IActionResult GetLanguages() + { + if(Languages is null) + { + _logger.LogWarning("There is no language assigned under languages key in appesettings.json"); + return StatusCode(statusCode: StatusCodes.Status500InternalServerError); } + else + return Ok(Languages); } private string UserLanguage { get { - return Request.Cookies[CookieRequestCultureProvider.DefaultCookieName] ?? _configuration.GetSection("Languages").Get()![0]; + var cookieValue = Request.Cookies[CookieRequestCultureProvider.DefaultCookieName]; + + if (string.IsNullOrEmpty(cookieValue)) + return _cultures.Default.Language; + + var culture = CookieRequestCultureProvider.ParseCookieValue(cookieValue)?.Cultures[0]; + return culture?.Value ?? _cultures.Default.Language; } set { - Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName, CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(value)), new CookieOptions() + var cookieOptions = new CookieOptions() { Expires = DateTimeOffset.UtcNow.AddYears(1), - }); + Secure = false, + SameSite = SameSiteMode.Strict, + HttpOnly = true + }; + + Response.Cookies.Append( + CookieRequestCultureProvider.DefaultCookieName, + CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(value)), + cookieOptions); } } + private string[]? Languages => _configuration.GetSection("Languages").Get(); + public IActionResult Error404() => this.ViewError404(); } } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Controllers/Test/TestLocalizerController.cs b/EnvelopeGenerator.Web/Controllers/Test/TestLocalizerController.cs index 65370554..fec38a90 100644 --- a/EnvelopeGenerator.Web/Controllers/Test/TestLocalizerController.cs +++ b/EnvelopeGenerator.Web/Controllers/Test/TestLocalizerController.cs @@ -1,5 +1,6 @@ -using EnvelopeGenerator.Application; -using EnvelopeGenerator.Application.Resources; +using AngleSharp.Common; +using EnvelopeGenerator.Application; +using EnvelopeGenerator.Web.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Localization; @@ -10,10 +11,21 @@ namespace EnvelopeGenerator.Web.Controllers.Test public class TestLocalizerController : ControllerBase { private readonly IStringLocalizer _localizer; + private readonly Cultures _cultures; - public TestLocalizerController(IStringLocalizer localizer) => _localizer = localizer; + public TestLocalizerController(IStringLocalizer localizer, Cultures cultures) + { + _localizer = localizer; + _cultures = cultures; + } [HttpGet] - public IActionResult Localize([FromQuery] string key = "Hello") => Ok(_localizer[key]); + public IActionResult Localize([FromQuery] string key = "de_DE") => Ok(_localizer[key]); + + [HttpGet("fi-class")] + public IActionResult GetFIClass(string? lang = null) => lang is null ? Ok(_cultures.FIClasses) : Ok(_cultures.FIClassOf(lang)); + + [HttpGet("culture")] + public IActionResult GetCultures(string? lang = null) => lang is null ? Ok(_cultures) : Ok(_cultures.CultureOf(lang)); } } diff --git a/EnvelopeGenerator.Web/MessageKey.cs b/EnvelopeGenerator.Web/MessageKey.cs deleted file mode 100644 index b4f49475..00000000 --- a/EnvelopeGenerator.Web/MessageKey.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace EnvelopeGenerator.Web -{ - public static class MessageKey - { - public static readonly string ServiceOutputNullError = "ServiceOutputNullError"; - public static readonly string UnexpectedError = "UnexpectedError"; - public static readonly string FailedToSendAccessCode = "FailedToSendAccessCode"; - public static readonly string WrongEnvelopeReceiverId = "WrongEnvelopeReceiverId"; - public static readonly string DataIntegrityError = "DataIntegrityError"; - public static readonly string NonDecodableEnvelopeReceiverId = "NonDecodableEnvelopeReceiverId"; - } -} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Models/Culture.cs b/EnvelopeGenerator.Web/Models/Culture.cs new file mode 100644 index 00000000..8ffbdee4 --- /dev/null +++ b/EnvelopeGenerator.Web/Models/Culture.cs @@ -0,0 +1,8 @@ +namespace EnvelopeGenerator.Web.Models +{ + public class Culture + { + public string Language { get; init; } = string.Empty; + public string FIClass { get; init; } = string.Empty; + } +} diff --git a/EnvelopeGenerator.Web/Models/Cultures.cs b/EnvelopeGenerator.Web/Models/Cultures.cs new file mode 100644 index 00000000..680efab0 --- /dev/null +++ b/EnvelopeGenerator.Web/Models/Cultures.cs @@ -0,0 +1,15 @@ +namespace EnvelopeGenerator.Web.Models +{ + public class Cultures : List + { + public IEnumerable Languages => this.Select(c => c.Language); + + public IEnumerable FIClasses => this.Select(c => c.FIClass); + + public Culture? CultureOf(string? language) => language is null ? null : this.Where(c => c.Language == language).FirstOrDefault(); + + public Culture Default => this.First(); + + public string FIClassOf(string? language) => language is null ? string.Empty : CultureOf(language)?.FIClass ?? string.Empty; + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Program.cs b/EnvelopeGenerator.Web/Program.cs index 92f52837..994bdcb4 100644 --- a/EnvelopeGenerator.Web/Program.cs +++ b/EnvelopeGenerator.Web/Program.cs @@ -16,7 +16,7 @@ using EnvelopeGenerator.Web.Models; using DigitalData.Core.DTO; using System.Text.Encodings.Web; using Ganss.Xss; -using EnvelopeGenerator.Web; +using Microsoft.Extensions.Options; var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); logger.Info("Logging initialized!"); @@ -156,8 +156,8 @@ try builder.Services.AddCookieConsentSettings(); - builder.Services.AddCookieBasedLocalizer(); - + builder.Services.AddCookieBasedLocalizer("Resources"); + builder.Services.AddSingleton(HtmlEncoder.Default); builder.Services.AddSingleton(UrlEncoder.Default); builder.Services.AddSingleton(_ => @@ -167,6 +167,10 @@ try return sanitizer; }); + // Register the FlagIconCssClass instance as a singleton + builder.Services.Configure(builder.Configuration.GetSection("Cultures")); + builder.Services.AddSingleton(sp => sp.GetRequiredService>().Value); + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -177,6 +181,14 @@ try app.UseHsts(); } + //Content-Security-Policy + if (config.GetValue("TestCSP") || !app.Environment.IsDevelopment()) + { + var csp_list = config.GetSection("Content-Security-Policy").Get(); + if (csp_list is not null) + app.UseCSPMiddleware($"{string.Join("; ", csp_list)};"); + } + if (config.GetValue("EnableSwagger")) { app.UseSwagger(); @@ -185,26 +197,33 @@ try app.UseHttpsRedirection(); - var csp_list = config.GetSection("Content-Security-Policy").Get(); - if(csp_list is not null) - app.UseCSPMiddleware($"{string.Join("; ", csp_list)};"); - app.UseStaticFiles(); - app.UseCookiePolicy(); + //app.UseCookiePolicy(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); - var languages = config.GetSection("Languages").Get() ?? - throw new InvalidOperationException("Languages section is missing in the configuration."); - if(languages.Length == 0) - throw new InvalidOperationException("There is no languages in languages section."); + var cultures = app.Services.GetRequiredService(); + if(cultures.Any()) + 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 + FIClass is the css class (in wwwroot/lib/flag-icons-main) for the flag of country. + ""Cultures"": [ + { + ""Language"": ""de-DE"", + ""FIClass"": ""fi-de"" + }, + { + ""Language"": ""en-US"", + ""FIClass"": ""fi-us"" + } + ]"); if(!config.GetValue("DisableMultiLanguage")) - app.UseCookieBasedLocalizer(languages); + app.UseCookieBasedLocalizer(cultures.Languages.ToArray()); app.UseCors("SameOriginPolicy"); diff --git a/EnvelopeGenerator.Web/Views/Home/EnvelopeLocked.cshtml b/EnvelopeGenerator.Web/Views/Home/EnvelopeLocked.cshtml index 0ca30e90..fbb03591 100644 --- a/EnvelopeGenerator.Web/Views/Home/EnvelopeLocked.cshtml +++ b/EnvelopeGenerator.Web/Views/Home/EnvelopeLocked.cshtml @@ -4,7 +4,6 @@ @{ ViewData["Title"] = "Dokument geschützt"; var userLanguage = ViewData["UserLanguage"] as string; - var languages = ViewData["Languages"] as string[]; }
@@ -14,72 +13,60 @@
-

Dokument erfordert einen Zugriffscode

+

@_localizer[WebKey.LockedTitle]

-

Wir haben Ihnen gerade den Zugriffscode an die hinterlegte Email Adresse gesendet. Dies kann evtl. einige Minuten dauern.

+

@_localizer[WebKey.LockedBody]

-
+
- - + +
- +
-
-
- -
+
+
- Sie haben keinen Zugriffscode erhalten? -

Bitte überprüfen Sie Ihr Email Postfach inklusive Spam-Ordner. Sie können auch den Absender bitten, Ihnen den Code auf anderem Wege zukommen zu lassen.

+ @_localizer[WebKey.LockedFooterTitle] +

@_localizer[WebKey.LockedFooterBody]

+ \ No newline at end of file diff --git a/EnvelopeGenerator.Web/Views/Shared/_Layout.cshtml b/EnvelopeGenerator.Web/Views/Shared/_Layout.cshtml index 40a6853f..7baa0247 100644 --- a/EnvelopeGenerator.Web/Views/Shared/_Layout.cshtml +++ b/EnvelopeGenerator.Web/Views/Shared/_Layout.cshtml @@ -8,6 +8,7 @@ + diff --git a/EnvelopeGenerator.Web/Views/_ViewImports.cshtml b/EnvelopeGenerator.Web/Views/_ViewImports.cshtml index a7b4fc62..67a39c0c 100644 --- a/EnvelopeGenerator.Web/Views/_ViewImports.cshtml +++ b/EnvelopeGenerator.Web/Views/_ViewImports.cshtml @@ -1,9 +1,10 @@ @using EnvelopeGenerator.Web @using EnvelopeGenerator.Web.Models -@using Microsoft.Extensions.Localization; -@using EnvelopeGenerator.Application.Resources; -@inject IStringLocalizer _localizer; +@using Microsoft.Extensions.Localization +@using EnvelopeGenerator.Application +@inject IStringLocalizer _localizer @inject System.Text.Encodings.Web.UrlEncoder _encoder @inject Ganss.Xss.HtmlSanitizer _sanitizer @inject Microsoft.AspNetCore.Http.IHttpContextAccessor _accessor +@inject Cultures _cultures @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers \ No newline at end of file diff --git a/EnvelopeGenerator.Web/WebKey.cs b/EnvelopeGenerator.Web/WebKey.cs new file mode 100644 index 00000000..f65aeeee --- /dev/null +++ b/EnvelopeGenerator.Web/WebKey.cs @@ -0,0 +1,20 @@ +namespace EnvelopeGenerator.Web +{ + public static class WebKey + { + public static readonly string ServiceOutputNullError = nameof(ServiceOutputNullError); + public static readonly string UnexpectedError = nameof(UnexpectedError); + public static readonly string FailedToSendAccessCode = nameof(FailedToSendAccessCode); + public static readonly string WrongEnvelopeReceiverId = nameof(WrongEnvelopeReceiverId); + public static readonly string DataIntegrityError = nameof(DataIntegrityError); + public static readonly string NonDecodableEnvelopeReceiverId = nameof(NonDecodableEnvelopeReceiverId); + public static readonly string de_DE = nameof(de_DE).Replace("_", "-"); + public static readonly string en_US = nameof(en_US).Replace("_", "-"); + public static readonly string LockedTitle = nameof(LockedTitle); + public static readonly string LockedBody = nameof(LockedBody); + public static readonly string LocakedOpen = nameof(LocakedOpen); + public static readonly string LockedAccessCode = nameof(LockedAccessCode); + public static readonly string LockedFooterTitle = nameof(LockedFooterTitle); + public static readonly string LockedFooterBody = nameof(LockedFooterBody); + } +} \ No newline at end of file diff --git a/EnvelopeGenerator.Web/appsettings.json b/EnvelopeGenerator.Web/appsettings.json index 11b6d4d4..27287230 100644 --- a/EnvelopeGenerator.Web/appsettings.json +++ b/EnvelopeGenerator.Web/appsettings.json @@ -12,6 +12,7 @@ }, "PSPDFKitLicenseKey": null, /* The first format parameter {0} will be replaced by the nonce value. */ + "TestCSP": false, "Content-Security-Policy": [ "default-src 'self'", "script-src 'self' 'nonce-{0}'", @@ -94,6 +95,15 @@ /* 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. */ - "Languages": [ "de_DE", "en_US" ], - "DisableMultiLanguage": true + "Cultures": [ + { + "Language": "en-US", + "FIClass": "fi-us" + }, + { + "Language": "de-DE", + "FIClass": "fi-de" + } + ], + "DisableMultiLanguage": false } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/wwwroot/css/site.css b/EnvelopeGenerator.Web/wwwroot/css/site.css index fa8ab370..1fb08ec5 100644 --- a/EnvelopeGenerator.Web/wwwroot/css/site.css +++ b/EnvelopeGenerator.Web/wwwroot/css/site.css @@ -209,4 +209,8 @@ footer#page-footer a:focus { .lang-item { font-size: 0.85rem; +} + +#langDropdownMenuButton{ + min-width: 4vw; } \ No newline at end of file diff --git a/EnvelopeGenerator.Web/wwwroot/js/network.js b/EnvelopeGenerator.Web/wwwroot/js/network.js index c74b837a..8153c853 100644 --- a/EnvelopeGenerator.Web/wwwroot/js/network.js +++ b/EnvelopeGenerator.Web/wwwroot/js/network.js @@ -159,3 +159,39 @@ class WrappedResponse { this.fatal = (data === null && error === null) } } + +async function setLangAsync(language, flagCode) { + document.getElementById('selectedFlag').className = 'fi ' + flagCode + ' me-2'; + + await fetch(`/lang/${language}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }) +} + +async function setLanguage(language) { + + const hasLang = await fetch('/lang', { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }) + .then(res => res.json()) + .then(langs => langs.includes(language)) + .catch(err => false); + + if(hasLang) + return await fetch(`/lang/${language}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }) + .then(response => { + if (response.redirected) + window.location.href = response.url; + else if (!response.ok) + return Promise.reject('Failed to set language'); + }); +} \ No newline at end of file