From 6fc9e60f62a9ea8e0360944f95b52da80d6c084a Mon Sep 17 00:00:00 2001 From: Ikuo Ohba Date: Fri, 2 May 2025 23:45:13 +0900 Subject: [PATCH 01/34] fixed serverside resource path --- Oqtane.Server/Oqtane.Server.csproj | 3 +++ .../wwwroot/Modules/Templates/External/Server/AssemblyInfo.cs | 4 ++++ 2 files changed, 7 insertions(+) create mode 100644 Oqtane.Server/wwwroot/Modules/Templates/External/Server/AssemblyInfo.cs diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index b6c55409..5a9c6c03 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -74,4 +74,7 @@ + + + diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/AssemblyInfo.cs b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/AssemblyInfo.cs new file mode 100644 index 00000000..7c4c9cbe --- /dev/null +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Resources; +using Microsoft.Extensions.Localization; + +[assembly: RootNamespace("[Owner].Module.[Module].Server")] From 8aa967fa1b77140d68bd021a1a8830af91e5ff7b Mon Sep 17 00:00:00 2001 From: Cody Date: Tue, 6 May 2025 13:29:26 -0700 Subject: [PATCH 02/34] Remove Unnecessary Namespaces Removes Unnecessary Namespaces 'System' and 'System.ComponentModel.DataAnnotations.Schema;'. --- Oqtane.Shared/Modules/HtmlText/Models/HtmlText.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Oqtane.Shared/Modules/HtmlText/Models/HtmlText.cs b/Oqtane.Shared/Modules/HtmlText/Models/HtmlText.cs index d4f49d37..024c266a 100644 --- a/Oqtane.Shared/Modules/HtmlText/Models/HtmlText.cs +++ b/Oqtane.Shared/Modules/HtmlText/Models/HtmlText.cs @@ -1,7 +1,5 @@ -using System; using Oqtane.Models; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; using Oqtane.Documentation; namespace Oqtane.Modules.HtmlText.Models From 0d708124c204a9e869bf34ff2d4439a23012419d Mon Sep 17 00:00:00 2001 From: Ikuo Ohba Date: Wed, 7 May 2025 08:31:34 +0900 Subject: [PATCH 03/34] Undo Oqtane.Server.csproj --- Oqtane.Server/Oqtane.Server.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 5a9c6c03..b6c55409 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -74,7 +74,4 @@ - - - From 3f5f3ef10bbde2de3fa4adb58df8589308bc0f08 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:10:58 -0700 Subject: [PATCH 04/34] Update Version to 6.1.3 --- Oqtane.Client/Oqtane.Client.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index c974f27a..9da5f4ce 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -4,7 +4,7 @@ net9.0 Exe Debug;Release - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 https://github.com/oqtane/oqtane.framework Git Oqtane From a728cd2d917f93105d31725b04ec32d0cdec3eec Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:14:12 -0700 Subject: [PATCH 05/34] Update Package Dependencies & Version to 6.1.3 --- Oqtane.Maui/Oqtane.Maui.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Oqtane.Maui/Oqtane.Maui.csproj b/Oqtane.Maui/Oqtane.Maui.csproj index eed1f14c..08bf796f 100644 --- a/Oqtane.Maui/Oqtane.Maui.csproj +++ b/Oqtane.Maui/Oqtane.Maui.csproj @@ -6,7 +6,7 @@ Exe - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 https://github.com/oqtane/oqtane.framework Git Oqtane.Maui @@ -30,7 +30,7 @@ com.oqtane.maui - 6.1.2 + 6.1.3 1 @@ -72,9 +72,9 @@ - - - + + + From 25667499e65a927016e3cffa208ecc6b820f1e29 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:16:38 -0700 Subject: [PATCH 06/34] Update Package Dependencies & Version to 6.1.3 --- Oqtane.Server/Oqtane.Server.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index b6c55409..ddf80b3d 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -3,7 +3,7 @@ net9.0 Debug;Release - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 https://github.com/oqtane/oqtane.framework Git Oqtane @@ -36,7 +36,7 @@ - + all @@ -46,7 +46,7 @@ - + From db9a40db2bbacfd888d9abf9f0236e0453f50859 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:17:17 -0700 Subject: [PATCH 07/34] Update Version to 6.1.3 --- Oqtane.Shared/Oqtane.Shared.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index c991ee17..65ae667e 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -3,7 +3,7 @@ net9.0 Debug;Release - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 https://github.com/oqtane/oqtane.framework Git Oqtane From 8efdcb9c49b014ded4e2dfa94d974673f5f225f2 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:17:54 -0700 Subject: [PATCH 08/34] Update Version to 6.1.3 --- Oqtane.Updater/Oqtane.Updater.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Updater/Oqtane.Updater.csproj b/Oqtane.Updater/Oqtane.Updater.csproj index 0efc8228..7474506c 100644 --- a/Oqtane.Updater/Oqtane.Updater.csproj +++ b/Oqtane.Updater/Oqtane.Updater.csproj @@ -3,7 +3,7 @@ net9.0 Exe - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 https://github.com/oqtane/oqtane.framework Git Oqtane From 994429f098ba9f519d7bc6d1fe08f213a4f677d4 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:18:40 -0700 Subject: [PATCH 09/34] Update Version to 6.1.3 --- Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj index c10d12b1..13ecf1ac 100644 --- a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj +++ b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj @@ -2,7 +2,7 @@ net9.0 - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 https://github.com/oqtane/oqtane.framework Git true From 5507006c531f17e653b9bd76866ca5e4fcb18f1c Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:19:05 -0700 Subject: [PATCH 10/34] Update Version to 6.1.3 --- Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj index cc1be87e..ae1e04a8 100644 --- a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj +++ b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj @@ -2,7 +2,7 @@ net9.0 - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 https://github.com/oqtane/oqtane.framework Git true From a348913888f8ca21aa22877db22892af309a8baf Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:20:15 -0700 Subject: [PATCH 11/34] Update Version to 6.1.3 --- 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 c30e49a5..e6703b08 100644 --- a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj +++ b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj @@ -2,7 +2,7 @@ net9.0 - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 https://github.com/oqtane/oqtane.framework Git true From e76e0fc3515435feb417cc4fd779fcb8468a70d3 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:20:52 -0700 Subject: [PATCH 12/34] Update Version to 6.1.3 --- Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj index 3e6f1890..ace07515 100644 --- a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj +++ b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj @@ -2,7 +2,7 @@ net9.0 - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 https://github.com/oqtane/oqtane.framework Git true From f2aa39aa8562d4933b5cafdcc2659b7f257cad9b Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:22:04 -0700 Subject: [PATCH 13/34] Update Version to 6.1.3 --- Oqtane.Package/Oqtane.Client.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Package/Oqtane.Client.nuspec b/Oqtane.Package/Oqtane.Client.nuspec index 0d0217f8..b50b816c 100644 --- a/Oqtane.Package/Oqtane.Client.nuspec +++ b/Oqtane.Package/Oqtane.Client.nuspec @@ -2,7 +2,7 @@ Oqtane.Client - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 readme.md icon.png oqtane From ad868ba841231f9f2c4944ae9ff6816c0540d926 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:22:47 -0700 Subject: [PATCH 14/34] Update Version to 6.1.3 --- Oqtane.Package/Oqtane.Framework.nuspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Oqtane.Package/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec index 0ebabcbb..a9e739dc 100644 --- a/Oqtane.Package/Oqtane.Framework.nuspec +++ b/Oqtane.Package/Oqtane.Framework.nuspec @@ -2,7 +2,7 @@ Oqtane.Framework - 6.1.2 + 6.1.3 Shaun Walker .NET Foundation Oqtane Framework @@ -11,8 +11,8 @@ .NET Foundation false MIT - https://github.com/oqtane/oqtane.framework/releases/download/v6.1.2/Oqtane.Framework.6.1.2.Upgrade.zip - https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/download/v6.1.3/Oqtane.Framework.6.1.3.Upgrade.zip + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 readme.md icon.png oqtane framework From 0ae38f8a40fc01624800575a420a975bc80fec75 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:23:11 -0700 Subject: [PATCH 15/34] Update Version to 6.1.3 --- Oqtane.Package/Oqtane.Server.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Package/Oqtane.Server.nuspec b/Oqtane.Package/Oqtane.Server.nuspec index 06dcf42c..b5e58742 100644 --- a/Oqtane.Package/Oqtane.Server.nuspec +++ b/Oqtane.Package/Oqtane.Server.nuspec @@ -2,7 +2,7 @@ Oqtane.Server - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 readme.md icon.png oqtane From ca19496b5c2bc4e2e306582becba0efc72f096d8 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:23:32 -0700 Subject: [PATCH 16/34] Update Version to 6.1.3 --- Oqtane.Package/Oqtane.Shared.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Package/Oqtane.Shared.nuspec b/Oqtane.Package/Oqtane.Shared.nuspec index a240d6a2..9c2041be 100644 --- a/Oqtane.Package/Oqtane.Shared.nuspec +++ b/Oqtane.Package/Oqtane.Shared.nuspec @@ -2,7 +2,7 @@ Oqtane.Shared - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 readme.md icon.png oqtane From fc403f920b6368f499033bdbca4bdefc0724b664 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:24:13 -0700 Subject: [PATCH 17/34] Update Version to 6.1.3 --- Oqtane.Package/Oqtane.Updater.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Package/Oqtane.Updater.nuspec b/Oqtane.Package/Oqtane.Updater.nuspec index 8ef6a84a..09453309 100644 --- a/Oqtane.Package/Oqtane.Updater.nuspec +++ b/Oqtane.Package/Oqtane.Updater.nuspec @@ -2,7 +2,7 @@ Oqtane.Updater - 6.1.2 + 6.1.3 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/v6.1.2 + https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3 readme.md icon.png oqtane From 48c6796128abaff66d8825e48520aa201e4e5b59 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:24:36 -0700 Subject: [PATCH 18/34] Update Version to 6.1.3 --- Oqtane.Package/install.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Package/install.ps1 b/Oqtane.Package/install.ps1 index 3f8d7188..fb0b6d7f 100644 --- a/Oqtane.Package/install.ps1 +++ b/Oqtane.Package/install.ps1 @@ -1 +1 @@ -Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.2.Install.zip" -Force +Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.3.Install.zip" -Force From 8ccb1a24f8dcaa0354cec3070a01794976f5da24 Mon Sep 17 00:00:00 2001 From: Cody Date: Wed, 7 May 2025 10:25:27 -0700 Subject: [PATCH 19/34] Update Version to 6.1.3 --- Oqtane.Package/upgrade.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Package/upgrade.ps1 b/Oqtane.Package/upgrade.ps1 index 9b53797a..e2f26321 100644 --- a/Oqtane.Package/upgrade.ps1 +++ b/Oqtane.Package/upgrade.ps1 @@ -1 +1 @@ -Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.2.Upgrade.zip" -Force +Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.3.Upgrade.zip" -Force From 3d9c81d8500cf77fe96ed76a66a6748cf4ee3b85 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 7 May 2025 17:12:14 -0400 Subject: [PATCH 20/34] change Synchronize button to Check For Updates to improve clarity --- .../Resources/Modules/Admin/ModuleDefinitions/Index.resx | 6 +++--- Oqtane.Client/Resources/Modules/Admin/Themes/Index.resx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Index.resx b/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Index.resx index 90ffbe18..8d8e5cac 100644 --- a/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Index.resx @@ -160,12 +160,12 @@ Enabled? - Synchronize + Check For Updates - Modules Have Been Successfully Synchronized With The Marketplace + Module Information Has Been Retrieved From The Marketplace - Error Synchronizing Modules With The Marketplace + Error Retrieving Module Information From The Marketplace \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/Themes/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Themes/Index.resx index 4edc516c..0d3b97ed 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Themes/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Themes/Index.resx @@ -160,12 +160,12 @@ Assign - Synchronize + Check For Updates - Themes Have Been Successfully Synchronized With The Marketplace + Theme Information Has Been Retrieved From The Marketplace - Error Synchronizing Themes With The Marketplace + Error Retrieving Theme Information From The Marketplace \ No newline at end of file From 60da903360db84b6accc8dd3710e41c42f588a3c Mon Sep 17 00:00:00 2001 From: sbwalker Date: Thu, 8 May 2025 16:09:00 -0400 Subject: [PATCH 21/34] update version to 6.1.3 --- Oqtane.Shared/Shared/Constants.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 6e70f5bc..a11a80cc 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 = "6.1.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,5.2.3,5.2.4,6.0.0,6.0.1,6.1.0,6.1.1,6.1.2"; + public static readonly string Version = "6.1.3"; + public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.1.1,5.1.2,5.2.0,5.2.1,5.2.2,5.2.3,5.2.4,6.0.0,6.0.1,6.1.0,6.1.1,6.1.2,6.1.3"; public const string PackageId = "Oqtane.Framework"; public const string ClientId = "Oqtane.Client"; public const string UpdaterPackageId = "Oqtane.Updater"; From 90d72489d9e020a97048a3490c02c1b17a5f6523 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 12 May 2025 08:41:57 -0400 Subject: [PATCH 22/34] fix #5287 - allow deletion of folder which contains files --- Oqtane.Client/Modules/Admin/Files/Edit.razor | 14 +++----------- .../Resources/Modules/Admin/Files/Edit.resx | 3 --- Oqtane.Server/Controllers/FolderController.cs | 12 +++++++++++- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index ec8881b5..b3099c5e 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -265,17 +265,9 @@ } if (!isparent) { - var files = await FileService.GetFilesAsync(_folderId); - if (files.Count == 0) - { - await FolderService.DeleteFolderAsync(_folderId); - await logger.LogInformation("Folder Deleted {Folder}", _folderId); - NavigationManager.NavigateTo(NavigateUrl()); - } - else - { - AddModuleMessage(Localizer["Message.Folder.Files.InvalidDelete"], MessageType.Warning); - } + await FolderService.DeleteFolderAsync(_folderId); + await logger.LogInformation("Folder Deleted {Folder}", _folderId); + NavigationManager.NavigateTo(NavigateUrl()); } else { diff --git a/Oqtane.Client/Resources/Modules/Admin/Files/Edit.resx b/Oqtane.Client/Resources/Modules/Admin/Files/Edit.resx index c35e179e..c0c4cb8c 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Files/Edit.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Files/Edit.resx @@ -135,9 +135,6 @@ Error Saving Folder - - Folder Has Files And Cannot Be Deleted - Folder Has Subfolders And Cannot Be Deleted diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index 62545760..866f649b 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -20,14 +20,16 @@ namespace Oqtane.Controllers { private readonly IFolderRepository _folders; private readonly IUserPermissions _userPermissions; + private readonly IFileRepository _files; private readonly ISyncManager _syncManager; private readonly ILogManager _logger; private readonly Alias _alias; - public FolderController(IFolderRepository folders, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager) + public FolderController(IFolderRepository folders, IUserPermissions userPermissions, IFileRepository files, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager) { _folders = folders; _userPermissions = userPermissions; + _files = files; _syncManager = syncManager; _logger = logger; _alias = tenantManager.GetAlias(); @@ -283,12 +285,20 @@ namespace Oqtane.Controllers var folderPath = _folders.GetFolderPath(folder); if (Directory.Exists(folderPath)) { + // remove all files from disk (including thumbnails, etc...) foreach (var filePath in Directory.GetFiles(folderPath)) { System.IO.File.Delete(filePath); } Directory.Delete(folderPath); } + + // remove files from database + foreach (var file in _files.GetFiles(id)) + { + _files.DeleteFile(file.FileId); + } + _folders.DeleteFolder(id); _syncManager.AddSyncEvent(_alias, EntityNames.Folder, folder.FolderId, SyncEventActions.Delete); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Folder Deleted {FolderId}", id); From b53f54295df82b33ba886b12ee1e5f3469d82ca7 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Mon, 12 May 2025 11:32:27 -0400 Subject: [PATCH 23/34] fix #5292 - fix External Login Provider Info link --- Oqtane.Shared/Shared/ExternalLoginProviders.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Oqtane.Shared/Shared/ExternalLoginProviders.cs b/Oqtane.Shared/Shared/ExternalLoginProviders.cs index 8b62bad9..57cf9322 100644 --- a/Oqtane.Shared/Shared/ExternalLoginProviders.cs +++ b/Oqtane.Shared/Shared/ExternalLoginProviders.cs @@ -68,7 +68,7 @@ namespace Oqtane.Shared Name = "Facebook", Settings = new Dictionary() { - { "ExternalLogin:ProviderUrl", "https://developers.facebook.com/apps/" }, + { "ExternalLogin:ProviderUrl", "https://developers.facebook.com" }, { "ExternalLogin:ProviderType", "oauth2" }, { "ExternalLogin:ProviderName", "Facebook" }, { "ExternalLogin:AuthorizationUrl", "https://www.facebook.com/v18.0/dialog/oauth" }, From 9f18c460d8e221770ed0b59a1a2255b99827b656 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 13 May 2025 09:24:17 -0400 Subject: [PATCH 24/34] add time zone support for sites and users --- .../OqtaneServiceCollectionExtensions.cs | 1 + .../Modules/Admin/Register/Index.razor | 18 ++ Oqtane.Client/Modules/Admin/Site/Index.razor | 26 ++- .../Modules/Admin/UserProfile/Index.razor | 187 ++++++++++-------- Oqtane.Client/Modules/Admin/Users/Add.razor | 50 +++-- Oqtane.Client/Modules/Admin/Users/Edit.razor | 154 ++++++++------- .../Modules/Admin/Register/Index.resx | 6 + .../Resources/Modules/Admin/Site/Index.resx | 6 + .../Modules/Admin/UserProfile/Index.resx | 6 + .../Resources/Modules/Admin/Users/Add.resx | 6 + .../Resources/Modules/Admin/Users/Edit.resx | 6 + .../Services/Interfaces/ITimeZoneService.cs | 18 ++ Oqtane.Client/Services/TimeZoneService.cs | 22 +++ .../Controllers/TimeZoneController.cs | 29 +++ Oqtane.Server/Controllers/UserController.cs | 1 + .../OqtaneServiceCollectionExtensions.cs | 1 + .../Migrations/Tenant/06010301_AddTimeZone.cs | 31 +++ Oqtane.Shared/Models/Site.cs | 6 + Oqtane.Shared/Models/TimeZone.cs | 10 + Oqtane.Shared/Models/User.cs | 5 + 20 files changed, 417 insertions(+), 172 deletions(-) create mode 100644 Oqtane.Client/Services/Interfaces/ITimeZoneService.cs create mode 100644 Oqtane.Client/Services/TimeZoneService.cs create mode 100644 Oqtane.Server/Controllers/TimeZoneController.cs create mode 100644 Oqtane.Server/Migrations/Tenant/06010301_AddTimeZone.cs create mode 100644 Oqtane.Shared/Models/TimeZone.cs diff --git a/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs index c5590d51..9a89bce8 100644 --- a/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs @@ -54,6 +54,7 @@ namespace Microsoft.Extensions.DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); // providers services.AddScoped(); diff --git a/Oqtane.Client/Modules/Admin/Register/Index.razor b/Oqtane.Client/Modules/Admin/Register/Index.razor index 88ccdd56..13920441 100644 --- a/Oqtane.Client/Modules/Admin/Register/Index.razor +++ b/Oqtane.Client/Modules/Admin/Register/Index.razor @@ -3,6 +3,7 @@ @inherits ModuleBase @inject NavigationManager NavigationManager @inject IUserService UserService +@inject ITimeZoneService TimeZoneService @inject IStringLocalizer Localizer @inject IStringLocalizer SharedLocalizer @inject ISettingService SettingService @@ -56,6 +57,18 @@ +
+ +
+ +
+

@@ -77,6 +90,7 @@ else } @code { + private List _timezones; private string _passwordrequirements; private string _username = string.Empty; private ElementReference form; @@ -87,6 +101,7 @@ else private string _confirm = string.Empty; private string _email = string.Empty; private string _displayname = string.Empty; + private string _timezoneid = string.Empty; private bool _userCreated = false; private bool _allowsitelogin = true; @@ -96,6 +111,8 @@ else { _passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId); _allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true")); + _timezones = await TimeZoneService.GetTimeZonesAsync(); + _timezoneid = PageState.Site.TimeZoneId; } protected override void OnParametersSet() @@ -124,6 +141,7 @@ else Password = _password, Email = _email, DisplayName = (_displayname == string.Empty ? _username : _displayname), + TimeZoneId = _timezoneid, PhotoFileId = null }; user = await UserService.AddUserAsync(user); diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index 5333cd3b..172c4225 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -10,6 +10,7 @@ @inject IAliasService AliasService @inject IThemeService ThemeService @inject ISettingService SettingService +@inject ITimeZoneService TimeZoneService @inject IServiceProvider ServiceProvider @inject IStringLocalizer Localizer @inject INotificationService NotificationService @@ -41,6 +42,18 @@ +
+ +
+ +
+
@@ -133,7 +146,7 @@
- +
@@ -416,9 +429,11 @@ private List _themes = new List(); private List _containers = new List(); private List _pages; + private List _timezones; private string _name = string.Empty; private string _homepageid = "-"; + private string _timezoneid = string.Empty; private string _isdeleted; private string _sitemap = ""; private string _siteguid = ""; @@ -435,7 +450,7 @@ private Dictionary _textEditors = new Dictionary(); private string _textEditor = ""; - private string _imageFiles = string.Empty; + private string _imagefiles = string.Empty; private string _headcontent = string.Empty; private string _bodycontent = string.Empty; @@ -493,11 +508,13 @@ Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId); if (site != null) { + _timezones = await TimeZoneService.GetTimeZonesAsync(); var settings = await SettingService.GetSiteSettingsAsync(site.SiteId); _pages = await PageService.GetPagesAsync(PageState.Site.SiteId); _name = site.Name; + _timezoneid = site.TimeZoneId; if (site.HomePageId != null) { _homepageid = site.HomePageId.Value.ToString(); @@ -531,8 +548,8 @@ _textEditors.Add(textEditor.Name, Utilities.GetFullTypeName(textEditor.GetType().AssemblyQualifiedName)); } _textEditor = SettingService.GetSetting(settings, "TextEditor", Constants.DefaultTextEditor); - _imageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles); - _imageFiles = (string.IsNullOrEmpty(_imageFiles)) ? Constants.ImageFiles : _imageFiles; + _imagefiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles); + _imagefiles = (string.IsNullOrEmpty(_imagefiles)) ? Constants.ImageFiles : _imagefiles; // page content _headcontent = site.HeadContent; @@ -650,6 +667,7 @@ if (site != null) { site.Name = _name; + site.TimeZoneId = _timezoneid; site.HomePageId = (_homepageid != "-" ? int.Parse(_homepageid) : null); site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted)); diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index 63486fc7..2517c538 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -9,6 +9,7 @@ @inject INotificationService NotificationService @inject IFileService FileService @inject IFolderService FolderService +@inject ITimeZoneService TimeZoneService @inject IJSRuntime jsRuntime @inject IServiceProvider ServiceProvider @inject IStringLocalizer Localizer @@ -16,9 +17,9 @@ @if (_initialized) { - @if (PageState.User != null && photo != null) + @if (PageState.User != null && _photo != null) { - @displayname + @_displayname } else { @@ -31,7 +32,7 @@
- +
@@ -47,17 +48,17 @@
- +
- @if (allowtwofactor) + @if (_allowtwofactor) {
- @@ -67,19 +68,31 @@
- +
- +
- +
- + +
+
+
+ +
+
@@ -91,17 +104,17 @@
- @foreach (Profile profile in profiles) + @foreach (Profile profile in _profiles) { var p = profile; if (!p.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) { - if (p.Category != category) + if (p.Category != _category) {
@p.Category
- category = p.Category; + _category = p.Category; }
@@ -150,12 +163,12 @@ @if (p.IsRequired) { + @attributes="@(p.MaxLength > 0 ? new Dictionary {{"maxlength", p.MaxLength }} : null)" /> } else { + @attributes="@(p.MaxLength > 0 ? new Dictionary {{"maxlength", p.MaxLength }} : null)" /> } } else @@ -163,12 +176,12 @@ @if (p.IsRequired) { + @attributes="@(p.MaxLength > 0 ? new Dictionary {{"maxlength", p.MaxLength }} : null)" /> } else { + @attributes="@(p.MaxLength > 0 ? new Dictionary {{"maxlength", p.MaxLength }} : null)" /> } } } @@ -179,12 +192,12 @@ @if (p.IsRequired) { + @attributes="@(p.MaxLength > 0 ? new Dictionary {{"maxlength", p.MaxLength }} : null)"> } else { + @attributes="@(p.MaxLength > 0 ? new Dictionary {{"maxlength", p.MaxLength }} : null)"> } } else @@ -192,12 +205,12 @@ @if (p.IsRequired) { + @attributes="@(p.MaxLength > 0 ? new Dictionary {{"maxlength", p.MaxLength }} : null)"> } else { + @attributes="@(p.MaxLength > 0 ? new Dictionary {{"maxlength", p.MaxLength }} : null)"> } } } @@ -220,11 +233,11 @@
- @if (filter == "to") + @if (_filter == "to") { - @if (notifications.Any()) + @if (_notifications.Any()) { - +
    @@ -260,15 +273,15 @@ context.Body = context.Body.Replace("\n", ""); context.Body = context.Body.Replace("\r", ""); } - notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body; + _notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body; } @if (context.IsRead) { - @notificationSummary + @_notificationSummary } else { - @notificationSummary + @_notificationSummary } @@ -285,9 +298,9 @@ } else { - @if (notifications.Any()) + @if (_notifications.Any()) { - +
@@ -324,15 +337,15 @@ context.Body = context.Body.Replace("\n", ""); context.Body = context.Body.Replace("\r", ""); } - notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body; + _notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body; } @if (context.IsRead) { - @notificationSummary + @_notificationSummary } else { - @notificationSummary + @_notificationSummary } @@ -354,29 +367,32 @@ } @code { + private List _timezones; private bool _initialized = false; private string _passwordrequirements; - private string username = string.Empty; + 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 bool allowtwofactor = false; - private string twofactor = "False"; - private string email = string.Empty; - private string displayname = string.Empty; - private FileManager filemanager; - private int folderid = -1; - private int photofileid = -1; - private File photo = null; - private string _ImageFiles = string.Empty; - private List profiles; - private Dictionary userSettings; - private string category = string.Empty; + private string _confirm = string.Empty; + private bool _allowtwofactor = false; + private string _twofactor = "False"; + private string _email = string.Empty; + private string _displayname = string.Empty; + private FileManager _filemanager; + private int _folderid = -1; + private string _timezoneid = string.Empty; + private int _photofileid = -1; + private File _photo = null; + private string _imagefiles = string.Empty; - private string filter = "to"; - private List notifications; - private string notificationSummary = string.Empty; + private List _profiles; + private Dictionary _userSettings; + private string _category = string.Empty; + + private string _filter = "to"; + private List _notifications; + private string _notificationSummary = string.Empty; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View; @@ -386,17 +402,19 @@ { _passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId); _togglepassword = SharedLocalizer["ShowPassword"]; - allowtwofactor = (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "true"); - profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); + _allowtwofactor = (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "true"); + _profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); + _timezones = await TimeZoneService.GetTimeZonesAsync(); if (PageState.User != null) { - username = PageState.User.Username; - twofactor = PageState.User.TwoFactorRequired.ToString(); - email = PageState.User.Email; - displayname = PageState.User.DisplayName; + _username = PageState.User.Username; + _twofactor = PageState.User.TwoFactorRequired.ToString(); + _email = PageState.User.Email; + _displayname = PageState.User.DisplayName; + _timezoneid = PageState.User.TimeZoneId; - if (string.IsNullOrEmpty(email)) + if (string.IsNullOrEmpty(_email)) { AddModuleMessage(Localizer["Message.User.NoEmail"], MessageType.Warning); } @@ -405,24 +423,24 @@ var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath); if (folder != null) { - folderid = folder.FolderId; + _folderid = folder.FolderId; } if (PageState.User.PhotoFileId != null) { - photofileid = PageState.User.PhotoFileId.Value; - photo = await FileService.GetFileAsync(photofileid); + _photofileid = PageState.User.PhotoFileId.Value; + _photo = await FileService.GetFileAsync(_photofileid); } else { - photofileid = -1; - photo = null; + _photofileid = -1; + _photo = null; } - userSettings = PageState.User.Settings; + _userSettings = PageState.User.Settings; var sitesettings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); - _ImageFiles = SettingService.GetSetting(userSettings, "ImageFiles", Constants.ImageFiles); - _ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles; + _imagefiles = SettingService.GetSetting(_userSettings, "ImageFiles", Constants.ImageFiles); + _imagefiles = (string.IsNullOrEmpty(_imagefiles)) ? Constants.ImageFiles : _imagefiles; await LoadNotificationsAsync(); @@ -442,13 +460,13 @@ private async Task LoadNotificationsAsync() { - notifications = await NotificationService.GetNotificationsAsync(PageState.Site.SiteId, filter, PageState.User.UserId); - notifications = notifications.Where(item => item.DeletedBy != PageState.User.Username).ToList(); + _notifications = await NotificationService.GetNotificationsAsync(PageState.Site.SiteId, _filter, PageState.User.UserId); + _notifications = _notifications.Where(item => item.DeletedBy != PageState.User.Username).ToList(); } private string GetProfileValue(string SettingName, string DefaultValue) { - string value = SettingService.GetSetting(userSettings, SettingName, DefaultValue); + string value = SettingService.GetSetting(_userSettings, SettingName, DefaultValue); if (value.Contains("]")) { value = value.Substring(value.IndexOf("]") + 1); @@ -460,38 +478,39 @@ { try { - if (username != string.Empty && email != string.Empty) + if (_username != string.Empty && _email != string.Empty) { - if (_password == confirm) + if (_password == _confirm) { if (ValidateProfiles()) { var user = PageState.User; - user.Username = username; + user.Username = _username; user.Password = _password; - user.TwoFactorRequired = bool.Parse(twofactor); - user.Email = email; - user.DisplayName = (displayname == string.Empty ? username : displayname); - user.PhotoFileId = filemanager.GetFileId(); + user.TwoFactorRequired = bool.Parse(_twofactor); + user.Email = _email; + user.DisplayName = (_displayname == string.Empty ? _username : _displayname); + user.TimeZoneId = _timezoneid; + user.PhotoFileId = _filemanager.GetFileId(); if (user.PhotoFileId == -1) { user.PhotoFileId = null; } if (user.PhotoFileId != null) { - photofileid = user.PhotoFileId.Value; - photo = await FileService.GetFileAsync(photofileid); + _photofileid = user.PhotoFileId.Value; + _photo = await FileService.GetFileAsync(_photofileid); } else { - photofileid = -1; - photo = null; + _photofileid = -1; + _photo = null; } user = await UserService.UpdateUserAsync(user); if (user != null) { - await SettingService.UpdateUserSettingsAsync(userSettings, PageState.User.UserId); + await SettingService.UpdateUserSettingsAsync(_userSettings, PageState.User.UserId); await logger.LogInformation("User Profile Saved"); if (!string.IsNullOrEmpty(PageState.ReturnUrl)) @@ -557,12 +576,12 @@ private bool ValidateProfiles() { - foreach (Profile profile in profiles) + foreach (Profile profile in _profiles) { var value = GetProfileValue(profile.Name, string.Empty); if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue)) { - userSettings = SettingService.SetSetting(userSettings, profile.Name, profile.DefaultValue); + _userSettings = SettingService.SetSetting(_userSettings, profile.Name, profile.DefaultValue); } if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) { @@ -594,7 +613,7 @@ private void ProfileChanged(ChangeEventArgs e, string SettingName) { var value = (string)e.Value; - userSettings = SettingService.SetSetting(userSettings, SettingName, value); + _userSettings = SettingService.SetSetting(_userSettings, SettingName, value); } private async Task Delete(Notification Notification) @@ -624,7 +643,7 @@ private async void FilterChanged(ChangeEventArgs e) { - filter = (string)e.Value; + _filter = (string)e.Value; await LoadNotificationsAsync(); StateHasChanged(); } @@ -634,7 +653,7 @@ try { ShowProgressIndicator(); - foreach(var Notification in notifications) + foreach(var Notification in _notifications) { if (!Notification.IsDeleted) { diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor index 10cf19e8..510c87e7 100644 --- a/Oqtane.Client/Modules/Admin/Users/Add.razor +++ b/Oqtane.Client/Modules/Admin/Users/Add.razor @@ -5,6 +5,7 @@ @inject IUserService UserService @inject IProfileService ProfileService @inject ISettingService SettingService +@inject ITimeZoneService TimeZoneService @inject IStringLocalizer Localizer @inject IStringLocalizer SharedLocalizer @@ -12,7 +13,7 @@ { - @if (profiles != null) + @if (_profiles != null) {
@@ -33,6 +34,18 @@
+
+ +
+ +
+
@@ -48,20 +61,20 @@
- @foreach (Profile profile in profiles) + @foreach (Profile profile in _profiles) { var p = profile; - if (p.Category != category) + if (p.Category != _category) {
@p.Category
- category = p.Category; + _category = p.Category; }
-
- @if (!string.IsNullOrEmpty(p.Options)) +
+ @if (!string.IsNullOrEmpty(p.Options)) { +
@@ -35,7 +36,7 @@
- +
@@ -43,13 +44,25 @@
- +
- + +
+
+
+ +
+
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) @@ -57,7 +70,7 @@
- @@ -67,13 +80,13 @@
- +
- +
@@ -81,15 +94,15 @@
- @foreach (Profile profile in profiles) + @foreach (Profile profile in _profiles) { var p = profile; - if (p.Category != category) + if (p.Category != _category) {
@p.Category
- category = p.Category; + _category = p.Category; }
@@ -131,44 +144,46 @@ @SharedLocalizer["Cancel"] - @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) && PageState.Runtime != Shared.Runtime.Hybrid && !ishost) + @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) && PageState.Runtime != Shared.Runtime.Hybrid && !_ishost) { } - @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && isdeleted == "True") + @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && _isdeleted == "True") { }

- + } @code { + private List _timezones; private bool _initialized = false; private string _passwordrequirements; - private int userid; - private string username = string.Empty; + private int _userid; + 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 isdeleted; - private string lastlogin; - private string lastipaddress; - private bool ishost = false; + private string _confirm = string.Empty; + private string _email = string.Empty; + private string _displayname = string.Empty; + private string _timezoneid = string.Empty; + private string _isdeleted; + private string _lastlogin; + private string _lastipaddress; + private bool _ishost = false; - private List profiles; - private Dictionary userSettings; - private string category = string.Empty; + private List _profiles; + private Dictionary _settings; + private string _category = string.Empty; - private string createdby; - private DateTime createdon; - private string modifiedby; - private DateTime modifiedon; - private string deletedby; - private DateTime? deletedon; + private string _createdby; + private DateTime _createdon; + private string _modifiedby; + private DateTime _modifiedon; + private string _deletedby; + private DateTime? _deletedon; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; @@ -178,29 +193,31 @@ { _passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId); _togglepassword = SharedLocalizer["ShowPassword"]; - profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId); + _profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId); + _timezones = await TimeZoneService.GetTimeZonesAsync(); if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int UserId)) { - userid = UserId; - var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); + _userid = UserId; + var user = await UserService.GetUserAsync(_userid, PageState.Site.SiteId); if (user != null) { - username = user.Username; - email = user.Email; - displayname = user.DisplayName; - isdeleted = user.IsDeleted.ToString(); - lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn); - lastipaddress = user.LastIPAddress; - ishost = UserSecurity.ContainsRole(user.Roles, RoleNames.Host); + _username = user.Username; + _email = user.Email; + _displayname = user.DisplayName; + _timezoneid = PageState.User.TimeZoneId; + _isdeleted = user.IsDeleted.ToString(); + _lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn); + _lastipaddress = user.LastIPAddress; + _ishost = UserSecurity.ContainsRole(user.Roles, RoleNames.Host); - userSettings = user.Settings; - createdby = user.CreatedBy; - createdon = user.CreatedOn; - modifiedby = user.ModifiedBy; - modifiedon = user.ModifiedOn; - deletedby = user.DeletedBy; - deletedon = user.DeletedOn; + _settings = user.Settings; + _createdby = user.CreatedBy; + _createdon = user.CreatedOn; + _modifiedby = user.ModifiedBy; + _modifiedon = user.ModifiedOn; + _deletedby = user.DeletedBy; + _deletedon = user.DeletedOn; } } @@ -208,14 +225,14 @@ } catch (Exception ex) { - await logger.LogError(ex, "Error Loading User {UserId} {Error}", userid, ex.Message); + await logger.LogError(ex, "Error Loading User {UserId} {Error}", _userid, ex.Message); AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error); } } private string GetProfileValue(string SettingName, string DefaultValue) { - string value = SettingService.GetSetting(userSettings, SettingName, DefaultValue); + string value = SettingService.GetSetting(_settings, SettingName, DefaultValue); if (value.Contains("]")) { value = value.Substring(value.IndexOf("]") + 1); @@ -227,27 +244,28 @@ { try { - if (username != string.Empty && email != string.Empty) + if (_username != string.Empty && _email != string.Empty) { - if (_password == confirm) + if (_password == _confirm) { if (ValidateProfiles()) { - var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); + var user = await UserService.GetUserAsync(_userid, PageState.Site.SiteId); user.SiteId = PageState.Site.SiteId; - user.Username = username; + user.Username = _username; user.Password = _password; - user.Email = email; - user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; + user.Email = _email; + user.DisplayName = string.IsNullOrWhiteSpace(_displayname) ? _username : _displayname; + user.TimeZoneId = _timezoneid; if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) { - user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted)); + user.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted)); } user = await UserService.UpdateUserAsync(user); if (user != null) { - await SettingService.UpdateUserSettingsAsync(userSettings, user.UserId); + await SettingService.UpdateUserSettingsAsync(_settings, user.UserId); await logger.LogInformation("User Saved {User}", user); NavigationManager.NavigateTo(NavigateUrl()); } @@ -269,7 +287,7 @@ } catch (Exception ex) { - await logger.LogError(ex, "Error Saving User {Username} {Email} {Error}", username, email, ex.Message); + await logger.LogError(ex, "Error Saving User {Username} {Email} {Error}", _username, _email, ex.Message); AddModuleMessage(Localizer["Error.User.Save"], MessageType.Error); } } @@ -278,17 +296,17 @@ { try { - await logger.LogInformation(LogFunction.Security, "User {Username} Impersonated By Administrator {Administrator}", username, PageState.User.Username); + await logger.LogInformation(LogFunction.Security, "User {Username} Impersonated By Administrator {Administrator}", _username, PageState.User.Username); // post back to the server so that the cookies are set correctly var interop = new Interop(JSRuntime); - var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = username, returnurl = PageState.Alias.Path }; + var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, returnurl = PageState.Alias.Path }; string url = Utilities.TenantUrl(PageState.Alias, "/pages/impersonate/"); await interop.SubmitForm(url, fields); } catch (Exception ex) { - await logger.LogError(ex, "Error Impersonating User {Username} {Error}", username, ex.Message); + await logger.LogError(ex, "Error Impersonating User {Username} {Error}", _username, ex.Message); AddModuleMessage(Localizer["Error.User.Impersonate"], MessageType.Error); } } @@ -297,9 +315,9 @@ { try { - if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && userid != PageState.User.UserId) + if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && _userid != PageState.User.UserId) { - var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); + var user = await UserService.GetUserAsync(_userid, PageState.Site.SiteId); await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId); await logger.LogInformation("User Permanently Deleted {User}", user); NavigationManager.NavigateTo(NavigateUrl()); @@ -307,19 +325,19 @@ } catch (Exception ex) { - await logger.LogError(ex, "Error Permanently Deleting User {UserId} {Error}", userid, ex.Message); + await logger.LogError(ex, "Error Permanently Deleting User {UserId} {Error}", _userid, ex.Message); AddModuleMessage(Localizer["Error.DeleteUser"], MessageType.Error); } } private bool ValidateProfiles() { - foreach (Profile profile in profiles) + foreach (Profile profile in _profiles) { var value = GetProfileValue(profile.Name, string.Empty); if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue)) { - userSettings = SettingService.SetSetting(userSettings, profile.Name, profile.DefaultValue); + _settings = SettingService.SetSetting(_settings, profile.Name, profile.DefaultValue); } if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) { @@ -346,7 +364,7 @@ private void ProfileChanged(ChangeEventArgs e, string SettingName) { var value = (string)e.Value; - userSettings = SettingService.SetSetting(userSettings, SettingName, value); + _settings = SettingService.SetSetting(_settings, SettingName, value); } private void TogglePassword() diff --git a/Oqtane.Client/Resources/Modules/Admin/Register/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Register/Index.resx index 5a0af9ed..47e95add 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Register/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Register/Index.resx @@ -180,4 +180,10 @@ Already have account? Login now. + + Time Zone: + + + Your time zone + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/Site/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Site/Index.resx index 0127c7cc..acfd022e 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Site/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Site/Index.resx @@ -447,4 +447,10 @@ Site Map Cache Cleared + + Time Zone: + + + The default time zone for the site + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/UserProfile/Index.resx b/Oqtane.Client/Resources/Modules/Admin/UserProfile/Index.resx index a6f0a739..d6136ee8 100644 --- a/Oqtane.Client/Resources/Modules/Admin/UserProfile/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/UserProfile/Index.resx @@ -246,4 +246,10 @@ Logout Everywhere + + Time Zone: + + + Your time zone + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/Users/Add.resx b/Oqtane.Client/Resources/Modules/Admin/Users/Add.resx index b25b2477..536de9b4 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Users/Add.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Users/Add.resx @@ -156,4 +156,10 @@ Notify? + + Time Zone: + + + The user's time zone + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/Users/Edit.resx b/Oqtane.Client/Resources/Modules/Admin/Users/Edit.resx index 3dbd1d1e..df4ccc95 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Users/Edit.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Users/Edit.resx @@ -210,4 +210,10 @@ Unable To Impersonate User + + Time Zone: + + + The user's time zone + \ No newline at end of file diff --git a/Oqtane.Client/Services/Interfaces/ITimeZoneService.cs b/Oqtane.Client/Services/Interfaces/ITimeZoneService.cs new file mode 100644 index 00000000..c31f90b6 --- /dev/null +++ b/Oqtane.Client/Services/Interfaces/ITimeZoneService.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Oqtane.Models; + +namespace Oqtane.Services +{ + /// + /// Service to store and retrieve entries + /// + public interface ITimeZoneService + { + /// + /// Get the list of time zones + /// + /// + Task> GetTimeZonesAsync(); + } +} diff --git a/Oqtane.Client/Services/TimeZoneService.cs b/Oqtane.Client/Services/TimeZoneService.cs new file mode 100644 index 00000000..f7983b14 --- /dev/null +++ b/Oqtane.Client/Services/TimeZoneService.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; +using Oqtane.Documentation; +using Oqtane.Models; +using Oqtane.Shared; + +namespace Oqtane.Services +{ + [PrivateApi("Don't show in the documentation, as everything should use the Interface")] + public class TimeZoneService : ServiceBase, ITimeZoneService + { + public TimeZoneService(HttpClient http, SiteState siteState) : base(http, siteState) { } + + private string Apiurl => CreateApiUrl("TimeZone"); + + public async Task> GetTimeZonesAsync() + { + return await GetJsonAsync>($"{Apiurl}"); + } + } +} diff --git a/Oqtane.Server/Controllers/TimeZoneController.cs b/Oqtane.Server/Controllers/TimeZoneController.cs new file mode 100644 index 00000000..158d9f72 --- /dev/null +++ b/Oqtane.Server/Controllers/TimeZoneController.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Oqtane.Models; +using Oqtane.Shared; + +namespace Oqtane.Controllers +{ + [Route(ControllerRoutes.ApiRoute)] + public class TimeZoneController : Controller + { + public TimeZoneController() {} + + // GET: api/ + [HttpGet] + public IEnumerable Get() + { + return TimeZoneInfo.GetSystemTimeZones() + .Select(item => new Models.TimeZone + { + Id = item.Id, + DisplayName = item.DisplayName + }) + .OrderBy(item => item.DisplayName); + } + } +} diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 92aef5c5..7a0f3bfd 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -135,6 +135,7 @@ namespace Oqtane.Controllers if (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) || _userPermissions.GetUser(User).UserId == user.UserId) { filtered.Email = user.Email; + filtered.TimeZoneId = user.TimeZoneId; filtered.PhotoFileId = user.PhotoFileId; filtered.LastLoginOn = user.LastLoginOn; filtered.LastIPAddress = user.LastIPAddress; diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index 9b4b09e9..98a392cc 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -104,6 +104,7 @@ namespace Microsoft.Extensions.DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); // providers services.AddScoped(); diff --git a/Oqtane.Server/Migrations/Tenant/06010301_AddTimeZone.cs b/Oqtane.Server/Migrations/Tenant/06010301_AddTimeZone.cs new file mode 100644 index 00000000..0e7d40c0 --- /dev/null +++ b/Oqtane.Server/Migrations/Tenant/06010301_AddTimeZone.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Databases.Interfaces; +using Oqtane.Migrations.EntityBuilders; +using Oqtane.Repository; + +namespace Oqtane.Migrations.Tenant +{ + [DbContext(typeof(TenantDBContext))] + [Migration("Tenant.06.01.03.01")] + public class AddTimeZone : MultiDatabaseMigration + { + public AddTimeZone(IDatabase database) : base(database) + { + } + + protected override void Up(MigrationBuilder migrationBuilder) + { + var siteEntityBuilder = new SiteEntityBuilder(migrationBuilder, ActiveDatabase); + siteEntityBuilder.AddStringColumn("TimeZoneId", 50, true); + + var userEntityBuilder = new UserEntityBuilder(migrationBuilder, ActiveDatabase); + userEntityBuilder.AddStringColumn("TimeZoneId", 50, true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + // not implemented + } + } +} diff --git a/Oqtane.Shared/Models/Site.cs b/Oqtane.Shared/Models/Site.cs index aeb6e37b..e674be2e 100644 --- a/Oqtane.Shared/Models/Site.cs +++ b/Oqtane.Shared/Models/Site.cs @@ -26,6 +26,11 @@ namespace Oqtane.Models /// public string Name { get; set; } + /// + /// The default time zone for the site + /// + public string TimeZoneId { get; set; } + /// /// Reference to a which has the Logo for this site. /// Should be an image. @@ -200,6 +205,7 @@ namespace Oqtane.Models SiteId = SiteId, TenantId = TenantId, Name = Name, + TimeZoneId = TimeZoneId, LogoFileId = LogoFileId, FaviconFileId = FaviconFileId, DefaultThemeType = DefaultThemeType, diff --git a/Oqtane.Shared/Models/TimeZone.cs b/Oqtane.Shared/Models/TimeZone.cs new file mode 100644 index 00000000..a2ff00f0 --- /dev/null +++ b/Oqtane.Shared/Models/TimeZone.cs @@ -0,0 +1,10 @@ +namespace Oqtane.Models +{ + public class TimeZone + { + public string Id { get; set; } + + public string DisplayName { get; set; } + + } +} diff --git a/Oqtane.Shared/Models/User.cs b/Oqtane.Shared/Models/User.cs index d5009fb5..7b6b398b 100644 --- a/Oqtane.Shared/Models/User.cs +++ b/Oqtane.Shared/Models/User.cs @@ -29,6 +29,11 @@ namespace Oqtane.Models /// public string Email { get; set; } + /// + /// User time zone + /// + public string TimeZoneId { get; set; } + /// /// Reference to a containing the users photo. /// From 139793f3c0892dc5f1811ef7f325fd24d3721f2c Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 13 May 2025 11:29:26 -0400 Subject: [PATCH 25/34] display local datetimes in the Job Scheduler (using time zones) --- Oqtane.Client/Modules/Admin/Jobs/Edit.razor | 18 ++++++------ Oqtane.Client/Modules/Admin/Jobs/Index.razor | 2 +- Oqtane.Client/Modules/ModuleBase.cs | 29 ++++++++++++++++++++ 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor index 1d359f66..bcb500ff 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor @@ -132,13 +132,13 @@ _isEnabled = job.IsEnabled.ToString(); _interval = job.Interval.ToString(); _frequency = job.Frequency; - _startDate = Utilities.UtcAsLocalDate(job.StartDate); - _startTime = Utilities.UtcAsLocalDateTime(job.StartDate); - _endDate = Utilities.UtcAsLocalDate(job.EndDate); - _endTime = Utilities.UtcAsLocalDateTime(job.EndDate); + _startDate = UtcToLocal(job.StartDate); + _startTime = UtcToLocal(job.StartDate); + _endDate = UtcToLocal(job.EndDate); + _endTime = UtcToLocal(job.EndDate); _retentionHistory = job.RetentionHistory.ToString(); - _nextDate = Utilities.UtcAsLocalDate(job.NextExecution); - _nextTime = Utilities.UtcAsLocalDateTime(job.NextExecution); + _nextDate = UtcToLocal(job.NextExecution); + _nextTime = UtcToLocal(job.NextExecution); createdby = job.CreatedBy; createdon = job.CreatedOn; modifiedby = job.ModifiedBy; @@ -176,10 +176,10 @@ { job.Interval = int.Parse(_interval); } - job.StartDate = Utilities.LocalDateAndTimeAsUtc(_startDate, _startTime); - job.EndDate = Utilities.LocalDateAndTimeAsUtc(_endDate, _endTime); + job.StartDate = LocalToUtc(_startDate.Value.Date.Add(_startTime.Value.TimeOfDay)); + job.EndDate = LocalToUtc(_endDate.Value.Date.Add(_endTime.Value.TimeOfDay)); job.RetentionHistory = int.Parse(_retentionHistory); - job.NextExecution = Utilities.LocalDateAndTimeAsUtc(_nextDate, _nextTime); + job.NextExecution = LocalToUtc(_nextDate.Value.Date.Add(_nextTime.Value.TimeOfDay)); try { diff --git a/Oqtane.Client/Modules/Admin/Jobs/Index.razor b/Oqtane.Client/Modules/Admin/Jobs/Index.razor index 4a2f5de6..033c3787 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Index.razor @@ -29,7 +29,7 @@ else @context.Name @DisplayStatus(context.IsEnabled, context.IsExecuting) @DisplayFrequency(context.Interval, context.Frequency) - @context.NextExecution?.ToLocalTime() + @UtcToLocal(context.NextExecution) @if (context.IsStarted) { diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 5bbfc855..49d6b00b 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -487,6 +487,35 @@ namespace Oqtane.Modules return content; } + // date methods + public DateTime? UtcToLocal(DateTime? datetime) + { + TimeZoneInfo timezone = null; + if (PageState.User != null && !string.IsNullOrEmpty(PageState.User.TimeZoneId)) + { + timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.User.TimeZoneId); + } + else if (!string.IsNullOrEmpty(PageState.Site.TimeZoneId)) + { + timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.Site.TimeZoneId); + } + return Utilities.UtcAsLocalDateTime(datetime, timezone); + } + + public DateTime? LocalToUtc(DateTime? datetime) + { + TimeZoneInfo timezone = null; + if (PageState.User != null && !string.IsNullOrEmpty(PageState.User.TimeZoneId)) + { + timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.User.TimeZoneId); + } + else if (!string.IsNullOrEmpty(PageState.Site.TimeZoneId)) + { + timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.Site.TimeZoneId); + } + return Utilities.LocalDateAndTimeAsUtc(datetime, timezone); + } + // logging methods public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args) { From deb460708115e9a7f3a130fe29221961a9e3fafe Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 13 May 2025 13:55:01 -0400 Subject: [PATCH 26/34] adding time zone support to admin modules --- Oqtane.Client/Modules/Admin/Files/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Jobs/Log.razor | 4 ++-- Oqtane.Client/Modules/Admin/Logs/Detail.razor | 2 +- Oqtane.Client/Modules/Admin/Logs/Index.razor | 2 +- Oqtane.Client/Modules/Admin/UrlMappings/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Users/Edit.razor | 2 +- Oqtane.Client/Modules/Admin/Users/Index.razor | 2 +- Oqtane.Client/Modules/Admin/Visitors/Detail.razor | 4 ++-- Oqtane.Client/Modules/Admin/Visitors/Index.razor | 4 ++-- Oqtane.Client/Modules/Controls/AuditInfo.razor | 6 +++--- 10 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Files/Index.razor b/Oqtane.Client/Modules/Admin/Files/Index.razor index 06f7e61c..b730603b 100644 --- a/Oqtane.Client/Modules/Admin/Files/Index.razor +++ b/Oqtane.Client/Modules/Admin/Files/Index.razor @@ -53,7 +53,7 @@ else @context.Name - @context.ModifiedOn + @UtcToLocal(context.ModifiedOn) @context.Extension.ToUpper() @SharedLocalizer["File"] @string.Format("{0:0.00}", ((decimal)context.Size / 1000)) KB diff --git a/Oqtane.Client/Modules/Admin/Jobs/Log.razor b/Oqtane.Client/Modules/Admin/Jobs/Log.razor index f5453315..55efc788 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Log.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Log.razor @@ -23,8 +23,8 @@ else @context.Job.Name @DisplayStatus(context.Job.IsExecuting, context.Succeeded) - @context.StartDate - @context.FinishDate + @UtcToLocal(context.StartDate) + @UtcToLocal(context.FinishDate) @((MarkupString)context.Notes) diff --git a/Oqtane.Client/Modules/Admin/Logs/Detail.razor b/Oqtane.Client/Modules/Admin/Logs/Detail.razor index fd980bce..379a4f3c 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Detail.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Detail.razor @@ -141,7 +141,7 @@ var log = await LogService.GetLogAsync(_logId); if (log != null) { - _logDate = log.LogDate.ToString(CultureInfo.CurrentCulture); + _logDate = UtcToLocal(log.LogDate).Value.ToString(CultureInfo.CurrentCulture); _level = log.Level; _feature = log.Feature; _function = log.Function; diff --git a/Oqtane.Client/Modules/Admin/Logs/Index.razor b/Oqtane.Client/Modules/Admin/Logs/Index.razor index dfb0cf8d..594393ca 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Index.razor @@ -64,7 +64,7 @@ else
- @context.LogDate + @UtcToLocal(context.LogDate) @context.Level @context.Feature @context.Function diff --git a/Oqtane.Client/Modules/Admin/UrlMappings/Index.razor b/Oqtane.Client/Modules/Admin/UrlMappings/Index.razor index 75a4c845..554213fa 100644 --- a/Oqtane.Client/Modules/Admin/UrlMappings/Index.razor +++ b/Oqtane.Client/Modules/Admin/UrlMappings/Index.razor @@ -48,7 +48,7 @@ else } @context.Requests - @context.RequestedOn + @UtcToLocal(context.RequestedOn)
diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor index 37d8f165..ae66baef 100644 --- a/Oqtane.Client/Modules/Admin/Users/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor @@ -207,7 +207,7 @@ _displayname = user.DisplayName; _timezoneid = PageState.User.TimeZoneId; _isdeleted = user.IsDeleted.ToString(); - _lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn); + _lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", UtcToLocal(user.LastLoginOn)); _lastipaddress = user.LastIPAddress; _ishost = UserSecurity.ContainsRole(user.Roles, RoleNames.Host); diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor index d3eb9bc7..7d34670f 100644 --- a/Oqtane.Client/Modules/Admin/Users/Index.razor +++ b/Oqtane.Client/Modules/Admin/Users/Index.razor @@ -43,7 +43,7 @@ else @context.User.Username @context.User.DisplayName @((MarkupString)string.Format("{1}", @context.User.Email, @context.User.Email)) - @((context.User.LastLoginOn != DateTime.MinValue) ? string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn) : "") + @((context.User.LastLoginOn != DateTime.MinValue) ? string.Format("{0:dd-MMM-yyyy HH:mm:ss}", UtcToLocal(context.User.LastLoginOn)) : "")
diff --git a/Oqtane.Client/Modules/Admin/Visitors/Detail.razor b/Oqtane.Client/Modules/Admin/Visitors/Detail.razor index e0570431..13dd456f 100644 --- a/Oqtane.Client/Modules/Admin/Visitors/Detail.razor +++ b/Oqtane.Client/Modules/Admin/Visitors/Detail.razor @@ -101,8 +101,8 @@ _url = visitor.Url; _referrer = visitor.Referrer; _visits = visitor.Visits.ToString(); - _visited = visitor.VisitedOn.ToString(CultureInfo.CurrentCulture); - _created = visitor.CreatedOn.ToString(CultureInfo.CurrentCulture); + _visited = UtcToLocal(visitor.VisitedOn).Value.ToString(CultureInfo.CurrentCulture); + _created = UtcToLocal(visitor.CreatedOn).Value.ToString(CultureInfo.CurrentCulture); if (visitor.UserId != null) { diff --git a/Oqtane.Client/Modules/Admin/Visitors/Index.razor b/Oqtane.Client/Modules/Admin/Visitors/Index.razor index 1cb46d3e..2dccb94b 100644 --- a/Oqtane.Client/Modules/Admin/Visitors/Index.razor +++ b/Oqtane.Client/Modules/Admin/Visitors/Index.razor @@ -53,8 +53,8 @@ else @context.Language @context.Visits - @context.VisitedOn - @context.CreatedOn + @UtcToLocal(context.VisitedOn) + @UtcToLocal(context.CreatedOn) diff --git a/Oqtane.Client/Modules/Controls/AuditInfo.razor b/Oqtane.Client/Modules/Controls/AuditInfo.razor index acbb3dc6..e2a025ec 100644 --- a/Oqtane.Client/Modules/Controls/AuditInfo.razor +++ b/Oqtane.Client/Modules/Controls/AuditInfo.razor @@ -52,7 +52,7 @@ if (CreatedOn != null) { - _text += $" {Localizer["On"]} {CreatedOn.Value.ToString(DateTimeFormat)}"; + _text += $" {Localizer["On"]} {UtcToLocal(CreatedOn).Value.ToString(DateTimeFormat)}"; } _text += "

"; @@ -69,7 +69,7 @@ if (ModifiedOn != null) { - _text += $" {Localizer["On"]} {ModifiedOn.Value.ToString(DateTimeFormat)}"; + _text += $" {Localizer["On"]} {UtcToLocal(ModifiedOn).Value.ToString(DateTimeFormat)}"; } _text += "

"; @@ -86,7 +86,7 @@ if (DeletedOn != null) { - _text += $" {Localizer["On"]} {DeletedOn.Value.ToString(DateTimeFormat)}"; + _text += $" {Localizer["On"]} {UtcToLocal(DeletedOn).Value.ToString(DateTimeFormat)}"; } _text += "

"; From a0f41341acd9335e000cb0654a6acb12118dd64e Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 13 May 2025 15:49:16 -0400 Subject: [PATCH 27/34] fix #5398 - editing page permissions --- Oqtane.Client/Modules/Admin/Pages/Edit.razor | 57 +++++++++++++------ Oqtane.Client/Modules/Admin/Users/Edit.razor | 2 +- .../Resources/Modules/Admin/Pages/Edit.resx | 6 ++ Oqtane.Server/Controllers/PageController.cs | 53 +++++++++-------- Oqtane.Shared/Models/Page.cs | 6 ++ 5 files changed, 82 insertions(+), 42 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 6d27b990..df9db9bb 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -30,16 +30,16 @@
- + + @foreach (Page page in _pages) + { + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList) && page.PageId != _pageId) { - if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList) && page.PageId != _pageId) - { - - } + } - + } +
@@ -217,6 +217,9 @@

+ + +

@@ -225,15 +228,28 @@
+

+
+ +
+ +
+
+
+ +
-   -   - @Localizer["ModuleTitle"] - @Localizer["ModuleDefinition"] +   +   + @Localizer["ModuleTitle"] + @Localizer["ModuleDefinition"]
@@ -247,8 +263,10 @@ { @_themeSettingsComponent +
+ +
-
} } @@ -299,19 +317,21 @@
+
+ + @if (_themeSettingsType != null) { @_themeSettingsComponent +
+ +
-
} } -
- - } @@ -348,6 +368,7 @@ private string _bodycontent; private List _permissions = null; private PermissionGrid _permissionGrid; + private string _updatemodulepermissions; private List _pageModules; private string _createdby; private DateTime _createdon; @@ -436,6 +457,7 @@ // permissions _permissions = _page.PermissionList; + _updatemodulepermissions = "True"; // page modules var modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId); @@ -651,6 +673,7 @@ if (_page.UserId == null) { _page.PermissionList = _permissionGrid.GetPermissionList(); + _page.UpdateModulePermissions = bool.Parse(_updatemodulepermissions); } _page = await PageService.UpdatePageAsync(_page); diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor index ae66baef..549059e4 100644 --- a/Oqtane.Client/Modules/Admin/Users/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor @@ -141,7 +141,7 @@
- +
@SharedLocalizer["Cancel"] @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) && PageState.Runtime != Shared.Runtime.Hybrid && !_ishost) diff --git a/Oqtane.Client/Resources/Modules/Admin/Pages/Edit.resx b/Oqtane.Client/Resources/Modules/Admin/Pages/Edit.resx index cf720e19..ded50502 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Pages/Edit.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Pages/Edit.resx @@ -303,4 +303,10 @@ Provide a url path for your personalized page. Please note that spaces and punctuation will be replaced by a dash. + + Update Module Permissions? + + + Specify if changes made to page permissions should be propagated to the modules on this page + \ No newline at end of file diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index f8ad1925..ee2ff20c 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -295,38 +295,43 @@ namespace Oqtane.Controllers var removed = GetPermissionsDifferences(currentPermissions, page.PermissionList); // synchronize module permissions - if (added.Count > 0 || removed.Count > 0) + if (page.UpdateModulePermissions && (added.Count > 0 || removed.Count > 0)) { - foreach (PageModule pageModule in _pageModules.GetPageModules(page.SiteId).Where(item => item.PageId == page.PageId).ToList()) + var pageModules = _pageModules.GetPageModules(page.SiteId); + foreach (PageModule pageModule in pageModules.Where(item => item.PageId == page.PageId).ToList()) { - var modulePermissions = _permissionRepository.GetPermissions(pageModule.Module.SiteId, EntityNames.Module, pageModule.Module.ModuleId).ToList(); - // permissions added - foreach (Permission permission in added) + // ignore "shared" modules + if (!pageModules.Any(item => item.ModuleId == pageModule.ModuleId && item.PageId != pageModule.PageId)) { - if (!modulePermissions.Any(item => item.PermissionName == permission.PermissionName - && item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized)) + var modulePermissions = _permissionRepository.GetPermissions(pageModule.Module.SiteId, EntityNames.Module, pageModule.Module.ModuleId).ToList(); + // permissions added + foreach (Permission permission in added) { - _permissionRepository.AddPermission(new Permission + if (!modulePermissions.Any(item => item.PermissionName == permission.PermissionName + && item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized)) { - SiteId = page.SiteId, - EntityName = EntityNames.Module, - EntityId = pageModule.ModuleId, - PermissionName = permission.PermissionName, - RoleId = permission.RoleId, - UserId = permission.UserId, - IsAuthorized = permission.IsAuthorized - }); + _permissionRepository.AddPermission(new Permission + { + SiteId = page.SiteId, + EntityName = EntityNames.Module, + EntityId = pageModule.ModuleId, + PermissionName = permission.PermissionName, + RoleId = permission.RoleId, + UserId = permission.UserId, + IsAuthorized = permission.IsAuthorized + }); + } } - } - // permissions removed - foreach (Permission permission in removed) - { - var modulePermission = modulePermissions.FirstOrDefault(item => item.PermissionName == permission.PermissionName - && item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized); - if (modulePermission != null) + // permissions removed + foreach (Permission permission in removed) { - _permissionRepository.DeletePermission(modulePermission.PermissionId); + var modulePermission = modulePermissions.FirstOrDefault(item => item.PermissionName == permission.PermissionName + && item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized); + if (modulePermission != null) + { + _permissionRepository.DeletePermission(modulePermission.PermissionId); + } } } } diff --git a/Oqtane.Shared/Models/Page.cs b/Oqtane.Shared/Models/Page.cs index bec0347e..15d640fe 100644 --- a/Oqtane.Shared/Models/Page.cs +++ b/Oqtane.Shared/Models/Page.cs @@ -122,6 +122,12 @@ namespace Oqtane.Models [NotMapped] public bool HasChildren { get; set; } + /// + /// Indicates if module permissions should be updated to be consistent with page permissions + /// + [NotMapped] + public bool UpdateModulePermissions { get; set; } + /// /// List of permissions for this page /// From 128bcecfe3e847ac7ad888674aefd44f4484aed0 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 13 May 2025 16:38:48 -0400 Subject: [PATCH 28/34] rollback change which moved ConfigureOqtaneAssemblies(env); --- Oqtane.Server/Startup.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index b51609ae..51b60555 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -195,6 +195,9 @@ namespace Oqtane app.UseHsts(); } + // execute any IServerStartup logic + app.ConfigureOqtaneAssemblies(env); + // allow oqtane localization middleware app.UseOqtaneLocalization(); @@ -245,9 +248,6 @@ namespace Oqtane .AddAdditionalAssemblies(typeof(SiteRouter).Assembly); }); - // execute any IServerStartup logic - app.ConfigureOqtaneAssemblies(env); - // simulate the fallback routing approach of traditional Blazor - allowing the custom SiteRouter to handle all routing concerns app.UseEndpoints(endpoints => { From e8f9888a41baaf48755dffbc62164cb321e6dcc6 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 13 May 2025 16:49:46 -0400 Subject: [PATCH 29/34] upgrade to .NET SDK 9.0.5 --- Oqtane.Client/Oqtane.Client.csproj | 8 ++++---- .../Oqtane.Database.PostgreSQL.csproj | 2 +- .../Oqtane.Database.SqlServer.csproj | 2 +- .../Oqtane.Database.Sqlite.csproj | 2 +- Oqtane.Maui/Oqtane.Maui.csproj | 16 ++++++++-------- Oqtane.Server/Oqtane.Server.csproj | 14 +++++++------- Oqtane.Shared/Oqtane.Shared.csproj | 8 ++++---- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index 9da5f4ce..bb7671d6 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -22,10 +22,10 @@ - - - - + + + + diff --git a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj index ace07515..1238c9cb 100644 --- a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj +++ b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj @@ -34,7 +34,7 @@ - + diff --git a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj index ae1e04a8..6a594463 100644 --- a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj +++ b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj @@ -33,7 +33,7 @@ - + diff --git a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj index 13ecf1ac..c13db7c9 100644 --- a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj +++ b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj @@ -33,7 +33,7 @@ - + diff --git a/Oqtane.Maui/Oqtane.Maui.csproj b/Oqtane.Maui/Oqtane.Maui.csproj index 08bf796f..6f0b4c92 100644 --- a/Oqtane.Maui/Oqtane.Maui.csproj +++ b/Oqtane.Maui/Oqtane.Maui.csproj @@ -67,14 +67,14 @@ - - - - - - - - + + + + + + + + diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index ddf80b3d..76bb68c8 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -34,17 +34,17 @@ - - + + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index 65ae667e..fbffe080 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -19,11 +19,11 @@ - - - + + + - + From ffef1f4820e5fed088d30ce450e097a239ded536 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Tue, 13 May 2025 16:52:39 -0400 Subject: [PATCH 30/34] update default templates to .NET SDK 9.0.5 --- .../Client/[Owner].Module.[Module].Client.csproj | 10 +++++----- .../Server/[Owner].Module.[Module].Server.csproj | 8 ++++---- .../Client/[Owner].Theme.[Theme].Client.csproj | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/[Owner].Module.[Module].Client.csproj b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/[Owner].Module.[Module].Client.csproj index f4e222ee..c40f7c41 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/[Owner].Module.[Module].Client.csproj +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/[Owner].Module.[Module].Client.csproj @@ -13,11 +13,11 @@ - - - - - + + + + + diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].Module.[Module].Server.csproj b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].Module.[Module].Server.csproj index 3844f51a..75da4858 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].Module.[Module].Server.csproj +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].Module.[Module].Server.csproj @@ -19,10 +19,10 @@ - - - - + + + + diff --git a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/[Owner].Theme.[Theme].Client.csproj b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/[Owner].Theme.[Theme].Client.csproj index a454ee59..77ae8f52 100644 --- a/Oqtane.Server/wwwroot/Themes/Templates/External/Client/[Owner].Theme.[Theme].Client.csproj +++ b/Oqtane.Server/wwwroot/Themes/Templates/External/Client/[Owner].Theme.[Theme].Client.csproj @@ -13,9 +13,9 @@ - - - + + + From 9000f05961d6621e4b51d1d66c439d7517867763 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 14 May 2025 11:12:58 -0400 Subject: [PATCH 31/34] move ConfigureOqtaneAssemblies to occur before UseEndpoints --- Oqtane.Server/Startup.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 51b60555..2101a861 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -195,9 +195,6 @@ namespace Oqtane app.UseHsts(); } - // execute any IServerStartup logic - app.ConfigureOqtaneAssemblies(env); - // allow oqtane localization middleware app.UseOqtaneLocalization(); @@ -228,6 +225,9 @@ namespace Oqtane app.UseAuthorization(); app.UseAntiforgery(); + // execute any IServerStartup logic + app.ConfigureOqtaneAssemblies(env); + if (_useSwagger) { app.UseSwagger(); From 57d443be8dc20236a2c55dbd6c8163247330ab6c Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 14 May 2025 12:18:37 -0400 Subject: [PATCH 32/34] support for module header and footer content --- .../Modules/Admin/Modules/Settings.razor | 75 +++++++++++++------ .../Modules/Admin/Modules/Settings.resx | 15 ++++ Oqtane.Client/UI/ModuleInstance.razor | 9 +++ Oqtane.Server/Controllers/ModuleController.cs | 2 + Oqtane.Server/Controllers/PageController.cs | 4 + .../Tenant/06010302_AddModuleHeaderFooter.cs | 29 +++++++ Oqtane.Server/Repository/SiteRepository.cs | 2 + Oqtane.Server/Services/SiteService.cs | 2 + Oqtane.Shared/Models/Module.cs | 14 ++++ Oqtane.Shared/Models/PageModule.cs | 13 ++++ Oqtane.Shared/Models/SiteTemplate.cs | 4 + 11 files changed, 147 insertions(+), 22 deletions(-) create mode 100644 Oqtane.Server/Migrations/Tenant/06010302_AddModuleHeaderFooter.cs diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index a949271f..b7cf4f4c 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -97,6 +97,23 @@
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
} @@ -144,6 +161,8 @@ private string _pane; private string _containerType; private string _allPages = "false"; + private string _header = ""; + private string _footer = ""; private string _permissionNames = ""; private List _permissions = null; private string _pageId; @@ -167,37 +186,47 @@ protected override async Task OnInitializedAsync() { SetModuleTitle(Localizer["ModuleSettings.Title"]); - - _title = ModuleState.Title; _moduleSettingsTitle = Localizer["ModuleSettings.Heading"]; - _pane = ModuleState.Pane; + _containers = ThemeService.GetContainerControls(PageState.Site.Themes, PageState.Page.ThemeType); - _containerType = ModuleState.ContainerType; - _allPages = ModuleState.AllPages.ToString(); - _permissions = ModuleState.PermissionList; - _pageId = ModuleState.PageId.ToString(); - createdby = ModuleState.CreatedBy; - createdon = ModuleState.CreatedOn; - modifiedby = ModuleState.ModifiedBy; - modifiedon = ModuleState.ModifiedOn; - _effectivedate = Utilities.UtcAsLocalDate(ModuleState.EffectiveDate); - _expirydate = Utilities.UtcAsLocalDate(ModuleState.ExpiryDate); _pages = await PageService.GetPagesAsync(PageState.Site.SiteId); - if (ModuleState.ModuleDefinition != null) - { - _module = ModuleState.ModuleDefinition.Name; - _permissionNames = ModuleState.ModuleDefinition?.PermissionNames; + var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); - if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType)) + _pageId = pagemodule.PageId.ToString(); + _title = pagemodule.Title; + _pane = pagemodule.Pane; + _containerType = pagemodule.ContainerType; + if (string.IsNullOrEmpty(_containerType)) + { + _containerType = (!string.IsNullOrEmpty(PageState.Page.DefaultContainerType)) ? PageState.Page.DefaultContainerType : PageState.Site.DefaultContainerType; + } + _header = pagemodule.Header; + _footer = pagemodule.Footer; + _effectivedate = Utilities.UtcAsLocalDate(pagemodule.EffectiveDate); + _expirydate = Utilities.UtcAsLocalDate(pagemodule.ExpiryDate); + + _allPages = pagemodule.Module.AllPages.ToString(); + createdby = pagemodule.Module.CreatedBy; + createdon = pagemodule.Module.CreatedOn; + modifiedby = pagemodule.Module.ModifiedBy; + modifiedon = pagemodule.Module.ModifiedOn; + _permissions = pagemodule.Module.PermissionList; + + if (pagemodule.Module.ModuleDefinition != null) + { + _module = pagemodule.Module.ModuleDefinition.Name; + _permissionNames = pagemodule.Module.ModuleDefinition?.PermissionNames; + + if (!string.IsNullOrEmpty(pagemodule.Module.ModuleDefinition.SettingsType)) { // module settings type explicitly declared in IModule interface - _moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType); + _moduleSettingsType = Type.GetType(pagemodule.Module.ModuleDefinition.SettingsType); } else { // legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module ) - _moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true); + _moduleSettingsType = Type.GetType(pagemodule.Module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true); } if (_moduleSettingsType != null) { @@ -218,7 +247,7 @@ } else { - AddModuleMessage(string.Format(Localizer["Error.Module.Load"], ModuleState.ModuleDefinitionName), MessageType.Error); + AddModuleMessage(string.Format(Localizer["Error.Module.Load"], pagemodule.Module.ModuleDefinitionName), MessageType.Error); } var theme = PageState.Site.Themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType))); @@ -270,10 +299,12 @@ { pagemodule.ContainerType = string.Empty; } + pagemodule.Header = _header; + pagemodule.Footer = _footer; await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); - var module = ModuleState; + var module = await ModuleService.GetModuleAsync(ModuleState.ModuleId); module.AllPages = bool.Parse(_allPages); module.PageModuleId = ModuleState.PageModuleId; module.PermissionList = _permissionGrid.GetPermissionList(); diff --git a/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx b/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx index a91aa36e..4f8f895c 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx @@ -189,4 +189,19 @@ Module Settings + + Header: + + + Optionally provide content to be injected above the module instance + + + Footer: + + + Optionally provide content to be injected below the module instance + + + Content + \ No newline at end of file diff --git a/Oqtane.Client/UI/ModuleInstance.razor b/Oqtane.Client/UI/ModuleInstance.razor index 1bd5296f..e8353a82 100644 --- a/Oqtane.Client/UI/ModuleInstance.razor +++ b/Oqtane.Client/UI/ModuleInstance.razor @@ -1,6 +1,7 @@ @namespace Oqtane.UI @inject SiteState SiteState +@((MarkupString)ModuleState.Header) @if (_comment != null) { @((MarkupString)_comment) @@ -13,6 +14,7 @@ } } +@((MarkupString)ModuleState.Footer) @code { [CascadingParameter] @@ -23,6 +25,8 @@ private bool _prerender; private string _comment; + private string _header; + private string _footer; protected override void OnParametersSet() { @@ -39,11 +43,16 @@ } _comment += " -->"; + _header = ModuleState.Header; + _footer = ModuleState.Footer; + if (PageState.RenderMode == RenderModes.Static && ModuleState.RenderMode == RenderModes.Interactive) { // trim PageState to mitigate page bloat caused by Blazor serializing/encrypting state when crossing render mode boundaries // please note that this performance optimization results in the PageState.Pages property not being available for use in Interactive components PageState.Site.Pages = new List(); + ModuleState.Header = string.Empty; + ModuleState.Footer = string.Empty; } } diff --git a/Oqtane.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs index a7c09fcb..02f6f8c6 100644 --- a/Oqtane.Server/Controllers/ModuleController.cs +++ b/Oqtane.Server/Controllers/ModuleController.cs @@ -76,6 +76,8 @@ namespace Oqtane.Controllers module.ContainerType = pagemodule.ContainerType; module.EffectiveDate = pagemodule.EffectiveDate; module.ExpiryDate = pagemodule.ExpiryDate; + module.Header = pagemodule.Header; + module.Footer = pagemodule.Footer; module.ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName)); diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index ee2ff20c..6086bd0f 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -246,6 +246,10 @@ namespace Oqtane.Controllers pagemodule.Pane = pm.Pane; pagemodule.Order = pm.Order; pagemodule.ContainerType = pm.ContainerType; + pagemodule.EffectiveDate = pm.EffectiveDate; + pagemodule.ExpiryDate = pm.ExpiryDate; + pagemodule.Header = pm.Header; + pagemodule.Footer = pm.Footer; _pageModules.AddPageModule(pagemodule); } diff --git a/Oqtane.Server/Migrations/Tenant/06010302_AddModuleHeaderFooter.cs b/Oqtane.Server/Migrations/Tenant/06010302_AddModuleHeaderFooter.cs new file mode 100644 index 00000000..39e01287 --- /dev/null +++ b/Oqtane.Server/Migrations/Tenant/06010302_AddModuleHeaderFooter.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Databases.Interfaces; +using Oqtane.Migrations.EntityBuilders; +using Oqtane.Repository; + +namespace Oqtane.Migrations.Tenant +{ + [DbContext(typeof(TenantDBContext))] + [Migration("Tenant.06.01.03.02")] + public class AddModuleHeaderFooter : MultiDatabaseMigration + { + public AddModuleHeaderFooter(IDatabase database) : base(database) + { + } + + protected override void Up(MigrationBuilder migrationBuilder) + { + var pageModuleEntityBuilder = new PageModuleEntityBuilder(migrationBuilder, ActiveDatabase); + pageModuleEntityBuilder.AddMaxStringColumn("Header", true); + pageModuleEntityBuilder.AddMaxStringColumn("Footer", true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + // not implemented + } + } +} diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index 10de3576..48620ead 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -442,6 +442,8 @@ namespace Oqtane.Repository pageModule.Pane = (string.IsNullOrEmpty(pageTemplateModule.Pane)) ? PaneNames.Default : pageTemplateModule.Pane; pageModule.Order = (pageTemplateModule.Order == 0) ? 1 : pageTemplateModule.Order; pageModule.ContainerType = pageTemplateModule.ContainerType; + pageModule.Header = pageTemplateModule.Header; + pageModule.Footer = pageTemplateModule.Footer; pageModule.IsDeleted = pageTemplateModule.IsDeleted; pageModule.Module.PermissionList = new List(); foreach (var permission in pageTemplateModule.PermissionList) diff --git a/Oqtane.Server/Services/SiteService.cs b/Oqtane.Server/Services/SiteService.cs index cc0991d8..f316b766 100644 --- a/Oqtane.Server/Services/SiteService.cs +++ b/Oqtane.Server/Services/SiteService.cs @@ -285,6 +285,8 @@ namespace Oqtane.Services ContainerType = pagemodule.ContainerType, EffectiveDate = pagemodule.EffectiveDate, ExpiryDate = pagemodule.ExpiryDate, + Header = pagemodule.Header, + Footer = pagemodule.Footer, ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == pagemodule.Module.ModuleDefinitionName)), diff --git a/Oqtane.Shared/Models/Module.cs b/Oqtane.Shared/Models/Module.cs index 55f31357..7775fcf5 100644 --- a/Oqtane.Shared/Models/Module.cs +++ b/Oqtane.Shared/Models/Module.cs @@ -113,6 +113,18 @@ namespace Oqtane.Models [NotMapped] public DateTime? ExpiryDate { get; set; } + /// + /// Header content to include at the top of a module instance in the UI + /// + [NotMapped] + public string Header { get; set; } + + /// + /// Footer content to include below a module instance in the UI + /// + [NotMapped] + public string Footer { get; set; } + #endregion #region SiteRouter properties @@ -218,6 +230,8 @@ namespace Oqtane.Models ContainerType = ContainerType, EffectiveDate = EffectiveDate, ExpiryDate = ExpiryDate, + Header = Header, + Footer = Footer, CreatedBy = CreatedBy, CreatedOn = CreatedOn, ModifiedBy = ModifiedBy, diff --git a/Oqtane.Shared/Models/PageModule.cs b/Oqtane.Shared/Models/PageModule.cs index 0e7c812f..20985e93 100644 --- a/Oqtane.Shared/Models/PageModule.cs +++ b/Oqtane.Shared/Models/PageModule.cs @@ -41,14 +41,27 @@ namespace Oqtane.Models /// Reference to a Razor Container which wraps this module instance. /// public string ContainerType { get; set; } + /// /// Start of when this assignment is valid. See also /// public DateTime? EffectiveDate { get; set; } + /// /// End of when this assignment is valid. See also /// public DateTime? ExpiryDate { get; set; } + + /// + /// Header content to include above the module instance in the UI + /// + public string Header { get; set; } + + /// + /// Footer content to include below the module instance in the UI + /// + public string Footer { get; set; } + #region IDeletable Properties public string DeletedBy { get; set; } diff --git a/Oqtane.Shared/Models/SiteTemplate.cs b/Oqtane.Shared/Models/SiteTemplate.cs index 348e1834..42cbffee 100644 --- a/Oqtane.Shared/Models/SiteTemplate.cs +++ b/Oqtane.Shared/Models/SiteTemplate.cs @@ -95,6 +95,8 @@ namespace Oqtane.Models Pane = PaneNames.Default; Order = 1; ContainerType = ""; + Header = ""; + Footer = ""; IsDeleted = false; PermissionList = new List() { @@ -110,6 +112,8 @@ namespace Oqtane.Models public string Pane { get; set; } public int Order { get; set; } public string ContainerType { get; set; } + public string Header { get; set; } + public string Footer { get; set; } public bool IsDeleted { get; set; } public List PermissionList { get; set; } public List Settings { get; set; } From f1791a709c8b9c8b88441058df9a934449875368 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 14 May 2025 14:20:44 -0400 Subject: [PATCH 33/34] fix issue with module header/footer --- Oqtane.Client/UI/ModuleInstance.razor | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Oqtane.Client/UI/ModuleInstance.razor b/Oqtane.Client/UI/ModuleInstance.razor index e8353a82..e9a634d3 100644 --- a/Oqtane.Client/UI/ModuleInstance.razor +++ b/Oqtane.Client/UI/ModuleInstance.razor @@ -1,7 +1,10 @@ @namespace Oqtane.UI @inject SiteState SiteState -@((MarkupString)ModuleState.Header) +@if (PageState.ModuleId == -1) +{ + @((MarkupString)ModuleState.Header) +} @if (_comment != null) { @((MarkupString)_comment) @@ -14,7 +17,11 @@ } } -@((MarkupString)ModuleState.Footer) +@if (PageState.ModuleId == -1) +{ + @((MarkupString)ModuleState.Footer) +} + @code { [CascadingParameter] @@ -25,8 +32,6 @@ private bool _prerender; private string _comment; - private string _header; - private string _footer; protected override void OnParametersSet() { @@ -43,16 +48,11 @@ } _comment += " -->"; - _header = ModuleState.Header; - _footer = ModuleState.Footer; - if (PageState.RenderMode == RenderModes.Static && ModuleState.RenderMode == RenderModes.Interactive) { // trim PageState to mitigate page bloat caused by Blazor serializing/encrypting state when crossing render mode boundaries // please note that this performance optimization results in the PageState.Pages property not being available for use in Interactive components PageState.Site.Pages = new List(); - ModuleState.Header = string.Empty; - ModuleState.Footer = string.Empty; } } From f3fcef52ddcaa4a07f0381aa4b399536c7601c97 Mon Sep 17 00:00:00 2001 From: sbwalker Date: Wed, 14 May 2025 15:51:51 -0400 Subject: [PATCH 34/34] fix #5200 - sort folders alphabetically, display folders hierarchically --- Oqtane.Client/Modules/Admin/Files/Edit.razor | 25 ++++++++--- Oqtane.Client/Services/FolderService.cs | 8 ---- .../Services/Interfaces/IFolderService.cs | 9 ---- Oqtane.Server/Controllers/FolderController.cs | 43 +++---------------- 4 files changed, 25 insertions(+), 60 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index b3099c5e..49ac137a 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -15,22 +15,34 @@
- - } + + } + else + { + + + }
- + @if (_isSystem) + { + + } + else + { + + }
@@ -229,7 +241,6 @@ if (folder != null) { - await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); await logger.LogInformation("Folder Saved {Folder}", folder); NavigationManager.NavigateTo(NavigateUrl()); } diff --git a/Oqtane.Client/Services/FolderService.cs b/Oqtane.Client/Services/FolderService.cs index 556309ad..2ce045db 100644 --- a/Oqtane.Client/Services/FolderService.cs +++ b/Oqtane.Client/Services/FolderService.cs @@ -42,14 +42,6 @@ namespace Oqtane.Services return await PutJsonAsync($"{ApiUrl}/{folder.FolderId}", folder); } - public async Task UpdateFolderOrderAsync(int siteId, int folderId, int? parentId) - { - var parent = parentId == null - ? string.Empty - : parentId.ToString(); - await PutAsync($"{ApiUrl}/?siteid={siteId}&folderid={folderId}&parentid={parent}"); - } - public async Task DeleteFolderAsync(int folderId) { await DeleteAsync($"{ApiUrl}/{folderId}"); diff --git a/Oqtane.Client/Services/Interfaces/IFolderService.cs b/Oqtane.Client/Services/Interfaces/IFolderService.cs index a3085f3a..35e96956 100644 --- a/Oqtane.Client/Services/Interfaces/IFolderService.cs +++ b/Oqtane.Client/Services/Interfaces/IFolderService.cs @@ -39,15 +39,6 @@ namespace Oqtane.Services /// Task UpdateFolderAsync(Folder folder); - /// - /// Update the internal Folder-Order within the list of Folders. - /// - /// Reference to the - /// Reference to a for the security check - /// Reference to the Parent or null - this Folders children will be re-sorted. - /// - Task UpdateFolderOrderAsync(int siteId, int folderId, int? parentId); - /// /// Delete a /// diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index 866f649b..12a9c3fa 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -43,7 +43,8 @@ namespace Oqtane.Controllers int SiteId; if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId) { - foreach (Folder folder in _folders.GetFolders(SiteId)) + var hierarchy = GetFoldersHierarchy(_folders.GetFolders(SiteId).ToList()); + foreach (Folder folder in hierarchy) { // note that Browse permission is used for this method if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList)) @@ -51,7 +52,6 @@ namespace Oqtane.Controllers folders.Add(folder); } } - folders = GetFoldersHierarchy(folders); } else { @@ -246,34 +246,6 @@ namespace Oqtane.Controllers return folder; } - // PUT api//?siteid=x&folderid=y&parentid=z - [HttpPut] - [Authorize(Roles = RoleNames.Registered)] - public void Put(int siteid, int folderid, int? parentid) - { - if (siteid == _alias.SiteId && _folders.GetFolder(folderid, false) != null && _userPermissions.IsAuthorized(User, siteid, EntityNames.Folder, folderid, PermissionNames.Edit)) - { - int order = 1; - List folders = _folders.GetFolders(siteid).ToList(); - foreach (Folder folder in folders.Where(item => item.ParentId == parentid).OrderBy(item => item.Order)) - { - if (folder.Order != order) - { - folder.Order = order; - _folders.UpdateFolder(folder); - _syncManager.AddSyncEvent(_alias, EntityNames.Folder, folder.FolderId, SyncEventActions.Update); - } - order += 2; - } - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Order Updated {SiteId} {FolderId} {ParentId}", siteid, folderid, parentid); - } - else - { - _logger.Log(LogLevel.Error, this, LogFunction.Update, "Unauthorized Folder Put Attempt {SiteId} {FolderId} {ParentId}", siteid, folderid, parentid); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; - } - } - // DELETE api//5 [HttpDelete("{id}")] [Authorize(Roles = RoleNames.Registered)] @@ -314,7 +286,6 @@ namespace Oqtane.Controllers { List hierarchy = new List(); Action, Folder> getPath = null; - var folders1 = folders; getPath = (folderList, folder) => { IEnumerable children; @@ -322,23 +293,23 @@ namespace Oqtane.Controllers if (folder == null) { level = -1; - children = folders1.Where(item => item.ParentId == null); + children = folders.Where(item => item.ParentId == null); } else { level = folder.Level; - children = folders1.Where(item => item.ParentId == folder.FolderId); + children = folders.Where(item => item.ParentId == folder.FolderId); } foreach (Folder child in children) { child.Level = level + 1; - child.HasChildren = folders1.Any(item => item.ParentId == child.FolderId); + child.HasChildren = folders.Any(item => item.ParentId == child.FolderId); hierarchy.Add(child); - if (getPath != null) getPath(folderList, child); + getPath(folderList, child); } }; - folders = folders.OrderBy(item => item.Order).ToList(); + folders = folders.OrderBy(item => item.Name).ToList(); getPath(folders, null); // add any non-hierarchical items to the end of the list