diff --git a/.gitignore b/.gitignore index 69863764..04ce32b7 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,9 @@ Oqtane.Server/Packages Oqtane.Server/wwwroot/Content Oqtane.Server/wwwroot/Packages/*.log +Oqtane.Server/wwwroot/_content/* +!Oqtane.Server/wwwroot/_content/Placeholder.txt + Oqtane.Server/wwwroot/Modules/* !Oqtane.Server/wwwroot/Modules/Oqtane.Modules.* !Oqtane.Server/wwwroot/Modules/Templates 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/Installer/Installer.razor b/Oqtane.Client/Installer/Installer.razor index 94f9cc39..c0187971 100644 --- a/Oqtane.Client/Installer/Installer.razor +++ b/Oqtane.Client/Installer/Installer.razor @@ -15,7 +15,7 @@
-
@SharedLocalizer["Version"] @Constants.Version (.NET 8)
+
@SharedLocalizer["Version"] @Constants.Version (.NET 9)

@@ -156,129 +156,130 @@ private List _templates; private string _template = Constants.DefaultSiteTemplate; private bool _register = true; - private string _message = string.Empty; - private string _loadingDisplay = "display: none;"; + private string _message = string.Empty; + private string _loadingDisplay = "display: none;"; - protected override async Task OnInitializedAsync() - { + protected override async Task OnInitializedAsync() + { // include CSS var content = $""; SiteState.AppendHeadContent(content); _togglePassword = SharedLocalizer["ShowPassword"]; - _toggleConfirmPassword = SharedLocalizer["ShowPassword"]; + _toggleConfirmPassword = SharedLocalizer["ShowPassword"]; - _databases = await DatabaseService.GetDatabasesAsync(); - if (_databases.Exists(item => item.IsDefault)) - { - _databaseName = _databases.Find(item => item.IsDefault).Name; - } - else - { - _databaseName = "LocalDB"; - } - LoadDatabaseConfigComponent(); + _databases = await DatabaseService.GetDatabasesAsync(); + if (_databases.Exists(item => item.IsDefault)) + { + _databaseName = _databases.Find(item => item.IsDefault).Name; + } + else + { + _databaseName = "LocalDB"; + } + LoadDatabaseConfigComponent(); _templates = await SiteTemplateService.GetSiteTemplatesAsync(); } - private void DatabaseChanged(ChangeEventArgs eventArgs) - { - try - { - _databaseName = (string)eventArgs.Value; - _showConnectionString = false; - LoadDatabaseConfigComponent(); - } - catch - { - _message = Localizer["Error.DbConfig.Load"]; - } - } + private void DatabaseChanged(ChangeEventArgs eventArgs) + { + try + { + _databaseName = (string)eventArgs.Value; + _showConnectionString = false; + LoadDatabaseConfigComponent(); + } + catch + { + _message = Localizer["Error.DbConfig.Load"]; + } + } - private void LoadDatabaseConfigComponent() - { - var database = _databases.SingleOrDefault(d => d.Name == _databaseName); - if (database != null) - { - _databaseConfigType = Type.GetType(database.ControlType); - DatabaseConfigComponent = builder => - { - builder.OpenComponent(0, _databaseConfigType); - builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); }); - builder.CloseComponent(); - }; - } - } + private void LoadDatabaseConfigComponent() + { + var database = _databases.SingleOrDefault(d => d.Name == _databaseName); + if (database != null) + { + _databaseConfigType = Type.GetType(database.ControlType); + DatabaseConfigComponent = builder => + { + builder.OpenComponent(0, _databaseConfigType); + builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); }); + builder.CloseComponent(); + }; + } + } - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { // include JavaScript - var interop = new Interop(JSRuntime); + var interop = new Interop(JSRuntime); await interop.IncludeScript("", Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous", "", "head"); - } - } + } + } - private async Task Install() - { - var connectionString = String.Empty; - if (_showConnectionString) - { - connectionString = _connectionString; - } - else - { - if (_databaseConfig is IDatabaseConfigControl databaseConfigControl) - { - connectionString = databaseConfigControl.GetConnectionString(); - } - } + private async Task Install() + { + var connectionString = String.Empty; + if (_showConnectionString) + { + connectionString = _connectionString; + } + else + { + if (_databaseConfig is IDatabaseConfigControl databaseConfigControl) + { + connectionString = databaseConfigControl.GetConnectionString(); + } + } - if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@")) - { - if (await UserService.ValidatePasswordAsync(_hostPassword)) - { - _loadingDisplay = ""; - StateHasChanged(); + if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@")) + { + var result = await UserService.ValidateUserAsync(_hostUsername, _hostEmail, _hostPassword); + if (result.Succeeded) + { + _loadingDisplay = ""; + StateHasChanged(); - Uri uri = new Uri(NavigationManager.Uri); + Uri uri = new Uri(NavigationManager.Uri); - var database = _databases.SingleOrDefault(d => d.Name == _databaseName); + var database = _databases.SingleOrDefault(d => d.Name == _databaseName); - var config = new InstallConfig - { - DatabaseType = database.DBType, - ConnectionString = connectionString, - Aliases = uri.Authority, - HostUsername = _hostUsername, - HostPassword = _hostPassword, - HostEmail = _hostEmail, - HostName = _hostUsername, - TenantName = TenantNames.Master, - IsNewTenant = true, - SiteName = Constants.DefaultSite, - Register = _register, - SiteTemplate = _template, - RenderMode = RenderModes.Static, - Runtime = Runtimes.Server - }; + var config = new InstallConfig + { + DatabaseType = database.DBType, + ConnectionString = connectionString, + Aliases = uri.Authority, + HostUsername = _hostUsername, + HostPassword = _hostPassword, + HostEmail = _hostEmail, + HostName = _hostUsername, + TenantName = TenantNames.Master, + IsNewTenant = true, + SiteName = Constants.DefaultSite, + Register = _register, + SiteTemplate = _template, + RenderMode = RenderModes.Static, + Runtime = Runtimes.Server + }; - var installation = await InstallationService.Install(config); - if (installation.Success) - { - NavigationManager.NavigateTo(uri.Scheme + "://" + uri.Authority, true); - } - else - { - _message = installation.Message; - _loadingDisplay = "display: none;"; - } - } - else - { - _message = Localizer["Message.Password.Invalid"]; + var installation = await InstallationService.Install(config); + if (installation.Success) + { + NavigationManager.NavigateTo(uri.Scheme + "://" + uri.Authority, true); + } + else + { + _message = installation.Message; + _loadingDisplay = "display: none;"; + } + } + else + { + _message = string.Join("
", result.Errors.Select(i => !string.IsNullOrEmpty(i.Value) ? i.Value : Localizer[i.Key])); } } else diff --git a/Oqtane.Client/Modules/Admin/Languages/Add.razor b/Oqtane.Client/Modules/Admin/Languages/Add.razor index 52a958c2..fedc045b 100644 --- a/Oqtane.Client/Modules/Admin/Languages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Languages/Add.razor @@ -1,7 +1,6 @@ @namespace Oqtane.Modules.Admin.Languages @inherits ModuleBase @using System.Globalization -@using Microsoft.AspNetCore.Localization @inject NavigationManager NavigationManager @inject ILocalizationService LocalizationService @inject ILanguageService LanguageService diff --git a/Oqtane.Client/Modules/Admin/Languages/Edit.razor b/Oqtane.Client/Modules/Admin/Languages/Edit.razor index 7aa074c7..3a4060ac 100644 --- a/Oqtane.Client/Modules/Admin/Languages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Languages/Edit.razor @@ -1,7 +1,6 @@ @namespace Oqtane.Modules.Admin.Languages @inherits ModuleBase @using System.Globalization -@using Microsoft.AspNetCore.Localization @inject NavigationManager NavigationManager @inject ILocalizationService LocalizationService @inject ILanguageService LanguageService diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Create.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Create.razor index f673ca10..60241e2a 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Create.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Create.razor @@ -27,7 +27,7 @@
- +
@@ -118,6 +118,7 @@ { if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-") { + if (string.IsNullOrEmpty(_description)) _description = _module; if (IsValidXML(_description)) { var template = _templates.FirstOrDefault(item => item.Name == _template); diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index ffa2f1e3..4693891e 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -1,7 +1,6 @@ @namespace Oqtane.Modules.Admin.ModuleDefinitions @inherits ModuleBase @using System.Globalization -@using Microsoft.AspNetCore.Localization @inject IModuleDefinitionService ModuleDefinitionService @inject IPackageService PackageService @inject ILanguageService LanguageService diff --git a/Oqtane.Client/Modules/Admin/Search/Index.razor b/Oqtane.Client/Modules/Admin/Search/Index.razor index 8a32ff22..ee47feea 100644 --- a/Oqtane.Client/Modules/Admin/Search/Index.razor +++ b/Oqtane.Client/Modules/Admin/Search/Index.razor @@ -63,7 +63,7 @@ private string _enabled = "True"; private string _lastIndexedOn = ""; private string _ignorePages = ""; - private string _ignoreEntities = ""; + private string _ignoreEntities = "File"; private string _minimumWordLength = "3"; private string _ignoreWords = "the,be,to,of,and,a,i,in,that,have,it,for,not,on,with,he,as,you,do,at,this,but,his,by,from,they,we,say,her,she,or,an,will,my,one,all,would,there,their,what,so,up,out,if,about,who,get,which,go,me,when,make,can,like,time,no,just,him,know,take,people,into,year,your,good,some,could,them,see,other,than,then,now,look,only,come,its,over,think,also,back,after,use,two,how,our,work,first,well,way,even,new,want,because,any,these,give,day,most,us"; @@ -85,7 +85,7 @@ { var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); settings = SettingService.SetSetting(settings, "Search_SearchProvider", _searchProvider); - settings = SettingService.SetSetting(settings, "Search_Enabled", _enabled, true); + settings = SettingService.SetSetting(settings, "Search_Enabled", _enabled); settings = SettingService.SetSetting(settings, "Search_LastIndexedOn", _lastIndexedOn, true); settings = SettingService.SetSetting(settings, "Search_IgnorePages", _ignorePages, true); settings = SettingService.SetSetting(settings, "Search_IgnoreEntities", _ignoreEntities, true); @@ -106,9 +106,7 @@ try { _lastIndexedOn = DateTime.MinValue.ToString(); - var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); - settings = SettingService.SetSetting(settings, "Search_LastIndexedOn", _lastIndexedOn, true); - await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId); + await Save(); AddModuleMessage(Localizer["Message.Reindex"], MessageType.Success); } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor index a59b161d..a5211c55 100644 --- a/Oqtane.Client/Modules/Admin/Users/Index.razor +++ b/Oqtane.Client/Modules/Admin/Users/Index.razor @@ -691,6 +691,10 @@ else await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message); AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error); } + finally + { + await ScrollToPageTop(); + } } private void ProviderChanged(ChangeEventArgs e) diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor index a52f359e..509cef2d 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -196,7 +196,7 @@ else { FolderId = -1; - _message = "Folder Path " + Folder + "Does Not Exist"; + _message = "Folder Path " + Folder + " Does Not Exist"; _messagetype = MessageType.Error; } } @@ -226,9 +226,9 @@ } else { - FileId = -1; // file does not exist - _message = "FileId " + FileId.ToString() + "Does Not Exist"; + _message = "FileId " + FileId.ToString() + " Does Not Exist"; _messagetype = MessageType.Error; + FileId = -1; // file does not exist } } diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index 72c6447c..4823e285 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -1,10 +1,10 @@ - net8.0 + net9.0 Exe Debug;Release - 5.2.4 + 6.0.0 Oqtane Shaun Walker .NET Foundation @@ -12,7 +12,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 https://github.com/oqtane/oqtane.framework Git Oqtane @@ -22,11 +22,10 @@ - - - - - + + + + diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index 9f5ed2ff..d71131cb 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; @@ -13,13 +12,13 @@ using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Microsoft.AspNetCore.Localization; using Microsoft.Extensions.DependencyInjection; using Microsoft.JSInterop; using Oqtane.Documentation; using Oqtane.Models; using Oqtane.Modules; using Oqtane.Services; +using Oqtane.Shared; using Oqtane.UI; namespace Oqtane.Client @@ -258,7 +257,7 @@ namespace Oqtane.Client var jsRuntime = serviceProvider.GetRequiredService(); var interop = new Interop(jsRuntime); var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName); - var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICultures?[0].Value; + var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICulture.Name; var localizationService = serviceProvider.GetRequiredService(); var cultures = await localizationService.GetCulturesAsync(false); diff --git a/Oqtane.Client/Resources/Installer/Installer.resx b/Oqtane.Client/Resources/Installer/Installer.resx index 23e0f7d7..a06752d9 100644 --- a/Oqtane.Client/Resources/Installer/Installer.resx +++ b/Oqtane.Client/Resources/Installer/Installer.resx @@ -183,4 +183,7 @@ Select a site template + + The Username Provided Does Not Meet The System Requirement, It Can Only Contains Letters Or Digits. + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/Search/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Search/Index.resx index 0f8e26f4..e9c7d34f 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Search/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Search/Index.resx @@ -139,7 +139,7 @@ Ignore Entities: - Comma delimited list of entities which should be ignored + Comma delimited list of entities which should be ignored. By default File entities are ignored. Word Length: @@ -154,7 +154,7 @@ Comma delimited list of words which should be ignored - Search Settings Saved Successfully + Search Settings Saved Successfully. You Will Need Reindex For Your Changes To Be Reflected In The Search Results. Error Saving Search Settings 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/Interfaces/IUserService.cs b/Oqtane.Client/Services/Interfaces/IUserService.cs index 534466a5..b32a66f0 100644 --- a/Oqtane.Client/Services/Interfaces/IUserService.cs +++ b/Oqtane.Client/Services/Interfaces/IUserService.cs @@ -113,6 +113,15 @@ namespace Oqtane.Services /// Task VerifyTwoFactorAsync(User user, string token); + /// + /// Validate identity user info. + /// + /// + /// + /// + /// + Task ValidateUserAsync(string username, string email, string password); + /// /// Validate a users password against the password policy /// 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/Services/RemoteServiceBase.cs b/Oqtane.Client/Services/RemoteServiceBase.cs index 75c1435d..281d4d6c 100644 --- a/Oqtane.Client/Services/RemoteServiceBase.cs +++ b/Oqtane.Client/Services/RemoteServiceBase.cs @@ -4,7 +4,6 @@ using System.Net.Http; using System.Net.Http.Json; using System.Threading; using System.Threading.Tasks; -using Microsoft.Net.Http.Headers; using Oqtane.Shared; namespace Oqtane.Services @@ -28,9 +27,9 @@ namespace Oqtane.Services private HttpClient GetHttpClient(string AuthorizationToken) { var httpClient = _httpClientFactory.CreateClient("Remote"); - if (!httpClient.DefaultRequestHeaders.Contains(HeaderNames.Authorization) && !string.IsNullOrEmpty(AuthorizationToken)) + if (!httpClient.DefaultRequestHeaders.Contains("Authorization") && !string.IsNullOrEmpty(AuthorizationToken)) { - httpClient.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + AuthorizationToken); + httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + AuthorizationToken); } return httpClient; } diff --git a/Oqtane.Client/Services/UserService.cs b/Oqtane.Client/Services/UserService.cs index 2133d2de..d69aa10d 100644 --- a/Oqtane.Client/Services/UserService.cs +++ b/Oqtane.Client/Services/UserService.cs @@ -89,6 +89,11 @@ namespace Oqtane.Services return await PostJsonAsync($"{Apiurl}/twofactor?token={token}", user); } + public async Task ValidateUserAsync(string username, string email, string password) + { + return await GetJsonAsync($"{Apiurl}/validateuser?username={WebUtility.UrlEncode(username)}&email={WebUtility.UrlEncode(email)}&password={WebUtility.UrlEncode(password)}"); + } + public async Task ValidatePasswordAsync(string password) { return await GetJsonAsync($"{Apiurl}/validate/{WebUtility.UrlEncode(password)}"); diff --git a/Oqtane.Client/Themes/Controls/Theme/LanguageSwitcher.razor b/Oqtane.Client/Themes/Controls/Theme/LanguageSwitcher.razor index 78a6a99c..eaf3f48c 100644 --- a/Oqtane.Client/Themes/Controls/Theme/LanguageSwitcher.razor +++ b/Oqtane.Client/Themes/Controls/Theme/LanguageSwitcher.razor @@ -1,10 +1,9 @@ @using System.Globalization -@using Microsoft.AspNetCore.Localization -@using Microsoft.AspNetCore.Http @using Oqtane.Models @namespace Oqtane.Themes.Controls @inherits ThemeControlBase @inject ILanguageService LanguageService +@inject ILocalizationCookieService LocalizationCookieService @inject NavigationManager NavigationManager @if (_supportedCultures?.Count() > 1) @@ -38,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; @@ -52,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/Themes/Controls/Theme/Search.razor b/Oqtane.Client/Themes/Controls/Theme/Search.razor index 19eec4cc..ccb4b327 100644 --- a/Oqtane.Client/Themes/Controls/Theme/Search.razor +++ b/Oqtane.Client/Themes/Controls/Theme/Search.razor @@ -18,7 +18,7 @@ placeholder="@Localizer["SearchPlaceHolder"]" aria-label="Search" /> } - 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.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index 0d5ebc1b..31ac6c21 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -1,6 +1,5 @@ @using System.Diagnostics.CodeAnalysis @using System.Net -@using Microsoft.AspNetCore.Http @using System.Globalization @using System.Security.Claims @namespace Oqtane.UI diff --git a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj index d9911acd..cce8ad12 100644 --- a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj +++ b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj @@ -1,8 +1,8 @@ - net8.0 - 5.2.4 + net9.0 + 6.0.0 Oqtane Shaun Walker .NET Foundation @@ -10,7 +10,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 https://github.com/oqtane/oqtane.framework Git true @@ -33,7 +33,7 @@ - + @@ -42,7 +42,7 @@ - + diff --git a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj index 111a1251..09f36744 100644 --- a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj +++ b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj @@ -1,8 +1,8 @@ - net8.0 - 5.2.4 + net9.0 + 6.0.0 Oqtane Shaun Walker .NET Foundation @@ -10,7 +10,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 https://github.com/oqtane/oqtane.framework Git true @@ -25,17 +25,18 @@ - 1701;1702;EF1001;AD0001 + 1701;1702;EF1001;AD0001;NU1608 - 1701;1702;EF1001;AD0001 + 1701;1702;EF1001;AD0001;NU1608 - - + + + @@ -43,7 +44,7 @@ - + diff --git a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj index 48f7282f..8c5a767b 100644 --- a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj +++ b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj @@ -1,8 +1,8 @@ - net8.0 - 5.2.4 + net9.0 + 6.0.0 Oqtane Shaun Walker .NET Foundation @@ -10,7 +10,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 https://github.com/oqtane/oqtane.framework Git true @@ -33,7 +33,7 @@ - + @@ -41,7 +41,7 @@ - + diff --git a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj index 1fa10f5b..254b21db 100644 --- a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj +++ b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj @@ -1,8 +1,8 @@ - net8.0 - 5.2.4 + net9.0 + 6.0.0 Oqtane Shaun Walker .NET Foundation @@ -10,7 +10,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 https://github.com/oqtane/oqtane.framework Git true @@ -33,7 +33,7 @@ - + @@ -41,7 +41,7 @@ - + diff --git a/Oqtane.Maui/App.xaml.cs b/Oqtane.Maui/App.xaml.cs index 0d60ea85..95ffaf1b 100644 --- a/Oqtane.Maui/App.xaml.cs +++ b/Oqtane.Maui/App.xaml.cs @@ -5,7 +5,10 @@ public partial class App : Application public App() { InitializeComponent(); - - MainPage = new MainPage(); } + + protected override Window CreateWindow(IActivationState activationState) + { + return new Window(new MainPage()); + } } diff --git a/Oqtane.Maui/Oqtane.Maui.csproj b/Oqtane.Maui/Oqtane.Maui.csproj index 9697adab..32d34828 100644 --- a/Oqtane.Maui/Oqtane.Maui.csproj +++ b/Oqtane.Maui/Oqtane.Maui.csproj @@ -1,12 +1,12 @@ - $(TargetFrameworks);net8.0-windows10.0.19041.0 + $(TargetFrameworks);net9.0-windows10.0.19041.0 - - + + Exe - 5.2.4 + 6.0.0 Oqtane Shaun Walker .NET Foundation @@ -14,7 +14,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 https://github.com/oqtane/oqtane.framework Git Oqtane.Maui @@ -28,19 +28,21 @@ com.oqtane.maui - 0E29FC31-1B83-48ED-B6E0-9F3C67B775D4 - 5.2.4 + 6.0.0 1 - 14.2 - 14.0 - 24.0 - 10.0.17763.0 - 10.0.17763.0 - 6.5 - + + None + + 15.0 + 15.0 + 24.0 + 10.0.17763.0 + 10.0.17763.0 + 6.5 + @@ -65,23 +67,22 @@ - - - - - - - - - + + + + + + + + - ..\Oqtane.Server\bin\Debug\net8.0\Oqtane.Client.dll + ..\Oqtane.Server\bin\Debug\net9.0\Oqtane.Client.dll - ..\Oqtane.Server\bin\Debug\net8.0\Oqtane.Shared.dll + ..\Oqtane.Server\bin\Debug\net9.0\Oqtane.Shared.dll diff --git a/Oqtane.Maui/Properties/launchSettings.json b/Oqtane.Maui/Properties/launchSettings.json index edf8aadc..4f857936 100644 --- a/Oqtane.Maui/Properties/launchSettings.json +++ b/Oqtane.Maui/Properties/launchSettings.json @@ -1,7 +1,7 @@ { "profiles": { "Windows Machine": { - "commandName": "MsixPackage", + "commandName": "Project", "nativeDebugging": false } } diff --git a/Oqtane.Maui/wwwroot/js/interop.js b/Oqtane.Maui/wwwroot/js/interop.js index ef6043f9..675cebca 100644 --- a/Oqtane.Maui/wwwroot/js/interop.js +++ b/Oqtane.Maui/wwwroot/js/interop.js @@ -417,11 +417,20 @@ Oqtane.Interop = { } }, scrollTo: function (top, left, behavior) { - window.scrollTo({ - top: top, - left: left, - behavior: behavior - }); + const modal = document.querySelector('.modal'); + if (modal) { + modal.scrollTo({ + top: top, + left: left, + behavior: behavior + }); + } else { + window.scrollTo({ + top: top, + left: left, + behavior: behavior + }); + } }, scrollToId: function (id) { var element = document.getElementById(id); diff --git a/Oqtane.Package/Oqtane.Client.nuspec b/Oqtane.Package/Oqtane.Client.nuspec index 79a8088a..a01cd05d 100644 --- a/Oqtane.Package/Oqtane.Client.nuspec +++ b/Oqtane.Package/Oqtane.Client.nuspec @@ -2,7 +2,7 @@ Oqtane.Client - 5.2.4 + 6.0.0 Shaun Walker .NET Foundation Oqtane Framework @@ -12,14 +12,14 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 readme.md icon.png oqtane - - + + diff --git a/Oqtane.Package/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec index 5ddceeef..80b1baaa 100644 --- a/Oqtane.Package/Oqtane.Framework.nuspec +++ b/Oqtane.Package/Oqtane.Framework.nuspec @@ -2,7 +2,7 @@ Oqtane.Framework - 5.2.4 + 6.0.0 Shaun Walker .NET Foundation Oqtane Framework @@ -11,8 +11,8 @@ .NET Foundation false MIT - https://github.com/oqtane/oqtane.framework/releases/download/v5.2.4/Oqtane.Framework.5.2.4.Upgrade.zip - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/download/v6.0.0/Oqtane.Framework.6.0.0.Upgrade.zip + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 readme.md icon.png oqtane framework diff --git a/Oqtane.Package/Oqtane.Server.nuspec b/Oqtane.Package/Oqtane.Server.nuspec index d43e9a3d..1be6a86d 100644 --- a/Oqtane.Package/Oqtane.Server.nuspec +++ b/Oqtane.Package/Oqtane.Server.nuspec @@ -2,7 +2,7 @@ Oqtane.Server - 5.2.4 + 6.0.0 Shaun Walker .NET Foundation Oqtane Framework @@ -12,14 +12,14 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 readme.md icon.png oqtane - - + + diff --git a/Oqtane.Package/Oqtane.Shared.nuspec b/Oqtane.Package/Oqtane.Shared.nuspec index b9f466f8..9186c018 100644 --- a/Oqtane.Package/Oqtane.Shared.nuspec +++ b/Oqtane.Package/Oqtane.Shared.nuspec @@ -2,7 +2,7 @@ Oqtane.Shared - 5.2.4 + 6.0.0 Shaun Walker .NET Foundation Oqtane Framework @@ -12,14 +12,14 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 readme.md icon.png oqtane - - + + diff --git a/Oqtane.Package/Oqtane.Updater.nuspec b/Oqtane.Package/Oqtane.Updater.nuspec index 8e3cc368..6a7911f8 100644 --- a/Oqtane.Package/Oqtane.Updater.nuspec +++ b/Oqtane.Package/Oqtane.Updater.nuspec @@ -2,7 +2,7 @@ Oqtane.Updater - 5.2.4 + 6.0.0 Shaun Walker .NET Foundation Oqtane Framework @@ -12,13 +12,13 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 readme.md icon.png oqtane - + diff --git a/Oqtane.Package/install.ps1 b/Oqtane.Package/install.ps1 index 23cd8498..9f4ef87b 100644 --- a/Oqtane.Package/install.ps1 +++ b/Oqtane.Package/install.ps1 @@ -1 +1 @@ -Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.2.4.Install.zip" -Force +Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.0.0.Install.zip" -Force diff --git a/Oqtane.Package/release.cmd b/Oqtane.Package/release.cmd index 0b11fe27..3aa117db 100644 --- a/Oqtane.Package/release.cmd +++ b/Oqtane.Package/release.cmd @@ -6,14 +6,22 @@ nuget.exe pack Oqtane.Client.nuspec nuget.exe pack Oqtane.Server.nuspec nuget.exe pack Oqtane.Shared.nuspec nuget.exe pack Oqtane.Framework.nuspec -del /F/Q/S "..\Oqtane.Server\bin\Release\net8.0\publish" > NUL -rmdir /Q/S "..\Oqtane.Server\bin\Release\net8.0\publish" +del /F/Q/S "..\Oqtane.Server\bin\Release\net9.0\publish" > NUL +rmdir /Q/S "..\Oqtane.Server\bin\Release\net9.0\publish" dotnet publish ..\Oqtane.Server\Oqtane.Server.csproj /p:Configuration=Release -del /F/Q/S "..\Oqtane.Server\bin\Release\net8.0\publish\wwwroot\Content" > NUL -rmdir /Q/S "..\Oqtane.Server\bin\Release\net8.0\publish\wwwroot\Content" +del /F/Q/S "..\Oqtane.Server\bin\Release\net9.0\publish\wwwroot\Content" > NUL +rmdir /Q/S "..\Oqtane.Server\bin\Release\net9.0\publish\wwwroot\Content" setlocal ENABLEDELAYEDEXPANSION +set retain=Placeholder.txt +for /D %%i in ("..\Oqtane.Server\bin\Release\net9.0\publish\wwwroot\_content\*") do ( +set /A found=0 +for %%j in (%retain%) do ( +if "%%~nxi" == "%%j" set /A found=1 +) +if not !found! == 1 rmdir /Q/S "%%i" +) set retain=Oqtane.Modules.Admin.Login,Oqtane.Modules.HtmlText -for /D %%i in ("..\Oqtane.Server\bin\Release\net8.0\publish\wwwroot\Modules\*") do ( +for /D %%i in ("..\Oqtane.Server\bin\Release\net9.0\publish\wwwroot\Modules\*") do ( set /A found=0 for %%j in (%retain%) do ( if "%%~nxi" == "%%j" set /A found=1 @@ -21,18 +29,18 @@ if "%%~nxi" == "%%j" set /A found=1 if not !found! == 1 rmdir /Q/S "%%i" ) set retain=Oqtane.Themes.BlazorTheme,Oqtane.Themes.OqtaneTheme -for /D %%i in ("..\Oqtane.Server\bin\Release\net8.0\publish\wwwroot\Themes\*") do ( +for /D %%i in ("..\Oqtane.Server\bin\Release\net9.0\publish\wwwroot\Themes\*") do ( set /A found=0 for %%j in (%retain%) do ( if "%%~nxi" == "%%j" set /A found=1 ) if not !found! == 1 rmdir /Q/S "%%i" ) -del "..\Oqtane.Server\bin\Release\net8.0\publish\appsettings.json" -ren "..\Oqtane.Server\bin\Release\net8.0\publish\appsettings.release.json" "appsettings.json" +del "..\Oqtane.Server\bin\Release\net9.0\publish\appsettings.json" +ren "..\Oqtane.Server\bin\Release\net9.0\publish\appsettings.release.json" "appsettings.json" C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\install.ps1" -del "..\Oqtane.Server\bin\Release\net8.0\publish\appsettings.json" -del "..\Oqtane.Server\bin\Release\net8.0\publish\web.config" +del "..\Oqtane.Server\bin\Release\net9.0\publish\appsettings.json" +del "..\Oqtane.Server\bin\Release\net9.0\publish\web.config" C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\upgrade.ps1" dotnet clean -c Release ..\Oqtane.Updater.sln dotnet build -c Release ..\Oqtane.Updater.sln diff --git a/Oqtane.Package/upgrade.ps1 b/Oqtane.Package/upgrade.ps1 index 19cc36ec..7ece0bea 100644 --- a/Oqtane.Package/upgrade.ps1 +++ b/Oqtane.Package/upgrade.ps1 @@ -1 +1 @@ -Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.2.4.Upgrade.zip" -Force +Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.0.0.Upgrade.zip" -Force diff --git a/Oqtane.Server/Components/App.razor b/Oqtane.Server/Components/App.razor index 35d71322..e8c7ca76 100644 --- a/Oqtane.Server/Components/App.razor +++ b/Oqtane.Server/Components/App.razor @@ -179,10 +179,6 @@ ManageScripts(resources, alias); // generate scripts - if (_renderMode == RenderModes.Interactive && _runtime == Runtimes.Server) - { - _scripts += CreateReconnectScript(); - } if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null) { _scripts += CreatePWAScript(alias, site, route); @@ -196,28 +192,29 @@ _bodyResources += ParseScripts(site.BodyContent); // set culture if not specified - string culture = Context.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName]; - if (culture == null) + string cultureCookie = Context.Request.Cookies[Shared.CookieRequestCultureProvider.DefaultCookieName]; + if (cultureCookie == null) { // get default language for site if (site.Languages.Any()) { // use default language if specified otherwise use first language in collection - culture = (site.Languages.Where(l => l.IsDefault).SingleOrDefault() ?? site.Languages.First()).Code; + cultureCookie = (site.Languages.Where(l => l.IsDefault).SingleOrDefault() ?? site.Languages.First()).Code; } else { - culture = LocalizationManager.GetDefaultCulture(); + // fallback language + cultureCookie = LocalizationManager.GetDefaultCulture(); } - SetLocalizationCookie(culture); + // convert language code to culture cookie format (ie. "c=en|uic=en") + cultureCookie = Shared.CookieRequestCultureProvider.MakeCookieValue(new Models.RequestCulture(cultureCookie)); + SetLocalizationCookie(cultureCookie); } // set language for page - if (!string.IsNullOrEmpty(culture)) + if (!string.IsNullOrEmpty(cultureCookie)) { - // localization cookie value in form of c=en|uic=en - _language = culture.Split('|')[0]; - _language = _language.Replace("c=", ""); + _language = Shared.CookieRequestCultureProvider.ParseCookieValue(cultureCookie).Culture.Name; } // create initial PageState @@ -494,25 +491,6 @@ "" + Environment.NewLine; } - private string CreateReconnectScript() - { - return Environment.NewLine + - "" + Environment.NewLine; - } - private string CreateScrollPositionScript() { return Environment.NewLine + @@ -602,7 +580,7 @@ } } - private void SetLocalizationCookie(string culture) + private void SetLocalizationCookie(string cookieValue) { var cookieOptions = new Microsoft.AspNetCore.Http.CookieOptions { @@ -613,8 +591,8 @@ }; Context.Response.Cookies.Append( - CookieRequestCultureProvider.DefaultCookieName, - CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)), + Shared.CookieRequestCultureProvider.DefaultCookieName, + cookieValue, cookieOptions ); } diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 1e8a1740..70d17d1d 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -517,7 +517,7 @@ namespace Oqtane.Controllers bool success = true; using (var stream = new FileStream(Path.Combine(folder, filename + ".tmp"), FileMode.Create)) { - foreach (string filepart in fileparts) + foreach (string filepart in fileparts.Order()) { try { diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index a637be69..601d5ce8 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -351,9 +351,9 @@ namespace Oqtane.Controllers return new Dictionary() { { "FrameworkVersion", Constants.Version }, - { "ClientReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll" }, - { "ServerReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Server.dll" }, - { "SharedReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll" }, + { "ClientReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net9.0\\Oqtane.Client.dll" }, + { "ServerReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net9.0\\Oqtane.Server.dll" }, + { "SharedReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net9.0\\Oqtane.Shared.dll" }, }; }); } diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index 1eeee244..f42fc01b 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -267,8 +267,8 @@ namespace Oqtane.Controllers return new Dictionary() { { "FrameworkVersion", Constants.Version }, - { "ClientReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll" }, - { "SharedReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll" }, + { "ClientReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net9.0\\Oqtane.Client.dll" }, + { "SharedReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net9.0\\Oqtane.Shared.dll" }, }; }); } diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 9e80be8d..acbcb1a1 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -347,6 +347,13 @@ namespace Oqtane.Controllers return user; } + // GET api//validate/x + [HttpGet("validateuser")] + public async Task ValidateUser(string username, string email, string password) + { + return await _userManager.ValidateUser(username, email, password); + } + // GET api//validate/x [HttpGet("validate/{password}")] public async Task Validate(string password) diff --git a/Oqtane.Server/Extensions/DbContextOptionsBuilderExtensions.cs b/Oqtane.Server/Extensions/DbContextOptionsBuilderExtensions.cs index a35ecf77..2134708d 100644 --- a/Oqtane.Server/Extensions/DbContextOptionsBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/DbContextOptionsBuilderExtensions.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; using Oqtane.Databases.Interfaces; // ReSharper disable ConvertToUsingDeclaration @@ -9,7 +10,8 @@ namespace Oqtane.Extensions { public static DbContextOptionsBuilder UseOqtaneDatabase([NotNull] this DbContextOptionsBuilder optionsBuilder, IDatabase database, string connectionString) { - database.UseDatabase(optionsBuilder, connectionString); + database.UseDatabase(optionsBuilder, connectionString) + .ConfigureWarnings(warnings => warnings.Log(RelationalEventId.PendingModelChangesWarning)); return optionsBuilder; } 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/Infrastructure/Jobs/SearchIndexJob.cs b/Oqtane.Server/Infrastructure/Jobs/SearchIndexJob.cs index 21b28e40..9b2d2f83 100644 --- a/Oqtane.Server/Infrastructure/Jobs/SearchIndexJob.cs +++ b/Oqtane.Server/Infrastructure/Jobs/SearchIndexJob.cs @@ -59,8 +59,15 @@ namespace Oqtane.Infrastructure var currentTime = DateTime.UtcNow; var lastIndexedOn = Convert.ToDateTime(siteSettings.GetValue(SearchLastIndexedOnSetting, DateTime.MinValue.ToString())); + if (lastIndexedOn == DateTime.MinValue) + { + // reset index + log += $"*Site Index Reset*
"; + await searchService.DeleteSearchContentsAsync(site.SiteId); + } + var ignorePages = siteSettings.GetValue(SearchIgnorePagesSetting, "").Split(','); - var ignoreEntities = siteSettings.GetValue(SearchIgnoreEntitiesSetting, "").Split(','); + var ignoreEntities = siteSettings.GetValue(SearchIgnoreEntitiesSetting, "File").Split(','); var pages = pageRepository.GetPages(site.SiteId); var pageModules = pageModuleRepository.GetPageModules(site.SiteId); diff --git a/Oqtane.Server/Managers/Interfaces/IUserManager.cs b/Oqtane.Server/Managers/Interfaces/IUserManager.cs index 5ada9827..4fde062c 100644 --- a/Oqtane.Server/Managers/Interfaces/IUserManager.cs +++ b/Oqtane.Server/Managers/Interfaces/IUserManager.cs @@ -19,6 +19,7 @@ namespace Oqtane.Managers Task ResetPassword(User user, string token); User VerifyTwoFactor(User user, string token); Task LinkExternalAccount(User user, string token, string type, string key, string name); + Task ValidateUser(string username, string email, string password); Task ValidatePassword(string password); Task> ImportUsers(int siteId, string filePath, bool notify); } diff --git a/Oqtane.Server/Managers/UserManager.cs b/Oqtane.Server/Managers/UserManager.cs index e0e92e97..03bff1bb 100644 --- a/Oqtane.Server/Managers/UserManager.cs +++ b/Oqtane.Server/Managers/UserManager.cs @@ -540,6 +540,30 @@ namespace Oqtane.Managers return user; } + public async Task ValidateUser(string username, string email, string password) + { + var validateResult = new UserValidateResult { Succeeded = true }; + + //validate username + var allowedChars = _identityUserManager.Options.User.AllowedUserNameCharacters; + if (string.IsNullOrWhiteSpace(username) || (!string.IsNullOrEmpty(allowedChars) && username.Any(c => !allowedChars.Contains(c)))) + { + validateResult.Succeeded = false; + validateResult.Errors.Add("Message.Username.Invalid", string.Empty); + } + + //validate password + var passwordValidator = new PasswordValidator(); + var passwordResult = await passwordValidator.ValidateAsync(_identityUserManager, null, password); + if (!passwordResult.Succeeded) + { + validateResult.Succeeded = false; + validateResult.Errors.Add("Message.Password.Invalid", string.Empty); + } + + return validateResult; + } + public async Task ValidatePassword(string password) { var validator = new PasswordValidator(); diff --git a/Oqtane.Server/Migrations/Framework/ForeignKey.cs b/Oqtane.Server/Migrations/Framework/ForeignKey.cs index 5f1bcd75..0a7e9e7f 100644 --- a/Oqtane.Server/Migrations/Framework/ForeignKey.cs +++ b/Oqtane.Server/Migrations/Framework/ForeignKey.cs @@ -44,7 +44,7 @@ namespace Oqtane.Migrations public string PrincipalColumn { get; } - public string PrincipalSchema { get; } + public string PrincipalSchema { get; } = ""; } diff --git a/Oqtane.Server/Modules/Admin/Files/Manager/FileManager.cs b/Oqtane.Server/Modules/Admin/Files/Manager/FileManager.cs index 96b75879..43dbda33 100644 --- a/Oqtane.Server/Modules/Admin/Files/Manager/FileManager.cs +++ b/Oqtane.Server/Modules/Admin/Files/Manager/FileManager.cs @@ -45,10 +45,25 @@ namespace Oqtane.Modules.Admin.Files.Manager var path = folder.Path + file.Name; var body = ""; - if (DocumentExtensions.Contains(Path.GetExtension(file.Name))) + if (System.IO.File.Exists(_fileRepository.GetFilePath(file))) { - // get the contents of the file - body = System.IO.File.ReadAllText(_fileRepository.GetFilePath(file)); + // only non-binary files can be indexed + if (DocumentExtensions.Contains(Path.GetExtension(file.Name))) + { + // get the contents of the file + try + { + body = System.IO.File.ReadAllText(_fileRepository.GetFilePath(file)); + } + catch + { + // could not read the file + } + } + } + else + { + removed = true; // file does not exist on disk } var searchContent = new SearchContent diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index d5c51ea8..993d40e2 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -1,9 +1,9 @@ - net8.0 + net9.0 Debug;Release - 5.2.4 + 6.0.0 Oqtane Shaun Walker .NET Foundation @@ -11,7 +11,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 https://github.com/oqtane/oqtane.framework Git Oqtane @@ -33,21 +33,21 @@ - - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + + diff --git a/Oqtane.Server/Providers/DatabaseSearchProvider.cs b/Oqtane.Server/Providers/DatabaseSearchProvider.cs index c3e5d28e..a662b106 100644 --- a/Oqtane.Server/Providers/DatabaseSearchProvider.cs +++ b/Oqtane.Server/Providers/DatabaseSearchProvider.cs @@ -235,9 +235,9 @@ namespace Oqtane.Providers return text; } - public Task ResetIndex() + public Task DeleteSearchContent(int siteId) { - _searchContentRepository.DeleteAllSearchContent(); + _searchContentRepository.DeleteAllSearchContent(siteId); return Task.CompletedTask; } } diff --git a/Oqtane.Server/Repository/Interfaces/ISearchContentRepository.cs b/Oqtane.Server/Repository/Interfaces/ISearchContentRepository.cs index 8511b438..022c8012 100644 --- a/Oqtane.Server/Repository/Interfaces/ISearchContentRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISearchContentRepository.cs @@ -12,7 +12,7 @@ namespace Oqtane.Repository void DeleteSearchContent(int searchContentId); void DeleteSearchContent(string entityName, string entryId); void DeleteSearchContent(string uniqueKey); - void DeleteAllSearchContent(); + void DeleteAllSearchContent(int siteId); SearchWord GetSearchWord(string word); SearchWord AddSearchWord(SearchWord searchWord); diff --git a/Oqtane.Server/Repository/SearchContentRepository.cs b/Oqtane.Server/Repository/SearchContentRepository.cs index 53d736a5..5aa214ae 100644 --- a/Oqtane.Server/Repository/SearchContentRepository.cs +++ b/Oqtane.Server/Repository/SearchContentRepository.cs @@ -152,11 +152,17 @@ namespace Oqtane.Repository } } - public void DeleteAllSearchContent() + public void DeleteAllSearchContent(int siteId) { using var db = _dbContextFactory.CreateDbContext(); - db.SearchContent.RemoveRange(db.SearchContent); - db.SaveChanges(); + // delete in batches of 100 records + var searchContents = db.SearchContent.Where(item => item.SiteId == siteId).Take(100).ToList(); + while (searchContents.Count > 0) + { + db.SearchContent.RemoveRange(searchContents); + db.SaveChanges(); + searchContents = db.SearchContent.Where(item => item.SiteId == siteId).Take(100).ToList(); + } } public SearchWord GetSearchWord(string word) 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; + } + } +} diff --git a/Oqtane.Server/Services/SearchService.cs b/Oqtane.Server/Services/SearchService.cs index 528f48e1..9a4f7ea1 100644 --- a/Oqtane.Server/Services/SearchService.cs +++ b/Oqtane.Server/Services/SearchService.cs @@ -149,6 +149,12 @@ namespace Oqtane.Services return result; } + public async Task DeleteSearchContentsAsync(int siteId) + { + var searchProvider = GetSearchProvider(siteId); + await searchProvider.DeleteSearchContent(siteId); + } + private ISearchProvider GetSearchProvider(int siteId) { var providerName = GetSearchProviderSetting(siteId); diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Services/[Module]Service.cs b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Services/[Module]Service.cs index 0161a66b..23f10772 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Services/[Module]Service.cs +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Services/[Module]Service.cs @@ -9,7 +9,7 @@ namespace [Owner].Module.[Module].Services { public class [Module]Service : ServiceBase, I[Module]Service { - public [Module]Service(IHttpClientFactory http, SiteState siteState) : base(http, siteState) { } + public [Module]Service(HttpClient http, SiteState siteState) : base(http, siteState) { } private string Apiurl => CreateApiUrl("[Module]"); diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/[Owner].Module.[Module].Client.csproj b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/[Owner].Module.[Module].Client.csproj index 21ef0312..0bc7b7c7 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/[Owner].Module.[Module].Client.csproj +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/[Owner].Module.[Module].Client.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 1.0.0 [Owner] [Owner] @@ -13,12 +13,11 @@ - - - - - - + + + + + diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].Module.[Module].Package.csproj b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].Module.[Module].Package.csproj index e7843bff..b255b8bb 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].Module.[Module].Package.csproj +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].Module.[Module].Package.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 false false diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].Module.[Module].nuspec b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].Module.[Module].nuspec index a09ebb95..4098fedf 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].Module.[Module].nuspec +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/[Owner].Module.[Module].nuspec @@ -20,12 +20,12 @@
- - - - - - + + + + + + diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/debug.cmd b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/debug.cmd index e59e74cd..87d0dcf3 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/debug.cmd +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/debug.cmd @@ -1,7 +1,7 @@ -XCOPY "..\Client\bin\Debug\net8.0\[Owner].Module.[Module].Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net8.0\" /Y -XCOPY "..\Client\bin\Debug\net8.0\[Owner].Module.[Module].Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net8.0\" /Y -XCOPY "..\Server\bin\Debug\net8.0\[Owner].Module.[Module].Server.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net8.0\" /Y -XCOPY "..\Server\bin\Debug\net8.0\[Owner].Module.[Module].Server.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net8.0\" /Y -XCOPY "..\Shared\bin\Debug\net8.0\[Owner].Module.[Module].Shared.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net8.0\" /Y -XCOPY "..\Shared\bin\Debug\net8.0\[Owner].Module.[Module].Shared.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net8.0\" /Y +XCOPY "..\Client\bin\Debug\net9.0\[Owner].Module.[Module].Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y +XCOPY "..\Client\bin\Debug\net9.0\[Owner].Module.[Module].Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y +XCOPY "..\Server\bin\Debug\net9.0\[Owner].Module.[Module].Server.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y +XCOPY "..\Server\bin\Debug\net9.0\[Owner].Module.[Module].Server.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y +XCOPY "..\Shared\bin\Debug\net9.0\[Owner].Module.[Module].Shared.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y +XCOPY "..\Shared\bin\Debug\net9.0\[Owner].Module.[Module].Shared.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y XCOPY "..\Server\wwwroot\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\" /Y /S /I diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/debug.sh b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/debug.sh index 792ce75c..7a5cc808 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Package/debug.sh +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Package/debug.sh @@ -1,7 +1,7 @@ -cp -f "../Client/bin/Debug/net8.0/[Owner].Module.[Module].Client.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net8.0/" -cp -f "../Client/bin/Debug/net8.0/[Owner].Module.[Module].Client.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net8.0/" -cp -f "../Server/bin/Debug/net8.0/[Owner].Module.[Module].Server.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net8.0/" -cp -f "../Server/bin/Debug/net8.0/[Owner].Module.[Module].Server.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net8.0/" -cp -f "../Shared/bin/Debug/net8.0/[Owner].Module.[Module].Shared.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net8.0/" -cp -f "../Shared/bin/Debug/net8.0/[Owner].Module.[Module].Shared.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net8.0/" +cp -f "../Client/bin/Debug/net9.0/[Owner].Module.[Module].Client.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/" +cp -f "../Client/bin/Debug/net9.0/[Owner].Module.[Module].Client.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/" +cp -f "../Server/bin/Debug/net9.0/[Owner].Module.[Module].Server.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/" +cp -f "../Server/bin/Debug/net9.0/[Owner].Module.[Module].Server.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/" +cp -f "../Shared/bin/Debug/net9.0/[Owner].Module.[Module].Shared.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/" +cp -f "../Shared/bin/Debug/net9.0/[Owner].Module.[Module].Shared.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/" cp -rf "../Server/wwwroot/"* "../../oqtane.framework/Oqtane.Server/wwwroot/" diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].Module.[Module].Server.csproj b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].Module.[Module].Server.csproj index 8a9cfe06..550e7e32 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].Module.[Module].Server.csproj +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].Module.[Module].Server.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 true 1.0.0 [Owner].Module.[Module] @@ -19,10 +19,10 @@ - - - - + + + + diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Shared/[Owner].Module.[Module].Shared.csproj b/Oqtane.Server/wwwroot/Modules/Templates/External/Shared/[Owner].Module.[Module].Shared.csproj index a4cc724b..dd2e3d40 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Shared/[Owner].Module.[Module].Shared.csproj +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Shared/[Owner].Module.[Module].Shared.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 1.0.0 [Owner].Module.[Module] [Owner] diff --git a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/[Owner].Theme.[Theme].Client.csproj b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/[Owner].Theme.[Theme].Client.csproj index 48056a8a..a40bfb11 100644 --- a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/[Owner].Theme.[Theme].Client.csproj +++ b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/[Owner].Theme.[Theme].Client.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 1.0.0 [Owner] [Owner] @@ -13,9 +13,9 @@ - - - + + + diff --git a/Oqtane.Server/wwwroot/Themes/Templates/External/Package/[Owner].Theme.[Theme].Package.csproj b/Oqtane.Server/wwwroot/Themes/Templates/External/Package/[Owner].Theme.[Theme].Package.csproj index 0f6cc8ea..e77b1274 100644 --- a/Oqtane.Server/wwwroot/Themes/Templates/External/Package/[Owner].Theme.[Theme].Package.csproj +++ b/Oqtane.Server/wwwroot/Themes/Templates/External/Package/[Owner].Theme.[Theme].Package.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 false false diff --git a/Oqtane.Server/wwwroot/Themes/Templates/External/Package/[Owner].Theme.[Theme].nuspec b/Oqtane.Server/wwwroot/Themes/Templates/External/Package/[Owner].Theme.[Theme].nuspec index 584fc6a6..917b02c9 100644 --- a/Oqtane.Server/wwwroot/Themes/Templates/External/Package/[Owner].Theme.[Theme].nuspec +++ b/Oqtane.Server/wwwroot/Themes/Templates/External/Package/[Owner].Theme.[Theme].nuspec @@ -20,8 +20,8 @@ - - + + diff --git a/Oqtane.Server/wwwroot/Themes/Templates/External/Package/debug.cmd b/Oqtane.Server/wwwroot/Themes/Templates/External/Package/debug.cmd index 2ece53b9..31c7cf33 100644 --- a/Oqtane.Server/wwwroot/Themes/Templates/External/Package/debug.cmd +++ b/Oqtane.Server/wwwroot/Themes/Templates/External/Package/debug.cmd @@ -1,3 +1,3 @@ -XCOPY "..\Client\bin\Debug\net8.0\[Owner].Theme.[Theme].Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net8.0\" /Y -XCOPY "..\Client\bin\Debug\net8.0\[Owner].Theme.[Theme].Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net8.0\" /Y +XCOPY "..\Client\bin\Debug\net9.0\[Owner].Theme.[Theme].Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y +XCOPY "..\Client\bin\Debug\net9.0\[Owner].Theme.[Theme].Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y XCOPY "..\Client\wwwroot\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\" /Y /S /I diff --git a/Oqtane.Server/wwwroot/Themes/Templates/External/Package/debug.sh b/Oqtane.Server/wwwroot/Themes/Templates/External/Package/debug.sh index 52ec3384..4ac1ad54 100644 --- a/Oqtane.Server/wwwroot/Themes/Templates/External/Package/debug.sh +++ b/Oqtane.Server/wwwroot/Themes/Templates/External/Package/debug.sh @@ -1,3 +1,3 @@ -cp -f "../Client/bin/Debug/net8.0/[Owner].Theme.[Theme].Client.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net8.0/" -cp -f "../Client/bin/Debug/net8.0/[Owner].Theme.[Theme].Client.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net8.0/" +cp -f "../Client/bin/Debug/net9.0/[Owner].Theme.[Theme].Client.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/" +cp -f "../Client/bin/Debug/net9.0/[Owner].Theme.[Theme].Client.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/" cp -rf "../Server/wwwroot/"* "../../oqtane.framework/Oqtane.Server/wwwroot/" diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index ef6043f9..675cebca 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -417,11 +417,20 @@ Oqtane.Interop = { } }, scrollTo: function (top, left, behavior) { - window.scrollTo({ - top: top, - left: left, - behavior: behavior - }); + const modal = document.querySelector('.modal'); + if (modal) { + modal.scrollTo({ + top: top, + left: left, + behavior: behavior + }); + } else { + window.scrollTo({ + top: top, + left: left, + behavior: behavior + }); + } }, scrollToId: function (id) { var element = document.getElementById(id); diff --git a/Oqtane.Shared/Interfaces/ISearchProvider.cs b/Oqtane.Shared/Interfaces/ISearchProvider.cs index 43981e8d..23713334 100644 --- a/Oqtane.Shared/Interfaces/ISearchProvider.cs +++ b/Oqtane.Shared/Interfaces/ISearchProvider.cs @@ -11,7 +11,7 @@ namespace Oqtane.Services Task> GetSearchResultsAsync(SearchQuery searchQuery); Task SaveSearchContent(SearchContent searchContent, Dictionary siteSettings); - - Task ResetIndex(); + + Task DeleteSearchContent(int siteId); } } diff --git a/Oqtane.Shared/Interfaces/ISearchService.cs b/Oqtane.Shared/Interfaces/ISearchService.cs index 48cef29c..7acf9764 100644 --- a/Oqtane.Shared/Interfaces/ISearchService.cs +++ b/Oqtane.Shared/Interfaces/ISearchService.cs @@ -9,5 +9,7 @@ namespace Oqtane.Services Task GetSearchResultsAsync(SearchQuery searchQuery); Task SaveSearchContentsAsync(List searchContents, Dictionary siteSettings); + + Task DeleteSearchContentsAsync(int siteId); } } diff --git a/Oqtane.Shared/Models/RequestCulture.cs b/Oqtane.Shared/Models/RequestCulture.cs new file mode 100644 index 00000000..12eaaa7e --- /dev/null +++ b/Oqtane.Shared/Models/RequestCulture.cs @@ -0,0 +1,67 @@ +using System.Globalization; +using System; + +namespace Oqtane.Models +{ + /// + /// Culture information describing a Culture + /// + public class RequestCulture + { + /// + /// Creates a new object with its and + /// properties set to the same value. + /// + /// The for the request. + public RequestCulture(CultureInfo culture) + : this(culture, culture) + { + } + + /// + /// Creates a new object with its and + /// properties set to the same value. + /// + /// The culture for the request. + public RequestCulture(string culture) + : this(culture, culture) + { + } + + /// + /// Creates a new object with its and + /// properties set to the respective values provided. + /// + /// The culture for the request to be used for formatting. + /// The culture for the request to be used for text, i.e. language. + public RequestCulture(string culture, string uiCulture) + : this(new CultureInfo(culture), new CultureInfo(uiCulture)) + { + } + + /// + /// Creates a new object with its and + /// properties set to the respective values provided. + /// + /// The for the request to be used for formatting. + /// The for the request to be used for text, i.e. language. + public RequestCulture(CultureInfo culture, CultureInfo uiCulture) + { + ArgumentNullException.ThrowIfNull(culture); + ArgumentNullException.ThrowIfNull(uiCulture); + + Culture = culture; + UICulture = uiCulture; + } + + /// + /// Gets the for the request to be used for formatting. + /// + public CultureInfo Culture { get; } + + /// + /// Gets the for the request to be used for text, i.e. language; + /// + public CultureInfo UICulture { get; } + } +} diff --git a/Oqtane.Shared/Models/UserValidateResult.cs b/Oqtane.Shared/Models/UserValidateResult.cs new file mode 100644 index 00000000..d531ab82 --- /dev/null +++ b/Oqtane.Shared/Models/UserValidateResult.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Oqtane.Models +{ + public class UserValidateResult + { + public bool Succeeded { get; set; } + + public IDictionary Errors { get; set; } = new Dictionary(); + } +} diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index ab34bf21..c3854210 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -1,9 +1,9 @@ - net8.0 + net9.0 Debug;Release - 5.2.4 + 6.0.0 Oqtane Shaun Walker .NET Foundation @@ -11,7 +11,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 https://github.com/oqtane/oqtane.framework Git Oqtane @@ -19,11 +19,11 @@ - - - + + + - + diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 12edb80b..aa83db6c 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -4,8 +4,8 @@ namespace Oqtane.Shared { public class Constants { - public static readonly string Version = "5.2.4"; - public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.1.1,5.1.2,5.2.0,5.2.1,5.2.2,5.2.3,5.2.4"; + public static readonly string Version = "6.0.0"; + public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.1.1,5.1.2,5.2.0,5.2.1,5.2.2,5.2.3,5.2.4,6.0.0"; public const string PackageId = "Oqtane.Framework"; public const string ClientId = "Oqtane.Client"; public const string UpdaterPackageId = "Oqtane.Updater"; diff --git a/Oqtane.Shared/Shared/CookieRequestCultureProvider.cs b/Oqtane.Shared/Shared/CookieRequestCultureProvider.cs new file mode 100644 index 00000000..040d43b3 --- /dev/null +++ b/Oqtane.Shared/Shared/CookieRequestCultureProvider.cs @@ -0,0 +1,89 @@ +using System; +using Oqtane.Models; + +namespace Oqtane.Shared +{ + public class CookieRequestCultureProvider + { + private const char _cookieSeparator = '|'; + private const string _culturePrefix = "c="; + private const string _uiCulturePrefix = "uic="; + + /// + /// Represent the default cookie name used to track the user's preferred culture information, which is ".AspNetCore.Culture". + /// + public static readonly string DefaultCookieName = ".AspNetCore.Culture"; + + /// + /// The name of the cookie that contains the user's preferred culture information. + /// Defaults to . + /// + public string CookieName { get; set; } = DefaultCookieName; + + /// + /// Creates a string representation of a for placement in a cookie. + /// + /// The . + /// The cookie value. + public static string MakeCookieValue(RequestCulture requestCulture) + { + ArgumentNullException.ThrowIfNull(requestCulture); + + return string.Join(_cookieSeparator, + $"{_culturePrefix}{requestCulture.Culture.Name}", + $"{_uiCulturePrefix}{requestCulture.UICulture.Name}"); + } + + /// + /// Parses a from the specified cookie value. + /// Returns null if parsing fails. + /// + /// The cookie value to parse. + /// The or null if parsing fails. + public static RequestCulture ParseCookieValue(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + + Span parts = stackalloc Range[3]; + var valueSpan = value.AsSpan(); + if (valueSpan.Split(parts, _cookieSeparator, StringSplitOptions.RemoveEmptyEntries) != 2) + { + return null; + } + + var potentialCultureName = valueSpan[parts[0]]; + var potentialUICultureName = valueSpan[parts[1]]; + + if (!potentialCultureName.StartsWith(_culturePrefix, StringComparison.Ordinal) || ! + potentialUICultureName.StartsWith(_uiCulturePrefix, StringComparison.Ordinal)) + { + return null; + } + + var cultureName = potentialCultureName.Slice(_culturePrefix.Length); + var uiCultureName = potentialUICultureName.Slice(_uiCulturePrefix.Length); + + if (cultureName.IsEmpty && uiCultureName.IsEmpty) + { + // No values specified for either so no match + return null; + } + + if (!cultureName.IsEmpty && uiCultureName.IsEmpty) + { + // Value for culture but not for UI culture so default to culture value for both + uiCultureName = cultureName; + } + else if (cultureName.IsEmpty && !uiCultureName.IsEmpty) + { + // Value for UI culture but not for culture so default to UI culture value for both + cultureName = uiCultureName; + } + + return new RequestCulture(cultureName.ToString(), uiCultureName.ToString()); + } + } +} diff --git a/Oqtane.Updater/Oqtane.Updater.csproj b/Oqtane.Updater/Oqtane.Updater.csproj index aab4be52..8f111c0b 100644 --- a/Oqtane.Updater/Oqtane.Updater.csproj +++ b/Oqtane.Updater/Oqtane.Updater.csproj @@ -1,9 +1,9 @@ - net8.0 + net9.0 Exe - 5.2.4 + 6.0.0 Oqtane Shaun Walker .NET Foundation @@ -11,7 +11,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0 https://github.com/oqtane/oqtane.framework Git Oqtane diff --git a/README.md b/README.md index 8e9d7884..d7bda849 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Latest Release -[5.2.3](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.3) was released on September 23, 2024 and is a maintenance release including 55 pull requests by 8 different contributors, pushing the total number of project commits all-time to over 5800. The Oqtane framework continues to evolve at a rapid pace to meet the needs of .NET developers. +[5.2.4](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4) was released on October 17, 2024 and is a maintenance release including 51 pull requests by 7 different contributors, pushing the total number of project commits all-time to over 5900. The Oqtane framework continues to evolve at a rapid pace to meet the needs of .NET developers. [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Foqtane%2Foqtane.framework%2Fmaster%2Fazuredeploy.json) @@ -18,7 +18,7 @@ Please note that this project is owned by the .NET Foundation and is governed by **Installing using source code from the Dev/Master branch:** -- Install **[.NET 8.0.8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)**. +- Install **[.NET 8.0.10 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)**. - Install the latest edition (v17.9 or higher) of [Visual Studio 2022](https://visualstudio.microsoft.com/downloads) with the **ASP.NET and web development** workload enabled. Oqtane works with ALL editions of Visual Studio from Community to Enterprise. If you wish to use LocalDB for development ( not a requirement as Oqtane supports SQLite, mySQL, and PostgreSQL ) you must also install the **Data storage and processing**. @@ -83,11 +83,12 @@ Connect with other developers, get support, and share ideas by joining the Oqtan # Roadmap This project is open source, and therefore is a work in progress... + +6.0.0 (Nov 13, 2024) +- [x] Migration to .NET 9 -Backlog (TBD) -- [ ] Azure Autoscale support (ie. web farm) -- [ ] Folder Providers -- [ ] Generative AI Integration +[5.2.4](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4) (Oct 17, 2024) +- [x] Stabilization improvements [5.2.3](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.3) (Sep 23, 2024) - [x] Stabilization improvements