From 36f705e46fc038c0f8293815f97472026592eed4 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 22 Aug 2024 12:23:09 -0400 Subject: [PATCH 01/61] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1b011b82..daf11ffa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Latest Release -[5.2.0](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.0) was released on July 25, 2024 and is a major release including 109 pull requests by 8 different contributors, pushing the total number of project commits all-time to over 5600. The Oqtane framework continues to evolve at a rapid pace to meet the needs of .NET developers. +[5.2.1](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1) was released on August 25, 2024 and is a maintenance release including 41 pull requests by 5 different contributors, pushing the total number of project commits all-time to over 5700. 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 **Using Version 5:** -- Install **[.NET 8.0.7 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)**. +- Install **[.NET 8.0.8 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**. @@ -63,6 +63,10 @@ Backlog (TBD) - [ ] Folder Providers - [ ] Generative AI Integration +[5.2.1](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1) (Aug 22, 2024) +- [x] Stabilization improvements +- [x] Unzip support in File Management + [5.2.0](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.0) (Jul 25, 2024) - [x] Site Content Search - [x] RichTextEditor extensibility From 759b19e4449303b4802916bf3d155385a19e7d40 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Thu, 22 Aug 2024 12:23:21 -0400 Subject: [PATCH 02/61] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index daf11ffa..f64a04da 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Latest Release -[5.2.1](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1) was released on August 25, 2024 and is a maintenance release including 41 pull requests by 5 different contributors, pushing the total number of project commits all-time to over 5700. The Oqtane framework continues to evolve at a rapid pace to meet the needs of .NET developers. +[5.2.1](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1) was released on August 22, 2024 and is a maintenance release including 41 pull requests by 5 different contributors, pushing the total number of project commits all-time to over 5700. 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) From 4ae8df965238f26d2f48e20d04b9f61bca1ded80 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Thu, 22 Aug 2024 14:09:59 -0400 Subject: [PATCH 03/61] add readme.md to Oqtane nuget packages --- Oqtane.Package/Oqtane.Client.nuspec | 1 + Oqtane.Package/Oqtane.Framework.nuspec | 1 + Oqtane.Package/Oqtane.Server.nuspec | 1 + Oqtane.Package/Oqtane.Shared.nuspec | 1 + Oqtane.Package/Oqtane.Updater.nuspec | 1 + Oqtane.Package/readme.md | 9 +++++++++ 6 files changed, 14 insertions(+) create mode 100644 Oqtane.Package/readme.md diff --git a/Oqtane.Package/Oqtane.Client.nuspec b/Oqtane.Package/Oqtane.Client.nuspec index e0aa2ac0..236c4118 100644 --- a/Oqtane.Package/Oqtane.Client.nuspec +++ b/Oqtane.Package/Oqtane.Client.nuspec @@ -13,6 +13,7 @@ MIT https://github.com/oqtane/oqtane.framework https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1 + readme.md icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec index 888d4e23..06512810 100644 --- a/Oqtane.Package/Oqtane.Framework.nuspec +++ b/Oqtane.Package/Oqtane.Framework.nuspec @@ -13,6 +13,7 @@ MIT https://github.com/oqtane/oqtane.framework/releases/download/v5.2.1/Oqtane.Framework.5.2.1.Upgrade.zip https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1 + readme.md icon.png oqtane framework diff --git a/Oqtane.Package/Oqtane.Server.nuspec b/Oqtane.Package/Oqtane.Server.nuspec index b35f66f3..126e4f15 100644 --- a/Oqtane.Package/Oqtane.Server.nuspec +++ b/Oqtane.Package/Oqtane.Server.nuspec @@ -13,6 +13,7 @@ MIT https://github.com/oqtane/oqtane.framework https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1 + readme.md icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Shared.nuspec b/Oqtane.Package/Oqtane.Shared.nuspec index 334af891..293e85c6 100644 --- a/Oqtane.Package/Oqtane.Shared.nuspec +++ b/Oqtane.Package/Oqtane.Shared.nuspec @@ -13,6 +13,7 @@ MIT https://github.com/oqtane/oqtane.framework https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1 + readme.md icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Updater.nuspec b/Oqtane.Package/Oqtane.Updater.nuspec index 208c14ef..226992aa 100644 --- a/Oqtane.Package/Oqtane.Updater.nuspec +++ b/Oqtane.Package/Oqtane.Updater.nuspec @@ -13,6 +13,7 @@ MIT https://github.com/oqtane/oqtane.framework https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1 + readme.md icon.png oqtane diff --git a/Oqtane.Package/readme.md b/Oqtane.Package/readme.md new file mode 100644 index 00000000..189ef49a --- /dev/null +++ b/Oqtane.Package/readme.md @@ -0,0 +1,9 @@ +# Oqtane Framework + +![Oqtane](https://github.com/oqtane/framework/blob/master/oqtane.png?raw=true "Oqtane") + +Oqtane is an open source CMS and Application Framework that provides advanced functionality for developing web, mobile, and desktop applications on .NET. It leverages Blazor to compose a fully dynamic digital experience which can be hosted on Static Blazor, Blazor Server, Blazor WebAssembly, or Blazor Hybrid (via .NET MAUI). + +Oqtane was created by [Shaun Walker](https://www.linkedin.com/in/shaunbrucewalker/) and was inspired by his earlier efforts with DotNetNuke... however Oqtane is a native Blazor application written from the ground up using modern .NET Core technology and a Single Page Application (SPA) architecture. It is a modular application framework offering a fully dynamic page compositing model, multi-site support, designer friendly themes, and extensibility via third party modules. + +More information about Oqtane can be found at: [https://www.oqtane.org](https://www.oqtane.org) From 592255284f6fa2d6e4e45e773099907c1ed6b3e4 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 26 Aug 2024 09:38:10 -0400 Subject: [PATCH 04/61] fix #4562 - module template issue caused by gitignore --- .gitignore | 8 ++++-- .../External/Client/Startup/ClientStartup.cs | 14 ++++++++++ .../External/Server/Startup/ServerStartup.cs | 28 +++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 Oqtane.Server/wwwroot/Modules/Templates/External/Client/Startup/ClientStartup.cs create mode 100644 Oqtane.Server/wwwroot/Modules/Templates/External/Server/Startup/ServerStartup.cs diff --git a/.gitignore b/.gitignore index bd2d7057..69863764 100644 --- a/.gitignore +++ b/.gitignore @@ -22,10 +22,14 @@ Oqtane.Server/Packages Oqtane.Server/wwwroot/Content Oqtane.Server/wwwroot/Packages/*.log -Oqtane.Server/wwwroot/Modules +Oqtane.Server/wwwroot/Modules/* !Oqtane.Server/wwwroot/Modules/Oqtane.Modules.* !Oqtane.Server/wwwroot/Modules/Templates +Oqtane.Server/wwwroot/Modules/Templates/* +!Oqtane.Server/wwwroot/Modules/Templates/External -Oqtane.Server/wwwroot/Themes +Oqtane.Server/wwwroot/Themes/* !Oqtane.Server/wwwroot/Themes/Oqtane.Themes.* !Oqtane.Server/wwwroot/Themes/Templates +Oqtane.Server/wwwroot/Themes/Templates/* +Oqtane.Server/wwwroot/Themes/Templates/External diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Startup/ClientStartup.cs b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Startup/ClientStartup.cs new file mode 100644 index 00000000..611b5a8e --- /dev/null +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Startup/ClientStartup.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.DependencyInjection; +using Oqtane.Services; +using [Owner].Module.[Module].Services; + +namespace [Owner].Module.[Module].Startup +{ + public class ClientStartup : IClientStartup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + } + } +} diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/Startup/ServerStartup.cs b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/Startup/ServerStartup.cs new file mode 100644 index 00000000..a5491925 --- /dev/null +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/Startup/ServerStartup.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Oqtane.Infrastructure; +using [Owner].Module.[Module].Repository; +using [Owner].Module.[Module].Services; + +namespace [Owner].Module.[Module].Startup +{ + public class ServerStartup : IServerStartup + { + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + // not implemented + } + + public void ConfigureMvc(IMvcBuilder mvcBuilder) + { + // not implemented + } + + public void ConfigureServices(IServiceCollection services) + { + services.AddTransient(); + services.AddDbContextFactory<[Module]Context>(opt => { }, ServiceLifetime.Transient); + } + } +} From aba3110e317744eebd4f465a4ae4c8a4b47cee60 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 26 Aug 2024 10:41:22 -0400 Subject: [PATCH 05/61] fix Search theme control so that it checks if search is enabled for site, and include AllowInput parameter to control input textbox --- .../Themes/Controls/Theme/Search.razor | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/Oqtane.Client/Themes/Controls/Theme/Search.razor b/Oqtane.Client/Themes/Controls/Theme/Search.razor index 0d8ebb51..b6816a90 100644 --- a/Oqtane.Client/Themes/Controls/Theme/Search.razor +++ b/Oqtane.Client/Themes/Controls/Theme/Search.razor @@ -2,28 +2,39 @@ @using System.Net @using Microsoft.AspNetCore.Http @inherits ThemeControlBase +@inject ISettingService SettingService @inject IStringLocalizer Localizer @inject NavigationManager NavigationManager @if (_searchResultsPage != null) { - + @if (AllowInput) + { + +
+ + + +
+
+ } + else + {
- -
-
+ } } - - @code { private Page _searchResultsPage; private string _keywords = ""; @@ -32,7 +43,10 @@ public string CssClass { get; set; } [Parameter] - public string SearchResultPagePath { get; set; } = "search"; + public bool AllowInput { get; set; } = true; // setting to false will display only the search icon button - not the textbox + + [Parameter] + public string SearchResultPagePath { get; set; } = "search"; // setting to "" will disable search [CascadingParameter] HttpContext HttpContext { get; set; } @@ -42,9 +56,12 @@ protected override void OnInitialized() { - if(!string.IsNullOrEmpty(SearchResultPagePath)) + if (bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "Search_Enabled", "True"))) { - _searchResultsPage = PageState.Pages.FirstOrDefault(i => i.Path == SearchResultPagePath); + if (!string.IsNullOrEmpty(SearchResultPagePath)) + { + _searchResultsPage = PageState.Pages.FirstOrDefault(i => i.Path == SearchResultPagePath); + } } } From 197d5ca1f230fb665b2fa58dcadd57a11240b247 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 26 Aug 2024 10:47:18 -0400 Subject: [PATCH 06/61] change parameter name to AllowTextInput for clarity --- Oqtane.Client/Themes/Controls/Theme/Search.razor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Client/Themes/Controls/Theme/Search.razor b/Oqtane.Client/Themes/Controls/Theme/Search.razor index b6816a90..4b04dafe 100644 --- a/Oqtane.Client/Themes/Controls/Theme/Search.razor +++ b/Oqtane.Client/Themes/Controls/Theme/Search.razor @@ -8,7 +8,7 @@ @if (_searchResultsPage != null) { - @if (AllowInput) + @if (AllowTextInput) {
@@ -43,7 +43,7 @@ public string CssClass { get; set; } [Parameter] - public bool AllowInput { get; set; } = true; // setting to false will display only the search icon button - not the textbox + public bool AllowTextInput { get; set; } = true; // setting to false will display only the search icon button - not the textbox [Parameter] public string SearchResultPagePath { get; set; } = "search"; // setting to "" will disable search From 59bd9fdc227d1be3779fd89abe4a31ef6862f513 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 26 Aug 2024 12:04:50 -0400 Subject: [PATCH 07/61] fix #4559 - prevednt Log fields from exceeding column length --- Oqtane.Server/Repository/LogRepository.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Oqtane.Server/Repository/LogRepository.cs b/Oqtane.Server/Repository/LogRepository.cs index 313c9881..12f43a21 100644 --- a/Oqtane.Server/Repository/LogRepository.cs +++ b/Oqtane.Server/Repository/LogRepository.cs @@ -48,6 +48,12 @@ namespace Oqtane.Repository public void AddLog(Log log) { + if (log.Url.Length > 2048) log.Url = log.Url.Substring(0, 2048); + if (log.Server.Length > 200) log.Server = log.Server.Substring(0, 200); + if (log.Category.Length > 200) log.Category = log.Category.Substring(0, 200); + if (log.Feature.Length > 200) log.Feature = log.Feature.Substring(0, 200); + if (log.Function.Length > 20) log.Function = log.Function.Substring(0, 20); + if (log.Level.Length > 20) log.Level = log.Level.Substring(0, 20); using var db = _dbContextFactory.CreateDbContext(); db.Log.Add(log); db.SaveChanges(); From 46a68bd5e77a135120c45fa23aa5d35775baa8b1 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 26 Aug 2024 16:51:53 -0400 Subject: [PATCH 08/61] Update README.md --- README.md | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f64a04da..6ff53ce3 100644 --- a/README.md +++ b/README.md @@ -14,26 +14,44 @@ Oqtane is being developed based on some fundamental principles which are outline Please note that this project is owned by the .NET Foundation and is governed by the **[.NET Foundation Contributor Covenant Code of Conduct](https://dotnetfoundation.org/code-of-conduct)** -# Getting Started +# Getting Started (Version 5.x) -**Using Version 5:** +**Installing using source code from the Dev/Master branch:** - Install **[.NET 8.0.8 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**. -- Clone the Oqtane dev branch source code to your local system. +- Clone (or download) the Oqtane Master or Dev branch source code to your local system. - Open the **Oqtane.sln** solution file. -- **Important:** Rebuild the entire solution before running it. +- **Important:** Rebuild the entire solution before running it (ie. Build / Rebuild Solution). -- Make sure you specify Oqtane.Server as the Startup Project +- Make sure you specify Oqtane.Server as the Startup Project. -- Run the application. +- Run the application... an Installation Wizard screen will be displayed which will allow you to configure your preferred database and create a host user account. + +**Developing a custom module:** + +- follow the instructions for installing using source code outlined above + +- login as the host user + +- navigate to Control Panel, Admin Dashboard, Module Management + +- select Create Module + +- enter information corresponding to the module you wish to create and then select the Create button + +- make note of the Location where the code was generated and open the solution file in Visual Studio + +- Build / Rebuild Solution, ensure the Oqtane.Server is set as the Startup Project, and hit F5 to run the solution **Installing an official release:** +- A detailed set of instructions for installing Oqtane on Azure is located here: [Installing Oqtane on Azure](https://blazorhelpwebsite.com/ViewBlogPost/1) + - A detailed set of instructions for installing Oqtane on IIS is located here: [Installing Oqtane on IIS](https://www.oqtane.org/Resources/Blog/PostId/542/installing-oqtane-on-iis) - Instructions for upgrading Oqtane are located here: [Upgrading Oqtane](https://www.oqtane.org/Resources/Blog/PostId/543/upgrading-oqtane) From 3d5c44e8aa9a5fdee9882494423d38d2fcdda08b Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 26 Aug 2024 16:53:25 -0400 Subject: [PATCH 09/61] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ff53ce3..3e6553cc 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Please note that this project is owned by the .NET Foundation and is governed by - login as the host user -- navigate to Control Panel, Admin Dashboard, Module Management +- navigate to Control Panel (gear icon at top-right of page), Admin Dashboard, Module Management - select Create Module From 5a38b6614dd3ed198f0eac40d33922125447a3b3 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 26 Aug 2024 16:54:45 -0400 Subject: [PATCH 10/61] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e6553cc..9cd4337e 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ Backlog (TBD) ➡️ Full list and older versions can be found in the [docs roadmap](https://docs.oqtane.org/guides/roadmap/index.html) # Background -Oqtane was created by [Shaun Walker](https://www.linkedin.com/in/shaunbrucewalker/) and is inspired by the DotNetNuke web application framework. Initially created as a proof of concept, Oqtane is a native Blazor application written from the ground up using modern .NET Core technology and a Single Page Application (SPA) architecture. It is a modular application framework offering a fully dynamic page compositing model, multi-site support, designer friendly themes, and extensibility via third party modules. +Oqtane was created by [Shaun Walker](https://www.linkedin.com/in/shaunbrucewalker/) and is inspired by the DotNetNuke web application framework. Oqtane is a native Blazor application written from the ground up using modern .NET Core technology and a Single Page Application (SPA) architecture. It is a modular application framework offering a fully dynamic page compositing model, multi-site support, designer friendly themes, and extensibility via third party modules. # Reference Implementations From a56ef6f39832426690ccdec72e9c643f5e8e1652 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 26 Aug 2024 16:55:30 -0400 Subject: [PATCH 11/61] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9cd4337e..8bc3385f 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ The following diagram visualizes the client and server components in the Oqtane # Databases -As of version 2.1, Oqtane supports multiple relational database providers. +As of version 2.1 (June 2021) Oqtane supports multiple relational database providers. ![Databases](https://github.com/oqtane/framework/blob/dev/screenshots/databases.png?raw=true "Oqtane Databases") From 3d6d7b7cb94928212cc945dd33914027906caf4c Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 26 Aug 2024 16:56:08 -0400 Subject: [PATCH 12/61] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8bc3385f..c35fd69d 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ The following diagram visualizes the client and server components in the Oqtane # Databases -As of version 2.1 (June 2021) Oqtane supports multiple relational database providers. +As of version 2.1 (June 2021) Oqtane supports multiple relational database providers - SQL Server, SQLite, MySQL, PostgreSQL ![Databases](https://github.com/oqtane/framework/blob/dev/screenshots/databases.png?raw=true "Oqtane Databases") From cde08c64cee48fe25973a29fbb09c1498acf1cee Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 26 Aug 2024 17:00:07 -0400 Subject: [PATCH 13/61] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c35fd69d..89c79d8b 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,8 @@ Please note that this project is owned by the .NET Foundation and is governed by **Installing an official release:** +- all official releases of Oqtane are distributed on [GitHub](https://github.com/oqtane/oqtane.framework/releases). Releases include an Install.zip package for new installations and an Upgrade.zip for existing installations. + - A detailed set of instructions for installing Oqtane on Azure is located here: [Installing Oqtane on Azure](https://blazorhelpwebsite.com/ViewBlogPost/1) - A detailed set of instructions for installing Oqtane on IIS is located here: [Installing Oqtane on IIS](https://www.oqtane.org/Resources/Blog/PostId/542/installing-oqtane-on-iis) From ca19d8a842ae34badf2e6b1849ae38c73dd344ef Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 27 Aug 2024 10:22:41 -0400 Subject: [PATCH 14/61] allow progress indicator to be displayed in search results --- Oqtane.Client/Modules/Admin/SearchResults/Index.razor | 1 + 1 file changed, 1 insertion(+) diff --git a/Oqtane.Client/Modules/Admin/SearchResults/Index.razor b/Oqtane.Client/Modules/Admin/SearchResults/Index.razor index e69a0f63..80de5e11 100644 --- a/Oqtane.Client/Modules/Admin/SearchResults/Index.razor +++ b/Oqtane.Client/Modules/Admin/SearchResults/Index.razor @@ -7,6 +7,7 @@ @inject ISettingService SettingService @inject IStringLocalizer Localizer @inject IStringLocalizer SharedLocalizer +@attribute [StreamRendering] // attribute allows the progress indicator to be displayed
From e096af320f0a790955ab9780245b7f17b3648b43 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 27 Aug 2024 12:16:36 -0400 Subject: [PATCH 15/61] provide better support for AllowTextInput on Search component --- .../Themes/Controls/Theme/Search.razor | 29 +++++++------------ Oqtane.Server/wwwroot/css/app.css | 19 ++++++++++-- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/Oqtane.Client/Themes/Controls/Theme/Search.razor b/Oqtane.Client/Themes/Controls/Theme/Search.razor index 4b04dafe..26210b31 100644 --- a/Oqtane.Client/Themes/Controls/Theme/Search.razor +++ b/Oqtane.Client/Themes/Controls/Theme/Search.razor @@ -8,34 +8,26 @@ @if (_searchResultsPage != null) { - @if (AllowTextInput) - { - - - + + + + @if (AllowTextInput) + { - - - - } - else - { -
- -
- } +
} @code { + private string _defaultCssClass; private Page _searchResultsPage; private string _keywords = ""; @@ -43,7 +35,7 @@ public string CssClass { get; set; } [Parameter] - public bool AllowTextInput { get; set; } = true; // setting to false will display only the search icon button - not the textbox + public bool AllowTextInput { get; set; } = true; // setting to false will display only the search icon button - not the textbox [Parameter] public string SearchResultPagePath { get; set; } = "search"; // setting to "" will disable search @@ -58,6 +50,7 @@ { if (bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "Search_Enabled", "True"))) { + _defaultCssClass = (AllowTextInput) ? "app-search" : "app-search-noinput"; if (!string.IsNullOrEmpty(SearchResultPagePath)) { _searchResultsPage = PageState.Pages.FirstOrDefault(i => i.Path == SearchResultPagePath); diff --git a/Oqtane.Server/wwwroot/css/app.css b/Oqtane.Server/wwwroot/css/app.css index a55abaeb..b7143eae 100644 --- a/Oqtane.Server/wwwroot/css/app.css +++ b/Oqtane.Server/wwwroot/css/app.css @@ -235,20 +235,33 @@ app { .app-form-inline { display: inline; } -.app-search{ + +.app-search { display: inline-block; position: relative; } -.app-search input + button{ +.app-search input + button { background: none; border: none; position: absolute; right: 0; top: 0; } -.app-search input + button .oi{ +.app-search input + button .oi { top: 0; } +.app-search-noinput { + display: inline-block; + position: relative; +} +.app-search-noinput button { + background: none; + border: none; + color: var(--bs-heading-color); +} +.app-search-noinput button:hover { + color: var(--bs-heading-color); +} /* Text Editor */ .text-area-editor > textarea { From 6cd7ca755e06154420c2b89de11583ed0c6c50ef Mon Sep 17 00:00:00 2001 From: Leigh Pointer Date: Wed, 28 Aug 2024 10:57:42 +0200 Subject: [PATCH 16/61] Fix for Deleted Modules showing in AddExisting & CopyExisting dropdown #4572 --- .../Themes/Controls/Theme/ControlPanelInteractive.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Client/Themes/Controls/Theme/ControlPanelInteractive.razor b/Oqtane.Client/Themes/Controls/Theme/ControlPanelInteractive.razor index 730e7dd8..524f264b 100644 --- a/Oqtane.Client/Themes/Controls/Theme/ControlPanelInteractive.razor +++ b/Oqtane.Client/Themes/Controls/Theme/ControlPanelInteractive.razor @@ -331,7 +331,7 @@ if (_pageId != "-") { _modules = await ModuleService.GetModulesAsync(PageState.Page.SiteId); - _modules = _modules.Where(module => module.PageId == int.Parse(_pageId) && + _modules = _modules.Where(module => module.PageId == int.Parse(_pageId) && module.IsDeleted == false && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList) && (_moduleType == "add" || module.ModuleDefinition.IsPortable)) .ToList(); From 7917cc3eb510455e06c8691ae78e71ba67f85024 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 28 Aug 2024 21:48:40 +0800 Subject: [PATCH 17/61] display the upgrade progress. --- Oqtane.Server/wwwroot/app_offline.bak | 26 ++++--- Oqtane.Updater/Program.cs | 98 ++++++++++++++++++--------- 2 files changed, 82 insertions(+), 42 deletions(-) diff --git a/Oqtane.Server/wwwroot/app_offline.bak b/Oqtane.Server/wwwroot/app_offline.bak index 6f558c45..401e1f3a 100644 --- a/Oqtane.Server/wwwroot/app_offline.bak +++ b/Oqtane.Server/wwwroot/app_offline.bak @@ -5,24 +5,30 @@ Upgrade Framework - + + - +


Please Wait... Upgrade In Progress...

(this process can take a few minutes... please be patient)

- -
+
+
+
+
+
+ [STATUS] +
+
- - + \ No newline at end of file diff --git a/Oqtane.Updater/Program.cs b/Oqtane.Updater/Program.cs index 22c0b4d9..267b9c60 100644 --- a/Oqtane.Updater/Program.cs +++ b/Oqtane.Updater/Program.cs @@ -31,10 +31,6 @@ namespace Oqtane.Updater if (Directory.Exists(deployfolder)) { - string log = "Upgrade Process Started: " + DateTime.UtcNow.ToString() + Environment.NewLine; - log += "ContentRootPath: " + contentrootfolder + Environment.NewLine; - log += "WebRootPath: " + webrootfolder + Environment.NewLine; - string packagename = ""; string[] packages = Directory.GetFiles(deployfolder, "Oqtane.Framework.*.Upgrade.zip"); if (packages.Length > 0) @@ -42,15 +38,27 @@ namespace Oqtane.Updater packagename = packages[packages.Length - 1]; // use highest version } + // create upgrade log file + var logFilePath = Path.Combine(deployfolder, $"{Path.GetFileNameWithoutExtension(packagename)}.log"); + if (File.Exists(logFilePath)) + { + File.Delete(logFilePath); + } + + WriteLog(logFilePath, "Upgrade Process Started: " + DateTime.UtcNow.ToString() + Environment.NewLine); + WriteLog(logFilePath, "ContentRootPath: " + contentrootfolder + Environment.NewLine); + WriteLog(logFilePath, "WebRootPath: " + webrootfolder + Environment.NewLine); if (packagename != "" && File.Exists(Path.Combine(webrootfolder, "app_offline.bak"))) { - log += "Located Upgrade Package: " + packagename + Environment.NewLine; + WriteLog(logFilePath, "Located Upgrade Package: " + packagename + Environment.NewLine); - log += "Stopping Application Using: " + Path.Combine(contentrootfolder, "app_offline.htm") + Environment.NewLine; - File.Copy(Path.Combine(webrootfolder, "app_offline.bak"), Path.Combine(contentrootfolder, "app_offline.htm"), true); + WriteLog(logFilePath, "Stopping Application Using: " + Path.Combine(contentrootfolder, "app_offline.htm") + Environment.NewLine); + var offlineTemplate = File.ReadAllText(Path.Combine(webrootfolder, "app_offline.bak")); + var offlineFilePath = Path.Combine(contentrootfolder, "app_offline.htm"); // get list of files in package with local paths - log += "Retrieving List Of Files From Upgrade Package..." + Environment.NewLine; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 5, "Retrieving List Of Files From Upgrade Package"); + WriteLog(logFilePath, "Retrieving List Of Files From Upgrade Package..." + Environment.NewLine); List files = new List(); using (ZipArchive archive = ZipFile.OpenRead(packagename)) { @@ -59,15 +67,18 @@ namespace Oqtane.Updater if (!string.IsNullOrEmpty(entry.Name)) { files.Add(Path.Combine(contentrootfolder, entry.FullName)); + WriteLog(logFilePath, "Check File: " + entry.FullName + Environment.NewLine); } } } + bool success = true; // ensure files are not locked if (CanAccessFiles(files)) { - log += "Preparing Backup Folder: " + backupfolder + Environment.NewLine; - bool success = true; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 10, "Preparing Backup Folder"); + WriteLog(logFilePath, "Preparing Backup Folder: " + backupfolder + Environment.NewLine); + try { // clear out backup folder @@ -79,14 +90,16 @@ namespace Oqtane.Updater } catch (Exception ex) { - log += "Error Creating Backup Folder: " + ex.Message + Environment.NewLine; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 95, "Error Creating Backup Folder", "bg-danger"); + WriteLog(logFilePath, "Error Creating Backup Folder: " + ex.Message + Environment.NewLine); success = false; } // backup files if (success) { - log += "Backing Up Files..." + Environment.NewLine; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 15, "Backing Up Files"); + WriteLog(logFilePath, "Backing Up Files..." + Environment.NewLine); foreach (string file in files) { string filename = Path.Combine(backupfolder, file.Replace(contentrootfolder + Path.DirectorySeparatorChar, "")); @@ -99,12 +112,15 @@ namespace Oqtane.Updater Directory.CreateDirectory(Path.GetDirectoryName(filename)); } File.Copy(file, filename); + WriteLog(logFilePath, "Copy File: " + filename + Environment.NewLine); } } catch (Exception ex) { - log += "Error Backing Up Files: " + ex.Message + Environment.NewLine; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 95, "Error Backing Up Files", "bg-danger"); + WriteLog(logFilePath, "Error Backing Up Files: " + ex.Message + Environment.NewLine); success = false; + break; } } } @@ -112,7 +128,8 @@ namespace Oqtane.Updater // extract files if (success) { - log += "Extracting Files From Upgrade Package..." + Environment.NewLine; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 50, "Extracting Files From Upgrade Package"); + WriteLog(logFilePath, "Extracting Files From Upgrade Package..." + Environment.NewLine); try { using (ZipArchive archive = ZipFile.OpenRead(packagename)) @@ -127,6 +144,7 @@ namespace Oqtane.Updater Directory.CreateDirectory(Path.GetDirectoryName(filename)); } entry.ExtractToFile(filename, true); + WriteLog(logFilePath, "Exact File: " + filename + Environment.NewLine); } } } @@ -134,12 +152,14 @@ namespace Oqtane.Updater catch (Exception ex) { success = false; - log += "Error Extracting Files From Upgrade Package: " + ex.Message + Environment.NewLine; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 95, "Error Extracting Files From Upgrade Package", "bg-danger"); + WriteLog(logFilePath, "Error Extracting Files From Upgrade Package: " + ex.Message + Environment.NewLine); } if (success) { - log += "Removing Backup Folder..." + Environment.NewLine; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 90, "Removing Backup Folder"); + WriteLog(logFilePath, "Removing Backup Folder..." + Environment.NewLine); try { // clean up backup @@ -149,12 +169,14 @@ namespace Oqtane.Updater } catch (Exception ex) { - log += "Error Removing Backup Folder: " + ex.Message + Environment.NewLine; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 95, "Error Extracting Files From Upgrade Package", "bg-warning"); + WriteLog(logFilePath, "Error Removing Backup Folder: " + ex.Message + Environment.NewLine); } } else { - log += "Restoring Files From Backup Folder..." + Environment.NewLine; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 50, "Upgrade Failed, Restoring Files From Backup Folder", "bg-warning"); + WriteLog(logFilePath, "Restoring Files From Backup Folder..." + Environment.NewLine); try { // restore on failure @@ -165,6 +187,7 @@ namespace Oqtane.Updater if (File.Exists(filename)) { File.Copy(filename, file); + WriteLog(logFilePath, "Restore File: " + filename + Environment.NewLine); } } // clean up backup @@ -172,41 +195,38 @@ namespace Oqtane.Updater } catch (Exception ex) { - log += "Error Restoring Files From Backup Folder: " + ex.Message + Environment.NewLine; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 95, "Error Restoring Files From Backup Folder", "bg-danger"); + WriteLog(logFilePath, "Error Restoring Files From Backup Folder: " + ex.Message + Environment.NewLine); } } } else { - log += "Upgrade Failed: Could Not Backup Files" + Environment.NewLine; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 95, "Upgrade Failed: Could Not Backup Files", "bg-danger"); + WriteLog(logFilePath, "Upgrade Failed: Could Not Backup Files" + Environment.NewLine); } } else { - log += "Upgrade Failed: Some Files Are Locked By The Hosting Environment" + Environment.NewLine; + UpdateOfflineContent(offlineFilePath, offlineTemplate, 95, "Upgrade Failed: Some Files Are Locked By The Hosting Environment", "bg-danger"); + WriteLog(logFilePath, "Upgrade Failed: Some Files Are Locked By The Hosting Environment" + Environment.NewLine); } + UpdateOfflineContent(offlineFilePath, offlineTemplate, 100, "Upgrade Process Finished, Reloading", success ? "" : "bg-danger"); + Thread.Sleep(3000); //wait for 3 seconds to complete the upgrade process. // bring the app back online if (File.Exists(Path.Combine(contentrootfolder, "app_offline.htm"))) { - log += "Restarting Application By Removing: " + Path.Combine(contentrootfolder, "app_offline.htm") + Environment.NewLine; + WriteLog(logFilePath, "Restarting Application By Removing: " + Path.Combine(contentrootfolder, "app_offline.htm") + Environment.NewLine); File.Delete(Path.Combine(contentrootfolder, "app_offline.htm")); } } else { - log += "Framework Upgrade Package Not Found Or " + Path.Combine(webrootfolder, "app_offline.bak") + " Does Not Exist" + Environment.NewLine; + WriteLog(logFilePath, "Framework Upgrade Package Not Found Or " + Path.Combine(webrootfolder, "app_offline.bak") + " Does Not Exist" + Environment.NewLine); } - log += "Upgrade Process Ended: " + DateTime.UtcNow.ToString() + Environment.NewLine; - - // create upgrade log file - string logfile = Path.Combine(deployfolder, Path.GetFileNameWithoutExtension(packagename) + ".log"); - if (File.Exists(logfile)) - { - File.Delete(logfile); - } - File.WriteAllText(logfile, log); + WriteLog(logFilePath, "Upgrade Process Ended: " + DateTime.UtcNow.ToString() + Environment.NewLine); } else { @@ -269,5 +289,19 @@ namespace Oqtane.Updater } return canAccess; } + + private static void UpdateOfflineContent(string filePath, string contentTemplate, int progress, string status, string progressClass = "") + { + var content = contentTemplate + .Replace("[PROGRESS]", progress.ToString()) + .Replace("[PROGRESSCLASS]", progressClass) + .Replace("[STATUS]", status); + File.WriteAllText(filePath, content); + } + + private static void WriteLog(string logFilePath, string logContent) + { + File.AppendAllText(logFilePath, $"[{DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff")}] {logContent}"); + } } } From d4f71d50261c02da23a1956b8ed563dcb67652a1 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 28 Aug 2024 21:21:41 -0400 Subject: [PATCH 18/61] improve developer experience for Url helper methods --- Oqtane.Client/Modules/ModuleBase.cs | 73 +++++++++++++++++++++++++---- Oqtane.Client/Themes/ThemeBase.cs | 63 ++++++++++++++++++++++--- Oqtane.Shared/Shared/Utilities.cs | 7 +-- 3 files changed, 124 insertions(+), 19 deletions(-) diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 1731e22d..4a794630 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -134,6 +134,7 @@ namespace Oqtane.Modules // url methods + // navigate url public string NavigateUrl() { return NavigateUrl(PageState.Page.Path); @@ -149,24 +150,65 @@ namespace Oqtane.Modules return NavigateUrl(PageState.Page.Path, refresh); } - public string NavigateUrl(string path, string parameters) + public string NavigateUrl(string path, string querystring) { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); + return Utilities.NavigateUrl(PageState.Alias.Path, path, querystring); + } + + public string NavigateUrl(string path, Dictionary querystring) + { + return NavigateUrl(path, Utilities.CreateQueryString(querystring)); } public string NavigateUrl(string path, bool refresh) { - return Utilities.NavigateUrl(PageState.Alias.Path, path, refresh ? "refresh" : ""); + return NavigateUrl(path, refresh ? "refresh" : ""); } + public string NavigateUrl(int moduleId, string action) + { + return EditUrl(PageState.Page.Path, moduleId, action, ""); + } + + public string NavigateUrl(int moduleId, string action, string querystring) + { + return EditUrl(PageState.Page.Path, moduleId, action, querystring); + } + + public string NavigateUrl(int moduleId, string action, Dictionary querystring) + { + return EditUrl(PageState.Page.Path, moduleId, action, querystring); + } + + public string NavigateUrl(string path, int moduleId, string action) + { + return EditUrl(path, moduleId, action, ""); + } + + public string NavigateUrl(string path, int moduleId, string action, string querystring) + { + return EditUrl(path, moduleId, action, querystring); + } + + public string NavigateUrl(string path, int moduleId, string action, Dictionary querystring) + { + return EditUrl(path, moduleId, action, querystring); + } + + // edit url public string EditUrl(string action) { return EditUrl(ModuleState.ModuleId, action); } - public string EditUrl(string action, string parameters) + public string EditUrl(string action, string querystring) { - return EditUrl(ModuleState.ModuleId, action, parameters); + return EditUrl(ModuleState.ModuleId, action, querystring); + } + + public string EditUrl(string action, Dictionary querystring) + { + return EditUrl(ModuleState.ModuleId, action, querystring); } public string EditUrl(int moduleId, string action) @@ -174,16 +216,27 @@ namespace Oqtane.Modules return EditUrl(moduleId, action, ""); } - public string EditUrl(int moduleId, string action, string parameters) + public string EditUrl(int moduleId, string action, string querystring) { - return EditUrl(PageState.Page.Path, moduleId, action, parameters); + return EditUrl(PageState.Page.Path, moduleId, action, querystring); } - public string EditUrl(string path, int moduleid, string action, string parameters) + public string EditUrl(int moduleId, string action, Dictionary querystring) { - return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters); + return EditUrl(PageState.Page.Path, moduleId, action, querystring); } + public string EditUrl(string path, int moduleid, string action, string querystring) + { + return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, querystring); + } + + public string EditUrl(string path, int moduleid, string action, Dictionary querystring) + { + return EditUrl(path, moduleid, action, Utilities.CreateQueryString(querystring)); + } + + // file url public string FileUrl(string folderpath, string filename) { return FileUrl(folderpath, filename, false); @@ -203,6 +256,8 @@ namespace Oqtane.Modules return Utilities.FileUrl(PageState.Alias, fileid, download); } + // image url + public string ImageUrl(int fileid, int width, int height) { return ImageUrl(fileid, width, height, ""); diff --git a/Oqtane.Client/Themes/ThemeBase.cs b/Oqtane.Client/Themes/ThemeBase.cs index 68da1a70..d6f9789e 100644 --- a/Oqtane.Client/Themes/ThemeBase.cs +++ b/Oqtane.Client/Themes/ThemeBase.cs @@ -93,6 +93,7 @@ namespace Oqtane.Themes // url methods + // navigate url public string NavigateUrl() { return NavigateUrl(PageState.Page.Path); @@ -108,31 +109,78 @@ namespace Oqtane.Themes return NavigateUrl(PageState.Page.Path, refresh); } + public string NavigateUrl(string path, string querystring) + { + return Utilities.NavigateUrl(PageState.Alias.Path, path, querystring); + } + + public string NavigateUrl(string path, Dictionary querystring) + { + return NavigateUrl(path, Utilities.CreateQueryString(querystring)); + } + public string NavigateUrl(string path, bool refresh) { - return Utilities.NavigateUrl(PageState.Alias.Path, path, refresh ? "refresh" : ""); + return NavigateUrl(path, refresh ? "refresh" : ""); } - public string NavigateUrl(string path, string parameters) + public string NavigateUrl(int moduleid, string action) { - return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); + return EditUrl(moduleid, action, ""); } + public string NavigateUrl(int moduleid, string action, string querystring) + { + return EditUrl(PageState.Page.Path, moduleid, action, querystring); + } + + public string NavigateUrl(int moduleid, string action, Dictionary querystring) + { + return EditUrl(PageState.Page.Path, moduleid, action, Utilities.CreateQueryString(querystring)); + } + + public string NavigateUrl(string path, int moduleId, string action) + { + return EditUrl(path, moduleId, action, ""); + } + + public string NavigateUrl(string path, int moduleid, string action, string querystring) + { + return EditUrl(path, moduleid, action, querystring); + } + + public string NavigateUrl(string path, int moduleid, string action, Dictionary querystring) + { + return EditUrl(path, moduleid, action, querystring); + } + + // edit url public string EditUrl(int moduleid, string action) { return EditUrl(moduleid, action, ""); } - public string EditUrl(int moduleid, string action, string parameters) + public string EditUrl(int moduleid, string action, string querystring) { - return EditUrl(PageState.Page.Path, moduleid, action, parameters); + return EditUrl(PageState.Page.Path, moduleid, action, querystring); } - public string EditUrl(string path, int moduleid, string action, string parameters) + public string EditUrl(int moduleid, string action, Dictionary querystring) { - return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters); + return EditUrl(PageState.Page.Path, moduleid, action, querystring); } + public string EditUrl(string path, int moduleid, string action, string querystring) + { + return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, querystring); + } + + public string EditUrl(string path, int moduleid, string action, Dictionary querystring) + { + return EditUrl(path, moduleid, action, Utilities.CreateQueryString(querystring)); + } + + // file url public string FileUrl(string folderpath, string filename) { return FileUrl(folderpath, filename, false); @@ -152,6 +200,7 @@ namespace Oqtane.Themes return Utilities.FileUrl(PageState.Alias, fileid, download); } + // image url public string ImageUrl(int fileid, int width, int height) { return ImageUrl(fileid, width, height, ""); diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 408e889c..6e8f3933 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -23,12 +23,13 @@ namespace Oqtane.Shared public static (string UrlParameters, string Querystring, string Fragment) ParseParameters(string parameters) { - // /urlparameters /urlparameters?Id=1 /urlparameters#5 /urlparameters?Id=1#5 /urlparameters?reload#5 - // Id=1 Id=1#5 reload#5 reload + // /urlparameters /urlparameters?id=1 /urlparameters#5 /urlparameters?id=1#5 /urlparameters?reload#5 + // ?id=1 ?id=1#5 ?reload#5 ?reload + // id=1 id=1#5 reload#5 reload // #5 // create absolute url to convert to Uri - parameters = (!parameters.StartsWith("/") && !parameters.StartsWith("#") ? "?" : "") + parameters; + parameters = (!parameters.StartsWith("/") && !parameters.StartsWith("#") && !parameters.StartsWith("?") ? "?" : "") + parameters; parameters = Constants.PackageRegistryUrl + parameters; var uri = new Uri(parameters); var querystring = uri.Query.Replace("?", ""); From 229aed306ecbaf1e4bce4824e4833a785a2afb46 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 29 Aug 2024 19:41:27 +0800 Subject: [PATCH 19/61] update bootstrap reference. --- Oqtane.Client/Installer/Installer.razor | 4 ++-- Oqtane.Client/Themes/BlazorTheme/Themes/Default.razor | 4 +--- Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs | 4 +--- Oqtane.Server/wwwroot/app_offline.bak | 2 +- Oqtane.Shared/Shared/Constants.cs | 5 +++++ Oqtane.Updater.sln | 8 +++++++- Oqtane.Updater/Oqtane.Updater.csproj | 4 ++++ Oqtane.Updater/Program.cs | 3 +++ 8 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Oqtane.Client/Installer/Installer.razor b/Oqtane.Client/Installer/Installer.razor index 2ce996de..94f9cc39 100644 --- a/Oqtane.Client/Installer/Installer.razor +++ b/Oqtane.Client/Installer/Installer.razor @@ -162,7 +162,7 @@ protected override async Task OnInitializedAsync() { // include CSS - var content = ""; + var content = $""; SiteState.AppendHeadContent(content); _togglePassword = SharedLocalizer["ShowPassword"]; @@ -217,7 +217,7 @@ { // include JavaScript var interop = new Interop(JSRuntime); - await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js", "sha512-VK2zcvntEufaimc+efOYi622VN5ZacdnufnmX7zIhCPmjhKnOi9ZDMtg1/ug5l183f19gG1/cBstPO4D8N/Img==", "anonymous", "", "head"); + await interop.IncludeScript("", Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous", "", "head"); } } diff --git a/Oqtane.Client/Themes/BlazorTheme/Themes/Default.razor b/Oqtane.Client/Themes/BlazorTheme/Themes/Default.razor index 8d682ac9..ef853a79 100644 --- a/Oqtane.Client/Themes/BlazorTheme/Themes/Default.razor +++ b/Oqtane.Client/Themes/BlazorTheme/Themes/Default.razor @@ -41,9 +41,7 @@ Integrity = "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==", CrossOrigin = "anonymous" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" }, - new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js", - Integrity = "sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg==", - CrossOrigin = "anonymous", Location = ResourceLocation.Body }, + new Resource { ResourceType = ResourceType.Script, Url = Constants.BootstrapScriptUrl, Integrity = Constants.BootstrapScriptIntegrity, CrossOrigin = "anonymous", Location = ResourceLocation.Body }, }; } diff --git a/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs b/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs index a533d45d..bb528190 100644 --- a/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs +++ b/Oqtane.Client/Themes/OqtaneTheme/ThemeInfo.cs @@ -21,9 +21,7 @@ namespace Oqtane.Themes.OqtaneTheme Integrity = "sha512-M+Wrv9LTvQe81gFD2ZE3xxPTN5V2n1iLCXsldIxXvfs6tP+6VihBCwCMBkkjkQUZVmEHBsowb9Vqsq1et1teEg==", CrossOrigin = "anonymous" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" }, - new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js", - Integrity = "sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg==", - CrossOrigin = "anonymous", Location = ResourceLocation.Body }, + new Resource { ResourceType = ResourceType.Script, Url = Constants.BootstrapScriptUrl, Integrity = Constants.BootstrapScriptIntegrity, CrossOrigin = "anonymous", Location = ResourceLocation.Body } } }; } diff --git a/Oqtane.Server/wwwroot/app_offline.bak b/Oqtane.Server/wwwroot/app_offline.bak index 401e1f3a..4fcd2b88 100644 --- a/Oqtane.Server/wwwroot/app_offline.bak +++ b/Oqtane.Server/wwwroot/app_offline.bak @@ -5,7 +5,7 @@ Upgrade Framework - + diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 9ab5428b..2cf05683 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -83,6 +83,11 @@ namespace Oqtane.Shared public static readonly string[] InternalPagePaths = { "login", "register", "reset", "404" }; public const string DefaultTextEditor = "Oqtane.Modules.Controls.QuillJSTextEditor, Oqtane.Client"; + public const string BootstrapScriptUrl = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js"; + public const string BootstrapScriptIntegrity = "sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg=="; + public const string BootstrapStylesheetUrl = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css"; + public const string BootstrapStylesheetIntegrity = "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg=="; + // Obsolete constants const string RoleObsoleteMessage = "Use the corresponding member from Oqtane.Shared.RoleNames"; diff --git a/Oqtane.Updater.sln b/Oqtane.Updater.sln index ee633e20..f79c8fed 100644 --- a/Oqtane.Updater.sln +++ b/Oqtane.Updater.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28822.285 @@ -12,6 +12,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Updater", "Oqtane.Updater\Oqtane.Updater.csproj", "{2E8C6889-37CF-4C8D-88B1-505547F25098}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Shared", "Oqtane.Shared\Oqtane.Shared.csproj", "{E2512C17-291F-460A-A6D1-741C301DA184}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -22,6 +24,10 @@ Global {2E8C6889-37CF-4C8D-88B1-505547F25098}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E8C6889-37CF-4C8D-88B1-505547F25098}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E8C6889-37CF-4C8D-88B1-505547F25098}.Release|Any CPU.Build.0 = Release|Any CPU + {E2512C17-291F-460A-A6D1-741C301DA184}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2512C17-291F-460A-A6D1-741C301DA184}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2512C17-291F-460A-A6D1-741C301DA184}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2512C17-291F-460A-A6D1-741C301DA184}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Oqtane.Updater/Oqtane.Updater.csproj b/Oqtane.Updater/Oqtane.Updater.csproj index 7144958d..a9b3eb82 100644 --- a/Oqtane.Updater/Oqtane.Updater.csproj +++ b/Oqtane.Updater/Oqtane.Updater.csproj @@ -18,4 +18,8 @@ false + + + + diff --git a/Oqtane.Updater/Program.cs b/Oqtane.Updater/Program.cs index 267b9c60..cd8f8359 100644 --- a/Oqtane.Updater/Program.cs +++ b/Oqtane.Updater/Program.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Threading; +using Oqtane.Shared; namespace Oqtane.Updater { @@ -293,6 +294,8 @@ namespace Oqtane.Updater private static void UpdateOfflineContent(string filePath, string contentTemplate, int progress, string status, string progressClass = "") { var content = contentTemplate + .Replace("[BOOTSTRAPCSSURL]", Constants.BootstrapStylesheetUrl) + .Replace("[BOOTSTRAPCSSINTEGRITY]", Constants.BootstrapStylesheetIntegrity) .Replace("[PROGRESS]", progress.ToString()) .Replace("[PROGRESSCLASS]", progressClass) .Replace("[STATUS]", status); From 9620c5a98ffce684dd4452c17ed24a3939db946f Mon Sep 17 00:00:00 2001 From: sbwalker Date: Thu, 29 Aug 2024 17:53:11 -0400 Subject: [PATCH 20/61] fix issue adding existing user to a new site --- Oqtane.Client/Modules/Admin/Users/Add.razor | 91 +++++-------------- .../Resources/Modules/Admin/Users/Add.resx | 23 +---- Oqtane.Server/Controllers/UserController.cs | 2 + Oqtane.Server/Managers/UserManager.cs | 15 ++- 4 files changed, 35 insertions(+), 96 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor index 805155cb..537a3bac 100644 --- a/Oqtane.Client/Modules/Admin/Users/Add.razor +++ b/Oqtane.Client/Modules/Admin/Users/Add.razor @@ -14,7 +14,6 @@ @if (profiles != null) { -
@@ -22,24 +21,6 @@
-
- -
-
- - -
-
-
-
- -
-
- - -
-
-
@@ -123,12 +104,7 @@ @code { private bool _initialized = false; - private string _passwordrequirements; private string _username = string.Empty; - private string _password = string.Empty; - private string _passwordtype = "password"; - private string _togglepassword = string.Empty; - private string _confirm = string.Empty; private string _email = string.Empty; private string _displayname = string.Empty; private string _notify = "True"; @@ -142,8 +118,6 @@ { try { - _passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId); - _togglepassword = SharedLocalizer["ShowPassword"]; profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); settings = new Dictionary(); _initialized = true; @@ -169,39 +143,32 @@ { try { - if (_username != string.Empty && _password != string.Empty && _confirm != string.Empty && _email != string.Empty) + if (_username != string.Empty && _email != string.Empty) { - if (_password == _confirm) + if (ValidateProfiles()) { - if (ValidateProfiles()) + var user = new User(); + user.SiteId = PageState.Site.SiteId; + user.Username = _username; + user.Password = ""; // will be auto generated + user.Email = _email; + user.DisplayName = string.IsNullOrWhiteSpace(_displayname) ? _username : _displayname; + user.PhotoFileId = null; + user.SuppressNotification = !bool.Parse(_notify); + + user = await UserService.AddUserAsync(user); + + if (user != null) { - var user = new User(); - user.SiteId = PageState.Site.SiteId; - user.Username = _username; - user.Password = _password; - user.Email = _email; - user.DisplayName = string.IsNullOrWhiteSpace(_displayname) ? _username : _displayname; - user.PhotoFileId = null; - user.SuppressNotification = !bool.Parse(_notify); - - user = await UserService.AddUserAsync(user); - - if (user != null) - { - await SettingService.UpdateUserSettingsAsync(settings, user.UserId); - await logger.LogInformation("User Created {User}", user); - NavigationManager.NavigateTo(NavigateUrl()); - } - else - { - await logger.LogError("Error Adding User {Username} {Email}", _username, _email); - AddModuleMessage(Localizer["Error.User.AddCheckPass"], MessageType.Error); - } + await SettingService.UpdateUserSettingsAsync(settings, user.UserId); + await logger.LogInformation("User Created {User}", user); + NavigationManager.NavigateTo(NavigateUrl()); + } + else + { + await logger.LogError("Error Adding User {Username} {Email}", _username, _email); + AddModuleMessage(Localizer["Error.User.AddCheckPass"], MessageType.Error); } - } - else - { - AddModuleMessage(Localizer["Message.Password.NoMatch"], MessageType.Warning); } } else @@ -252,18 +219,4 @@ var value = (string)e.Value; settings = SettingService.SetSetting(settings, SettingName, value); } - - private void TogglePassword() - { - if (_passwordtype == "password") - { - _passwordtype = "text"; - _togglepassword = SharedLocalizer["HidePassword"]; - } - else - { - _passwordtype = "password"; - _togglepassword = SharedLocalizer["ShowPassword"]; - } - } } diff --git a/Oqtane.Client/Resources/Modules/Admin/Users/Add.resx b/Oqtane.Client/Resources/Modules/Admin/Users/Add.resx index 1ad645d9..b25b2477 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Users/Add.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Users/Add.resx @@ -117,12 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Error Adding User. Please Ensure Password Meets Complexity Requirements And Username And Email Is Not Already In Use. - - - Passwords Entered Do Not Match - Error Adding User @@ -133,17 +127,11 @@ Identity - You Must Provide A Username, Password, Email Address And All Required Profile Information + You Must Provide A Username, Email Address And All Required Profile Information Username Already Exists - - Please enter the password again to confirm it matches with the value above - - - Confirm Password: - The full name of the user @@ -156,21 +144,12 @@ Email: - - The user's password. Please choose a password which is sufficiently secure. - - - Password: - A unique username for a user. Note that this field can not be modified once it is saved. Username: - - Password - Indicate if new users should receive an email notification diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 549e00e3..fcf6e1ea 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -147,11 +147,13 @@ namespace Oqtane.Controllers if (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin)) { user.EmailConfirmed = true; + user.IsAuthenticated = true; allowregistration = true; } else { user.EmailConfirmed = false; + user.IsAuthenticated = false; allowregistration = _sites.GetSite(user.SiteId).AllowRegistration; } diff --git a/Oqtane.Server/Managers/UserManager.cs b/Oqtane.Server/Managers/UserManager.cs index 5f234c0e..53fa1f22 100644 --- a/Oqtane.Server/Managers/UserManager.cs +++ b/Oqtane.Server/Managers/UserManager.cs @@ -12,6 +12,7 @@ using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Models; using Oqtane.Repository; +using Oqtane.Security; using Oqtane.Shared; namespace Oqtane.Managers @@ -145,13 +146,17 @@ namespace Oqtane.Managers } else { - var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false); - succeeded = result.Succeeded; - if (!succeeded) + succeeded = true; + if (!user.IsAuthenticated) { - errors = "Password Not Valid For User"; + var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false); + succeeded = result.Succeeded; + if (!succeeded) + { + errors = "Password Not Valid For User"; + } + user.EmailConfirmed = succeeded; } - user.EmailConfirmed = succeeded; } if (succeeded) From 8b1f95c7435b78ef74b450e51b9a913b17de84e2 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 2 Sep 2024 11:08:25 -0400 Subject: [PATCH 21/61] remove reference to HttpContext as it is not used --- Oqtane.Client/Themes/Controls/Theme/Search.razor | 4 ---- Oqtane.Server/Managers/UserManager.cs | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Oqtane.Client/Themes/Controls/Theme/Search.razor b/Oqtane.Client/Themes/Controls/Theme/Search.razor index 26210b31..19eec4cc 100644 --- a/Oqtane.Client/Themes/Controls/Theme/Search.razor +++ b/Oqtane.Client/Themes/Controls/Theme/Search.razor @@ -1,6 +1,5 @@ @namespace Oqtane.Themes.Controls @using System.Net -@using Microsoft.AspNetCore.Http @inherits ThemeControlBase @inject ISettingService SettingService @inject IStringLocalizer Localizer @@ -40,9 +39,6 @@ [Parameter] public string SearchResultPagePath { get; set; } = "search"; // setting to "" will disable search - [CascadingParameter] - HttpContext HttpContext { get; set; } - [SupplyParameterFromForm(FormName = "SearchForm")] public string KeyWords { get => ""; set => _keywords = value; } diff --git a/Oqtane.Server/Managers/UserManager.cs b/Oqtane.Server/Managers/UserManager.cs index 53fa1f22..c0b2d711 100644 --- a/Oqtane.Server/Managers/UserManager.cs +++ b/Oqtane.Server/Managers/UserManager.cs @@ -474,6 +474,7 @@ namespace Oqtane.Managers IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); if (identityuser != null && !string.IsNullOrEmpty(token)) { + // note that ResetPasswordAsync checks password complexity rules var result = await _identityUserManager.ResetPasswordAsync(identityuser, token, user.Password); if (result.Succeeded) { From 473c265bac9ce7dedee59e2fa4a58c6593099e1c Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 2 Sep 2024 12:09:06 -0400 Subject: [PATCH 22/61] prepare for 5.2.2 --- Oqtane.Client/Oqtane.Client.csproj | 4 ++-- Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj | 4 ++-- .../Oqtane.Database.PostgreSQL.csproj | 4 ++-- Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj | 4 ++-- Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj | 4 ++-- Oqtane.Maui/Oqtane.Maui.csproj | 6 +++--- Oqtane.Package/Oqtane.Client.nuspec | 4 ++-- Oqtane.Package/Oqtane.Framework.nuspec | 6 +++--- Oqtane.Package/Oqtane.Server.nuspec | 4 ++-- Oqtane.Package/Oqtane.Shared.nuspec | 4 ++-- Oqtane.Package/Oqtane.Updater.nuspec | 4 ++-- Oqtane.Package/install.ps1 | 2 +- Oqtane.Package/upgrade.ps1 | 2 +- Oqtane.Server/Oqtane.Server.csproj | 4 ++-- Oqtane.Shared/Oqtane.Shared.csproj | 4 ++-- Oqtane.Shared/Shared/Constants.cs | 4 ++-- Oqtane.Updater/Oqtane.Updater.csproj | 4 ++-- 17 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index fd9c693b..b542bf7a 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -4,7 +4,7 @@ net8.0 Exe Debug;Release - 5.2.1 + 5.2.2 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.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 https://github.com/oqtane/oqtane.framework Git Oqtane diff --git a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj index 44bf4e03..ff755381 100644 --- a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj +++ b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj @@ -2,7 +2,7 @@ net8.0 - 5.2.0 + 5.2.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1 https://github.com/oqtane/oqtane.framework Git true diff --git a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj index 980e7582..18619910 100644 --- a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj +++ b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj @@ -2,7 +2,7 @@ net8.0 - 5.2.1 + 5.2.2 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.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 https://github.com/oqtane/oqtane.framework Git true diff --git a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj index 835bcee7..7fdfae00 100644 --- a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj +++ b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj @@ -2,7 +2,7 @@ net8.0 - 5.2.1 + 5.2.2 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.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 https://github.com/oqtane/oqtane.framework Git true diff --git a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj index 6caeb51a..1fe344e3 100644 --- a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj +++ b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj @@ -2,7 +2,7 @@ net8.0 - 5.2.1 + 5.2.2 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.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 https://github.com/oqtane/oqtane.framework Git true diff --git a/Oqtane.Maui/Oqtane.Maui.csproj b/Oqtane.Maui/Oqtane.Maui.csproj index 571517dd..f6b66171 100644 --- a/Oqtane.Maui/Oqtane.Maui.csproj +++ b/Oqtane.Maui/Oqtane.Maui.csproj @@ -6,7 +6,7 @@ Exe - 5.2.1 + 5.2.2 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.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 https://github.com/oqtane/oqtane.framework Git Oqtane.Maui @@ -31,7 +31,7 @@ 0E29FC31-1B83-48ED-B6E0-9F3C67B775D4 - 5.2.1 + 5.2.2 1 14.2 diff --git a/Oqtane.Package/Oqtane.Client.nuspec b/Oqtane.Package/Oqtane.Client.nuspec index 236c4118..1d9e0234 100644 --- a/Oqtane.Package/Oqtane.Client.nuspec +++ b/Oqtane.Package/Oqtane.Client.nuspec @@ -2,7 +2,7 @@ Oqtane.Client - 5.2.1 + 5.2.2 Shaun Walker .NET Foundation Oqtane Framework @@ -12,7 +12,7 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 readme.md icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec index 06512810..898ed6a2 100644 --- a/Oqtane.Package/Oqtane.Framework.nuspec +++ b/Oqtane.Package/Oqtane.Framework.nuspec @@ -2,7 +2,7 @@ Oqtane.Framework - 5.2.1 + 5.2.2 Shaun Walker .NET Foundation Oqtane Framework @@ -11,8 +11,8 @@ .NET Foundation false MIT - https://github.com/oqtane/oqtane.framework/releases/download/v5.2.1/Oqtane.Framework.5.2.1.Upgrade.zip - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1 + https://github.com/oqtane/oqtane.framework/releases/download/v5.2.2/Oqtane.Framework.5.2.2.Upgrade.zip + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 readme.md icon.png oqtane framework diff --git a/Oqtane.Package/Oqtane.Server.nuspec b/Oqtane.Package/Oqtane.Server.nuspec index 126e4f15..702ab506 100644 --- a/Oqtane.Package/Oqtane.Server.nuspec +++ b/Oqtane.Package/Oqtane.Server.nuspec @@ -2,7 +2,7 @@ Oqtane.Server - 5.2.1 + 5.2.2 Shaun Walker .NET Foundation Oqtane Framework @@ -12,7 +12,7 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 readme.md icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Shared.nuspec b/Oqtane.Package/Oqtane.Shared.nuspec index 293e85c6..78fa19dd 100644 --- a/Oqtane.Package/Oqtane.Shared.nuspec +++ b/Oqtane.Package/Oqtane.Shared.nuspec @@ -2,7 +2,7 @@ Oqtane.Shared - 5.2.1 + 5.2.2 Shaun Walker .NET Foundation Oqtane Framework @@ -12,7 +12,7 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 readme.md icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Updater.nuspec b/Oqtane.Package/Oqtane.Updater.nuspec index 226992aa..44a4196d 100644 --- a/Oqtane.Package/Oqtane.Updater.nuspec +++ b/Oqtane.Package/Oqtane.Updater.nuspec @@ -2,7 +2,7 @@ Oqtane.Updater - 5.2.1 + 5.2.2 Shaun Walker .NET Foundation Oqtane Framework @@ -12,7 +12,7 @@ false MIT https://github.com/oqtane/oqtane.framework - https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 readme.md icon.png oqtane diff --git a/Oqtane.Package/install.ps1 b/Oqtane.Package/install.ps1 index 4db2aa1e..b6230b38 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.1.Install.zip" -Force +Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.2.2.Install.zip" -Force diff --git a/Oqtane.Package/upgrade.ps1 b/Oqtane.Package/upgrade.ps1 index 5c8713b6..b2f11245 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.1.Upgrade.zip" -Force +Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.2.2.Upgrade.zip" -Force diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index fd2b6731..12a52f9b 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -3,7 +3,7 @@ net8.0 Debug;Release - 5.2.1 + 5.2.2 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.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 https://github.com/oqtane/oqtane.framework Git Oqtane diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index bf8779d0..9e5ae9d8 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -3,7 +3,7 @@ net8.0 Debug;Release - 5.2.1 + 5.2.2 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.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 https://github.com/oqtane/oqtane.framework Git Oqtane diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 9ab5428b..6fefc885 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.1"; - 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"; + public static readonly string Version = "5.2.2"; + 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"; public const string PackageId = "Oqtane.Framework"; public const string ClientId = "Oqtane.Client"; public const string UpdaterPackageId = "Oqtane.Updater"; diff --git a/Oqtane.Updater/Oqtane.Updater.csproj b/Oqtane.Updater/Oqtane.Updater.csproj index 7144958d..e2399e99 100644 --- a/Oqtane.Updater/Oqtane.Updater.csproj +++ b/Oqtane.Updater/Oqtane.Updater.csproj @@ -3,7 +3,7 @@ net8.0 Exe - 5.2.1 + 5.2.2 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.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 https://github.com/oqtane/oqtane.framework Git Oqtane From 744dbeb7a38bcc3af044abca850c40532aaa7fbc Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 4 Sep 2024 17:43:20 -0700 Subject: [PATCH 23/61] Prepare for 5.2.2 --- Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj index ff755381..f14ef03f 100644 --- a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj +++ b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj @@ -2,7 +2,7 @@ net8.0 - 5.2.1 + 5.2.2 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.1 + https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.2 https://github.com/oqtane/oqtane.framework Git true From 351ba22d640c0292bbbe221940dbbeb763e3b4ea Mon Sep 17 00:00:00 2001 From: David Montesinos <90258222+mdmontesinos@users.noreply.github.com> Date: Fri, 6 Sep 2024 16:35:44 +0200 Subject: [PATCH 24/61] Fix IconOnly not working --- Oqtane.Client/Modules/Controls/ActionDialog.razor | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor index 6fc51e15..b39b4f27 100644 --- a/Oqtane.Client/Modules/Controls/ActionDialog.razor +++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor @@ -173,6 +173,12 @@ else _editmode = bool.Parse(EditMode); } + Text = Localize(nameof(Text), Text); + Header = Localize(nameof(Header), Header); + Message = Localize(nameof(Message), Message); + + _openText = Text; + if (!string.IsNullOrEmpty(IconName)) { if (IconOnly) @@ -190,12 +196,7 @@ else _openIconSpan = $"{(IconOnly ? "" : " ")}"; _iconSpan = $" "; } - - Text = Localize(nameof(Text), Text); - Header = Localize(nameof(Header), Header); - Message = Localize(nameof(Message), Message); - - _openText = Text; + _permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList; _authorized = IsAuthorized(); From 9ea7dc8b3c23af64fdae5f068f930b424cc47485 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Fri, 6 Sep 2024 11:57:15 -0400 Subject: [PATCH 25/61] reverting #4507 --- Oqtane.Client/Modules/Controls/PermissionGrid.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Client/Modules/Controls/PermissionGrid.razor b/Oqtane.Client/Modules/Controls/PermissionGrid.razor index 99379903..6bd700bd 100644 --- a/Oqtane.Client/Modules/Controls/PermissionGrid.razor +++ b/Oqtane.Client/Modules/Controls/PermissionGrid.razor @@ -111,7 +111,7 @@ [Parameter] public List PermissionList { get; set; } - protected override async Task OnParametersSetAsync() + protected override async Task OnInitializedAsync() { if (!string.IsNullOrEmpty(Permissions)) { From 1cf36e2156f08ec7419c7bd918a5afd7c02cb237 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Sun, 8 Sep 2024 11:43:36 -0400 Subject: [PATCH 26/61] fix #4575 - add support for DateOnly and TimeOnly columns in migrations --- .../EntityBuilders/BaseEntityBuilder.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Oqtane.Server/Migrations/EntityBuilders/BaseEntityBuilder.cs b/Oqtane.Server/Migrations/EntityBuilders/BaseEntityBuilder.cs index b97a3d60..8f2cb20a 100644 --- a/Oqtane.Server/Migrations/EntityBuilders/BaseEntityBuilder.cs +++ b/Oqtane.Server/Migrations/EntityBuilders/BaseEntityBuilder.cs @@ -127,6 +127,46 @@ namespace Oqtane.Migrations.EntityBuilders return table.Column(name: RewriteName(name), nullable: nullable, defaultValue: defaultValue); } + public void AddDateOnlyColumn(string name, bool nullable = false) + { + _migrationBuilder.AddColumn(RewriteName(name), RewriteName(EntityTableName), nullable: nullable, schema: Schema); + } + + public void AddDateOnlyColumn(string name, bool nullable, DateOnly defaultValue) + { + _migrationBuilder.AddColumn(RewriteName(name), RewriteName(EntityTableName), nullable: nullable, defaultValue: defaultValue, schema: Schema); + } + + protected OperationBuilder AddDateOnlyColumn(ColumnsBuilder table, string name, bool nullable = false) + { + return table.Column(name: RewriteName(name), nullable: nullable); + } + + protected OperationBuilder AddDateOnlyColumn(ColumnsBuilder table, string name, bool nullable, DateOnly defaultValue) + { + return table.Column(name: RewriteName(name), nullable: nullable, defaultValue: defaultValue); + } + + public void AddTimeOnlyColumn(string name, bool nullable = false) + { + _migrationBuilder.AddColumn(RewriteName(name), RewriteName(EntityTableName), nullable: nullable, schema: Schema); + } + + public void AddTimeOnlyColumn(string name, bool nullable, TimeOnly defaultValue) + { + _migrationBuilder.AddColumn(RewriteName(name), RewriteName(EntityTableName), nullable: nullable, defaultValue: defaultValue, schema: Schema); + } + + protected OperationBuilder AddTimeOnlyColumn(ColumnsBuilder table, string name, bool nullable = false) + { + return table.Column(name: RewriteName(name), nullable: nullable); + } + + protected OperationBuilder AddTimeOnlyColumn(ColumnsBuilder table, string name, bool nullable, TimeOnly defaultValue) + { + return table.Column(name: RewriteName(name), nullable: nullable, defaultValue: defaultValue); + } + public void AddByteColumn(string name, bool nullable = false) { _migrationBuilder.AddColumn(RewriteName(name), RewriteName(EntityTableName), nullable: nullable, schema: Schema); From bbe85def23475fc7c97af8212408bcb9245fedf4 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 9 Sep 2024 09:17:27 -0400 Subject: [PATCH 27/61] fix #4503 - ensure all state is initialized before rendering --- .../Modules/Admin/Modules/Settings.razor | 218 +++++++++--------- 1 file changed, 112 insertions(+), 106 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index ffe021d9..e1183a82 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -9,129 +9,133 @@ @inject IStringLocalizer Localizer @inject IStringLocalizer SharedLocalizer -
- - - @if (_containers != null) - { -
-
- -
- +@if (_initialized) +{ + + + + @if (_containers != null) + { +
+
+ +
+ +
-
-
- -
- +
+ +
+ +
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- + @foreach (string pane in PageState.Page.Panes) { - foreach (Page p in _pages) + + } + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + +
-
- } - - - @if (_permissions != null) - { -
-
- + } + + + @if (_permissions != null) + { +
+
+ +
-
+ } + + @if (_moduleSettingsType != null) + { + + @ModuleSettingsComponent + } - - @if (_moduleSettingsType != null) - { - - @ModuleSettingsComponent - - } - @if (_containerSettingsType != null) - { - - @ContainerSettingsComponent - - } - -
- - @SharedLocalizer["Cancel"] -
-
- - + @if (_containerSettingsType != null) + { + + @ContainerSettingsComponent + + } + +
+ + @SharedLocalizer["Cancel"] +
+
+ + +} @code { public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; + private bool _initialized = false; private ElementReference form; private bool validated = false; private List _containers = new List(); @@ -231,6 +235,8 @@ }; } } + + _initialized = true; } private async Task SaveModule() From 3c0c5aed6059bd983d0506e298fa6f614958dc94 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Wed, 11 Sep 2024 02:09:02 +0300 Subject: [PATCH 28/61] Add null operator in ModuleState.ModuleDefinition --- Oqtane.Client/Modules/Admin/Modules/Settings.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index e1183a82..a4d7950b 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -167,7 +167,7 @@ { SetModuleTitle(Localizer["ModuleSettings.Title"]); - _module = ModuleState.ModuleDefinition.Name; + _module = ModuleState.ModuleDefinition?.Name; _title = ModuleState.Title; _moduleSettingsTitle = Localizer["ModuleSettings.Heading"]; _pane = ModuleState.Pane; From 044cee30a5bc449af683648b30a6451ee650370f Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 11 Sep 2024 17:21:12 -0400 Subject: [PATCH 29/61] fix #4600 - filter user settings in API layer --- Oqtane.Server/Controllers/UserController.cs | 65 ++++++++++++++------- Oqtane.Server/Managers/UserManager.cs | 4 +- 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index fcf6e1ea..b17efbc8 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -28,9 +28,10 @@ namespace Oqtane.Controllers private readonly IUserPermissions _userPermissions; private readonly IJwtManager _jwtManager; private readonly IFileRepository _files; + private readonly ISettingRepository _settings; private readonly ILogManager _logger; - public UserController(IUserRepository users, ITenantManager tenantManager, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, IFileRepository files, ILogManager logger) + public UserController(IUserRepository users, ITenantManager tenantManager, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, IFileRepository files, ISettingRepository settings, ILogManager logger) { _users = users; _tenantManager = tenantManager; @@ -39,6 +40,7 @@ namespace Oqtane.Controllers _userPermissions = userPermissions; _jwtManager = jwtManager; _files = files; + _settings = settings; _logger = logger; } @@ -110,31 +112,54 @@ namespace Oqtane.Controllers private User Filter(User user) { + // clone object to avoid mutating cache + User filtered = null; + if (user != null) { - user.Password = ""; - user.IsAuthenticated = false; - user.TwoFactorCode = ""; - user.TwoFactorExpiry = null; + filtered = new User(); - if (!_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) && User.Identity.Name?.ToLower() != user.Username.ToLower()) + // public properties + filtered.UserId = user.UserId; + filtered.Username = user.Username; + filtered.DisplayName = user.DisplayName; + filtered.Password = ""; + filtered.TwoFactorCode = ""; + + // include private properties if authenticated user is accessing their own user account os is an administrator + if (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) || _userPermissions.GetUser(User).UserId == user.UserId) { - user.Email = ""; - user.PhotoFileId = null; - user.LastLoginOn = DateTime.MinValue; - user.LastIPAddress = ""; - user.Roles = ""; - user.CreatedBy = ""; - user.CreatedOn = DateTime.MinValue; - user.ModifiedBy = ""; - user.ModifiedOn = DateTime.MinValue; - user.DeletedBy = ""; - user.DeletedOn = DateTime.MinValue; - user.IsDeleted = false; - user.TwoFactorRequired = false; + filtered.Email = user.Email; + filtered.PhotoFileId = user.PhotoFileId; + filtered.LastLoginOn = user.LastLoginOn; + filtered.LastIPAddress = user.LastIPAddress; + filtered.TwoFactorRequired = false; + filtered.Roles = user.Roles; + filtered.CreatedBy = user.CreatedBy; + filtered.CreatedOn = user.CreatedOn; + filtered.ModifiedBy = user.ModifiedBy; + filtered.ModifiedOn = user.ModifiedOn; + filtered.DeletedBy = user.DeletedBy; + filtered.DeletedOn = user.DeletedOn; + filtered.IsDeleted = user.IsDeleted; + } + + // if authenticated user is accessing their own user account + if (_userPermissions.GetUser(User).UserId == user.UserId) + { + // include all settings + filtered.Settings = user.Settings; + } + else + { + // include only public settings + filtered.Settings = _settings.GetSettings(EntityNames.User, user.UserId) + .Where(item => !item.IsPrivate) + .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); } } - return user; + + return filtered; } // POST api/ diff --git a/Oqtane.Server/Managers/UserManager.cs b/Oqtane.Server/Managers/UserManager.cs index c0b2d711..6de9c9d3 100644 --- a/Oqtane.Server/Managers/UserManager.cs +++ b/Oqtane.Server/Managers/UserManager.cs @@ -12,7 +12,6 @@ using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Models; using Oqtane.Repository; -using Oqtane.Security; using Oqtane.Shared; namespace Oqtane.Managers @@ -65,8 +64,7 @@ namespace Oqtane.Managers { user.SiteId = siteid; user.Roles = GetUserRoles(user.UserId, user.SiteId); - List settings = _settings.GetSettings(EntityNames.User, user.UserId).ToList(); - user.Settings = settings.Where(item => !item.IsPrivate || user.UserId == user.UserId) + user.Settings = _settings.GetSettings(EntityNames.User, user.UserId) .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); } return user; From 69bc06685fe0df5c7dc5f442070280eee7932673 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Thu, 12 Sep 2024 14:04:35 -0400 Subject: [PATCH 30/61] fix #4598 - user experience improvements for file upload --- .../Modules/Controls/FileManager.razor | 2 +- Oqtane.Server/Controllers/FileController.cs | 8 ++- Oqtane.Server/wwwroot/js/interop.js | 70 ++++++++++++------- 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor index b7c603ff..f75d8f29 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -387,7 +387,7 @@ var size = Int64.Parse(uploads[upload].Split(':')[1]); // bytes var megabits = (size / 1048576.0) * 8; // binary conversion - var uploadspeed = 2; // 2 Mbps (3G ranges from 300Kbps to 3Mbps) + var uploadspeed = (PageState.Alias.Name.Contains("localhost")) ? 100 : 3; // 3 Mbps is FCC minimum for broadband upload var uploadtime = (megabits / uploadspeed); // seconds var maxattempts = 5; // polling (minimum timeout duration will be 5 seconds) var sleep = (int)Math.Ceiling(uploadtime / maxattempts) * 1000; // milliseconds diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 030005f5..5aa8a66e 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -425,11 +425,11 @@ namespace Oqtane.Controllers // POST api//upload [EnableCors(Constants.MauiCorsPolicy)] [HttpPost("upload")] - public async Task UploadFile(string folder, IFormFile formfile) + public async Task UploadFile(string folder, IFormFile formfile) { if (formfile == null || formfile.Length <= 0) { - return; + return NoContent(); } // ensure filename is valid @@ -437,7 +437,7 @@ namespace Oqtane.Controllers if (!formfile.FileName.IsPathOrFileValid() || !formfile.FileName.Contains(token) || !HasValidFileExtension(formfile.FileName.Substring(0, formfile.FileName.IndexOf(token)))) { _logger.Log(LogLevel.Error, this, LogFunction.Security, "File Name Is Invalid Or Contains Invalid Extension {File}", formfile.FileName); - return; + return NoContent(); } string folderPath = ""; @@ -492,6 +492,8 @@ namespace Oqtane.Controllers _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Upload Attempt {Folder} {File}", folder, formfile.FileName); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } + + return NoContent(); } private async Task MergeFile(string folder, string filename) diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index b457ad1d..07a761b8 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -293,41 +293,49 @@ Oqtane.Interop = { }, uploadFiles: function (posturl, folder, id, antiforgerytoken, jwt) { var fileinput = document.getElementById('FileInput_' + id); - var files = fileinput.files; var progressinfo = document.getElementById('ProgressInfo_' + id); var progressbar = document.getElementById('ProgressBar_' + id); if (progressinfo !== null && progressbar !== null) { progressinfo.setAttribute("style", "display: inline;"); + progressinfo.innerHTML = ''; progressbar.setAttribute("style", "width: 100%; display: inline;"); + progressbar.value = 0; } + var files = fileinput.files; + var totalSize = 0; for (var i = 0; i < files.length; i++) { - var FileChunk = []; - var file = files[i]; - var MaxFileSizeMB = 1; - var BufferChunkSize = MaxFileSizeMB * (1024 * 1024); - var FileStreamPos = 0; - var EndPos = BufferChunkSize; - var Size = file.size; + totalSize = totalSize + files[i].size; + } - while (FileStreamPos < Size) { - FileChunk.push(file.slice(FileStreamPos, EndPos)); - FileStreamPos = EndPos; - EndPos = FileStreamPos + BufferChunkSize; + var maxChunkSizeMB = 1; + var bufferChunkSize = maxChunkSizeMB * (1024 * 1024); + var uploadedSize = 0; + + for (var i = 0; i < files.length; i++) { + var fileChunk = []; + var file = files[i]; + var fileStreamPos = 0; + var endPos = bufferChunkSize; + + while (fileStreamPos < file.size) { + fileChunk.push(file.slice(fileStreamPos, endPos)); + fileStreamPos = endPos; + endPos = fileStreamPos + bufferChunkSize; } - var TotalParts = FileChunk.length; - var PartCount = 0; + var totalParts = fileChunk.length; + var partCount = 0; - while (Chunk = FileChunk.shift()) { - PartCount++; - var FileName = file.name + ".part_" + PartCount.toString().padStart(3, '0') + "_" + TotalParts.toString().padStart(3, '0'); + while (chunk = fileChunk.shift()) { + partCount++; + var fileName = file.name + ".part_" + partCount.toString().padStart(3, '0') + "_" + totalParts.toString().padStart(3, '0'); var data = new FormData(); data.append('__RequestVerificationToken', antiforgerytoken); data.append('folder', folder); - data.append('formfile', Chunk, FileName); + data.append('formfile', chunk, fileName); var request = new XMLHttpRequest(); request.open('POST', posturl, true); if (jwt !== "") { @@ -335,28 +343,36 @@ Oqtane.Interop = { request.withCredentials = true; } request.upload.onloadstart = function (e) { - if (progressinfo !== null && progressbar !== null) { - progressinfo.innerHTML = file.name + ' 0%'; - progressbar.value = 0; + if (progressinfo !== null && progressbar !== null && progressinfo.innerHTML === '') { + if (files.length === 1) { + progressinfo.innerHTML = file.name; + } + else { + progressinfo.innerHTML = file.name + ", ..."; + } } }; request.upload.onprogress = function (e) { if (progressinfo !== null && progressbar !== null) { - var percent = Math.ceil((e.loaded / e.total) * 100); - progressinfo.innerHTML = file.name + '[' + PartCount + '] ' + percent + '%'; + var percent = Math.ceil(((uploadedSize + e.loaded) / totalSize) * 100); progressbar.value = (percent / 100); } }; request.upload.onloadend = function (e) { if (progressinfo !== null && progressbar !== null) { - progressinfo.innerHTML = file.name + ' 100%'; - progressbar.value = 1; + uploadedSize = uploadedSize + e.total; + var percent = Math.ceil((uploadedSize / totalSize) * 100); + progressbar.value = (percent / 100); } }; request.upload.onerror = function() { if (progressinfo !== null && progressbar !== null) { - progressinfo.innerHTML = file.name + ' Error: ' + request.statusText; - progressbar.value = 0; + if (files.length === 1) { + progressinfo.innerHTML = file.name + ' Error: ' + request.statusText; + } + else { + progressinfo.innerHTML = ' Error: ' + request.statusText; + } } }; request.send(data); From d9466fe4bcb3ae2d6dd77226b85888c552efff13 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Fri, 13 Sep 2024 14:15:34 +0300 Subject: [PATCH 31/61] Fix issue in toggle edit mode --- Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor b/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor index 9c195681..a58e82b8 100644 --- a/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor @@ -147,8 +147,7 @@ { if (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)) { - PageState.EditMode = true; - NavigationManager.NavigateTo(NavigateUrl(page.Path, "edit=" + ((PageState.EditMode) ? "true" : "false"))); + NavigationManager.NavigateTo(NavigateUrl(page.Path, "edit=" + PageState.EditMode.ToString())); } } } From caa2073d48639d0afc6c323c9a66b1df9aa6611d Mon Sep 17 00:00:00 2001 From: sbwalker Date: Fri, 13 Sep 2024 07:34:57 -0400 Subject: [PATCH 32/61] improve support for external login roles --- Oqtane.Client/Modules/Admin/Users/Index.razor | 29 +++++++++-- .../Resources/Modules/Admin/Users/Index.resx | 16 ++++++- ...taneSiteAuthenticationBuilderExtensions.cs | 48 +++++++++++++++++-- 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor index a04be4c3..fa493c36 100644 --- a/Oqtane.Client/Modules/Admin/Users/Index.razor +++ b/Oqtane.Client/Modules/Admin/Users/Index.razor @@ -333,12 +333,29 @@ else
- +
-
+
+ +
+ +
+
+
+ +
+
+ +
+
+
+
@@ -457,6 +474,8 @@ else private string _nameclaimtype; private string _emailclaimtype; private string _roleclaimtype; + private string _roleclaimmappings; + private string _synchronizeroles; private string _profileclaimtypes; private string _domainfilter; private string _createusers; @@ -521,6 +540,8 @@ else _nameclaimtype = SettingService.GetSetting(settings, "ExternalLogin:NameClaimType", "name"); _emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email"); _roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", ""); + _roleclaimmappings = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimMappings", ""); + _synchronizeroles = SettingService.GetSetting(settings, "ExternalLogin:SynchronizeRoles", "false"); _profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", ""); _domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", ""); _createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true"); @@ -614,7 +635,9 @@ else settings = SettingService.SetSetting(settings, "ExternalLogin:NameClaimType", _nameclaimtype, true); settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true); settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimMappings", _roleclaimmappings, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:SynchronizeRoles", _synchronizeroles, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true); settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true); settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true); settings = SettingService.SetSetting(settings, "ExternalLogin:VerifyUsers", _verifyusers, true); diff --git a/Oqtane.Client/Resources/Modules/Admin/Users/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Users/Index.resx index 6baae9c1..46c19999 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Users/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Users/Index.resx @@ -385,10 +385,22 @@ Parameters: - Optionally provide the type name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site. + Optionally provide the type name of the roles claim provided by the identity provider (the standard default is 'roles'). If role names from the identity provider do not exactly match your site role names, please use the Role Claim Mappings. - Role Claim: + Roles Claim: + + + Optionally provide a comma delimited list of role names provided by the identity provider, as well as mappings to your site roles. For example if the identity provider includes an 'Admin' role name and you want it to map to the 'Administrators' site role you should specify 'Admin:Administrators'. + + + Role Claim Mappings: + + + This option will add or remove role assignments so that the site roles exactly match the roles provided by the identity provider for a user + + + Synchronize Roles? Optionally provide a comma delimited list of user profile claim type names provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'. diff --git a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs index 604270c1..28a69745 100644 --- a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs @@ -20,6 +20,7 @@ using Microsoft.AspNetCore.Authentication.Cookies; using System.Net; using System.Text.Json.Nodes; using System.Globalization; +using System.Net.WebSockets; namespace Oqtane.Extensions { @@ -529,7 +530,8 @@ namespace Oqtane.Extensions { // create claims identity var _userRoles = httpContext.RequestServices.GetRequiredService(); - identity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList()); + var userRoles = _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList(); + identity = UserSecurity.CreateClaimsIdentity(alias, user, userRoles); identity.Label = ExternalLoginStatus.Success; // update user @@ -540,13 +542,49 @@ namespace Oqtane.Extensions // external roles if (!string.IsNullOrEmpty(httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", ""))) { - if (claimsPrincipal.Claims.Any(item => item.Type == ClaimTypes.Role)) + if (claimsPrincipal.Claims.Any(item => item.Type == httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", ""))) { - foreach (var claim in claimsPrincipal.Claims.Where(item => item.Type == ClaimTypes.Role)) + var _roles = httpContext.RequestServices.GetRequiredService(); + var roles = _roles.GetRoles(user.SiteId).ToList(); // global roles excluded ie. host users cannot be added/deleted + + var mappings = httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimMappings", "").Split(','); + foreach (var claim in claimsPrincipal.Claims.Where(item => item.Type == httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", ""))) { - if (!identity.Claims.Any(item => item.Type == ClaimTypes.Role && item.Value == claim.Value)) + var rolename = claim.Value; + if (mappings.Any(item => item.StartsWith(rolename + ":"))) { - identity.AddClaim(new Claim(ClaimTypes.Role, claim.Value)); + rolename = mappings.First(item => item.StartsWith(rolename + ":")).Split(':')[1]; + } + var role = roles.FirstOrDefault(item => item.Name == rolename); + if (role != null) + { + if (!userRoles.Any(item => item.RoleId == role.RoleId && item.UserId == user.UserId)) + { + var userRole = new UserRole(); + userRole.RoleId = role.RoleId; + userRole.UserId = user.UserId; + _userRoles.AddUserRole(userRole); + } + } + } + if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:SynchronizeRoles", "false"))) + { + userRoles = _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList(); + foreach (var userRole in userRoles) + { + var role = roles.FirstOrDefault(item => item.RoleId == userRole.RoleId); + if (role != null) + { + var rolename = role.Name; + if (mappings.Any(item => item.EndsWith(":" + rolename))) + { + rolename = mappings.First(item => item.EndsWith(":" + rolename)).Split(':')[0]; + } + if (!claimsPrincipal.Claims.Any(item => item.Type == httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", "") && item.Value == rolename)) + { + _userRoles.DeleteUserRole(userRole.UserRoleId); + } + } } } } From 26e4398905d2977d89c45e932b5a5181c77a18b0 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Fri, 13 Sep 2024 21:12:18 +0300 Subject: [PATCH 33/61] Address feedback --- Oqtane.Client/Modules/Admin/Modules/Settings.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index a4d7950b..d2ab823e 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -167,7 +167,6 @@ { SetModuleTitle(Localizer["ModuleSettings.Title"]); - _module = ModuleState.ModuleDefinition?.Name; _title = ModuleState.Title; _moduleSettingsTitle = Localizer["ModuleSettings.Heading"]; _pane = ModuleState.Pane; @@ -186,6 +185,7 @@ if (ModuleState.ModuleDefinition != null) { + _module = ModuleState.ModuleDefinition.Name; _permissionNames = ModuleState.ModuleDefinition?.PermissionNames; if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType)) From d196402dd0c8f741703bc4c385d1cc09df929408 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Fri, 13 Sep 2024 15:18:12 -0400 Subject: [PATCH 34/61] fix #4607 - Site HeadContent scripts being added twice --- Oqtane.Client/UI/ThemeBuilder.razor | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index a563dfd8..c02c74c8 100644 --- a/Oqtane.Client/UI/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -66,17 +66,18 @@ { if (!string.IsNullOrEmpty(content)) { - if (PageState.RenderMode == RenderModes.Interactive) + var elements = content.Split('<', StringSplitOptions.RemoveEmptyEntries); + foreach (var element in elements) { - // remove scripts - var index = content.IndexOf("= 0) + if (PageState.RenderMode == RenderModes.Static || (!element.ToLower().StartsWith("script") && !element.ToLower().StartsWith("/script"))) { - content = content.Remove(index, content.IndexOf("") + 9 - index); - index = content.IndexOf(" Date: Fri, 13 Sep 2024 16:13:01 -0400 Subject: [PATCH 35/61] fix #4606 - allow logo to show site name as fallback (credit @JanOlsmar) --- .../Themes/BlazorTheme/Themes/Default.razor | 2 +- Oqtane.Client/Themes/Controls/Theme/Logo.razor | 17 ++++++++++++++++- .../Themes/OqtaneTheme/Themes/Default.razor | 2 +- Oqtane.Server/wwwroot/css/app.css | 6 ++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Oqtane.Client/Themes/BlazorTheme/Themes/Default.razor b/Oqtane.Client/Themes/BlazorTheme/Themes/Default.razor index ef853a79..a4f651f5 100644 --- a/Oqtane.Client/Themes/BlazorTheme/Themes/Default.razor +++ b/Oqtane.Client/Themes/BlazorTheme/Themes/Default.razor @@ -9,7 +9,7 @@
diff --git a/Oqtane.Client/Themes/Controls/Theme/Logo.razor b/Oqtane.Client/Themes/Controls/Theme/Logo.razor index 594649f1..50beea35 100644 --- a/Oqtane.Client/Themes/Controls/Theme/Logo.razor +++ b/Oqtane.Client/Themes/Controls/Theme/Logo.razor @@ -8,4 +8,19 @@ @PageState.Site.Name -} \ No newline at end of file +} +else +{ + if (ShowName) + { + + } +} + +@code { + [Parameter] + public bool ShowName { get; set; } = false; +} + diff --git a/Oqtane.Client/Themes/OqtaneTheme/Themes/Default.razor b/Oqtane.Client/Themes/OqtaneTheme/Themes/Default.razor index e00dd363..d56c6f78 100644 --- a/Oqtane.Client/Themes/OqtaneTheme/Themes/Default.razor +++ b/Oqtane.Client/Themes/OqtaneTheme/Themes/Default.razor @@ -4,7 +4,7 @@