diff --git a/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs index 8facf91f..457af666 100644 --- a/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs @@ -51,6 +51,7 @@ namespace Microsoft.Extensions.DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); // providers services.AddScoped(); diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index 5860982c..99a738b1 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -26,7 +26,6 @@ - diff --git a/Oqtane.Client/Services/Interfaces/ILocalizationCookieService.cs b/Oqtane.Client/Services/Interfaces/ILocalizationCookieService.cs new file mode 100644 index 00000000..a422d432 --- /dev/null +++ b/Oqtane.Client/Services/Interfaces/ILocalizationCookieService.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; + +namespace Oqtane.Services +{ + /// + /// Service to set localization cookie + /// + public interface ILocalizationCookieService + { + /// + /// Set the localization cookie + /// + /// + /// + Task SetLocalizationCookieAsync(string culture); + } +} diff --git a/Oqtane.Client/Services/LocalizationCookieService.cs b/Oqtane.Client/Services/LocalizationCookieService.cs new file mode 100644 index 00000000..330607e6 --- /dev/null +++ b/Oqtane.Client/Services/LocalizationCookieService.cs @@ -0,0 +1,18 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Oqtane.Documentation; +using Oqtane.Shared; + +namespace Oqtane.Services +{ + [PrivateApi("Don't show in the documentation, as everything should use the Interface")] + public class LocalizationCookieService : ServiceBase, ILocalizationCookieService + { + public LocalizationCookieService(HttpClient http, SiteState siteState) : base(http, siteState) { } + + public Task SetLocalizationCookieAsync(string culture) + { + return Task.CompletedTask; // only used in server side rendering + } + } +} diff --git a/Oqtane.Client/Themes/Controls/Theme/LanguageSwitcher.razor b/Oqtane.Client/Themes/Controls/Theme/LanguageSwitcher.razor index 807ac9de..eaf3f48c 100644 --- a/Oqtane.Client/Themes/Controls/Theme/LanguageSwitcher.razor +++ b/Oqtane.Client/Themes/Controls/Theme/LanguageSwitcher.razor @@ -1,9 +1,9 @@ @using System.Globalization @using Oqtane.Models -@using Microsoft.AspNetCore.Http @namespace Oqtane.Themes.Controls @inherits ThemeControlBase @inject ILanguageService LanguageService +@inject ILocalizationCookieService LocalizationCookieService @inject NavigationManager NavigationManager @if (_supportedCultures?.Count() > 1) @@ -37,10 +37,7 @@ [Parameter] public string ButtonClass { get; set; } = "btn-outline-secondary"; - [CascadingParameter] - HttpContext HttpContext { get; set; } - - protected override void OnParametersSet() + protected override async Task OnParametersSetAsync() { MenuAlignment = DropdownAlignment.ToLower() == "right" ? "dropdown-menu-end" : string.Empty; @@ -51,17 +48,7 @@ var culture = PageState.QueryString["culture"]; if (_supportedCultures.Any(item => item.Name == culture)) { - var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)); - - HttpContext.Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, new CookieOptions - { - Path = "/", - Expires = DateTimeOffset.UtcNow.AddYears(365), - SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax, // Set SameSite attribute - Secure = true, // Ensure the cookie is only sent over HTTPS - HttpOnly = false // cookie is updated using JS Interop in Interactive render mode - }); - + await LocalizationCookieService.SetLocalizationCookieAsync(culture); } NavigationManager.NavigateTo(NavigationManager.Uri.Replace($"?culture={culture}", "")); } diff --git a/Oqtane.Client/UI/Routes.razor b/Oqtane.Client/UI/Routes.razor index de6503f2..20dc0a09 100644 --- a/Oqtane.Client/UI/Routes.razor +++ b/Oqtane.Client/UI/Routes.razor @@ -1,7 +1,5 @@ @namespace Oqtane.UI -@using Microsoft.AspNetCore.Http @inject IInstallationService InstallationService -@inject IJSRuntime JSRuntime @inject SiteState SiteState @if (_initialized) @@ -48,9 +46,6 @@ [Parameter] public string Platform { get; set; } = ""; - [CascadingParameter] - HttpContext HttpContext { get; set; } - private bool _initialized = false; private bool _installed = false; private string _display = "display: none;"; @@ -62,7 +57,7 @@ SiteState.AntiForgeryToken = AntiForgeryToken; SiteState.AuthorizationToken = AuthorizationToken; SiteState.Platform = Platform; - SiteState.IsPrerendering = (HttpContext != null) ? true : false; + SiteState.IsPrerendering = !RendererInfo.IsInteractive; if (Runtime == Runtimes.Hybrid) { diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index 2b03eec6..b04f2061 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -113,8 +113,11 @@ namespace Microsoft.Extensions.DependencyInjection internal static IServiceCollection AddOqtaneTransientServices(this IServiceCollection services) { - // repositories + // services services.AddTransient(); + services.AddTransient(); + + // repositories services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -130,7 +133,6 @@ namespace Microsoft.Extensions.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -153,12 +155,12 @@ namespace Microsoft.Extensions.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); - - // obsolete - replaced by ITenantManager - services.AddTransient(); - + services.AddTransient(); services.AddTransient(); + // obsolete + services.AddTransient(); // replaced by ITenantManager + return services; } diff --git a/Oqtane.Server/Services/LocalizationCookieService.cs b/Oqtane.Server/Services/LocalizationCookieService.cs new file mode 100644 index 00000000..1bedfc6f --- /dev/null +++ b/Oqtane.Server/Services/LocalizationCookieService.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Localization; +using Oqtane.Documentation; + +namespace Oqtane.Services +{ + [PrivateApi("Don't show in the documentation, as everything should use the Interface")] + public class ServerLocalizationCookieService : ILocalizationCookieService + { + private readonly IHttpContextAccessor _accessor; + + public ServerLocalizationCookieService(IHttpContextAccessor accessor) + { + _accessor = accessor; + } + + public Task SetLocalizationCookieAsync(string culture) + { + var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)); + + _accessor.HttpContext.Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, new CookieOptions + { + Path = "/", + Expires = DateTimeOffset.UtcNow.AddYears(365), + SameSite = SameSiteMode.Lax, + Secure = true, // Ensure the cookie is only sent over HTTPS + HttpOnly = false // cookie is updated using JS Interop in Interactive render mode + }); + + return Task.CompletedTask; + } + } +}