diff --git a/.github/workflows/browserslist-update-db.yml b/.github/workflows/browserslist-update-db.yml index 04709151be8..251cb0d7a0f 100644 --- a/.github/workflows/browserslist-update-db.yml +++ b/.github/workflows/browserslist-update-db.yml @@ -19,7 +19,7 @@ jobs: pull-requests: "write" # creates a pull request if there's an update steps: - name: "Checkout repository" - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6.0.2 + uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0 with: fetch-depth: 0 persist-credentials: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63b74acabe7..2afe3e8cfa5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,23 +59,29 @@ jobs: name: "Build and Validate" runs-on: "windows-2025-vs2026" permissions: - checks: "write" - pull-requests: "write" + checks: "write" # needed by EnricoMi/publish-unit-test-result-action + pull-requests: "write" # needed by EnricoMi/publish-unit-test-result-action defaults: run: shell: "pwsh" steps: - name: "Setup Environment Variables" run: | - "CAKE_TARGET=${{ inputs.CAKE_TARGET || env.CAKE_TARGET }}" >> $env:GITHUB_ENV; - "CAKE_VERBOSITY=${{ inputs.CAKE_VERBOSITY || env.CAKE_VERBOSITY }}" >> $env:GITHUB_ENV; - "RELEASE_MODE=${{ inputs.RELEASE_MODE || env.RELEASE_MODE }}" >> $env:GITHUB_ENV; - "RUN_TESTS=${{ inputs.RUN_TESTS || env.RUN_TESTS }}" >> $env:GITHUB_ENV; + "CAKE_TARGET=$env:CAKE_TARGET" >> $env:GITHUB_ENV; + "CAKE_VERBOSITY=$env:CAKE_VERBOSITY" >> $env:GITHUB_ENV; + "RELEASE_MODE=$env:RELEASE_MODE" >> $env:GITHUB_ENV; + "RUN_TESTS=$env:RUN_TESTS" >> $env:GITHUB_ENV; + env: + CAKE_TARGET: ${{ inputs.CAKE_TARGET || env.CAKE_TARGET }} + CAKE_VERBOSITY: ${{ inputs.CAKE_VERBOSITY || env.CAKE_VERBOSITY }} + RELEASE_MODE: ${{ inputs.RELEASE_MODE || env.RELEASE_MODE }} + RUN_TESTS: ${{ inputs.RUN_TESTS || env.RUN_TESTS }} - name: "Checkout" - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6.0.2 + uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0 with: fetch-depth: 0 # include all history for GitVersion + persist-credentials: false - name: "Setup Node.js" uses: "actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e" # v6.4.0 @@ -103,22 +109,22 @@ jobs: (Get-Content $path) | ForEach-Object { if ($_ -match $pattern3) { # We have found the matching line - '[assembly: AssemblyStatus(ReleaseMode.{0})]' -f '${{ env.RELEASE_MODE }}' + '[assembly: AssemblyStatus(ReleaseMode.{0})]' -f $env:RELEASE_MODE } else { $_ } } | Set-Content $path - name: "Run DNN Build via Cake" - run: "./build.ps1 --target=${{ env.CAKE_TARGET }} --verbosity=${{ env.CAKE_VERBOSITY }}" + run: "./build.ps1 --target=$env:CAKE_TARGET --verbosity=$env:CAKE_VERBOSITY" - name: "Run Unit Tests via Cake" if: ${{ env.RUN_TESTS == 'true' }} - run: "./build.ps1 --target=UnitTests --verbosity=${{ env.CAKE_VERBOSITY }}" + run: "./build.ps1 --target=UnitTests --verbosity=$env:CAKE_VERBOSITY" continue-on-error: true - name: "Publish Test Results" - uses: "EnricoMi/publish-unit-test-result-action/windows@c950f6fb443cb5af20a377fd0dfaa78838901040" # v2.23.0 + uses: "EnricoMi/publish-unit-test-result-action/windows@d0a4676d0e0b938bc201470d88276b7c74c712b3" # v2.24.0 if: ${{ !cancelled() && env.RUN_TESTS == 'true' }} with: files: "**/TestResults/*.trx" @@ -135,8 +141,8 @@ jobs: overwrite: true - name: "Create GitHub Pull Request" - if: ${{ success() && !contains('pull_request', github.event_name) && (contains(fromJSON('["develop", "main"]'), github.ref_name) || startsWith('release/', github.ref_name)) }} - run: "./build.ps1 --target=CreateGitHubPullRequest --verbosity=${{ env.CAKE_VERBOSITY }}" + if: ${{ success() && github.event_name == 'push' && (github.ref_name == 'develop' || startsWith('release/', github.ref_name)) }} + run: "./build.ps1 --target=CreateGitHubPullRequest --verbosity=$env:CAKE_VERBOSITY" env: GITHUB_APP_ID: ${{ secrets.GITHUB_APP_ID }} GITHUB_APP_PRIVATE_KEY: ${{ secrets.GITHUB_APP_PRIVATE_KEY }} @@ -150,9 +156,9 @@ jobs: if: ${{ github.event_name == 'workflow_dispatch' }} runs-on: "ubuntu-latest" permissions: - id-token: "write" + id-token: "write" # needed by actions/attest contents: "read" - attestations: "write" + attestations: "write" # needed by actions/attest steps: - name: "Download Artifacts" uses: "actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" # v8.0.1 @@ -174,7 +180,7 @@ jobs: if: ${{ github.event_name == 'workflow_dispatch' }} runs-on: "ubuntu-latest" permissions: - id-token: "write" + id-token: "write" # needed by NuGet/login defaults: run: shell: "pwsh" @@ -191,4 +197,7 @@ jobs: user: ${{ secrets.NUGET_USER }} - name: "NuGet Push" - run: "dotnet nuget push *.nupkg --api-key ${{ steps.nuget-login.outputs.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json" + run: "dotnet nuget push *.nupkg --api-key $env:NUGET_API_KEY --source https://api.nuget.org/v3/index.json" + env: + NUGET_API_KEY: ${{ steps.nuget-login.outputs.NUGET_API_KEY }} + diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 0efd04421e2..3541d46c08b 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -1,9 +1,9 @@ name: "Dependency Review" -on: +on: pull_request: merge_group: - types: + types: - "checks_requested" concurrency: @@ -14,11 +14,17 @@ permissions: {} jobs: dependency-review: + name: "Dependency Review" runs-on: "ubuntu-latest" permissions: contents: "read" steps: - name: "Checkout Repository" - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6.0.2 + uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0 + with: + persist-credentials: false + - name: "Dependency Review" uses: "actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294" # v5.0.0 + + diff --git a/.github/workflows/image-actions.yml b/.github/workflows/image-actions.yml index 56e77b4aaf2..621dd429c1c 100644 --- a/.github/workflows/image-actions.yml +++ b/.github/workflows/image-actions.yml @@ -36,7 +36,7 @@ jobs: contents: "read" steps: - name: "Checkout Repo" - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6.0.2 + uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0 with: persist-credentials: false diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 8e0742f4ce7..0e2765268f5 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -29,7 +29,7 @@ jobs: steps: - name: "Checkout code" - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6.0.2 + uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0 with: persist-credentials: false @@ -58,6 +58,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: "github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa" # v4.36.0 + uses: "github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e" # v4.36.2 with: sarif_file: "results.sarif" diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 6e468e6f623..7e49d504429 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -28,7 +28,7 @@ jobs: actions: read # Only needed for private repos. Needed for upload-sarif to read workflow run info. steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/.yarnrc.yml b/.yarnrc.yml index f787d935d16..101fcaa7250 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,8 +1,12 @@ +approvedGitRepositories: [] + compressionLevel: mixed +enableScripts: false + nodeLinker: node-modules -npmMinimalAgeGate: "1d" +npmMinimalAgeGate: 1d npmPreapprovedPackages: - "@dnncommunity/dnn-elements" diff --git a/Build/Tasks/PreparePackaging.cs b/Build/Tasks/PreparePackaging.cs index 40f9b980896..d11d4d3c45b 100644 --- a/Build/Tasks/PreparePackaging.cs +++ b/Build/Tasks/PreparePackaging.cs @@ -4,11 +4,15 @@ namespace DotNetNuke.Build.Tasks { using System; + using System.Collections.Generic; + using System.Globalization; using System.Linq; + using System.Reflection; using System.Xml.Linq; using Cake.Common.Diagnostics; using Cake.Common.IO; + using Cake.Core.IO; using Cake.Frosting; using Cake.Json; using Dnn.CakeUtils; @@ -60,14 +64,52 @@ private static void CreateWebConfig(Context context) context.PackagingPatterns = context.DeserializeJsonFromFile("./Build/Tasks/packaging.json"); var files = context.GetFilesByPatterns(context.WebsiteDir, BinFolderInclude, context.PackagingPatterns.InstallExclude); - var parsedAssemblies = files.ParseAssemblies(); - parsedAssemblies.RemoveAll(a => a.PublicKeyToken is null); - var redirects = parsedAssemblies.ConvertAll(a => a.AssemblyBindingRedirect()); + var redirects = BuildBindingRedirects(files); assemblyBinding.Add(redirects.ToArray()); // save XML document to target file var targetFile = context.WebsiteDir + context.File("web.config"); doc.Save(targetFile); } + + private static List BuildBindingRedirects(IEnumerable files) + { + XNamespace asm = "urn:schemas-microsoft-com:asm.v1"; + var redirects = new List(); + + foreach (var file in files) + { + AssemblyName assemblyName; + try + { + assemblyName = AssemblyName.GetAssemblyName(file.FullPath); + } + catch + { + continue; + } + + var tokenBytes = assemblyName.GetPublicKeyToken(); + if (tokenBytes == null || tokenBytes.Length == 0 || string.IsNullOrEmpty(assemblyName.Name) || assemblyName.Version == null) + { + continue; + } + + var token = string.Concat(tokenBytes.Select(static b => b.ToString("x2", CultureInfo.InvariantCulture))); + redirects.Add( + new XElement( + asm + "dependentAssembly", + new XElement( + asm + "assemblyIdentity", + new XAttribute("name", assemblyName.Name), + new XAttribute("publicKeyToken", token)), + new XElement( + asm + "bindingRedirect", + new XAttribute("oldVersion", "0.0.0.0-32767.32767.32767.32767"), + new XAttribute("newVersion", assemblyName.Version.ToString())))); + } + + return redirects; + } } } diff --git a/DNN Platform/Admin Modules/Dnn.Modules.Console/Dnn.Modules.Console.csproj b/DNN Platform/Admin Modules/Dnn.Modules.Console/Dnn.Modules.Console.csproj index 41469d8c0f6..f06ced21a49 100644 --- a/DNN Platform/Admin Modules/Dnn.Modules.Console/Dnn.Modules.Console.csproj +++ b/DNN Platform/Admin Modules/Dnn.Modules.Console/Dnn.Modules.Console.csproj @@ -103,6 +103,10 @@ + + {5FE5D021-9C8D-47A6-BD34-F328BA3E709C} + DotNetNuke.Internal.SourceGenerators + {6928A9B1-F88A-4581-A132-D3EB38669BB0} DotNetNuke.Abstractions diff --git a/DNN Platform/Admin Modules/Dnn.Modules.Console/ViewConsole.ascx b/DNN Platform/Admin Modules/Dnn.Modules.Console/ViewConsole.ascx index 246d8ba5146..823ae81e4a7 100644 --- a/DNN Platform/Admin Modules/Dnn.Modules.Console/ViewConsole.ascx +++ b/DNN Platform/Admin Modules/Dnn.Modules.Console/ViewConsole.ascx @@ -2,7 +2,7 @@ <%@ Register TagPrefix="dnn" Namespace="DotNetNuke.Web.UI.WebControls" Assembly="DotNetNuke.Web" %> diff --git a/DNN Platform/Admin Modules/Dnn.Modules.Console/ViewConsole.ascx.cs b/DNN Platform/Admin Modules/Dnn.Modules.Console/ViewConsole.ascx.cs index c72da3ae90b..35cf886a1a1 100644 --- a/DNN Platform/Admin Modules/Dnn.Modules.Console/ViewConsole.ascx.cs +++ b/DNN Platform/Admin Modules/Dnn.Modules.Console/ViewConsole.ascx.cs @@ -1,539 +1,566 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace Dnn.Modules.Console +namespace Dnn.Modules.Console; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Web; +using System.Web.UI.WebControls; + +using Dnn.Modules.Console.Components; +using DotNetNuke.Abstractions; +using DotNetNuke.Abstractions.Application; +using DotNetNuke.Abstractions.ClientResources; +using DotNetNuke.Common.Utilities; +using DotNetNuke.Entities.Modules; +using DotNetNuke.Entities.Tabs; +using DotNetNuke.Entities.Users; +using DotNetNuke.Framework.JavaScriptLibraries; +using DotNetNuke.Instrumentation; +using DotNetNuke.Internal.SourceGenerators; +using DotNetNuke.Security.Permissions; +using DotNetNuke.Security.Roles; +using DotNetNuke.Services.ClientDependency; +using DotNetNuke.Services.Exceptions; +using DotNetNuke.Services.Localization; +using DotNetNuke.Services.Personalization; +using Microsoft.Extensions.DependencyInjection; + +/// Implements the module view logic. +public partial class ViewConsole : PortalModuleBase { - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Text; - using System.Web; - using System.Web.UI.WebControls; - - using Dnn.Modules.Console.Components; - using DotNetNuke.Abstractions; - using DotNetNuke.Abstractions.Application; - using DotNetNuke.Abstractions.ClientResources; - using DotNetNuke.Common.Utilities; - using DotNetNuke.Entities.Modules; - using DotNetNuke.Entities.Tabs; - using DotNetNuke.Entities.Users; - using DotNetNuke.Framework.JavaScriptLibraries; - using DotNetNuke.Instrumentation; - using DotNetNuke.Security.Permissions; - using DotNetNuke.Security.Roles; - using DotNetNuke.Services.ClientDependency; - using DotNetNuke.Services.Exceptions; - using DotNetNuke.Services.Localization; - using DotNetNuke.Services.Personalization; - using Microsoft.Extensions.DependencyInjection; - - /// Implements the module view logic. - public partial class ViewConsole : PortalModuleBase + private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(ViewConsole)); + private readonly INavigationManager navigationManager; + private readonly IJavaScriptLibraryHelper javaScript; + private readonly IClientResourceController clientResourceController; + private readonly IHostSettings hostSettings; + + private string defaultSize = string.Empty; + private string defaultView = string.Empty; + private int groupTabId = -1; + private IList tabs; + + /// Initializes a new instance of the class. + [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] + public ViewConsole() + : this(null, null, null) { - private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(ViewConsole)); - private readonly INavigationManager navigationManager; - private readonly IJavaScriptLibraryHelper javaScript; - private readonly IClientResourceController clientResourceController; - private readonly IHostSettings hostSettings; - - private string defaultSize = string.Empty; - private string defaultView = string.Empty; - private int groupTabId = -1; - private IList tabs; - - /// Initializes a new instance of the class. - [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] - public ViewConsole() - : this(null, null, null) - { - } + } - /// Initializes a new instance of the class. - /// The navigation manager. - /// The JavaScript library helper. - /// The client resources controller. - [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] - public ViewConsole(INavigationManager navigationManager, IJavaScriptLibraryHelper javaScript, IClientResourceController clientResourceController) - : this(navigationManager, javaScript, clientResourceController, null) - { - } + /// Initializes a new instance of the class. + /// The navigation manager. + /// The JavaScript library helper. + /// The client resources controller. + [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] + public ViewConsole(INavigationManager navigationManager, IJavaScriptLibraryHelper javaScript, IClientResourceController clientResourceController) + : this(navigationManager, javaScript, clientResourceController, null) + { + } - /// Initializes a new instance of the class. - /// The navigation manager. - /// The JavaScript library helper. - /// The client resources controller. - /// The host settings. - public ViewConsole(INavigationManager navigationManager, IJavaScriptLibraryHelper javaScript, IClientResourceController clientResourceController, IHostSettings hostSettings) - { - this.navigationManager = navigationManager ?? this.DependencyProvider.GetRequiredService(); - this.javaScript = javaScript ?? this.DependencyProvider.GetRequiredService(); - this.clientResourceController = clientResourceController ?? this.DependencyProvider.GetRequiredService(); - this.hostSettings = hostSettings ?? this.DependencyProvider.GetRequiredService(); - } + /// Initializes a new instance of the class. + /// The navigation manager. + /// The JavaScript library helper. + /// The client resources controller. + /// The host settings. + public ViewConsole(INavigationManager navigationManager, IJavaScriptLibraryHelper javaScript, IClientResourceController clientResourceController, IHostSettings hostSettings) + { + this.navigationManager = navigationManager ?? this.DependencyProvider.GetRequiredService(); + this.javaScript = javaScript ?? this.DependencyProvider.GetRequiredService(); + this.clientResourceController = clientResourceController ?? this.DependencyProvider.GetRequiredService(); + this.hostSettings = hostSettings ?? this.DependencyProvider.GetRequiredService(); + } - /// Gets a value indicating whether the module settings allow size change. - public bool AllowSizeChange => !this.Settings.ContainsKey("AllowSizeChange") || bool.Parse(this.Settings["AllowSizeChange"].ToString()); + /// Gets a value indicating whether the module settings allow size change. + public bool AllowSizeChange => !this.Settings.ContainsKey("AllowSizeChange") || bool.Parse(this.Settings["AllowSizeChange"].ToString()); - /// Gets a value indicating whether the module settings allow to change view. - public bool AllowViewChange => !this.Settings.ContainsKey("AllowViewChange") || bool.Parse(this.Settings["AllowViewChange"].ToString()); + /// Gets a value indicating whether the module settings allow to change view. + public bool AllowViewChange => !this.Settings.ContainsKey("AllowViewChange") || bool.Parse(this.Settings["AllowViewChange"].ToString()); - /// Gets a value indicating whether the module settings indicate to include hidden pages. - public bool IncludeHiddenPages => this.Settings.ContainsKey("IncludeHiddenPages") && bool.Parse(this.Settings["IncludeHiddenPages"].ToString()); + /// Gets a value indicating whether the module settings indicate to include hidden pages. + public bool IncludeHiddenPages => this.Settings.ContainsKey("IncludeHiddenPages") && bool.Parse(this.Settings["IncludeHiddenPages"].ToString()); - /// Gets the id of the page (tab) for the root node of the console display. - public int ConsoleTabID => - (this.Mode == "Profile") - ? this.PortalSettings.UserTabId - : (this.Settings.ContainsKey("ParentTabID") - ? int.Parse(this.Settings["ParentTabID"].ToString()) - : this.TabId); + /// Gets the id of the page (tab) for the root node of the console display. + public int ConsoleTabID => + (this.Mode == "Profile") + ? this.PortalSettings.UserTabId + : (this.Settings.ContainsKey("ParentTabID") + ? int.Parse(this.Settings["ParentTabID"].ToString()) + : this.TabId); - /// Gets the configured console width or an empty string if not specified in the settings. - public string ConsoleWidth => this.Settings.ContainsKey("ConsoleWidth") ? this.Settings["ConsoleWidth"].ToString() : string.Empty; + /// Gets the configured console width or an empty string if not specified in the settings. + public string ConsoleWidth => this.Settings.ContainsKey("ConsoleWidth") ? this.Settings["ConsoleWidth"].ToString() : string.Empty; - /// Gets the default size for the console icons. - public string DefaultSize + /// Gets the default size for the console icons. + public string DefaultSize + { + get { - get + if (this.defaultSize == string.Empty && this.AllowSizeChange && this.UserId > Null.NullInteger) { - if (this.defaultSize == string.Empty && this.AllowSizeChange && this.UserId > Null.NullInteger) + object personalizedValue = this.GetUserSetting("DefaultSize"); + if (personalizedValue != null) { - object personalizedValue = this.GetUserSetting("DefaultSize"); - if (personalizedValue != null) - { - this.defaultSize = Convert.ToString(personalizedValue); - } - } - - if (this.defaultSize == string.Empty) - { - this.defaultSize = this.Settings.ContainsKey("DefaultSize") ? Convert.ToString(this.Settings["DefaultSize"]) : "IconFile"; + this.defaultSize = Convert.ToString(personalizedValue); } + } - return this.defaultSize; + if (this.defaultSize == string.Empty) + { + this.defaultSize = this.Settings.ContainsKey("DefaultSize") ? Convert.ToString(this.Settings["DefaultSize"]) : "IconFile"; } + + return this.defaultSize; } + } - /// Gets the default view module for the console. - public string DefaultView + /// Gets the default view module for the console. + public string DefaultView + { + get { - get + if (this.defaultView == string.Empty && this.AllowViewChange && this.UserId > Null.NullInteger) { - if (this.defaultView == string.Empty && this.AllowViewChange && this.UserId > Null.NullInteger) + object personalizedValue = this.GetUserSetting("DefaultView"); + if (personalizedValue != null) { - object personalizedValue = this.GetUserSetting("DefaultView"); - if (personalizedValue != null) - { - this.defaultView = Convert.ToString(personalizedValue); - } - } - - if (this.defaultView == string.Empty) - { - this.defaultView = this.Settings.ContainsKey("DefaultView") ? Convert.ToString(this.Settings["DefaultView"]) : "Hide"; + this.defaultView = Convert.ToString(personalizedValue); } + } - return this.defaultView; + if (this.defaultView == string.Empty) + { + this.defaultView = this.Settings.ContainsKey("DefaultView") ? Convert.ToString(this.Settings["DefaultView"]) : "Hide"; } + + return this.defaultView; } + } - /// Gets the group id, if not displayed in a group, will return . - public int GroupId + /// Gets the group id, if not displayed in a group, will return . + public int GroupId + { + get { - get + var groupId = Null.NullInteger; + if (!string.IsNullOrEmpty(this.Request.Params["GroupId"])) { - var groupId = Null.NullInteger; - if (!string.IsNullOrEmpty(this.Request.Params["GroupId"])) - { - groupId = int.Parse(this.Request.Params["GroupId"]); - } - - return groupId; + groupId = int.Parse(this.Request.Params["GroupId"]); } + + return groupId; } + } - /// Gets a value indicating whether the parent should be shown. - public bool IncludeParent => (this.Mode == "Profile") || (this.Settings.ContainsKey("IncludeParent") && bool.Parse(this.Settings["IncludeParent"].ToString())); + /// Gets a value indicating whether the parent should be shown. + public bool IncludeParent => (this.Mode == "Profile") || (this.Settings.ContainsKey("IncludeParent") && bool.Parse(this.Settings["IncludeParent"].ToString())); - /// Gets the module display mode. - public string Mode => this.Settings.ContainsKey("Mode") ? this.Settings["Mode"].ToString() : "Normal"; + /// Gets the module display mode. + public string Mode => this.Settings.ContainsKey("Mode") ? this.Settings["Mode"].ToString() : "Normal"; - /// Gets the id of the user when used in a user profile page, if not used on a user profile returns . - public int ProfileUserId + /// Gets the id of the user when used in a user profile page, if not used on a user profile returns . + public int ProfileUserId + { + get { - get + var userId = Null.NullInteger; + if (!string.IsNullOrEmpty(this.Request.Params["UserId"])) { - var userId = Null.NullInteger; - if (!string.IsNullOrEmpty(this.Request.Params["UserId"])) - { - userId = int.Parse(this.Request.Params["UserId"]); - } - - return userId; + userId = int.Parse(this.Request.Params["UserId"]); } + + return userId; } + } - /// Gets a value indicating whether the tooltips should be shown. - public bool ShowTooltip => !this.Settings.ContainsKey("ShowTooltip") || bool.Parse(this.Settings["ShowTooltip"].ToString()); + /// Gets a value indicating whether the tooltips should be shown. + public bool ShowTooltip => !this.Settings.ContainsKey("ShowTooltip") || bool.Parse(this.Settings["ShowTooltip"].ToString()); - /// Gets a value indicating whether the pages (tabs) should by ordered by their hierarchy. - public bool OrderTabsByHierarchy => this.Settings.ContainsKey("OrderTabsByHierarchy") && bool.Parse(this.Settings["OrderTabsByHierarchy"].ToString()); + /// Gets a value indicating whether the pages (tabs) should by ordered by their hierarchy. + public bool OrderTabsByHierarchy => this.Settings.ContainsKey("OrderTabsByHierarchy") && bool.Parse(this.Settings["OrderTabsByHierarchy"].ToString()); - /// - protected override void OnInit(EventArgs e) - { - base.OnInit(e); + /// + protected override void OnInit(EventArgs e) + { + base.OnInit(e); - try - { - this.javaScript.RequestRegistration(CommonJs.jQuery); + try + { + this.javaScript.RequestRegistration(CommonJs.jQuery); - this.clientResourceController.RegisterScript("~/desktopmodules/admin/console/scripts/jquery.console.js"); + this.clientResourceController.RegisterScript("~/desktopmodules/admin/console/scripts/jquery.console.js"); - this.DetailView.ItemDataBound += this.RepeaterItemDataBound; + this.DetailView.ItemDataBound += this.RepeaterItemDataBound; - // Save User Preferences - this.SavePersonalizedSettings(); - } - catch (Exception exc) - { - Exceptions.ProcessModuleLoadException(this, exc); - } + // Save User Preferences + this.SavePersonalizedSettings(); + } + catch (Exception exc) + { + Exceptions.ProcessModuleLoadException(this, exc); } + } - /// - protected override void OnLoad(EventArgs e) + /// + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + try { - base.OnLoad(e); - try - { - this.IconSize.Visible = this.AllowSizeChange; - this.View.Visible = this.AllowViewChange; + this.IconSize.Visible = this.AllowSizeChange; + this.View.Visible = this.AllowViewChange; - foreach (string val in ConsoleController.GetSizeValues()) - { - this.IconSize.Items.Add(new ListItem(Localization.GetString(val + ".Text", this.LocalResourceFile), val)); - } + foreach (string val in ConsoleController.GetSizeValues()) + { + this.IconSize.Items.Add(new ListItem(Localization.GetString(val + ".Text", this.LocalResourceFile), val)); + } - foreach (string val in ConsoleController.GetViewValues()) - { - this.View.Items.Add(new ListItem(Localization.GetString(val + ".Text", this.LocalResourceFile), val)); - } + foreach (string val in ConsoleController.GetViewValues()) + { + this.View.Items.Add(new ListItem(Localization.GetString(val + ".Text", this.LocalResourceFile), val)); + } - this.IconSize.SelectedValue = this.DefaultSize; - this.View.SelectedValue = this.DefaultView; + this.IconSize.SelectedValue = this.DefaultSize; + this.View.SelectedValue = this.DefaultView; - if (!this.IsPostBack) - { - this.Console.Attributes["class"] = this.Console.Attributes["class"] + " " + this.Mode.ToLower(CultureInfo.InvariantCulture); + if (!this.IsPostBack) + { + this.Console.Attributes["class"] = this.Console.Attributes["class"] + " " + this.Mode.ToLower(CultureInfo.InvariantCulture); - this.SettingsBreak.Visible = this.AllowSizeChange && this.AllowViewChange; + this.SettingsBreak.Visible = this.AllowSizeChange && this.AllowViewChange; - List tempTabs = this.IsHostTab() - ? TabController.GetTabsBySortOrder(Null.NullInteger).OrderBy(t => t.Level).ThenBy(t => t.LocalizedTabName).ToList() - : TabController.GetTabsBySortOrder(this.PortalId).OrderBy(t => t.Level).ThenBy(t => t.LocalizedTabName).ToList(); + List tempTabs = this.IsHostTab() + ? TabController.GetTabsBySortOrder(Null.NullInteger).OrderBy(t => t.Level).ThenBy(t => t.LocalizedTabName).ToList() + : TabController.GetTabsBySortOrder(this.PortalId).OrderBy(t => t.Level).ThenBy(t => t.LocalizedTabName).ToList(); - this.tabs = new List(); + this.tabs = new List(); - IList tabIdList = new List(); - tabIdList.Add(this.ConsoleTabID); + IList tabIdList = new List(); + tabIdList.Add(this.ConsoleTabID); - if (this.IncludeParent) + if (this.IncludeParent) + { + TabInfo consoleTab = TabController.Instance.GetTab(this.ConsoleTabID, this.PortalId); + if (consoleTab != null) { - TabInfo consoleTab = TabController.Instance.GetTab(this.ConsoleTabID, this.PortalId); - if (consoleTab != null) - { - this.tabs.Add(consoleTab); - } + this.tabs.Add(consoleTab); } + } - foreach (TabInfo tab in tempTabs) + foreach (TabInfo tab in tempTabs) + { + if (!this.CanShowTab(tab)) { - if (!this.CanShowTab(tab)) - { - continue; - } - - if (tabIdList.Contains(tab.ParentId)) - { - if (!tabIdList.Contains(tab.TabID)) - { - tabIdList.Add(tab.TabID); - } - - this.tabs.Add(tab); - } + continue; } - // if OrderTabsByHierarchy set to true, we need reorder the tab list to move tabs which have child tabs to the end of list. - // so that the list display in UI can show tabs in same level in same area, and not break by child tabs. - if (this.OrderTabsByHierarchy) + if (tabIdList.Contains(tab.ParentId)) { - this.tabs = this.tabs.OrderBy(t => t.HasChildren).ToList(); - } + if (!tabIdList.Contains(tab.TabID)) + { + tabIdList.Add(tab.TabID); + } - int minLevel = -1; - if (this.tabs.Count > 0) - { - minLevel = this.tabs.Min(t => t.Level); + this.tabs.Add(tab); } + } - this.DetailView.DataSource = (minLevel > -1) ? this.tabs.Where(t => t.Level == minLevel) : this.tabs; - this.DetailView.DataBind(); + // if OrderTabsByHierarchy set to true, we need reorder the tab list to move tabs which have child tabs to the end of list. + // so that the list display in UI can show tabs in same level in same area, and not break by child tabs. + if (this.OrderTabsByHierarchy) + { + this.tabs = this.tabs.OrderBy(t => t.HasChildren).ToList(); } - if (this.ConsoleWidth != string.Empty) + int minLevel = -1; + if (this.tabs.Count > 0) { - this.Console.Attributes.Add("style", "width:" + this.ConsoleWidth); + minLevel = this.tabs.Min(t => t.Level); } + + this.DetailView.DataSource = (minLevel > -1) ? this.tabs.Where(t => t.Level == minLevel) : this.tabs; + this.DetailView.DataBind(); } - catch (Exception exc) + + if (this.ConsoleWidth != string.Empty) { - Exceptions.ProcessModuleLoadException(this, exc); + this.Console.Attributes.Add("style", $"width:{this.ConsoleWidth}"); } } + catch (Exception exc) + { + Exceptions.ProcessModuleLoadException(this, exc); + } + } - /// Gets the HTML rendering of the console view according to the module settings. - /// The root page to render the console from, . - /// A string containing the rendered HTML. - protected string GetHtml(TabInfo tab) + /// Gets the HTML rendering of the console view according to the module settings. + /// The root page to render the console from, . + /// A string containing the rendered HTML. + protected string GetHtml(TabInfo tab) + { + string returnValue = string.Empty; + if (this.groupTabId > -1 && this.groupTabId != tab.ParentId) { - string returnValue = string.Empty; - if (this.groupTabId > -1 && this.groupTabId != tab.ParentId) + this.groupTabId = -1; + if (!tab.DisableLink) { - this.groupTabId = -1; - if (!tab.DisableLink) - { - returnValue = "

"; - } + returnValue = "

"; } + } - if (tab.DisableLink) + if (tab.DisableLink) + { + returnValue += $"

{WebUtility.HtmlEncode(tab.TabName)}


"; + this.groupTabId = tab.TabID; + } + else + { + var sb = new StringBuilder(); + sb.Append( + tab.TabID == this.PortalSettings.ActiveTab.TabID + ? "
" + : "
"); + + sb.Append(""); + + if (this.DefaultSize != "IconNone" || this.AllowSizeChange || this.AllowViewChange) { - const string headerHtml = "

{0}


"; - returnValue += string.Format(headerHtml, tab.TabName); - this.groupTabId = tab.TabID; + sb.Append("\"{3}\""); + sb.Append("\"{3}\""); } - else - { - var sb = new StringBuilder(); - if (tab.TabID == this.PortalSettings.ActiveTab.TabID) - { - sb.Append("
"); - } - else - { - sb.Append("
"); - } - sb.Append(""); + sb.Append(""); + sb.Append("

{3}

"); + sb.Append("
{4}
"); + sb.Append("
"); - if (this.DefaultSize != "IconNone" || (this.AllowSizeChange || this.AllowViewChange)) - { - sb.Append("\"{3}\""); - sb.Append("\"{3}\""); - } + ////const string contentHtml = "
" + "\"{3}\"\"{3}\"" + "

{3}

" + "
{4}
" + "
"; + var tabUrl = tab.FullUrl; + if (this.ProfileUserId > -1) + { + tabUrl = this.navigationManager.NavigateURL(tab.TabID, string.Empty, "UserId=" + this.ProfileUserId.ToString(CultureInfo.InvariantCulture)); + } - sb.Append(""); - sb.Append("

{3}

"); - sb.Append("
{4}
"); - sb.Append("
"); + if (this.GroupId > -1) + { + tabUrl = this.navigationManager.NavigateURL(tab.TabID, string.Empty, "GroupId=" + this.GroupId.ToString(CultureInfo.InvariantCulture)); + } - ////const string contentHtml = "
" + "\"{3}\"\"{3}\"" + "

{3}

" + "
{4}
" + "
"; - var tabUrl = tab.FullUrl; - if (this.ProfileUserId > -1) - { - tabUrl = this.navigationManager.NavigateURL(tab.TabID, string.Empty, "UserId=" + this.ProfileUserId.ToString(CultureInfo.InvariantCulture)); - } + returnValue += string.Format( + sb.ToString(), + tabUrl, + this.GetIconUrl(tab.IconFile, "IconFile"), + this.GetIconUrl(tab.IconFileLarge, "IconFileLarge"), + WebUtility.HtmlEncode(tab.LocalizedTabName), + WebUtility.HtmlEncode(tab.Description)); + } - if (this.GroupId > -1) - { - tabUrl = this.navigationManager.NavigateURL(tab.TabID, string.Empty, "GroupId=" + this.GroupId.ToString(CultureInfo.InvariantCulture)); - } + return returnValue; + } - returnValue += string.Format( - sb.ToString(), - tabUrl, - this.GetIconUrl(tab.IconFile, "IconFile"), - this.GetIconUrl(tab.IconFileLarge, "IconFileLarge"), - tab.LocalizedTabName, - tab.Description); - } + /// Gets the client side settings for the module. + /// A settings string ready to use by the .dnnConsole jQuery plugin. + [DnnDeprecated(10, 3, 3, "Use GetClientSideSettingsObject")] + protected partial string GetClientSideSettings() + { + string tabModuleId = "-1"; + if (this.UserId > -1) + { + tabModuleId = this.TabModuleId.ToString(CultureInfo.InvariantCulture); + } + + return string.Format( + "allowIconSizeChange: {0}, allowDetailChange: {1}, selectedSize: '{2}', showDetails: '{3}', tabModuleID: {4}, showTooltip: {5}", + this.AllowSizeChange.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(), + this.AllowViewChange.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(), + HttpUtility.JavaScriptStringEncode(this.DefaultSize), + HttpUtility.JavaScriptStringEncode(this.DefaultView), + tabModuleId, + this.ShowTooltip.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()); + } - return returnValue; + /// Gets the client side settings for the module. + /// A JSON object with settings for the .dnnConsole jQuery plugin. + protected IHtmlString GetClientSideSettingsObject() + { + var tabModuleId = -1; + if (this.UserId > -1) + { + tabModuleId = this.TabModuleId; } - /// Gets the client side settings for the module. - /// - /// A settings string ready to use by the .dnnConsole jQuery plugin. - /// - protected string GetClientSideSettings() + var json = new { - string tabModuleId = "-1"; - if (this.UserId > -1) - { - tabModuleId = this.TabModuleId.ToString(CultureInfo.InvariantCulture); - } + allowIconSizeChange = this.AllowSizeChange, + allowDetailChange = this.AllowViewChange, + selectedSize = this.DefaultSize, + showDetails = this.DefaultView, + tabModuleID = tabModuleId, + showTooltip = this.ShowTooltip, + }.ToJson(); + return new HtmlString(json); + } - return string.Format( - "allowIconSizeChange: {0}, allowDetailChange: {1}, selectedSize: '{2}', showDetails: '{3}', tabModuleID: {4}, showTooltip: {5}", - this.AllowSizeChange.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(), - this.AllowViewChange.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(), - HttpUtility.JavaScriptStringEncode(this.DefaultSize), - HttpUtility.JavaScriptStringEncode(this.DefaultView), - tabModuleId, - this.ShowTooltip.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()); + private bool CanShowTab(TabInfo tab) + { + var canShowTab = TabPermissionController.CanViewPage(tab) && + !tab.IsDeleted && + (this.IncludeHiddenPages || tab.IsVisible) && + (tab.StartDate < DateTime.Now || tab.StartDate == Null.NullDate); + + if (!canShowTab) + { + return false; } - private bool CanShowTab(TabInfo tab) + var key = $"TabVisibility{tab.TabPath.Replace("//", "-")}"; + var visibility = this.Settings.ContainsKey(key) ? this.Settings[key].ToString() : "AllUsers"; + + switch (visibility) { - bool canShowTab = TabPermissionController.CanViewPage(tab) && - !tab.IsDeleted && - (this.IncludeHiddenPages || tab.IsVisible) && - (tab.StartDate < DateTime.Now || tab.StartDate == Null.NullDate); + case "Owner": + canShowTab = this.UserInfo.Social.Roles.SingleOrDefault(ur => ur.RoleID == this.GroupId && ur.IsOwner) != null; + break; + case "Members": + var group = RoleController.Instance.GetRole(this.PortalId, r => r.RoleID == this.GroupId); + canShowTab = (group != null) && this.UserInfo.IsInRole(group.RoleName); + break; + case "Friends": + var profileUser = UserController.GetUserById(this.hostSettings, this.PortalId, this.ProfileUserId); + canShowTab = profileUser?.Social.Friend != null || this.UserId == this.ProfileUserId; + break; + case "User": + canShowTab = this.UserId == this.ProfileUserId; + break; + case "AllUsers": + break; + } - if (canShowTab) - { - var key = $"TabVisibility{tab.TabPath.Replace("//", "-")}"; - var visibility = this.Settings.ContainsKey(key) ? this.Settings[key].ToString() : "AllUsers"; + return canShowTab; + } - switch (visibility) - { - case "Owner": - canShowTab = this.UserInfo.Social.Roles.SingleOrDefault(ur => ur.RoleID == this.GroupId && ur.IsOwner) != null; - break; - case "Members": - var group = RoleController.Instance.GetRole(this.PortalId, r => r.RoleID == this.GroupId); - canShowTab = (group != null) && this.UserInfo.IsInRole(group.RoleName); - break; - case "Friends": - var profileUser = UserController.GetUserById(this.hostSettings, this.PortalId, this.ProfileUserId); - canShowTab = profileUser?.Social.Friend != null || this.UserId == this.ProfileUserId; - break; - case "User": - canShowTab = this.UserId == this.ProfileUserId; - break; - case "AllUsers": - break; - } - } + private string GetIconUrl(string iconURL, string size) + { + if (string.IsNullOrEmpty(iconURL)) + { + iconURL = size == "IconFile" + ? "~/images/icon_unknown_16px.gif" + : "~/images/icon_unknown_32px.gif"; + } - return canShowTab; + if (!iconURL.Contains("~", StringComparison.Ordinal)) + { + iconURL = Path.Combine(this.PortalSettings.HomeDirectory, iconURL); } - private string GetIconUrl(string iconURL, string size) + return this.ResolveUrl(iconURL); + } + + private object GetUserSetting(string key) + { + return Personalization.GetProfile(this.ModuleConfiguration.ModuleDefinition.FriendlyName, this.PersonalizationKey(key)); + } + + private bool IsHostTab() + { + var returnValue = false; + if (this.ConsoleTabID != this.TabId) { - if (string.IsNullOrEmpty(iconURL)) + if (this.UserInfo is not { IsSuperUser: true, }) { - iconURL = (size == "IconFile") ? "~/images/icon_unknown_16px.gif" : "~/images/icon_unknown_32px.gif"; + return false; } - if (iconURL.Contains("~") == false) + var hostTabs = TabController.Instance.GetTabsByPortal(Null.NullInteger); + if (hostTabs.Keys.Any(key => key == this.ConsoleTabID)) { - iconURL = Path.Combine(this.PortalSettings.HomeDirectory, iconURL); + returnValue = true; } - - return this.ResolveUrl(iconURL); } + else + { + returnValue = this.PortalSettings.ActiveTab.IsSuperTab; + } + + return returnValue; + } + + private string PersonalizationKey(string key) + { + return $"{this.PortalId}_{this.TabModuleId}_{key}"; + } - private object GetUserSetting(string key) + private void SavePersonalizedSettings() + { + if (this.UserId <= -1) { - return Personalization.GetProfile(this.ModuleConfiguration.ModuleDefinition.FriendlyName, this.PersonalizationKey(key)); + return; } - private bool IsHostTab() + var consoleModuleId = -1; + try { - var returnValue = false; - if (this.ConsoleTabID != this.TabId) - { - if (this.UserInfo != null && this.UserInfo.IsSuperUser) - { - var hostTabs = TabController.Instance.GetTabsByPortal(Null.NullInteger); - if (hostTabs.Keys.Any(key => key == this.ConsoleTabID)) - { - returnValue = true; - } - } - } - else + if (this.Request.QueryString["CTMID"] != null) { - returnValue = this.PortalSettings.ActiveTab.IsSuperTab; + consoleModuleId = Convert.ToInt32(this.Request.QueryString["CTMID"]); } + } + catch (Exception exc) + { + Logger.Error(exc); - return returnValue; + consoleModuleId = -1; } - private string PersonalizationKey(string key) + if (consoleModuleId != this.TabModuleId) { - return string.Format("{0}_{1}_{2}", this.PortalId, this.TabModuleId, key); + return; } - private void SavePersonalizedSettings() + var consoleSize = string.Empty; + if (this.Request.QueryString["CS"] != null) { - if (this.UserId > -1) - { - int consoleModuleID = -1; - try - { - if (this.Request.QueryString["CTMID"] != null) - { - consoleModuleID = Convert.ToInt32(this.Request.QueryString["CTMID"]); - } - } - catch (Exception exc) - { - Logger.Error(exc); - - consoleModuleID = -1; - } - - if (consoleModuleID == this.TabModuleId) - { - string consoleSize = string.Empty; - if (this.Request.QueryString["CS"] != null) - { - consoleSize = this.Request.QueryString["CS"]; - } - - string consoleView = string.Empty; - if (this.Request.QueryString["CV"] != null) - { - consoleView = this.Request.QueryString["CV"]; - } + consoleSize = this.Request.QueryString["CS"]; + } - if (consoleSize != string.Empty && ConsoleController.GetSizeValues().Contains(consoleSize)) - { - this.SaveUserSetting("DefaultSize", consoleSize); - } + var consoleView = string.Empty; + if (this.Request.QueryString["CV"] != null) + { + consoleView = this.Request.QueryString["CV"]; + } - if (consoleView != string.Empty && ConsoleController.GetViewValues().Contains(consoleView)) - { - this.SaveUserSetting("DefaultView", consoleView); - } - } - } + if (consoleSize != string.Empty && ConsoleController.GetSizeValues().Contains(consoleSize)) + { + this.SaveUserSetting("DefaultSize", consoleSize); } - private void SaveUserSetting(string key, object val) + if (consoleView != string.Empty && ConsoleController.GetViewValues().Contains(consoleView)) { - Personalization.SetProfile(this.ModuleConfiguration.ModuleDefinition.FriendlyName, this.PersonalizationKey(key), val); + this.SaveUserSetting("DefaultView", consoleView); } + } - private void RepeaterItemDataBound(object sender, RepeaterItemEventArgs e) + private void SaveUserSetting(string key, object val) + { + Personalization.SetProfile(this.ModuleConfiguration.ModuleDefinition.FriendlyName, this.PersonalizationKey(key), val); + } + + private void RepeaterItemDataBound(object sender, RepeaterItemEventArgs e) + { + var tab = (TabInfo)e.Item.DataItem; + e.Item.Controls.Add(new Literal() { Text = this.GetHtml(tab) }); + if (this.tabs.Any(t => t.ParentId == tab.TabID)) { - var tab = (TabInfo)e.Item.DataItem; - e.Item.Controls.Add(new Literal() { Text = this.GetHtml(tab) }); - if (this.tabs.Any(t => t.ParentId == tab.TabID)) - { - var repeater = new Repeater(); - repeater.ItemDataBound += this.RepeaterItemDataBound; - e.Item.Controls.Add(repeater); - repeater.DataSource = this.tabs.Where(t => t.ParentId == tab.TabID); - repeater.DataBind(); - } + var repeater = new Repeater(); + repeater.ItemDataBound += this.RepeaterItemDataBound; + e.Item.Controls.Add(repeater); + repeater.DataSource = this.tabs.Where(t => t.ParentId == tab.TabID); + repeater.DataBind(); } } } diff --git a/DNN Platform/Admin Modules/Dnn.Modules.Console/ViewConsole.ascx.designer.cs b/DNN Platform/Admin Modules/Dnn.Modules.Console/ViewConsole.ascx.designer.cs index 288b42a9292..f1ad10d01f4 100644 --- a/DNN Platform/Admin Modules/Dnn.Modules.Console/ViewConsole.ascx.designer.cs +++ b/DNN Platform/Admin Modules/Dnn.Modules.Console/ViewConsole.ascx.designer.cs @@ -1,8 +1,4 @@ -// -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // @@ -11,40 +7,52 @@ // //------------------------------------------------------------------------------ -namespace Dnn.Modules.Console { - - - public partial class ViewConsole { - - /// Console control. +namespace Dnn.Modules.Console +{ + + + public partial class ViewConsole + { + + /// + /// Console control. + /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.HtmlControls.HtmlGenericControl Console; - - /// IconSize control. + + /// + /// IconSize control. + /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.DropDownList IconSize; - - /// View control. + + /// + /// View control. + /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.DropDownList View; - - /// SettingsBreak control. + + /// + /// SettingsBreak control. + /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.HtmlControls.HtmlGenericControl SettingsBreak; - - /// DetailView control. + + /// + /// DetailView control. + /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. diff --git a/DNN Platform/Dnn.ClientSide/package.json b/DNN Platform/Dnn.ClientSide/package.json index 033e3b5a6ed..10d3f4d14fe 100644 --- a/DNN Platform/Dnn.ClientSide/package.json +++ b/DNN Platform/Dnn.ClientSide/package.json @@ -17,20 +17,20 @@ "not dead" ], "devDependencies": { - "@types/node": "^24.9.0", + "@types/node": "^25.9.2", "@types/postcss-import": "^14.0.3", - "autoprefixer": "^10.4.27", - "chokidar": "^4.0.3", - "cssnano": "^7.1.3", - "esbuild": "^0.28.0", - "eslint": "^10.2.1", + "autoprefixer": "^10.5.0", + "chokidar": "^5.0.0", + "cssnano": "^8.0.2", + "esbuild": "^0.28.1", + "eslint": "^10.5.0", "modern-normalize": "^3.0.1", - "postcss": "^8.5.8", + "postcss": "^8.5.15", "postcss-banner": "^4.0.1", "postcss-cli": "^11.0.1", "postcss-import": "^16.1.1", - "sass-embedded": "^1.98.0", - "tsx": "^4.21.0", + "sass-embedded": "^1.100.0", + "tsx": "^4.22.4", "typescript": "^5.9.3" } } diff --git a/DNN Platform/DotNetNuke.Abstractions/Framework/PagePipeline.cs b/DNN Platform/DotNetNuke.Abstractions/Framework/PagePipeline.cs new file mode 100644 index 00000000000..1467d38cd3c --- /dev/null +++ b/DNN Platform/DotNetNuke.Abstractions/Framework/PagePipeline.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information + +namespace DotNetNuke.Abstractions.Framework +{ + /// + /// Represents the page pipeline configuration for rendering pages in the DNN platform. + /// + public static class PagePipeline + { + /// QueryString key. + public const string QueryStringKey = "pipeline"; + + /// QueryString WebForms. + public const string QueryStringWebForms = "webforms"; + + /// QueryString MVC. + public const string QueryStringMvc = "mvc"; + + /// Setting name for the page pipeline configuration. + public const string SettingName = "DefaultPagePipeline"; + + /// + /// Defines the pipeline types for rendering pages in a portal. + /// + public enum PortalRenderingPipeline + { + /// + /// Specifies that pages should be rendered using the WebForms pipeline. + /// + WebForms, + + /// + /// Specifies that pages should be rendered using the MVC pipeline. + /// + MVC, + + /// + /// Specifies that the pipeline type should be automatically determined. + /// + Auto, + } + + /// + /// Defines the available pipeline types for rendering pages in the DNN platform. + /// + public enum PageRenderingPipeline + { + /// + /// Specifies that the pipeline type should be taken from the portal. + /// + Inherited = -1, + + /// + /// Specifies that pages should be rendered using the WebForms pipeline. + /// + WebForms = 0, + + /// + /// Specifies that pages should be rendered using the MVC pipeline. + /// + MVC = 1, + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.Client/Controls/DnnCssInclude.cs b/DNN Platform/DotNetNuke.Web.Client/Controls/DnnCssInclude.cs index 1457c8a18da..9936529d1c2 100644 --- a/DNN Platform/DotNetNuke.Web.Client/Controls/DnnCssInclude.cs +++ b/DNN Platform/DotNetNuke.Web.Client/Controls/DnnCssInclude.cs @@ -31,6 +31,7 @@ public DnnCssInclude(IClientResourceController clientResourceController) this.clientResourceController = clientResourceController ?? DependencyInjection.GetCurrentServiceProvider().GetRequiredService(); this.ForceProvider = ClientResourceProviders.DefaultCssProvider; this.DependencyType = ClientDependencyType.Css; + this.Priority = (int)FileOrder.Css.DefaultPriority; } /// diff --git a/DNN Platform/DotNetNuke.Web.Client/Controls/DnnJsInclude.cs b/DNN Platform/DotNetNuke.Web.Client/Controls/DnnJsInclude.cs index 4313446085f..9ae393453c0 100644 --- a/DNN Platform/DotNetNuke.Web.Client/Controls/DnnJsInclude.cs +++ b/DNN Platform/DotNetNuke.Web.Client/Controls/DnnJsInclude.cs @@ -32,6 +32,7 @@ public DnnJsInclude(IClientResourceController clientResourceController) this.clientResourceController = clientResourceController ?? DependencyInjection.GetCurrentServiceProvider().GetRequiredService(); this.ForceProvider = ClientResourceProviders.DefaultJsProvider; this.DependencyType = ClientDependencyType.Javascript; + this.Priority = (int)FileOrder.Js.DefaultPriority; } /// diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/ContentWorkflowServiceController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/ContentWorkflowServiceController.cs index 14a65a21fcb..34dc2412ddc 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/ContentWorkflowServiceController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/ContentWorkflowServiceController.cs @@ -2,217 +2,234 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web.Http; + +using DotNetNuke.Common; +using DotNetNuke.Entities.Content; +using DotNetNuke.Entities.Content.Common; +using DotNetNuke.Entities.Content.Workflow; +using DotNetNuke.Entities.Content.Workflow.Dto; +using DotNetNuke.Entities.Tabs; +using DotNetNuke.Framework; +using DotNetNuke.Internal.SourceGenerators; +using DotNetNuke.Services.Exceptions; +using DotNetNuke.Services.Social.Notifications; +using DotNetNuke.Web.Api; + +using Microsoft.Extensions.DependencyInjection; + +/// An API controller for managing content moving through its workflow. +/// The content controller. +/// The workflow engine. +/// The tab controller. +[DnnAuthorize] +public partial class ContentWorkflowServiceController(IContentController contentController, IWorkflowEngine workflowEngine, ITabController tabController) + : DnnApiController { - using System; - using System.Globalization; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Web.Http; - - using DotNetNuke.Common; - using DotNetNuke.Entities.Content; - using DotNetNuke.Entities.Content.Common; - using DotNetNuke.Entities.Content.Workflow; - using DotNetNuke.Entities.Content.Workflow.Dto; - using DotNetNuke.Entities.Tabs; - using DotNetNuke.Framework; - using DotNetNuke.Internal.SourceGenerators; - using DotNetNuke.Services.Exceptions; - using DotNetNuke.Services.Social.Notifications; - using DotNetNuke.Web.Api; - - using Microsoft.Extensions.DependencyInjection; - - /// An API controller for managing content moving through its workflow. - /// The content controller. - /// The workflow engine. - /// The tab controller. - [DnnAuthorize] - public partial class ContentWorkflowServiceController(IContentController contentController, IWorkflowEngine workflowEngine, ITabController tabController) - : DnnApiController + private readonly IContentController contentController = contentController ?? ContentController.Instance; + private readonly IWorkflowEngine workflowEngine = workflowEngine ?? WorkflowEngine.Instance; + private readonly ITabController tabController = tabController ?? TabController.Instance; + + /// Initializes a new instance of the class. + [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IContentController. Scheduled removal in v12.0.0.")] + public ContentWorkflowServiceController() + : this(null, null, null) { - private readonly IContentController contentController = contentController ?? ContentController.Instance; - private readonly IWorkflowEngine workflowEngine = workflowEngine ?? WorkflowEngine.Instance; - private readonly ITabController tabController = tabController ?? TabController.Instance; - - /// Initializes a new instance of the class. - [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IContentController. Scheduled removal in v12.0.0.")] - public ContentWorkflowServiceController() - : this(null, null, null) - { - } + } - /// Rejects a workflow. - /// The workflow notification to reject. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage Reject(NotificationDTO postData) + /// Rejects a workflow. + /// The workflow notification to reject. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnDeprecated(10, 3, 3, "Use overload taking NotificationRequest")] + public partial HttpResponseMessage Reject(NotificationDTO postData) + => this.Reject(postData?.ToNotificationRequest()); + + /// Rejects a workflow. + /// The workflow notification to reject. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage Reject(NotificationRequest requestBody) + { + try { - try + var notification = NotificationsController.Instance.GetNotification(requestBody.NotificationId); + if (notification != null) { - var notification = NotificationsController.Instance.GetNotification(postData.NotificationId); - if (notification != null) + if (string.IsNullOrEmpty(notification.Context)) { - if (string.IsNullOrEmpty(notification.Context)) - { - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); - } - - string[] parameters = notification.Context.Split(':'); - - var stateTransaction = new StateTransaction - { - ContentItemId = int.Parse(parameters[0], CultureInfo.InvariantCulture), - CurrentStateId = int.Parse(parameters[2], CultureInfo.InvariantCulture), - Message = new StateTransactionMessage(), - UserId = this.UserInfo.UserID, - }; - this.workflowEngine.DiscardState(stateTransaction); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); } - } - catch (Exception exc) - { - Exceptions.LogException(exc); - } - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + var parameters = notification.Context.Split(':'); + + var stateTransaction = new StateTransaction + { + ContentItemId = int.Parse(parameters[0], CultureInfo.InvariantCulture), + CurrentStateId = int.Parse(parameters[2], CultureInfo.InvariantCulture), + Message = new StateTransactionMessage(), + UserId = this.UserInfo.UserID, + }; + this.workflowEngine.DiscardState(stateTransaction); + + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); + } + } + catch (Exception exc) + { + Exceptions.LogException(exc); } - /// Approves a workflow. - /// The workflow notification to approve. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage Approve(NotificationDTO postData) + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + } + + /// Approves a workflow. + /// The workflow notification to approve. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnDeprecated(10, 3, 3, "Use overload taking NotificationRequest")] + public partial HttpResponseMessage Approve(NotificationDTO postData) + => this.Approve(postData?.ToNotificationRequest()); + + /// Approves a workflow. + /// The workflow notification to approve. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage Approve(NotificationRequest requestBody) + { + try { - try + var notification = NotificationsController.Instance.GetNotification(requestBody.NotificationId); + if (notification != null) { - var notification = NotificationsController.Instance.GetNotification(postData.NotificationId); - if (notification != null) + if (string.IsNullOrEmpty(notification.Context)) { - if (string.IsNullOrEmpty(notification.Context)) - { - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); - } - - string[] parameters = notification.Context.Split(':'); - - var stateTransaction = new StateTransaction - { - ContentItemId = int.Parse(parameters[0], CultureInfo.InvariantCulture), - CurrentStateId = int.Parse(parameters[2], CultureInfo.InvariantCulture), - Message = new StateTransactionMessage(), - UserId = this.UserInfo.UserID, - }; - this.workflowEngine.CompleteState(stateTransaction); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); } - } - catch (Exception exc) - { - Exceptions.LogException(exc); - } - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); - } + string[] parameters = notification.Context.Split(':'); - /// Complete a workflow state for the current page. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage CompleteState() - { - try - { - this.workflowEngine.CompleteState(this.BuildStateTransaction()); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success" }); - } - catch (Exception exc) - { - Exceptions.LogException(exc); + var stateTransaction = new StateTransaction + { + ContentItemId = int.Parse(parameters[0], CultureInfo.InvariantCulture), + CurrentStateId = int.Parse(parameters[2], CultureInfo.InvariantCulture), + Message = new StateTransactionMessage(), + UserId = this.UserInfo.UserID, + }; + this.workflowEngine.CompleteState(stateTransaction); + + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); } - - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); } - - /// Discards a workflow state for the current page. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage DiscardState() + catch (Exception exc) { - try - { - this.workflowEngine.DiscardState(this.BuildStateTransaction()); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success" }); - } - catch (Exception exc) - { - Exceptions.LogException(exc); - } - - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + Exceptions.LogException(exc); } - /// Complete a workflow for the current page. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage CompleteWorkflow() - { - try - { - this.workflowEngine.CompleteWorkflow(this.BuildStateTransaction()); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success" }); - } - catch (Exception exc) - { - Exceptions.LogException(exc); - } + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + } - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + /// Complete a workflow state for the current page. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage CompleteState() + { + try + { + this.workflowEngine.CompleteState(this.BuildStateTransaction()); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success" }); + } + catch (Exception exc) + { + Exceptions.LogException(exc); } - /// Discards a workflow for the current page. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage DiscardWorkflow() + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + } + + /// Discards a workflow state for the current page. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage DiscardState() + { + try { - try - { - this.workflowEngine.DiscardWorkflow(this.BuildStateTransaction()); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success" }); - } - catch (Exception exc) - { - Exceptions.LogException(exc); - } + this.workflowEngine.DiscardState(this.BuildStateTransaction()); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success" }); + } + catch (Exception exc) + { + Exceptions.LogException(exc); + } - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + } + + /// Complete a workflow for the current page. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage CompleteWorkflow() + { + try + { + this.workflowEngine.CompleteWorkflow(this.BuildStateTransaction()); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success" }); } + catch (Exception exc) + { + Exceptions.LogException(exc); + } + + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + } - private StateTransaction BuildStateTransaction() + /// Discards a workflow for the current page. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage DiscardWorkflow() + { + try { - var portalId = this.PortalSettings.PortalId; - var tabId = this.Request.FindTabId(); - var currentPage = this.tabController.GetTab(tabId, portalId); - var contentItemId = currentPage.ContentItemId; - var contentItem = this.contentController.GetContentItem(contentItemId); - var stateTransaction = new StateTransaction - { - ContentItemId = contentItem.ContentItemId, - CurrentStateId = contentItem.StateID, - Message = new StateTransactionMessage(), - UserId = this.UserInfo.UserID, - }; - return stateTransaction; + this.workflowEngine.DiscardWorkflow(this.BuildStateTransaction()); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success" }); + } + catch (Exception exc) + { + Exceptions.LogException(exc); } + + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + } + + private StateTransaction BuildStateTransaction() + { + var portalId = this.PortalSettings.PortalId; + var tabId = this.Request.FindTabId(); + var currentPage = this.tabController.GetTab(tabId, portalId); + var contentItemId = currentPage.ContentItemId; + var contentItem = this.contentController.GetContentItem(contentItemId); + var stateTransaction = new StateTransaction + { + ContentItemId = contentItem.ContentItemId, + CurrentStateId = contentItem.StateID, + Message = new StateTransactionMessage(), + UserId = this.UserInfo.UserID, + }; + return stateTransaction; } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/ControlBarController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/ControlBarController.cs index 55215cdce5f..8ec4dc8bc9a 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/ControlBarController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/ControlBarController.cs @@ -1,45 +1,89 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Web.Http; + +using DotNetNuke.Abstractions.Application; +using DotNetNuke.Abstractions.Logging; +using DotNetNuke.Abstractions.Modules; +using DotNetNuke.Abstractions.Portals; +using DotNetNuke.Abstractions.Security.Permissions; +using DotNetNuke.Common; +using DotNetNuke.Common.Utilities; +using DotNetNuke.Entities.Modules; +using DotNetNuke.Entities.Modules.Definitions; +using DotNetNuke.Entities.Portals; +using DotNetNuke.Entities.Tabs; +using DotNetNuke.Entities.Users; +using DotNetNuke.Instrumentation; +using DotNetNuke.Security; +using DotNetNuke.Security.Permissions; +using DotNetNuke.Services.Exceptions; +using DotNetNuke.Services.Installer.Packages; +using DotNetNuke.Services.Localization; +using DotNetNuke.Services.Log.EventLog; +using DotNetNuke.Services.Personalization; +using DotNetNuke.Web.Api; +using DotNetNuke.Web.Api.Internal; + +using Microsoft.Extensions.DependencyInjection; + +/// A web API for the control bar. +/// The business controller provider. +/// The personalization controller. +/// The application status. +/// The portal alias service. +/// The host settings service. +/// The portal controller. +/// The permission definition service. +/// The event logger. +/// The host settings. +[DnnAuthorize] +public class ControlBarController(IBusinessControllerProvider businessControllerProvider, PersonalizationController personalizationController, IApplicationStatusInfo appStatus, IPortalAliasService portalAliasService, IHostSettingsService hostSettingsService, IPortalController portalController, IPermissionDefinitionService permissionDefinitionService, IEventLogger eventLogger, IHostSettings hostSettings) + : DnnApiController { - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Net.Http.Headers; - using System.Threading; - using System.Web.Http; - - using DotNetNuke.Abstractions.Application; - using DotNetNuke.Abstractions.Logging; - using DotNetNuke.Abstractions.Modules; - using DotNetNuke.Abstractions.Portals; - using DotNetNuke.Abstractions.Security.Permissions; - using DotNetNuke.Common; - using DotNetNuke.Common.Utilities; - using DotNetNuke.Entities.Modules; - using DotNetNuke.Entities.Modules.Definitions; - using DotNetNuke.Entities.Portals; - using DotNetNuke.Entities.Tabs; - using DotNetNuke.Entities.Users; - using DotNetNuke.Instrumentation; - using DotNetNuke.Security; - using DotNetNuke.Security.Permissions; - using DotNetNuke.Services.Exceptions; - using DotNetNuke.Services.Installer.Packages; - using DotNetNuke.Services.Localization; - using DotNetNuke.Services.Log.EventLog; - using DotNetNuke.Services.Personalization; - using DotNetNuke.Web.Api; - using DotNetNuke.Web.Api.Internal; - - using Microsoft.Extensions.DependencyInjection; - - /// A web API for the control bar. + private const string DefaultExtensionImage = "icon_extensions_32px.png"; + private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(ControlBarController)); + private readonly IBusinessControllerProvider businessControllerProvider = businessControllerProvider ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly PersonalizationController personalizationController = personalizationController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IApplicationStatusInfo appStatus = appStatus ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IPortalAliasService portalAliasService = portalAliasService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IHostSettingsService hostSettingsService = hostSettingsService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IPortalController portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IPermissionDefinitionService permissionDefinitionService = permissionDefinitionService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IEventLogger eventLogger = eventLogger ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IHostSettings hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly Components.Controllers.IControlBarController controller = Components.Controllers.ControlBarController.Instance; + + /// Initializes a new instance of the class. + /// The business controller provider. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IApplicationStatusInfo. Scheduled removal in v12.0.0.")] + public ControlBarController(IBusinessControllerProvider businessControllerProvider) + : this(businessControllerProvider, null, null, null, null, null, null, null) + { + } + + /// Initializes a new instance of the class. + /// The business controller provider. + /// The personalization controller. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IApplicationStatusInfo. Scheduled removal in v12.0.0.")] + public ControlBarController(IBusinessControllerProvider businessControllerProvider, PersonalizationController personalizationController) + : this(businessControllerProvider, personalizationController, null, null, null, null, null, null) + { + } + + /// Initializes a new instance of the class. /// The business controller provider. /// The personalization controller. /// The application status. @@ -48,399 +92,321 @@ namespace DotNetNuke.Web.InternalServices /// The portal controller. /// The permission definition service. /// The event logger. - /// The host settings. - [DnnAuthorize] - public class ControlBarController(IBusinessControllerProvider businessControllerProvider, PersonalizationController personalizationController, IApplicationStatusInfo appStatus, IPortalAliasService portalAliasService, IHostSettingsService hostSettingsService, IPortalController portalController, IPermissionDefinitionService permissionDefinitionService, IEventLogger eventLogger, IHostSettings hostSettings) - : DnnApiController - { - private const string DefaultExtensionImage = "icon_extensions_32px.png"; - private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(ControlBarController)); - private readonly IBusinessControllerProvider businessControllerProvider = businessControllerProvider ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly PersonalizationController personalizationController = personalizationController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IApplicationStatusInfo appStatus = appStatus ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IPortalAliasService portalAliasService = portalAliasService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IHostSettingsService hostSettingsService = hostSettingsService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IPortalController portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IPermissionDefinitionService permissionDefinitionService = permissionDefinitionService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IEventLogger eventLogger = eventLogger ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IHostSettings hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly Components.Controllers.IControlBarController controller = Components.Controllers.ControlBarController.Instance; - - /// Initializes a new instance of the class. - /// The business controller provider. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IApplicationStatusInfo. Scheduled removal in v12.0.0.")] - public ControlBarController(IBusinessControllerProvider businessControllerProvider) - : this(businessControllerProvider, null, null, null, null, null, null, null) - { - } + [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] + public ControlBarController(IBusinessControllerProvider businessControllerProvider, PersonalizationController personalizationController, IApplicationStatusInfo appStatus, IPortalAliasService portalAliasService, IHostSettingsService hostSettingsService, IPortalController portalController, IPermissionDefinitionService permissionDefinitionService, IEventLogger eventLogger) + : this(businessControllerProvider, personalizationController, appStatus, portalAliasService, hostSettingsService, portalController, permissionDefinitionService, eventLogger, null) + { + } - /// Initializes a new instance of the class. - /// The business controller provider. - /// The personalization controller. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IApplicationStatusInfo. Scheduled removal in v12.0.0.")] - public ControlBarController(IBusinessControllerProvider businessControllerProvider, PersonalizationController personalizationController) - : this(businessControllerProvider, personalizationController, null, null, null, null, null, null) + /// Gets the desktop modules available to the portal. + /// The module category ("All" if or ). + /// The index. + /// The page size. + /// The search term. + /// A comma-delimited list of categories to exclude. + /// Whether to sort bookmarked modules to the top. + /// The friendly name of a module to display first in the list (only if is ). + /// A response containing a list of objects. + [HttpGet] + [DnnPageEditor] + public HttpResponseMessage GetPortalDesktopModules(string category, int loadingStartIndex, int loadingPageSize, string searchTerm, string excludeCategories = "", bool sortBookmarks = false, string topModule = "") + { + if (string.IsNullOrEmpty(category)) { + category = "All"; } - /// Initializes a new instance of the class. - /// The business controller provider. - /// The personalization controller. - /// The application status. - /// The portal alias service. - /// The host settings service. - /// The portal controller. - /// The permission definition service. - /// The event logger. - [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] - public ControlBarController(IBusinessControllerProvider businessControllerProvider, PersonalizationController personalizationController, IApplicationStatusInfo appStatus, IPortalAliasService portalAliasService, IHostSettingsService hostSettingsService, IPortalController portalController, IPermissionDefinitionService permissionDefinitionService, IEventLogger eventLogger) - : this(businessControllerProvider, personalizationController, appStatus, portalAliasService, hostSettingsService, portalController, permissionDefinitionService, eventLogger, null) + var bookmarkCategory = this.controller.GetBookmarkCategory(PortalSettings.Current.PortalId); + var bookmarkedModules = this.controller.GetBookmarkedDesktopModules(PortalSettings.Current.PortalId, UserController.Instance.GetCurrentUserInfo().UserID, searchTerm); + var bookmarkCategoryModules = this.controller.GetCategoryDesktopModules(this.PortalSettings.PortalId, bookmarkCategory, searchTerm); + + var filteredList = bookmarkCategory == category + ? bookmarkCategoryModules.OrderBy(m => m.Key).Union(bookmarkedModules.OrderBy(m => m.Key)).Distinct() + : this.controller.GetCategoryDesktopModules(this.PortalSettings.PortalId, category, searchTerm).OrderBy(m => m.Key); + + if (!string.IsNullOrEmpty(excludeCategories)) { + var excludeList = excludeCategories.ToLowerInvariant().Split(','); + filteredList = + filteredList.Where(kvp => + !excludeList.Contains(kvp.Value.DesktopModule.Category.ToLowerInvariant())); } - /// Gets the desktop modules available to the portal. - /// The module category ("All" if or ). - /// The index. - /// The page size. - /// The search term. - /// A comma-delimited list of categories to exclude. - /// Whether to sort bookmarked modules to the top. - /// The friendly name of a module to display first in the list (only if is ). - /// A response containing a list of objects. - [HttpGet] - [DnnPageEditor] - public HttpResponseMessage GetPortalDesktopModules(string category, int loadingStartIndex, int loadingPageSize, string searchTerm, string excludeCategories = "", bool sortBookmarks = false, string topModule = "") - { - if (string.IsNullOrEmpty(category)) - { - category = "All"; - } - - var bookmarkCategory = this.controller.GetBookmarkCategory(PortalSettings.Current.PortalId); - var bookmarkedModules = this.controller.GetBookmarkedDesktopModules(PortalSettings.Current.PortalId, UserController.Instance.GetCurrentUserInfo().UserID, searchTerm); - var bookmarkCategoryModules = this.controller.GetCategoryDesktopModules(this.PortalSettings.PortalId, bookmarkCategory, searchTerm); + if (sortBookmarks) + { + // sort bookmarked modules + filteredList = bookmarkedModules.OrderBy(m => m.Key).Concat(filteredList.Except(bookmarkedModules)); - var filteredList = bookmarkCategory == category - ? bookmarkCategoryModules.OrderBy(m => m.Key).Union(bookmarkedModules.OrderBy(m => m.Key)).Distinct() - : this.controller.GetCategoryDesktopModules(this.PortalSettings.PortalId, category, searchTerm).OrderBy(m => m.Key); + // move Html on top + filteredList = filteredList.Where(m => m.Key.Equals(topModule, StringComparison.OrdinalIgnoreCase)). + Concat(filteredList.Except(filteredList.Where(m => m.Key.Equals(topModule, StringComparison.OrdinalIgnoreCase)))); + } - if (!string.IsNullOrEmpty(excludeCategories)) - { - var excludeList = excludeCategories.ToLowerInvariant().Split(','); - filteredList = - filteredList.Where(kvp => - !excludeList.Contains(kvp.Value.DesktopModule.Category.ToLowerInvariant())); - } + filteredList = filteredList + .Skip(loadingStartIndex) + .Take(loadingPageSize); - if (sortBookmarks) - { - // sort bookmarked modules - filteredList = bookmarkedModules.OrderBy(m => m.Key).Concat(filteredList.Except(bookmarkedModules)); - - // move Html on top - filteredList = filteredList.Where(m => m.Key.Equals(topModule, StringComparison.OrdinalIgnoreCase)). - Concat(filteredList.Except(filteredList.Where(m => m.Key.Equals(topModule, StringComparison.OrdinalIgnoreCase)))); - } + var result = filteredList.Select(kvp => new ModuleDefDTO + { + ModuleID = kvp.Value.DesktopModuleID, + ModuleName = kvp.Key, + ModuleImage = GetDeskTopModuleImage(this.hostSettings, kvp.Value.DesktopModuleID), + Bookmarked = bookmarkedModules.Any(m => m.Key == kvp.Key), + ExistsInBookmarkCategory = bookmarkCategoryModules.Any(m => m.Key == kvp.Key), + }).ToList(); + return this.Request.CreateResponse(HttpStatusCode.OK, result); + } - filteredList = filteredList - .Skip(loadingStartIndex) - .Take(loadingPageSize); + /// Gets the pages for a portal. + /// The portal ID, or or for the current portal. + /// A response with a list of objects. + [HttpGet] + [DnnPageEditor] + public HttpResponseMessage GetPageList(string portal) + { + var portalSettings = this.GetPortalSettings(portal); - var result = filteredList.Select(kvp => new ModuleDefDTO - { - ModuleID = kvp.Value.DesktopModuleID, - ModuleName = kvp.Key, - ModuleImage = GetDeskTopModuleImage(this.hostSettings, kvp.Value.DesktopModuleID), - Bookmarked = bookmarkedModules.Any(m => m.Key == kvp.Key), - ExistsInBookmarkCategory = bookmarkCategoryModules.Any(m => m.Key == kvp.Key), - }).ToList(); - return this.Request.CreateResponse(HttpStatusCode.OK, result); + List tabList; + if (this.PortalSettings.PortalId == portalSettings.PortalId) + { + tabList = TabController.GetPortalTabs(this.hostSettings, this.appStatus, portalSettings.PortalId, this.PortalSettings.ActiveTab.TabID, false, string.Empty, true, false, false, false, true); } - - /// Gets the pages for a portal. - /// The portal ID, or or for the current portal. - /// A response with a list of objects. - [HttpGet] - [DnnPageEditor] - public HttpResponseMessage GetPageList(string portal) + else { - var portalSettings = this.GetPortalSettings(portal); + var groups = PortalGroupController.Instance.GetPortalGroups().ToArray(); - List tabList; - if (this.PortalSettings.PortalId == portalSettings.PortalId) - { - tabList = TabController.GetPortalTabs(this.hostSettings, this.appStatus, portalSettings.PortalId, this.PortalSettings.ActiveTab.TabID, false, string.Empty, true, false, false, false, true); - } - else - { - var groups = PortalGroupController.Instance.GetPortalGroups().ToArray(); - - var myGroup = ( + var myGroup = ( from @group in groups select PortalGroupController.Instance.GetPortalsByGroup(@group.PortalGroupId).Cast() into portals where portals.Any(x => x.PortalId == PortalSettings.Current.PortalId) select portals.ToArray()) - .FirstOrDefault(); + .FirstOrDefault(); - if (myGroup != null && myGroup.Any(p => p.PortalId == portalSettings.PortalId)) - { - tabList = TabController.GetPortalTabs(this.hostSettings, this.appStatus, portalSettings.PortalId, Null.NullInteger, false, string.Empty, true, false, false, false, false); - } - else - { - // try to get pages not allowed - return this.Request.CreateResponse(HttpStatusCode.InternalServerError); - } + if (myGroup != null && myGroup.Any(p => p.PortalId == portalSettings.PortalId)) + { + tabList = TabController.GetPortalTabs(this.hostSettings, this.appStatus, portalSettings.PortalId, Null.NullInteger, false, string.Empty, true, false, false, false, false); } - - List result = new List(); - foreach (var tab in tabList) + else { - if (tab.PortalID == this.PortalSettings.PortalId || (GetModules(tab.TabID).Count > 0 && tab.TabID != portalSettings.AdminTabId && tab.ParentId != portalSettings.AdminTabId)) - { - result.Add(new PageDefDTO { TabID = tab.TabID, IndentedTabName = tab.IndentedTabName }); - } + // try to get pages not allowed + return this.Request.CreateResponse(HttpStatusCode.InternalServerError); } - - return this.Request.CreateResponse(HttpStatusCode.OK, result); } - /// Gets the modules to a page. - /// The tab ID. - /// A response with a list of objects. - [HttpGet] - [DnnPageEditor] - public HttpResponseMessage GetTabModules(string tab) + List result = new List(); + foreach (var tab in tabList) { - if (int.TryParse(tab, out var tabId)) + if (tab.PortalID == this.PortalSettings.PortalId || (GetModules(tab.TabID).Count > 0 && tab.TabID != portalSettings.AdminTabId && tab.ParentId != portalSettings.AdminTabId)) { - var result = new List(); - if (tabId > 0) - { - var pageModules = GetModules(tabId); - - Dictionary resultDict = pageModules.ToDictionary(module => module.ModuleID, module => module.ModuleTitle); - result.AddRange(from kvp in resultDict - let imageUrl = GetTabModuleImage(this.hostSettings, tabId, kvp.Key) - select new ModuleDefDTO { ModuleID = kvp.Key, ModuleName = kvp.Value, ModuleImage = imageUrl }); - } - - return this.Request.CreateResponse(HttpStatusCode.OK, result); + result.Add(new PageDefDTO { TabID = tab.TabID, IndentedTabName = tab.IndentedTabName }); } - - return this.Request.CreateResponse(HttpStatusCode.InternalServerError); } - /// Copy permissions from the active page to its descendants. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - [DnnPageEditor] - public HttpResponseMessage CopyPermissionsToChildren() + return this.Request.CreateResponse(HttpStatusCode.OK, result); + } + + /// Gets the modules to a page. + /// The tab ID. + /// A response with a list of objects. + [HttpGet] + [DnnPageEditor] + public HttpResponseMessage GetTabModules(string tab) + { + if (int.TryParse(tab, out var tabId)) { - if (TabPermissionController.CanManagePage() && UserController.Instance.GetCurrentUserInfo().IsInRole("Administrators") - && this.ActiveTabHasChildren() && !this.PortalSettings.ActiveTab.IsSuperTab) + var result = new List(); + if (tabId > 0) { - TabController.CopyPermissionsToChildren(this.eventLogger, this.PortalSettings.ActiveTab, this.PortalSettings.ActiveTab.TabPermissions); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, }); + var pageModules = GetModules(tabId); + + Dictionary resultDict = pageModules.ToDictionary(module => module.ModuleID, module => module.ModuleTitle); + result.AddRange(from kvp in resultDict + let imageUrl = GetTabModuleImage(this.hostSettings, tabId, kvp.Key) + select new ModuleDefDTO { ModuleID = kvp.Key, ModuleName = kvp.Value, ModuleImage = imageUrl }); } - return this.Request.CreateResponse(HttpStatusCode.InternalServerError); + return this.Request.CreateResponse(HttpStatusCode.OK, result); } - /// Add a module to a page. - /// Information about the module to add. - /// A response with an object containing the tab-module ID of the new instance. - [HttpPost] - [ValidateAntiForgeryToken] - [DnnPageEditor] - public HttpResponseMessage AddModule(AddModuleDTO dto) + return this.Request.CreateResponse(HttpStatusCode.InternalServerError); + } + + /// Copy permissions from the active page to its descendants. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnPageEditor] + public HttpResponseMessage CopyPermissionsToChildren() + { + if (TabPermissionController.CanManagePage() && UserController.Instance.GetCurrentUserInfo().IsInRole("Administrators") + && this.ActiveTabHasChildren() && !this.PortalSettings.ActiveTab.IsSuperTab) { - if (TabPermissionController.CanAddContentToPage() && this.CanAddModuleToPage()) - { - int permissionType; - try - { - permissionType = int.Parse(dto.Visibility, CultureInfo.InvariantCulture); - } - catch (Exception exc) - { - Logger.Error(exc); - permissionType = 0; - } + TabController.CopyPermissionsToChildren(this.eventLogger, this.PortalSettings.ActiveTab, this.PortalSettings.ActiveTab.TabPermissions); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, }); + } - int positionId = -1; - if (!string.IsNullOrEmpty(dto.Sort)) - { - try - { - var sortId = int.Parse(dto.Sort, CultureInfo.InvariantCulture); - if (sortId >= 0) - { - positionId = GetPaneModuleOrder(dto.Pane, sortId); - } - } - catch (Exception exc) - { - Logger.Error(exc); - } - } + return this.Request.CreateResponse(HttpStatusCode.InternalServerError); + } - if (positionId == -1) - { - switch (dto.Position) - { - case "TOP": - case "0": - positionId = 0; - break; - case "BOTTOM": - case "-1": - positionId = -1; - break; - } - } + /// Add a module to a page. + /// Information about the module to add. + /// A response with an object containing the tab-module ID of the new instance. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnPageEditor] + public HttpResponseMessage AddModule(AddModuleDTO dto) + { + if (TabPermissionController.CanAddContentToPage() && this.CanAddModuleToPage()) + { + int permissionType; + try + { + permissionType = int.Parse(dto.Visibility, CultureInfo.InvariantCulture); + } + catch (Exception exc) + { + Logger.Error(exc); + permissionType = 0; + } - int moduleLstId; + int positionId = -1; + if (!string.IsNullOrEmpty(dto.Sort)) + { try { - moduleLstId = int.Parse(dto.Module, CultureInfo.InvariantCulture); + var sortId = int.Parse(dto.Sort, CultureInfo.InvariantCulture); + if (sortId >= 0) + { + positionId = GetPaneModuleOrder(dto.Pane, sortId); + } } catch (Exception exc) { Logger.Error(exc); - moduleLstId = -1; } + } - try - { - int tabModuleId = -1; - if (moduleLstId > -1) - { - if (dto.AddExistingModule == "true") - { - int pageId; - try - { - pageId = int.Parse(dto.Page, CultureInfo.InvariantCulture); - } - catch (Exception exc) - { - Logger.Error(exc); - pageId = -1; - } - - if (pageId > -1) - { - tabModuleId = this.DoAddExistingModule(moduleLstId, pageId, dto.Pane, positionId, string.Empty, dto.CopyModule == "true"); - } - } - else - { - tabModuleId = DoAddNewModule(this.hostSettings, string.Empty, moduleLstId, dto.Pane, positionId, permissionType, string.Empty); - } - } - - return this.Request.CreateResponse(HttpStatusCode.OK, new { TabModuleID = tabModuleId }); - } - catch (Exception ex) + if (positionId == -1) + { + switch (dto.Position) { - Logger.Error(ex); + case "TOP": + case "0": + positionId = 0; + break; + case "BOTTOM": + case "-1": + positionId = -1; + break; } } - return this.Request.CreateResponse(HttpStatusCode.InternalServerError); - } - - /// Clears the host cache. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - [RequireHost] - public HttpResponseMessage ClearHostCache() - { - if (UserController.Instance.GetCurrentUserInfo().IsSuperUser) + int moduleLstId; + try { - DataCache.ClearCache(); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, }); + moduleLstId = int.Parse(dto.Module, CultureInfo.InvariantCulture); } - - return this.Request.CreateResponse(HttpStatusCode.InternalServerError); - } - - /// Recycles the application pool. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - [RequireHost] - public HttpResponseMessage RecycleApplicationPool() - { - if (UserController.Instance.GetCurrentUserInfo().IsSuperUser) + catch (Exception exc) { - var log = new LogInfo { BypassBuffering = true, LogTypeKey = nameof(EventLogType.HOST_ALERT) }; - log.AddProperty("Message", "UserRestart"); - LogController.Instance.AddLog(log); - Config.Touch(this.appStatus); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, }); + Logger.Error(exc); + moduleLstId = -1; } - return this.Request.CreateResponse(HttpStatusCode.InternalServerError); - } - - /// Switches to a different portal/site. - /// Information about the site to switch to. - /// A response with an object containing a RedirectURL field. - [HttpPost] - [ValidateAntiForgeryToken] - [RequireHost] - public HttpResponseMessage SwitchSite(SwitchSiteDTO dto) - { - if (UserController.Instance.GetCurrentUserInfo().IsSuperUser) + try { - try + int tabModuleId = -1; + if (moduleLstId > -1) { - if (!string.IsNullOrEmpty(dto.Site)) + if (dto.AddExistingModule == "true") { - int selectedPortalId = int.Parse(dto.Site, CultureInfo.InvariantCulture); - var portalAliases = this.portalAliasService.GetPortalAliasesByPortalId(selectedPortalId).ToList(); + int pageId; + try + { + pageId = int.Parse(dto.Page, CultureInfo.InvariantCulture); + } + catch (Exception exc) + { + Logger.Error(exc); + pageId = -1; + } - if (portalAliases.Count > 0 && portalAliases[0] != null) + if (pageId > -1) { - return this.Request.CreateResponse(HttpStatusCode.OK, new { RedirectURL = Globals.AddHTTP(portalAliases[0].HttpAlias), }); + tabModuleId = this.DoAddExistingModule(moduleLstId, pageId, dto.Pane, positionId, string.Empty, dto.CopyModule == "true"); } } + else + { + tabModuleId = DoAddNewModule(this.hostSettings, string.Empty, moduleLstId, dto.Pane, positionId, permissionType, string.Empty); + } } - catch (ThreadAbortException) - { - // Do nothing we are not logging ThreadAbortExceptions caused by redirects - } - catch (Exception ex) - { - Exceptions.LogException(ex); - } + + return this.Request.CreateResponse(HttpStatusCode.OK, new { TabModuleID = tabModuleId }); + } + catch (Exception ex) + { + Logger.Error(ex); } + } + + return this.Request.CreateResponse(HttpStatusCode.InternalServerError); + } + + /// Clears the host cache. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [RequireHost] + public HttpResponseMessage ClearHostCache() + { + if (UserController.Instance.GetCurrentUserInfo().IsSuperUser) + { + DataCache.ClearCache(); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, }); + } + + return this.Request.CreateResponse(HttpStatusCode.InternalServerError); + } - return this.Request.CreateResponse(HttpStatusCode.InternalServerError); + /// Recycles the application pool. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [RequireHost] + public HttpResponseMessage RecycleApplicationPool() + { + if (UserController.Instance.GetCurrentUserInfo().IsSuperUser) + { + var log = new LogInfo { BypassBuffering = true, LogTypeKey = nameof(EventLogType.HOST_ALERT) }; + log.AddProperty("Message", "UserRestart"); + LogController.Instance.AddLog(log); + Config.Touch(this.appStatus); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, }); } - /// Updates the user's preferred language. - /// Information about the language switch. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage SwitchLanguage(SwitchLanguageDTO dto) + return this.Request.CreateResponse(HttpStatusCode.InternalServerError); + } + + /// Switches to a different portal/site. + /// Information about the site to switch to. + /// A response with an object containing a RedirectURL field. + [HttpPost] + [ValidateAntiForgeryToken] + [RequireHost] + public HttpResponseMessage SwitchSite(SwitchSiteDTO dto) + { + if (UserController.Instance.GetCurrentUserInfo().IsSuperUser) { try { - if (this.PortalSettings.AllowUserUICulture && this.PortalSettings.ContentLocalizationEnabled) + if (!string.IsNullOrEmpty(dto.Site)) { - if (!string.IsNullOrEmpty(dto.Language)) + int selectedPortalId = int.Parse(dto.Site, CultureInfo.InvariantCulture); + var portalAliases = this.portalAliasService.GetPortalAliasesByPortalId(selectedPortalId).ToList(); + + if (portalAliases.Count > 0 && portalAliases[0] != null) { - var personalization = this.personalizationController.LoadProfile(this.UserInfo.UserID, this.PortalSettings.PortalId); - personalization.Profile["Usability:UICulture"] = dto.Language; - personalization.IsModified = true; - this.personalizationController.SaveProfile(personalization); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, }); + return this.Request.CreateResponse(HttpStatusCode.OK, new { RedirectURL = Globals.AddHTTP(portalAliases[0].HttpAlias), }); } } } @@ -452,568 +418,601 @@ public HttpResponseMessage SwitchLanguage(SwitchLanguageDTO dto) { Exceptions.LogException(ex); } - - return this.Request.CreateResponse(HttpStatusCode.InternalServerError); } - /// Toggle between view and edit mode. - /// The user mode to switch to. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - [DnnPageEditor] - public HttpResponseMessage ToggleUserMode(UserModeDTO userMode) - { - if (userMode == null) - { - userMode = new UserModeDTO { UserMode = "VIEW" }; - } - - this.ToggleUserMode(userMode.UserMode); - var response = this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, }); + return this.Request.CreateResponse(HttpStatusCode.InternalServerError); + } - if (userMode.UserMode.Equals("VIEW", StringComparison.OrdinalIgnoreCase)) + /// Updates the user's preferred language. + /// Information about the language switch. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage SwitchLanguage(SwitchLanguageDTO dto) + { + try + { + if (this.PortalSettings.AllowUserUICulture && this.PortalSettings.ContentLocalizationEnabled) { - var cookie = this.Request.Headers.GetCookies("StayInEditMode").FirstOrDefault(); - if (cookie != null && !string.IsNullOrEmpty(cookie["StayInEditMode"].Value)) + if (!string.IsNullOrEmpty(dto.Language)) { - var expireCookie = new CookieHeaderValue("StayInEditMode", string.Empty); - expireCookie.Expires = DateTimeOffset.Now.AddDays(-1); - expireCookie.Path = !string.IsNullOrEmpty(Globals.ApplicationPath) ? Globals.ApplicationPath : "/"; - response.Headers.AddCookies(new List { expireCookie }); + var personalization = this.personalizationController.LoadProfile(this.UserInfo.UserID, this.PortalSettings.PortalId); + personalization.Profile["Usability:UICulture"] = dto.Language; + personalization.IsModified = true; + this.personalizationController.SaveProfile(personalization); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, }); } } - - return response; } - - /// Saves a bookmark for a user. - /// The bookmark to save. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - [DnnPageEditor] - public HttpResponseMessage SaveBookmark(BookmarkDTO bookmark) + catch (ThreadAbortException) { - if (string.IsNullOrEmpty(bookmark.Bookmark)) - { - bookmark.Bookmark = string.Empty; - } - - this.controller.SaveBookMark(this.PortalSettings.PortalId, this.UserInfo.UserID, bookmark.Title, bookmark.Bookmark); - - return this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, }); + // Do nothing we are not logging ThreadAbortExceptions caused by redirects } - - /// Locks or unlocks the instance. - /// The lock request. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - [RequireHost] - public HttpResponseMessage LockInstance(LockingDTO lockingRequest) + catch (Exception ex) { - this.hostSettingsService.Update("IsLocked", lockingRequest.Lock.ToString(), true); - return this.Request.CreateResponse(HttpStatusCode.OK); + Exceptions.LogException(ex); } - /// Locks or unlocks the current site. - /// The lock request. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - [RequireHost] - public HttpResponseMessage LockSite(LockingDTO lockingRequest) + return this.Request.CreateResponse(HttpStatusCode.InternalServerError); + } + + /// Toggle between view and edit mode. + /// The user mode to switch to. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnPageEditor] + public HttpResponseMessage ToggleUserMode(UserModeDTO userMode) + { + if (userMode == null) { - PortalController.UpdatePortalSetting(this.portalController, this.PortalSettings.PortalId, "IsLocked", lockingRequest.Lock.ToString(), true); - return this.Request.CreateResponse(HttpStatusCode.OK); + userMode = new UserModeDTO { UserMode = "VIEW" }; } - /// Gets a value indicating whether the current user can add a module to the current page. - /// . - [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Breaking change")] - public bool CanAddModuleToPage() - { - return true; + this.ToggleUserMode(userMode.UserMode); + var response = this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, }); - // If we are not in an edit page - ////return (string.IsNullOrEmpty(HttpContext.Current.Request.QueryString["mid"])) && (string.IsNullOrEmpty(HttpContext.Current.Request.QueryString["ctl"])); + if (userMode.UserMode.Equals("VIEW", StringComparison.OrdinalIgnoreCase)) + { + var cookie = this.Request.Headers.GetCookies("StayInEditMode").FirstOrDefault(); + if (cookie != null && !string.IsNullOrEmpty(cookie["StayInEditMode"].Value)) + { + var expireCookie = new CookieHeaderValue("StayInEditMode", string.Empty); + expireCookie.Expires = DateTimeOffset.Now.AddDays(-1); + expireCookie.Path = !string.IsNullOrEmpty(Globals.ApplicationPath) ? Globals.ApplicationPath : "/"; + response.Headers.AddCookies(new List { expireCookie }); + } } - private static void SetCloneModuleContext(bool cloneModuleContext) + return response; + } + + /// Saves a bookmark for a user. + /// The bookmark to save. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnPageEditor] + public HttpResponseMessage SaveBookmark(BookmarkDTO bookmark) + { + if (string.IsNullOrEmpty(bookmark.Bookmark)) { - Thread.SetData( - Thread.GetNamedDataSlot("CloneModuleContext"), - cloneModuleContext ? bool.TrueString : bool.FalseString); + bookmark.Bookmark = string.Empty; } - private static List GetModules(int tabID) - { - var isRemote = TabController.Instance.GetTab(tabID, Null.NullInteger, false).PortalID != PortalSettings.Current.PortalId; - var tabModules = ModuleController.Instance.GetTabModules(tabID); + this.controller.SaveBookMark(this.PortalSettings.PortalId, this.UserInfo.UserID, bookmark.Title, bookmark.Bookmark); - var pageModules = isRemote - ? tabModules.Values.Where(m => ModuleSupportsSharing(m) && !m.IsDeleted).ToList() - : tabModules.Values.Where(m => ModulePermissionController.HasModuleAccess(SecurityAccessLevel.Edit, "MANAGE", m) && !m.IsDeleted).ToList(); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, }); + } - return pageModules; - } + /// Locks or unlocks the instance. + /// The lock request. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [RequireHost] + public HttpResponseMessage LockInstance(LockingDTO lockingRequest) + { + this.hostSettingsService.Update("IsLocked", lockingRequest.Lock.ToString(), true); + return this.Request.CreateResponse(HttpStatusCode.OK); + } - private static bool ModuleSupportsSharing(ModuleInfo moduleInfo) - { - switch (moduleInfo.DesktopModule.Shareable) - { - case ModuleSharing.Supported: - case ModuleSharing.Unknown: - return moduleInfo.IsShareable; - default: - return false; - } - } + /// Locks or unlocks the current site. + /// The lock request. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [RequireHost] + public HttpResponseMessage LockSite(LockingDTO lockingRequest) + { + PortalController.UpdatePortalSetting(this.portalController, this.PortalSettings.PortalId, "IsLocked", lockingRequest.Lock.ToString(), true); + return this.Request.CreateResponse(HttpStatusCode.OK); + } - private static string GetDeskTopModuleImage(IHostSettings hostSettings, int moduleId) - { - var portalDesktopModules = DesktopModuleController.GetDesktopModules(hostSettings, PortalSettings.Current.PortalId); - var packages = PackageController.Instance.GetExtensionPackages(PortalSettings.Current.PortalId); + /// Gets a value indicating whether the current user can add a module to the current page. + /// . + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Breaking change")] + public bool CanAddModuleToPage() + { + return true; - string imageUrl = - (from package in packages - join portMods in portalDesktopModules on package.PackageID equals portMods.Value.PackageID - where portMods.Value.DesktopModuleID == moduleId - select package.IconFile).FirstOrDefault(); + // If we are not in an edit page + ////return (string.IsNullOrEmpty(HttpContext.Current.Request.QueryString["mid"])) && (string.IsNullOrEmpty(HttpContext.Current.Request.QueryString["ctl"])); + } - imageUrl = string.IsNullOrEmpty(imageUrl) ? Globals.ImagePath + DefaultExtensionImage : imageUrl; - return System.Web.VirtualPathUtility.ToAbsolute(imageUrl); - } + private static void SetCloneModuleContext(bool cloneModuleContext) + { + Thread.SetData( + Thread.GetNamedDataSlot("CloneModuleContext"), + cloneModuleContext ? bool.TrueString : bool.FalseString); + } + + private static List GetModules(int tabID) + { + var isRemote = TabController.Instance.GetTab(tabID, Null.NullInteger, false).PortalID != PortalSettings.Current.PortalId; + var tabModules = ModuleController.Instance.GetTabModules(tabID); + + var pageModules = isRemote + ? tabModules.Values.Where(m => ModuleSupportsSharing(m) && !m.IsDeleted).ToList() + : tabModules.Values.Where(m => ModulePermissionController.HasModuleAccess(SecurityAccessLevel.Edit, "MANAGE", m) && !m.IsDeleted).ToList(); + + return pageModules; + } - private static string GetTabModuleImage(IHostSettings hostSettings, int tabId, int moduleId) + private static bool ModuleSupportsSharing(ModuleInfo moduleInfo) + { + switch (moduleInfo.DesktopModule.Shareable) { - var tabModules = ModuleController.Instance.GetTabModules(tabId); - var portalDesktopModules = DesktopModuleController.GetDesktopModules(hostSettings, PortalSettings.Current.PortalId); - var moduleDefinitions = ModuleDefinitionController.GetModuleDefinitions(hostSettings); - var packages = PackageController.Instance.GetExtensionPackages(PortalSettings.Current.PortalId); + case ModuleSharing.Supported: + case ModuleSharing.Unknown: + return moduleInfo.IsShareable; + default: + return false; + } + } + + private static string GetDeskTopModuleImage(IHostSettings hostSettings, int moduleId) + { + var portalDesktopModules = DesktopModuleController.GetDesktopModules(hostSettings, PortalSettings.Current.PortalId); + var packages = PackageController.Instance.GetExtensionPackages(PortalSettings.Current.PortalId); - string imageUrl = (from package in packages + string imageUrl = + (from package in packages join portMods in portalDesktopModules on package.PackageID equals portMods.Value.PackageID - join modDefs in moduleDefinitions on portMods.Value.DesktopModuleID equals modDefs.Value.DesktopModuleID - join tabMods in tabModules on modDefs.Value.DesktopModuleID equals tabMods.Value.DesktopModuleID - where tabMods.Value.ModuleID == moduleId + where portMods.Value.DesktopModuleID == moduleId select package.IconFile).FirstOrDefault(); - imageUrl = string.IsNullOrEmpty(imageUrl) ? Globals.ImagePath + DefaultExtensionImage : imageUrl; - return System.Web.VirtualPathUtility.ToAbsolute(imageUrl); - } + imageUrl = string.IsNullOrEmpty(imageUrl) ? Globals.ImagePath + DefaultExtensionImage : imageUrl; + return System.Web.VirtualPathUtility.ToAbsolute(imageUrl); + } + + private static string GetTabModuleImage(IHostSettings hostSettings, int tabId, int moduleId) + { + var tabModules = ModuleController.Instance.GetTabModules(tabId); + var portalDesktopModules = DesktopModuleController.GetDesktopModules(hostSettings, PortalSettings.Current.PortalId); + var moduleDefinitions = ModuleDefinitionController.GetModuleDefinitions(hostSettings); + var packages = PackageController.Instance.GetExtensionPackages(PortalSettings.Current.PortalId); + + string imageUrl = (from package in packages + join portMods in portalDesktopModules on package.PackageID equals portMods.Value.PackageID + join modDefs in moduleDefinitions on portMods.Value.DesktopModuleID equals modDefs.Value.DesktopModuleID + join tabMods in tabModules on modDefs.Value.DesktopModuleID equals tabMods.Value.DesktopModuleID + where tabMods.Value.ModuleID == moduleId + select package.IconFile).FirstOrDefault(); + + imageUrl = string.IsNullOrEmpty(imageUrl) ? Globals.ImagePath + DefaultExtensionImage : imageUrl; + return System.Web.VirtualPathUtility.ToAbsolute(imageUrl); + } - private static void AddModulePermission(ModuleInfo objModule, IPermissionDefinitionInfo permission, int roleId, int userId, bool allowAccess) + private static void AddModulePermission(ModuleInfo objModule, IPermissionDefinitionInfo permission, int roleId, int userId, bool allowAccess) + { + IPermissionInfo objModulePermission = new ModulePermissionInfo { - IPermissionInfo objModulePermission = new ModulePermissionInfo - { - ModuleID = objModule.ModuleID, - PermissionKey = permission.PermissionKey, - AllowAccess = allowAccess, - }; - objModulePermission.PermissionId = permission.PermissionId; - objModulePermission.RoleId = roleId; - objModulePermission.UserId = userId; - - // add the permission to the collection - if (!objModule.ModulePermissions.Contains(objModulePermission)) - { - objModule.ModulePermissions.Add((ModulePermissionInfo)objModulePermission); - } - } + ModuleID = objModule.ModuleID, + PermissionKey = permission.PermissionKey, + AllowAccess = allowAccess, + }; + objModulePermission.PermissionId = permission.PermissionId; + objModulePermission.RoleId = roleId; + objModulePermission.UserId = userId; - private static int GetPaneModuleOrder(string pane, int sort) + // add the permission to the collection + if (!objModule.ModulePermissions.Contains(objModulePermission)) { - var items = new List(); + objModule.ModulePermissions.Add((ModulePermissionInfo)objModulePermission); + } + } - foreach (ModuleInfo m in PortalSettings.Current.ActiveTab.Modules) + private static int GetPaneModuleOrder(string pane, int sort) + { + var items = new List(); + + foreach (ModuleInfo m in PortalSettings.Current.ActiveTab.Modules) + { + // if user is allowed to view module and module is not deleted + if (ModulePermissionController.CanViewModule(m) && !m.IsDeleted) { - // if user is allowed to view module and module is not deleted - if (ModulePermissionController.CanViewModule(m) && !m.IsDeleted) + // modules which are displayed on all tabs should not be displayed on the Admin or Super tabs + if (!m.AllTabs || !PortalSettings.Current.ActiveTab.IsSuperTab) { - // modules which are displayed on all tabs should not be displayed on the Admin or Super tabs - if (!m.AllTabs || !PortalSettings.Current.ActiveTab.IsSuperTab) + if (string.Equals(m.PaneName, pane, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(m.PaneName, pane, StringComparison.OrdinalIgnoreCase)) - { - int moduleOrder = m.ModuleOrder; + int moduleOrder = m.ModuleOrder; - while (items.Contains(moduleOrder) || moduleOrder == 0) - { - moduleOrder++; - } - - items.Add(moduleOrder); + while (items.Contains(moduleOrder) || moduleOrder == 0) + { + moduleOrder++; } + + items.Add(moduleOrder); } } } + } - items.Sort(); - - if (items.Count > sort) - { - var itemOrder = items[sort]; - return itemOrder - 1; - } - else if (items.Count > 0) - { - return items.Last() + 1; - } + items.Sort(); - return 0; + if (items.Count > sort) + { + var itemOrder = items[sort]; + return itemOrder - 1; + } + else if (items.Count > 0) + { + return items.Last() + 1; } - private static int DoAddNewModule(IHostSettings hostSettings, string title, int desktopModuleId, string paneName, int position, int permissionType, string align) + return 0; + } + + private static int DoAddNewModule(IHostSettings hostSettings, string title, int desktopModuleId, string paneName, int position, int permissionType, string align) + { + try { - try - { - if (!DesktopModuleController.GetDesktopModules(hostSettings, PortalSettings.Current.PortalId).TryGetValue(desktopModuleId, out _)) - { - throw new ArgumentException($"Could not find desktop module with given ID: {desktopModuleId}", nameof(desktopModuleId)); - } - } - catch (Exception ex) + if (!DesktopModuleController.GetDesktopModules(hostSettings, PortalSettings.Current.PortalId).TryGetValue(desktopModuleId, out _)) { - Exceptions.LogException(ex); + throw new ArgumentException($"Could not find desktop module with given ID: {desktopModuleId}", nameof(desktopModuleId)); } + } + catch (Exception ex) + { + Exceptions.LogException(ex); + } + + var tabModuleId = Null.NullInteger; + foreach (ModuleDefinitionInfo objModuleDefinition in + ModuleDefinitionController.GetModuleDefinitionsByDesktopModuleID(desktopModuleId).Values) + { + var objModule = new ModuleInfo(); + objModule.Initialize(PortalSettings.Current.ActiveTab.PortalID); - var tabModuleId = Null.NullInteger; - foreach (ModuleDefinitionInfo objModuleDefinition in - ModuleDefinitionController.GetModuleDefinitionsByDesktopModuleID(desktopModuleId).Values) + objModule.PortalID = PortalSettings.Current.ActiveTab.PortalID; + objModule.TabID = PortalSettings.Current.ActiveTab.TabID; + objModule.ModuleOrder = position; + objModule.ModuleTitle = string.IsNullOrEmpty(title) ? objModuleDefinition.FriendlyName : title; + objModule.PaneName = paneName; + objModule.ModuleDefID = objModuleDefinition.ModuleDefID; + if (objModuleDefinition.DefaultCacheTime > 0) { - var objModule = new ModuleInfo(); - objModule.Initialize(PortalSettings.Current.ActiveTab.PortalID); - - objModule.PortalID = PortalSettings.Current.ActiveTab.PortalID; - objModule.TabID = PortalSettings.Current.ActiveTab.TabID; - objModule.ModuleOrder = position; - objModule.ModuleTitle = string.IsNullOrEmpty(title) ? objModuleDefinition.FriendlyName : title; - objModule.PaneName = paneName; - objModule.ModuleDefID = objModuleDefinition.ModuleDefID; - if (objModuleDefinition.DefaultCacheTime > 0) + objModule.CacheTime = objModuleDefinition.DefaultCacheTime; + if (PortalSettings.Current.DefaultModuleId > Null.NullInteger && PortalSettings.Current.DefaultTabId > Null.NullInteger) { - objModule.CacheTime = objModuleDefinition.DefaultCacheTime; - if (PortalSettings.Current.DefaultModuleId > Null.NullInteger && PortalSettings.Current.DefaultTabId > Null.NullInteger) + ModuleInfo defaultModule = ModuleController.Instance.GetModule(PortalSettings.Current.DefaultModuleId, PortalSettings.Current.DefaultTabId, true); + if (defaultModule != null) { - ModuleInfo defaultModule = ModuleController.Instance.GetModule(PortalSettings.Current.DefaultModuleId, PortalSettings.Current.DefaultTabId, true); - if (defaultModule != null) - { - objModule.CacheTime = defaultModule.CacheTime; - } + objModule.CacheTime = defaultModule.CacheTime; } } + } - ModuleController.Instance.InitialModulePermission(objModule, objModule.TabID, permissionType); + ModuleController.Instance.InitialModulePermission(objModule, objModule.TabID, permissionType); - if (PortalSettings.Current.ContentLocalizationEnabled) - { - Locale defaultLocale = LocaleController.Instance.GetDefaultLocale(PortalSettings.Current.PortalId); + if (PortalSettings.Current.ContentLocalizationEnabled) + { + Locale defaultLocale = LocaleController.Instance.GetDefaultLocale(PortalSettings.Current.PortalId); - // set the culture of the module to that of the tab - var tabInfo = TabController.Instance.GetTab(objModule.TabID, PortalSettings.Current.PortalId, false); - objModule.CultureCode = tabInfo != null ? tabInfo.CultureCode : defaultLocale.Code; - } - else - { - objModule.CultureCode = Null.NullString; - } + // set the culture of the module to that of the tab + var tabInfo = TabController.Instance.GetTab(objModule.TabID, PortalSettings.Current.PortalId, false); + objModule.CultureCode = tabInfo != null ? tabInfo.CultureCode : defaultLocale.Code; + } + else + { + objModule.CultureCode = Null.NullString; + } - objModule.AllTabs = false; - objModule.Alignment = align; + objModule.AllTabs = false; + objModule.Alignment = align; - ModuleController.Instance.AddModule(objModule); + ModuleController.Instance.AddModule(objModule); - if (tabModuleId == Null.NullInteger) - { - tabModuleId = objModule.ModuleID; - } - - // update the position to let later modules with add after previous one. - position = ModuleController.Instance.GetTabModule(objModule.TabModuleID).ModuleOrder + 1; + if (tabModuleId == Null.NullInteger) + { + tabModuleId = objModule.ModuleID; } - return tabModuleId; + // update the position to let later modules with add after previous one. + position = ModuleController.Instance.GetTabModule(objModule.TabModuleID).ModuleOrder + 1; } - private void ToggleUserMode(string mode) - { - var personalization = this.personalizationController.LoadProfile(this.UserInfo.UserID, this.PortalSettings.PortalId); - personalization.Profile["Usability:UserMode" + this.PortalSettings.PortalId] = mode.ToUpperInvariant(); - personalization.IsModified = true; - this.personalizationController.SaveProfile(personalization); - } + return tabModuleId; + } - private PortalSettings GetPortalSettings(string portal) - { - var portalSettings = PortalSettings.Current; + private void ToggleUserMode(string mode) + { + var personalization = this.personalizationController.LoadProfile(this.UserInfo.UserID, this.PortalSettings.PortalId); + personalization.Profile["Usability:UserMode" + this.PortalSettings.PortalId] = mode.ToUpperInvariant(); + personalization.IsModified = true; + this.personalizationController.SaveProfile(personalization); + } - try + private PortalSettings GetPortalSettings(string portal) + { + var portalSettings = PortalSettings.Current; + + try + { + if (!string.IsNullOrEmpty(portal)) { - if (!string.IsNullOrEmpty(portal)) + var selectedPortalId = int.Parse(portal, CultureInfo.InvariantCulture); + if (this.PortalSettings.PortalId != selectedPortalId) { - var selectedPortalId = int.Parse(portal, CultureInfo.InvariantCulture); - if (this.PortalSettings.PortalId != selectedPortalId) - { - portalSettings = new PortalSettings(selectedPortalId); - } + portalSettings = new PortalSettings(selectedPortalId); } } - catch (Exception) - { - portalSettings = PortalSettings.Current; - } - - return portalSettings; } - - private bool ActiveTabHasChildren() + catch (Exception) { - var children = TabController.GetTabsByParent(this.PortalSettings.ActiveTab.TabID, this.PortalSettings.ActiveTab.PortalID); + portalSettings = PortalSettings.Current; + } - if ((children == null) || children.Count < 1) - { - return false; - } + return portalSettings; + } - return true; + private bool ActiveTabHasChildren() + { + var children = TabController.GetTabsByParent(this.PortalSettings.ActiveTab.TabID, this.PortalSettings.ActiveTab.PortalID); + + if ((children == null) || children.Count < 1) + { + return false; } - private int DoAddExistingModule(int moduleId, int tabId, string paneName, int position, string align, bool cloneModule) + return true; + } + + private int DoAddExistingModule(int moduleId, int tabId, string paneName, int position, string align, bool cloneModule) + { + ModuleInfo moduleInfo = ModuleController.Instance.GetModule(moduleId, tabId, false); + + int userID = -1; + + UserInfo user = UserController.Instance.GetCurrentUserInfo(); + if (user != null) { - ModuleInfo moduleInfo = ModuleController.Instance.GetModule(moduleId, tabId, false); + userID = user.UserID; + } - int userID = -1; + if (moduleInfo is { IsDeleted: false }) + { + // Is this from a site other than our own? (i.e., is the user requesting "module sharing"?) + var remote = moduleInfo.PortalID != PortalSettings.Current.PortalId; + if (remote) + { + switch (moduleInfo.DesktopModule.Shareable) + { + case ModuleSharing.Unsupported: + // Should never happen since the module should not be listed in the first place. + throw new SharingUnsupportedException($"Module '{moduleInfo.DesktopModule.FriendlyName}' does not support Shareable and should not be listed in Add Existing Module from a different source site"); + case ModuleSharing.Supported: + case ModuleSharing.Unknown: + break; + } + } - UserInfo user = UserController.Instance.GetCurrentUserInfo(); - if (user != null) + if (!ModulePermissionController.HasModuleAccess(SecurityAccessLevel.Edit, "MANAGE", moduleInfo)) { - userID = user.UserID; + throw new SecurityException($"Module '{moduleInfo.ModuleID}' is not available in current context."); } - if (moduleInfo is { IsDeleted: false }) + // clone the module object ( to avoid creating an object reference to the data cache ) + ModuleInfo newModule = moduleInfo.Clone(); + + newModule.UniqueId = Guid.NewGuid(); // Cloned Module requires a different uniqueID + newModule.TabModuleID = Null.NullInteger; + newModule.PortalID = PortalSettings.Current.PortalId; + newModule.TabID = PortalSettings.Current.ActiveTab.TabID; + newModule.ModuleOrder = position; + newModule.PaneName = paneName; + newModule.Alignment = align; + + if (cloneModule) { - // Is this from a site other than our own? (i.e., is the user requesting "module sharing"?) - var remote = moduleInfo.PortalID != PortalSettings.Current.PortalId; - if (remote) + newModule.ModuleID = Null.NullInteger; + + // copy module settings and tab module settings + newModule.ModuleSettings.Clear(); + foreach (var key in moduleInfo.ModuleSettings.Keys) { - switch (moduleInfo.DesktopModule.Shareable) - { - case ModuleSharing.Unsupported: - // Should never happen since the module should not be listed in the first place. - throw new SharingUnsupportedException($"Module '{moduleInfo.DesktopModule.FriendlyName}' does not support Shareable and should not be listed in Add Existing Module from a different source site"); - case ModuleSharing.Supported: - case ModuleSharing.Unknown: - break; - } + newModule.ModuleSettings.Add(key, moduleInfo.ModuleSettings[key]); } - if (!ModulePermissionController.HasModuleAccess(SecurityAccessLevel.Edit, "MANAGE", moduleInfo)) + newModule.TabModuleSettings.Clear(); + foreach (var key in moduleInfo.TabModuleSettings.Keys) { - throw new SecurityException($"Module '{moduleInfo.ModuleID}' is not available in current context."); + newModule.TabModuleSettings.Add(key, moduleInfo.TabModuleSettings[key]); } - // clone the module object ( to avoid creating an object reference to the data cache ) - ModuleInfo newModule = moduleInfo.Clone(); - - newModule.UniqueId = Guid.NewGuid(); // Cloned Module requires a different uniqueID - newModule.TabModuleID = Null.NullInteger; - newModule.PortalID = PortalSettings.Current.PortalId; - newModule.TabID = PortalSettings.Current.ActiveTab.TabID; - newModule.ModuleOrder = position; - newModule.PaneName = paneName; - newModule.Alignment = align; + // reset the module id + newModule.ModuleID = ModuleController.Instance.AddModule(newModule); - if (cloneModule) + if (!string.IsNullOrEmpty(newModule.DesktopModule.BusinessControllerClass)) { - newModule.ModuleID = Null.NullInteger; - - // copy module settings and tab module settings - newModule.ModuleSettings.Clear(); - foreach (var key in moduleInfo.ModuleSettings.Keys) + var portable = this.businessControllerProvider.GetInstance(newModule); + if (portable is not null) { - newModule.ModuleSettings.Add(key, moduleInfo.ModuleSettings[key]); - } - - newModule.TabModuleSettings.Clear(); - foreach (var key in moduleInfo.TabModuleSettings.Keys) - { - newModule.TabModuleSettings.Add(key, moduleInfo.TabModuleSettings[key]); - } - - // reset the module id - newModule.ModuleID = ModuleController.Instance.AddModule(newModule); - - if (!string.IsNullOrEmpty(newModule.DesktopModule.BusinessControllerClass)) - { - var portable = this.businessControllerProvider.GetInstance(newModule); - if (portable is not null) + try { - try + SetCloneModuleContext(true); + var content = portable.ExportModule(moduleId); + if (!string.IsNullOrEmpty(content)) { - SetCloneModuleContext(true); - var content = portable.ExportModule(moduleId); - if (!string.IsNullOrEmpty(content)) - { - portable.ImportModule( - newModule.ModuleID, - content, - newModule.DesktopModule.Version, - userID); - } - } - finally - { - SetCloneModuleContext(false); + portable.ImportModule( + newModule.ModuleID, + content, + newModule.DesktopModule.Version, + userID); } } + finally + { + SetCloneModuleContext(false); + } } } - else + } + else + { + // copy tab module settings + newModule.TabModuleSettings.Clear(); + foreach (var key in moduleInfo.TabModuleSettings.Keys) { - // copy tab module settings - newModule.TabModuleSettings.Clear(); - foreach (var key in moduleInfo.TabModuleSettings.Keys) - { - newModule.TabModuleSettings.Add(key, moduleInfo.TabModuleSettings[key]); - } - - ModuleController.Instance.AddModule(newModule); + newModule.TabModuleSettings.Add(key, moduleInfo.TabModuleSettings[key]); } - // if the tab of original module has custom stylesheet defined, then also copy the stylesheet - // to the destination tab if its custom stylesheet is empty. - var originalTab = TabController.Instance.GetTab(moduleInfo.TabID, moduleInfo.PortalID); - var targetTab = PortalSettings.Current.ActiveTab; - if (originalTab != null - && originalTab.TabSettings.ContainsKey("CustomStylesheet") - && !string.IsNullOrEmpty(originalTab.TabSettings["CustomStylesheet"].ToString()) - && (!targetTab.TabSettings.ContainsKey("CustomStylesheet") || - string.IsNullOrEmpty(targetTab.TabSettings["CustomStylesheet"].ToString()))) - { - TabController.Instance.UpdateTabSetting(targetTab.TabID, "CustomStylesheet", originalTab.TabSettings["CustomStylesheet"].ToString()); - } + ModuleController.Instance.AddModule(newModule); + } - if (remote) - { - // Ensure the Portal Admin has View rights - var arrSystemModuleViewPermissions = this.permissionDefinitionService.GetDefinitionsByCodeAndKey("SYSTEM_MODULE_DEFINITION", "VIEW"); - AddModulePermission( - newModule, - arrSystemModuleViewPermissions.First(), - PortalSettings.Current.AdministratorRoleId, - Null.NullInteger, - true); - - // Set PortalID correctly - newModule.OwnerPortalID = newModule.PortalID; - newModule.PortalID = PortalSettings.Current.PortalId; - ModulePermissionController.SaveModulePermissions(newModule); - } + // if the tab of original module has custom stylesheet defined, then also copy the stylesheet + // to the destination tab if its custom stylesheet is empty. + var originalTab = TabController.Instance.GetTab(moduleInfo.TabID, moduleInfo.PortalID); + var targetTab = PortalSettings.Current.ActiveTab; + if (originalTab != null + && originalTab.TabSettings.ContainsKey("CustomStylesheet") + && !string.IsNullOrEmpty(originalTab.TabSettings["CustomStylesheet"].ToString()) + && (!targetTab.TabSettings.ContainsKey("CustomStylesheet") || + string.IsNullOrEmpty(targetTab.TabSettings["CustomStylesheet"].ToString()))) + { + TabController.Instance.UpdateTabSetting(targetTab.TabID, "CustomStylesheet", originalTab.TabSettings["CustomStylesheet"].ToString()); + } - // Add Event Log - this.eventLogger.AddLog(newModule, PortalSettings.Current, userID, string.Empty, EventLogType.MODULE_CREATED); + if (remote) + { + // Ensure the Portal Admin has View rights + var arrSystemModuleViewPermissions = this.permissionDefinitionService.GetDefinitionsByCodeAndKey("SYSTEM_MODULE_DEFINITION", "VIEW"); + AddModulePermission( + newModule, + arrSystemModuleViewPermissions.First(), + PortalSettings.Current.AdministratorRoleId, + Null.NullInteger, + true); - return newModule.ModuleID; + // Set PortalID correctly + newModule.OwnerPortalID = newModule.PortalID; + newModule.PortalID = PortalSettings.Current.PortalId; + ModulePermissionController.SaveModulePermissions(newModule); } - return -1; + // Add Event Log + this.eventLogger.AddLog(newModule, PortalSettings.Current, userID, string.Empty, EventLogType.MODULE_CREATED); + + return newModule.ModuleID; } - /// A data transfer object with information about a module definition. - public class ModuleDefDTO - { - /// Gets or sets the module ID. - public int ModuleID { get; set; } + return -1; + } - /// Gets or sets the module name. - public string ModuleName { get; set; } + /// A data transfer object with information about a module definition. + public class ModuleDefDTO + { + /// Gets or sets the module ID. + public int ModuleID { get; set; } - /// Gets or sets the path to the module's image. - public string ModuleImage { get; set; } + /// Gets or sets the module name. + public string ModuleName { get; set; } - /// Gets or sets a value indicating whether the module is bookmarked. - public bool Bookmarked { get; set; } + /// Gets or sets the path to the module's image. + public string ModuleImage { get; set; } - /// Gets or sets a value indicating whether the module is in a bookmarked category. - public bool ExistsInBookmarkCategory { get; set; } - } + /// Gets or sets a value indicating whether the module is bookmarked. + public bool Bookmarked { get; set; } - /// A data transfer object with information about a page. - public class PageDefDTO - { - /// Gets or sets the page's ID. - public int TabID { get; set; } + /// Gets or sets a value indicating whether the module is in a bookmarked category. + public bool ExistsInBookmarkCategory { get; set; } + } - /// Gets or sets the page's indented name. - public string IndentedTabName { get; set; } - } + /// A data transfer object with information about a page. + public class PageDefDTO + { + /// Gets or sets the page's ID. + public int TabID { get; set; } - /// A data transfer object with information about adding a module to a page. - public class AddModuleDTO - { - /// Gets or sets the visibility of the module. - public string Visibility { get; set; } + /// Gets or sets the page's indented name. + public string IndentedTabName { get; set; } + } - /// Gets or sets the position of the module. - public string Position { get; set; } + /// A data transfer object with information about adding a module to a page. + public class AddModuleDTO + { + /// Gets or sets the visibility of the module. + public string Visibility { get; set; } - /// Gets or sets the ID of an existing module. - public string Module { get; set; } + /// Gets or sets the position of the module. + public string Position { get; set; } - /// Gets or sets the ID of the page. - public string Page { get; set; } + /// Gets or sets the ID of an existing module. + public string Module { get; set; } - /// Gets or sets the pane name. - public string Pane { get; set; } + /// Gets or sets the ID of the page. + public string Page { get; set; } - /// Gets or sets a value indicating whether to add an existing module instead of a new module. - public string AddExistingModule { get; set; } + /// Gets or sets the pane name. + public string Pane { get; set; } - /// Gets or sets a value indicating whether to copy an existing module instead of making a shared reference. - public string CopyModule { get; set; } + /// Gets or sets a value indicating whether to add an existing module instead of a new module. + public string AddExistingModule { get; set; } - /// Gets or sets the sort of the module. - public string Sort { get; set; } - } + /// Gets or sets a value indicating whether to copy an existing module instead of making a shared reference. + public string CopyModule { get; set; } - /// A data transfer object with information about the user mode. - public class UserModeDTO - { - /// Gets or sets the user mode. - public string UserMode { get; set; } - } + /// Gets or sets the sort of the module. + public string Sort { get; set; } + } - /// A data transfer object with information about the site to switch to. - public class SwitchSiteDTO - { - /// Gets or sets the portal ID. - public string Site { get; set; } - } + /// A data transfer object with information about the user mode. + public class UserModeDTO + { + /// Gets or sets the user mode. + public string UserMode { get; set; } + } - /// A data transfer object with information about the language to switch to. - public class SwitchLanguageDTO - { - /// Gets or sets the language code. - public string Language { get; set; } - } + /// A data transfer object with information about the site to switch to. + public class SwitchSiteDTO + { + /// Gets or sets the portal ID. + public string Site { get; set; } + } - /// A data transfer object with information about a bookmark to add. - public class BookmarkDTO - { - /// Gets or sets the bookmark title. - public string Title { get; set; } + /// A data transfer object with information about the language to switch to. + public class SwitchLanguageDTO + { + /// Gets or sets the language code. + public string Language { get; set; } + } - /// Gets or sets the bookmark value. - public string Bookmark { get; set; } - } + /// A data transfer object with information about a bookmark to add. + public class BookmarkDTO + { + /// Gets or sets the bookmark title. + public string Title { get; set; } - /// A data transfer object with information about a lock/unlock request. - public class LockingDTO - { - /// Gets or sets a value indicating whether to lock or unlock the site or instance. - public bool Lock { get; set; } - } + /// Gets or sets the bookmark value. + public string Bookmark { get; set; } + } + + /// A data transfer object with information about a lock/unlock request. + public class LockingDTO + { + /// Gets or sets a value indicating whether to lock or unlock the site or instance. + public bool Lock { get; set; } } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/CountryRegionController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/CountryRegionController.cs index 7b738d87c47..a50fcb3d77c 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/CountryRegionController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/CountryRegionController.cs @@ -2,80 +2,79 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices -{ - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Web; - using System.Web.Http; +namespace DotNetNuke.Web.InternalServices; - using DotNetNuke.Common; - using DotNetNuke.Common.Lists; - using DotNetNuke.Common.Utilities; - using DotNetNuke.Web.Api; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web; +using System.Web.Http; - using Microsoft.Extensions.DependencyInjection; +using DotNetNuke.Common; +using DotNetNuke.Common.Lists; +using DotNetNuke.Common.Utilities; +using DotNetNuke.Web.Api; - /// A web API controller for retrieving regions and countries. - /// The list controller. - [AllowAnonymous] - public class CountryRegionController(ListController listController) - : DnnApiController - { - private readonly ListController listController = listController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); +using Microsoft.Extensions.DependencyInjection; - /// Initializes a new instance of the class. - [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with ListController. Scheduled removal in v12.0.0.")] - public CountryRegionController() - : this(null) - { - } +/// A web API controller for retrieving regions and countries. +/// The list controller. +[AllowAnonymous] +public class CountryRegionController(ListController listController) + : DnnApiController +{ + private readonly ListController listController = listController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - /// Gets the countries. - /// A response with an alphabetized list of objects. - [HttpGet] - public HttpResponseMessage Countries() - { - var searchString = (HttpContext.Current.Request.Params["SearchString"] ?? string.Empty).NormalizeString(); - var countries = CachedCountryList.GetCountryList(this.listController); - return this.Request.CreateResponse(HttpStatusCode.OK, countries.Values.Where( - x => x.NormalizedFullName.IndexOf(searchString, StringComparison.CurrentCulture) > -1).OrderBy(x => x.NormalizedFullName)); - } + /// Initializes a new instance of the class. + [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with ListController. Scheduled removal in v12.0.0.")] + public CountryRegionController() + : this(null) + { + } - /// Gets the regions for a country. - /// The country ID. - /// A response with an alphabetized list of objects. - [HttpGet] - public HttpResponseMessage Regions(int country) + /// Gets the countries. + /// A response with an alphabetized list of objects. + [HttpGet] + public HttpResponseMessage Countries() + { + var searchString = (HttpContext.Current.Request.Params["SearchString"] ?? string.Empty).NormalizeString(); + var countries = CachedCountryList.GetCountryList(this.listController); + return this.Request.CreateResponse(HttpStatusCode.OK, countries.Values.Where( + x => x.NormalizedFullName.IndexOf(searchString, StringComparison.CurrentCulture) > -1).OrderBy(x => x.NormalizedFullName)); + } + + /// Gets the regions for a country. + /// The country ID. + /// A response with an alphabetized list of objects. + [HttpGet] + public HttpResponseMessage Regions(int country) + { + List res = []; + foreach (ListEntryInfo r in this.listController.GetListEntryInfoItems("Region").Where(l => l.ParentID == country)) { - List res = []; - foreach (ListEntryInfo r in this.listController.GetListEntryInfoItems("Region").Where(l => l.ParentID == country)) + res.Add(new Region { - res.Add(new Region - { - Text = r.Text, - Value = r.EntryID.ToString(CultureInfo.InvariantCulture), - }); - } - - return this.Request.CreateResponse(HttpStatusCode.OK, res.OrderBy(r => r.Text)); + Text = r.Text, + Value = r.EntryID.ToString(CultureInfo.InvariantCulture), + }); } - /// A data transfer object representing a region. - public struct Region - { - /// Gets or sets the text. - [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] - public string Text; + return this.Request.CreateResponse(HttpStatusCode.OK, res.OrderBy(r => r.Text)); + } - /// Gets or sets the entry ID. - [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] - public string Value; - } + /// A data transfer object representing a region. + public struct Region + { + /// Gets or sets the text. + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] + public string Text; + + /// Gets or sets the entry ID. + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] + public string Value; } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/EventLogServiceController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/EventLogServiceController.cs index c220b21bb5d..bae61ad6c74 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/EventLogServiceController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/EventLogServiceController.cs @@ -2,96 +2,95 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices -{ - using System; - using System.Diagnostics.CodeAnalysis; - using System.Net; - using System.Net.Http; - using System.Text; - using System.Web; - using System.Web.Http; +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Web; +using System.Web.Http; + +using DotNetNuke.Abstractions.Logging; +using DotNetNuke.Common; +using DotNetNuke.Instrumentation; +using DotNetNuke.Services.Localization; +using DotNetNuke.Web.Api; - using DotNetNuke.Abstractions.Logging; - using DotNetNuke.Common; - using DotNetNuke.Instrumentation; - using DotNetNuke.Services.Localization; - using DotNetNuke.Web.Api; +using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.DependencyInjection; +/// A web API which gets event log details. +[DnnAuthorize] +public class EventLogServiceController : DnnApiController +{ + private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(EventLogServiceController)); + private readonly IEventLogService eventLogService; - /// A web API which gets event log details. - [DnnAuthorize] - public class EventLogServiceController : DnnApiController + /// Initializes a new instance of the class. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IEventLogService. Scheduled removal in v12.0.0.")] + public EventLogServiceController() + : this(null) { - private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(EventLogServiceController)); - private readonly IEventLogService eventLogService; + } - /// Initializes a new instance of the class. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IEventLogService. Scheduled removal in v12.0.0.")] - public EventLogServiceController() - : this(null) - { - } + /// Initializes a new instance of the class. + /// The event log service. + public EventLogServiceController(IEventLogService eventLogService) + { + this.eventLogService = eventLogService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + } - /// Initializes a new instance of the class. - /// The event log service. - public EventLogServiceController(IEventLogService eventLogService) + /// Gets event log details. + /// The event log GUID. + /// A response with an object with Title and Content fields. + [HttpGet] + [DnnAuthorize(StaticRoles = "Administrators")] + [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", Justification = "Breaking change")] + public HttpResponseMessage GetLogDetails(string guid) + { + if (string.IsNullOrEmpty(guid) || !Guid.TryParse(guid, out _)) { - this.eventLogService = eventLogService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + return this.Request.CreateResponse(HttpStatusCode.BadRequest); } - /// Gets event log details. - /// The event log GUID. - /// A response with an object with Title and Content fields. - [HttpGet] - [DnnAuthorize(StaticRoles = "Administrators")] - [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", Justification = "Breaking change")] - public HttpResponseMessage GetLogDetails(string guid) + try { - if (string.IsNullOrEmpty(guid) || !Guid.TryParse(guid, out _)) + if (this.eventLogService.GetLog(guid) is not ILogInfo logInfo) { return this.Request.CreateResponse(HttpStatusCode.BadRequest); } - try + return this.Request.CreateResponse(HttpStatusCode.OK, new { - if (this.eventLogService.GetLog(guid) is not ILogInfo logInfo) - { - return this.Request.CreateResponse(HttpStatusCode.BadRequest); - } - - return this.Request.CreateResponse(HttpStatusCode.OK, new - { - Title = Localization.GetSafeJSString("CriticalError.Error", Localization.SharedResourceFile), - Content = GetPropertiesText(logInfo), - }); - } - catch (Exception ex) - { - Logger.Error(ex); - return this.Request.CreateResponse(HttpStatusCode.BadRequest); - } + Title = Localization.GetSafeJSString("CriticalError.Error", Localization.SharedResourceFile), + Content = GetPropertiesText(logInfo), + }); } + catch (Exception ex) + { + Logger.Error(ex); + return this.Request.CreateResponse(HttpStatusCode.BadRequest); + } + } - private static string GetPropertiesText(ILogInfo logInfo) + private static string GetPropertiesText(ILogInfo logInfo) + { + var str = new StringBuilder(); + foreach (var ldi in logInfo.LogProperties) { - var str = new StringBuilder(); - foreach (var ldi in logInfo.LogProperties) + // display the values in the Panel child controls. + if (ldi.PropertyName == "Message") { - // display the values in the Panel child controls. - if (ldi.PropertyName == "Message") - { - str.Append($"

{ldi.PropertyName}:

{HttpUtility.HtmlEncode(ldi.PropertyValue)}

"); - } - else - { - str.Append($"

{ldi.PropertyName}:{HttpUtility.HtmlEncode(ldi.PropertyValue)}

"); - } + str.Append($"

{ldi.PropertyName}:

{HttpUtility.HtmlEncode(ldi.PropertyValue)}

"); + } + else + { + str.Append($"

{ldi.PropertyName}:{HttpUtility.HtmlEncode(ldi.PropertyValue)}

"); } - - str.Append($"

Server Name: {HttpUtility.HtmlEncode(logInfo.LogServerName)}

"); - return str.ToString(); } + + str.Append($"

Server Name: {HttpUtility.HtmlEncode(logInfo.LogServerName)}

"); + return str.ToString(); } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/FileUploadController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/FileUploadController.cs index 06cece5d667..ff95ccf8414 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/FileUploadController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/FileUploadController.cs @@ -2,828 +2,827 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using System.Web.Http; +using System.Web.UI.WebControls; + +using DotNetNuke.Abstractions.Application; +using DotNetNuke.Abstractions.Portals; +using DotNetNuke.Abstractions.Security; +using DotNetNuke.Common; +using DotNetNuke.Common.Utilities; +using DotNetNuke.Entities.Icons; +using DotNetNuke.Entities.Portals; +using DotNetNuke.Entities.Users; +using DotNetNuke.Instrumentation; +using DotNetNuke.Security; +using DotNetNuke.Security.Permissions; +using DotNetNuke.Services.FileSystem; +using DotNetNuke.Services.Localization; +using DotNetNuke.Web.Api; +using DotNetNuke.Web.Api.Internal; + +using Microsoft.Extensions.DependencyInjection; + +using FileInfo = DotNetNuke.Services.FileSystem.FileInfo; + +/// A web API for uploading files. +[DnnAuthorize] +public class FileUploadController : DnnApiController { - using System; - using System.Collections.Generic; - using System.Drawing; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Net.Http.Formatting; - using System.Net.Http.Headers; - using System.Runtime.Serialization; - using System.Text.RegularExpressions; - using System.Threading; - using System.Threading.Tasks; - using System.Web; - using System.Web.Http; - using System.Web.UI.WebControls; - - using DotNetNuke.Abstractions.Application; - using DotNetNuke.Abstractions.Portals; - using DotNetNuke.Abstractions.Security; - using DotNetNuke.Common; - using DotNetNuke.Common.Utilities; - using DotNetNuke.Entities.Icons; - using DotNetNuke.Entities.Portals; - using DotNetNuke.Entities.Users; - using DotNetNuke.Instrumentation; - using DotNetNuke.Security; - using DotNetNuke.Security.Permissions; - using DotNetNuke.Services.FileSystem; - using DotNetNuke.Services.Localization; - using DotNetNuke.Web.Api; - using DotNetNuke.Web.Api.Internal; - - using Microsoft.Extensions.DependencyInjection; - - using FileInfo = DotNetNuke.Services.FileSystem.FileInfo; - - /// A web API for uploading files. - [DnnAuthorize] - public class FileUploadController : DnnApiController + private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(FileUploadController)); + private static readonly Regex UserFolderEx = new Regex(@"users/\d+/\d+/(\d+)/", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly List ImageExtensions = Globals.ImageFileTypes.Split(',').ToList(); + + private readonly IHostSettings hostSettings; + private readonly ICryptographyProvider cryptographyProvider; + private readonly IPortalController portalController; + private readonly IApplicationStatusInfo appStatus; + private readonly IPortalGroupController portalGroupController; + + /// Initializes a new instance of the class. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Use overload with ICryptographyProvider. Scheduled for removal in v12.0.0.")] + public FileUploadController() + : this(null, null, null, null, null) { - private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(FileUploadController)); - private static readonly Regex UserFolderEx = new Regex(@"users/\d+/\d+/(\d+)/", RegexOptions.Compiled | RegexOptions.IgnoreCase); - private static readonly List ImageExtensions = Globals.ImageFileTypes.Split(',').ToList(); - - private readonly IHostSettings hostSettings; - private readonly ICryptographyProvider cryptographyProvider; - private readonly IPortalController portalController; - private readonly IApplicationStatusInfo appStatus; - private readonly IPortalGroupController portalGroupController; - - /// Initializes a new instance of the class. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Use overload with ICryptographyProvider. Scheduled for removal in v12.0.0.")] - public FileUploadController() - : this(null, null, null, null, null) - { - } + } - /// Initializes a new instance of the class. - /// The host settings. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Use overload with ICryptographyProvider. Scheduled for removal in v12.0.0.")] - public FileUploadController(IHostSettings hostSettings) - : this(hostSettings, null, null, null, null) - { - } + /// Initializes a new instance of the class. + /// The host settings. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Use overload with ICryptographyProvider. Scheduled for removal in v12.0.0.")] + public FileUploadController(IHostSettings hostSettings) + : this(hostSettings, null, null, null, null) + { + } + + /// Initializes a new instance of the class. + /// The host settings. + /// The cryptography provider. + /// The portal controller. + /// The application status. + /// The portal group controller. + public FileUploadController(IHostSettings hostSettings, ICryptographyProvider cryptographyProvider, IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController) + { + this.hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.cryptographyProvider = cryptographyProvider ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.appStatus = appStatus ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.portalGroupController = portalGroupController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + } - /// Initializes a new instance of the class. - /// The host settings. - /// The cryptography provider. - /// The portal controller. - /// The application status. - /// The portal group controller. - public FileUploadController(IHostSettings hostSettings, ICryptographyProvider cryptographyProvider, IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController) + /// Gets the URL for a file. + /// The ID of the file. + /// The URL. + public static string GetUrl(int fileId) + { + var file = FileManager.Instance.GetFile(fileId, true); + return FileManager.Instance.GetUrl(file); + } + + /// Gets the files in the folder. + /// Information about the folder. + /// A response with a list of objects. + [HttpPost] + public HttpResponseMessage LoadFiles(FolderItemDTO folderItem) + { + int effectivePortalId = this.PortalSettings.PortalId; + + if (folderItem.FolderId <= 0) { - this.hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.cryptographyProvider = cryptographyProvider ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.appStatus = appStatus ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.portalGroupController = portalGroupController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + return this.Request.CreateResponse(HttpStatusCode.BadRequest); } - /// Gets the URL for a file. - /// The ID of the file. - /// The URL. - public static string GetUrl(int fileId) + var folder = FolderManager.Instance.GetFolder(folderItem.FolderId); + + if (folder == null) { - var file = FileManager.Instance.GetFile(fileId, true); - return FileManager.Instance.GetUrl(file); + return this.Request.CreateResponse(HttpStatusCode.BadRequest); } - /// Gets the files in the folder. - /// Information about the folder. - /// A response with a list of objects. - [HttpPost] - public HttpResponseMessage LoadFiles(FolderItemDTO folderItem) + if (IsUserFolder(folder.FolderPath, out var userId)) { - int effectivePortalId = this.PortalSettings.PortalId; - - if (folderItem.FolderId <= 0) + var user = UserController.GetUserById(this.hostSettings, effectivePortalId, userId); + if (user is { IsSuperUser: true, }) { - return this.Request.CreateResponse(HttpStatusCode.BadRequest); + effectivePortalId = Null.NullInteger; } - - var folder = FolderManager.Instance.GetFolder(folderItem.FolderId); - - if (folder == null) + else { - return this.Request.CreateResponse(HttpStatusCode.BadRequest); + effectivePortalId = PortalController.GetEffectivePortalId(this.portalController, this.appStatus, this.portalGroupController, effectivePortalId); } + } - if (IsUserFolder(folder.FolderPath, out var userId)) + var list = Globals.GetFileList(effectivePortalId, folderItem.FileFilter, !folderItem.Required, folder.FolderPath); + var fileItems = list.OfType().ToList(); + + return this.Request.CreateResponse(HttpStatusCode.OK, fileItems); + } + + /// Gets the URL of an image file. + /// The file ID. + /// A response with either null or the image URL. + [HttpGet] + public HttpResponseMessage LoadImage(string fileId) + { + if (!string.IsNullOrEmpty(fileId)) + { + int file; + if (int.TryParse(fileId, out file)) { - var user = UserController.GetUserById(this.hostSettings, effectivePortalId, userId); - if (user is { IsSuperUser: true, }) - { - effectivePortalId = Null.NullInteger; - } - else - { - effectivePortalId = PortalController.GetEffectivePortalId(this.portalController, this.appStatus, this.portalGroupController, effectivePortalId); - } + var imageUrl = ShowImage(file); + return this.Request.CreateResponse(HttpStatusCode.OK, imageUrl); } + } - var list = Globals.GetFileList(effectivePortalId, folderItem.FileFilter, !folderItem.Required, folder.FolderPath); - var fileItems = list.OfType().ToList(); + return this.Request.CreateResponse(HttpStatusCode.InternalServerError); + } - return this.Request.CreateResponse(HttpStatusCode.OK, fileItems); - } + /// Uploads an image. + /// A response with an object. + /// If the request is not using a multipart MIME content type. + [HttpPost] + [IFrameSupportedValidateAntiForgeryToken] + public Task PostFile() + { + HttpRequestMessage request = this.Request; - /// Gets the URL of an image file. - /// The file ID. - /// A response with either null or the image URL. - [HttpGet] - public HttpResponseMessage LoadImage(string fileId) + if (!request.Content.IsMimeMultipartContent()) { - if (!string.IsNullOrEmpty(fileId)) + throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); + } + + var provider = new MultipartMemoryStreamProvider(); + + // local references for use in closure + IPortalSettings portalSettings = this.PortalSettings; + var currentSynchronizationContext = SynchronizationContext.Current; + var userInfo = this.UserInfo; + var task = request.Content.ReadAsMultipartAsync(provider) + .ContinueWith(_ => { - int file; - if (int.TryParse(fileId, out file)) + string folder = string.Empty; + string filter = string.Empty; + string fileName = string.Empty; + bool overwrite = false; + bool isHostMenu = false; + bool extract = false; + Stream stream = null; + var returnFileDto = new SavedFileDTO(); + + foreach (var item in provider.Contents) { - var imageUrl = ShowImage(file); - return this.Request.CreateResponse(HttpStatusCode.OK, imageUrl); - } - } + var name = item.Headers.ContentDisposition.Name; + switch (name.ToUpperInvariant()) + { + case "\"FOLDER\"": + folder = item.ReadAsStringAsync().Result ?? string.Empty; + break; - return this.Request.CreateResponse(HttpStatusCode.InternalServerError); - } + case "\"FILTER\"": + filter = item.ReadAsStringAsync().Result ?? string.Empty; + break; - /// Uploads an image. - /// A response with an object. - /// If the request is not using a multipart MIME content type. - [HttpPost] - [IFrameSupportedValidateAntiForgeryToken] - public Task PostFile() - { - HttpRequestMessage request = this.Request; + case "\"OVERWRITE\"": + if (!bool.TryParse(item.ReadAsStringAsync().Result, out overwrite)) + { + overwrite = false; + } - if (!request.Content.IsMimeMultipartContent()) - { - throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); - } + break; + + case "\"ISHOSTMENU\"": + if (!bool.TryParse(item.ReadAsStringAsync().Result, out isHostMenu)) + { + isHostMenu = false; + } - var provider = new MultipartMemoryStreamProvider(); + break; - // local references for use in closure - IPortalSettings portalSettings = this.PortalSettings; - var currentSynchronizationContext = SynchronizationContext.Current; - var userInfo = this.UserInfo; - var task = request.Content.ReadAsMultipartAsync(provider) - .ContinueWith(_ => - { - string folder = string.Empty; - string filter = string.Empty; - string fileName = string.Empty; - bool overwrite = false; - bool isHostMenu = false; - bool extract = false; - Stream stream = null; - var returnFileDto = new SavedFileDTO(); - - foreach (var item in provider.Contents) - { - var name = item.Headers.ContentDisposition.Name; - switch (name.ToUpperInvariant()) + case "\"EXTRACT\"": + if (!bool.TryParse(item.ReadAsStringAsync().Result, out extract)) { - case "\"FOLDER\"": - folder = item.ReadAsStringAsync().Result ?? string.Empty; - break; - - case "\"FILTER\"": - filter = item.ReadAsStringAsync().Result ?? string.Empty; - break; - - case "\"OVERWRITE\"": - if (!bool.TryParse(item.ReadAsStringAsync().Result, out overwrite)) - { - overwrite = false; - } - - break; - - case "\"ISHOSTMENU\"": - if (!bool.TryParse(item.ReadAsStringAsync().Result, out isHostMenu)) - { - isHostMenu = false; - } - - break; - - case "\"EXTRACT\"": - if (!bool.TryParse(item.ReadAsStringAsync().Result, out extract)) - { - extract = false; - } - - break; - - case "\"POSTFILE\"": - fileName = item.Headers.ContentDisposition.FileName.Replace("\"", string.Empty); - if (fileName.IndexOf(@"\", StringComparison.Ordinal) != -1) - { - fileName = Path.GetFileName(fileName); - } - - stream = item.ReadAsStreamAsync().Result; - break; + extract = false; } - } - var errorMessage = string.Empty; - var alreadyExists = false; - if (!string.IsNullOrEmpty(fileName) && stream != null) - { - // Everything ready - - // The SynchronizationContext keeps the main thread context. Send method is synchronous - currentSynchronizationContext.Send( - _ => - { - returnFileDto = SaveFile(stream, this.portalController, this.appStatus, this.portalGroupController, this.hostSettings, portalSettings, userInfo, folder, filter, fileName, overwrite, isHostMenu, extract, out alreadyExists, out errorMessage); - }, - null); - } - - /* Response Content Type cannot be application/json - * because IE9 with iframe-transport manages the response - * as a file download - */ - var mediaTypeFormatter = new JsonMediaTypeFormatter(); - mediaTypeFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain")); - - if (!string.IsNullOrEmpty(errorMessage)) - { - return this.Request.CreateResponse( - HttpStatusCode.BadRequest, - new - { - AlreadyExists = alreadyExists, - Message = string.Format( - CultureInfo.CurrentCulture, - GetLocalizedString("ErrorMessage"), - fileName, - errorMessage), - }, - mediaTypeFormatter, - "text/plain"); - } - - return this.Request.CreateResponse(HttpStatusCode.OK, returnFileDto, mediaTypeFormatter, "text/plain"); - }); - - return task; - } + break; - /// Uploads a file to the current portal. - /// A response with a object. - [HttpPost] - [IFrameSupportedValidateAntiForgeryToken] - [AllowAnonymous] - public Task UploadFromLocal() - { - return this.UploadFromLocal(this.PortalSettings.PortalId); - } + case "\"POSTFILE\"": + fileName = item.Headers.ContentDisposition.FileName.Replace("\"", string.Empty); + if (fileName.IndexOf(@"\", StringComparison.Ordinal) != -1) + { + fileName = Path.GetFileName(fileName); + } - /// Uploads a file. - /// The ID of the portal to which to upload it. - /// A response with a object. - /// If the request is not using a multipart MIME content type. - [HttpPost] - [IFrameSupportedValidateAntiForgeryToken] - [AllowAnonymous] - public Task UploadFromLocal(int portalId) - { - var request = this.Request; - FileUploadDto result = null; - if (!request.Content.IsMimeMultipartContent()) - { - throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); - } + stream = item.ReadAsStreamAsync().Result; + break; + } + } - if (portalId > -1) - { - if (!this.IsPortalIdValid(portalId)) + var errorMessage = string.Empty; + var alreadyExists = false; + if (!string.IsNullOrEmpty(fileName) && stream != null) { - throw new HttpResponseException(HttpStatusCode.Unauthorized); + // Everything ready + + // The SynchronizationContext keeps the main thread context. Send method is synchronous + currentSynchronizationContext.Send( + _ => + { + returnFileDto = SaveFile(stream, this.portalController, this.appStatus, this.portalGroupController, this.hostSettings, portalSettings, userInfo, folder, filter, fileName, overwrite, isHostMenu, extract, out alreadyExists, out errorMessage); + }, + null); } - } - else - { - portalId = this.PortalSettings.PortalId; - } - var provider = new MultipartMemoryStreamProvider(); + /* Response Content Type cannot be application/json + * because IE9 with iframe-transport manages the response + * as a file download + */ + var mediaTypeFormatter = new JsonMediaTypeFormatter(); + mediaTypeFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain")); - // local references for use in closure - var currentSynchronizationContext = SynchronizationContext.Current; - var userInfo = this.UserInfo; - var task = request.Content.ReadAsMultipartAsync(provider) - .ContinueWith(_ => + if (!string.IsNullOrEmpty(errorMessage)) { - var folder = string.Empty; - var filter = string.Empty; - var fileName = string.Empty; - var validationCode = string.Empty; - var overwrite = false; - var isHostPortal = false; - var extract = false; - Stream stream = null; - - foreach (var item in provider.Contents) - { - var name = item.Headers.ContentDisposition.Name; - switch (name.ToUpperInvariant()) + return this.Request.CreateResponse( + HttpStatusCode.BadRequest, + new { - case "\"FOLDER\"": - folder = item.ReadAsStringAsync().Result ?? string.Empty; - break; - - case "\"FILTER\"": - filter = item.ReadAsStringAsync().Result ?? string.Empty; - break; + AlreadyExists = alreadyExists, + Message = string.Format( + CultureInfo.CurrentCulture, + GetLocalizedString("ErrorMessage"), + fileName, + errorMessage), + }, + mediaTypeFormatter, + "text/plain"); + } - case "\"OVERWRITE\"": - if (!bool.TryParse(item.ReadAsStringAsync().Result, out overwrite)) - { - overwrite = false; - } + return this.Request.CreateResponse(HttpStatusCode.OK, returnFileDto, mediaTypeFormatter, "text/plain"); + }); - break; + return task; + } - case "\"ISHOSTPORTAL\"": - if (!bool.TryParse(item.ReadAsStringAsync().Result, out isHostPortal)) - { - isHostPortal = false; - } + /// Uploads a file to the current portal. + /// A response with a object. + [HttpPost] + [IFrameSupportedValidateAntiForgeryToken] + [AllowAnonymous] + public Task UploadFromLocal() + { + return this.UploadFromLocal(this.PortalSettings.PortalId); + } - break; + /// Uploads a file. + /// The ID of the portal to which to upload it. + /// A response with a object. + /// If the request is not using a multipart MIME content type. + [HttpPost] + [IFrameSupportedValidateAntiForgeryToken] + [AllowAnonymous] + public Task UploadFromLocal(int portalId) + { + var request = this.Request; + FileUploadDto result = null; + if (!request.Content.IsMimeMultipartContent()) + { + throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); + } - case "\"EXTRACT\"": - if (!bool.TryParse(item.ReadAsStringAsync().Result, out extract)) - { - extract = false; - } + if (portalId > -1) + { + if (!this.IsPortalIdValid(portalId)) + { + throw new HttpResponseException(HttpStatusCode.Unauthorized); + } + } + else + { + portalId = this.PortalSettings.PortalId; + } - break; + var provider = new MultipartMemoryStreamProvider(); - case "\"PORTALID\"": - if (userInfo.IsSuperUser) - { - var originalPortalId = portalId; - if (!int.TryParse(item.ReadAsStringAsync().Result, out portalId)) - { - portalId = originalPortalId; - } - } + // local references for use in closure + var currentSynchronizationContext = SynchronizationContext.Current; + var userInfo = this.UserInfo; + var task = request.Content.ReadAsMultipartAsync(provider) + .ContinueWith(_ => + { + var folder = string.Empty; + var filter = string.Empty; + var fileName = string.Empty; + var validationCode = string.Empty; + var overwrite = false; + var isHostPortal = false; + var extract = false; + Stream stream = null; + + foreach (var item in provider.Contents) + { + var name = item.Headers.ContentDisposition.Name; + switch (name.ToUpperInvariant()) + { + case "\"FOLDER\"": + folder = item.ReadAsStringAsync().Result ?? string.Empty; + break; - break; - case "\"VALIDATIONCODE\"": - validationCode = item.ReadAsStringAsync().Result ?? string.Empty; - break; - case "\"POSTFILE\"": - fileName = item.Headers.ContentDisposition.FileName.Replace("\"", string.Empty); - if (fileName.IndexOf(@"\", StringComparison.Ordinal) != -1) - { - fileName = Path.GetFileName(fileName); - } + case "\"FILTER\"": + filter = item.ReadAsStringAsync().Result ?? string.Empty; + break; - if (!Globals.FileEscapingRegex.IsMatch(fileName)) - { - stream = item.ReadAsStreamAsync().Result; - } + case "\"OVERWRITE\"": + if (!bool.TryParse(item.ReadAsStringAsync().Result, out overwrite)) + { + overwrite = false; + } - break; - } - } + break; - if (!string.IsNullOrEmpty(fileName) && stream != null) - { - // The SynchronizationContext keeps the main thread context. Send method is synchronous - currentSynchronizationContext.Send( - _ => + case "\"ISHOSTPORTAL\"": + if (!bool.TryParse(item.ReadAsStringAsync().Result, out isHostPortal)) { - result = UploadFile(this.cryptographyProvider, this.hostSettings, stream, portalId, userInfo, folder, filter, fileName, overwrite, isHostPortal, extract, validationCode); - }, - null); - } + isHostPortal = false; + } - var mediaTypeFormatter = new JsonMediaTypeFormatter(); - mediaTypeFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain")); + break; - /* Response Content Type cannot be application/json - * because IE9 with iframe-transport manages the response - * as a file download - */ - return this.Request.CreateResponse( - HttpStatusCode.OK, - result, - mediaTypeFormatter, - "text/plain"); - }); + case "\"EXTRACT\"": + if (!bool.TryParse(item.ReadAsStringAsync().Result, out extract)) + { + extract = false; + } - return task; - } + break; - private static SavedFileDTO SaveFile( - Stream stream, - IPortalController portalController, - IApplicationStatusInfo appStatus, - IPortalGroupController portalGroupController, - IHostSettings hostSettings, - IPortalSettings portalSettings, - UserInfo userInfo, - string folder, - string filter, - string fileName, - bool overwrite, - bool isHostMenu, - bool extract, - out bool alreadyExists, - out string errorMessage) - { - alreadyExists = false; - var savedFileDto = new SavedFileDTO(); - try - { - var extension = Path.GetExtension(fileName).ValueOrEmpty().Replace(".", string.Empty); - if (!string.IsNullOrEmpty(filter) && !filter.ToLowerInvariant().Contains(extension.ToLowerInvariant())) - { - errorMessage = GetLocalizedString("ExtensionNotAllowed"); - return savedFileDto; - } + case "\"PORTALID\"": + if (userInfo.IsSuperUser) + { + var originalPortalId = portalId; + if (!int.TryParse(item.ReadAsStringAsync().Result, out portalId)) + { + portalId = originalPortalId; + } + } - var folderManager = FolderManager.Instance; + break; + case "\"VALIDATIONCODE\"": + validationCode = item.ReadAsStringAsync().Result ?? string.Empty; + break; + case "\"POSTFILE\"": + fileName = item.Headers.ContentDisposition.FileName.Replace("\"", string.Empty); + if (fileName.IndexOf(@"\", StringComparison.Ordinal) != -1) + { + fileName = Path.GetFileName(fileName); + } - // Check if this is a User Folder - var effectivePortalId = isHostMenu ? Null.NullInteger : PortalController.GetEffectivePortalId(portalController, appStatus, portalGroupController, portalSettings.PortalId); - var folderInfo = folderManager.GetFolder(effectivePortalId, folder); - if (IsUserFolder(folder, out var userId)) - { - var user = UserController.GetUserById(hostSettings, effectivePortalId, userId); - if (user != null) - { - folderInfo = folderManager.GetUserFolder(user); + if (!Globals.FileEscapingRegex.IsMatch(fileName)) + { + stream = item.ReadAsStreamAsync().Result; + } + + break; } } - if (!PortalSecurity.IsInRoles(userInfo, portalSettings, folderInfo.FolderPermissions.ToString("WRITE")) - && !PortalSecurity.IsInRoles(userInfo, portalSettings, folderInfo.FolderPermissions.ToString("ADD"))) + if (!string.IsNullOrEmpty(fileName) && stream != null) { - errorMessage = GetLocalizedString("NoPermission"); - return savedFileDto; + // The SynchronizationContext keeps the main thread context. Send method is synchronous + currentSynchronizationContext.Send( + _ => + { + result = UploadFile(this.cryptographyProvider, this.hostSettings, stream, portalId, userInfo, folder, filter, fileName, overwrite, isHostPortal, extract, validationCode); + }, + null); } - const bool AlreadyCheckedPermissions = true; - if (!overwrite && FileManager.Instance.FileExists(folderInfo, fileName, true)) - { - errorMessage = GetLocalizedString("AlreadyExists"); - alreadyExists = true; - savedFileDto.FilePath = Path.Combine(folderInfo.PhysicalPath, fileName); - return savedFileDto; - } + var mediaTypeFormatter = new JsonMediaTypeFormatter(); + mediaTypeFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain")); + + /* Response Content Type cannot be application/json + * because IE9 with iframe-transport manages the response + * as a file download + */ + return this.Request.CreateResponse( + HttpStatusCode.OK, + result, + mediaTypeFormatter, + "text/plain"); + }); + + return task; + } - var contentType = FileContentTypeManager.Instance.GetContentType(Path.GetExtension(fileName)); - var file = FileManager.Instance.AddFile(folderInfo, fileName, stream, true, !AlreadyCheckedPermissions, contentType, userInfo.UserID); + private static SavedFileDTO SaveFile( + Stream stream, + IPortalController portalController, + IApplicationStatusInfo appStatus, + IPortalGroupController portalGroupController, + IHostSettings hostSettings, + IPortalSettings portalSettings, + UserInfo userInfo, + string folder, + string filter, + string fileName, + bool overwrite, + bool isHostMenu, + bool extract, + out bool alreadyExists, + out string errorMessage) + { + alreadyExists = false; + var savedFileDto = new SavedFileDTO(); + try + { + var extension = Path.GetExtension(fileName).ValueOrEmpty().Replace(".", string.Empty); + if (!string.IsNullOrEmpty(filter) && !filter.ToLowerInvariant().Contains(extension.ToLowerInvariant())) + { + errorMessage = GetLocalizedString("ExtensionNotAllowed"); + return savedFileDto; + } - if (extract && extension.Equals("zip", StringComparison.OrdinalIgnoreCase)) + var folderManager = FolderManager.Instance; + + // Check if this is a User Folder + var effectivePortalId = isHostMenu ? Null.NullInteger : PortalController.GetEffectivePortalId(portalController, appStatus, portalGroupController, portalSettings.PortalId); + var folderInfo = folderManager.GetFolder(effectivePortalId, folder); + if (IsUserFolder(folder, out var userId)) + { + var user = UserController.GetUserById(hostSettings, effectivePortalId, userId); + if (user != null) { - FileManager.Instance.UnzipFile(file); - FileManager.Instance.DeleteFile(file); + folderInfo = folderManager.GetUserFolder(user); } + } - errorMessage = string.Empty; - savedFileDto.FileId = file.FileId.ToString(CultureInfo.InvariantCulture); - savedFileDto.FilePath = FileManager.Instance.GetUrl(file); + if (!PortalSecurity.IsInRoles(userInfo, portalSettings, folderInfo.FolderPermissions.ToString("WRITE")) + && !PortalSecurity.IsInRoles(userInfo, portalSettings, folderInfo.FolderPermissions.ToString("ADD"))) + { + errorMessage = GetLocalizedString("NoPermission"); return savedFileDto; } - catch (InvalidFileExtensionException) + + const bool AlreadyCheckedPermissions = true; + if (!overwrite && FileManager.Instance.FileExists(folderInfo, fileName, true)) { - errorMessage = GetLocalizedString("ExtensionNotAllowed"); + errorMessage = GetLocalizedString("AlreadyExists"); + alreadyExists = true; + savedFileDto.FilePath = Path.Combine(folderInfo.PhysicalPath, fileName); return savedFileDto; } - catch (Exception ex) + + var contentType = FileContentTypeManager.Instance.GetContentType(Path.GetExtension(fileName)); + var file = FileManager.Instance.AddFile(folderInfo, fileName, stream, true, !AlreadyCheckedPermissions, contentType, userInfo.UserID); + + if (extract && extension.Equals("zip", StringComparison.OrdinalIgnoreCase)) { - Logger.Error(ex); - errorMessage = ex.Message; - return savedFileDto; + FileManager.Instance.UnzipFile(file); + FileManager.Instance.DeleteFile(file); } - } - private static string GetLocalizedString(string key) + errorMessage = string.Empty; + savedFileDto.FileId = file.FileId.ToString(CultureInfo.InvariantCulture); + savedFileDto.FilePath = FileManager.Instance.GetUrl(file); + return savedFileDto; + } + catch (InvalidFileExtensionException) { - const string resourceFile = "/App_GlobalResources/FileUpload.resx"; - return Localization.GetString(key, resourceFile); + errorMessage = GetLocalizedString("ExtensionNotAllowed"); + return savedFileDto; } - - private static bool IsUserFolder(string folderPath, out int userId) + catch (Exception ex) { - var match = UserFolderEx.Match(folderPath); - userId = match.Success ? int.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture) : Null.NullInteger; - - return match.Success; + Logger.Error(ex); + errorMessage = ex.Message; + return savedFileDto; } + } - private static string ShowImage(int fileId) - { - var image = (FileInfo)FileManager.Instance.GetFile(fileId); + private static string GetLocalizedString(string key) + { + const string resourceFile = "/App_GlobalResources/FileUpload.resx"; + return Localization.GetString(key, resourceFile); + } - if (image != null && IsImageExtension(image.Extension)) - { - var imageUrl = FileManager.Instance.GetUrl(image); - return imageUrl; - } + private static bool IsUserFolder(string folderPath, out int userId) + { + var match = UserFolderEx.Match(folderPath); + userId = match.Success ? int.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture) : Null.NullInteger; - return null; - } + return match.Success; + } - private static bool IsImageExtension(string extension) - { - return ImageExtensions.Any(e => e.Equals(extension, StringComparison.OrdinalIgnoreCase)); - } + private static string ShowImage(int fileId) + { + var image = (FileInfo)FileManager.Instance.GetFile(fileId); - private static bool IsImage(string fileName) + if (image != null && IsImageExtension(image.Extension)) { - return ImageExtensions.Any(extension => fileName.EndsWith("." + extension, StringComparison.OrdinalIgnoreCase)); + var imageUrl = FileManager.Instance.GetUrl(image); + return imageUrl; } - private static FileUploadDto UploadFile( - ICryptographyProvider cryptographyProvider, - IHostSettings hostSettings, - Stream stream, - int portalId, - UserInfo userInfo, - string folder, - string filter, - string fileName, - bool overwrite, - bool isHostPortal, - bool extract, - string validationCode) + return null; + } + + private static bool IsImageExtension(string extension) + { + return ImageExtensions.Any(e => e.Equals(extension, StringComparison.OrdinalIgnoreCase)); + } + + private static bool IsImage(string fileName) + { + return ImageExtensions.Any(extension => fileName.EndsWith("." + extension, StringComparison.OrdinalIgnoreCase)); + } + + private static FileUploadDto UploadFile( + ICryptographyProvider cryptographyProvider, + IHostSettings hostSettings, + Stream stream, + int portalId, + UserInfo userInfo, + string folder, + string filter, + string fileName, + bool overwrite, + bool isHostPortal, + bool extract, + string validationCode) + { + var result = new FileUploadDto(); + BinaryReader reader = null; + Stream fileContent = null; + try { - var result = new FileUploadDto(); - BinaryReader reader = null; - Stream fileContent = null; - try + var extensionList = new List(); + if (!string.IsNullOrWhiteSpace(filter)) { - var extensionList = new List(); - if (!string.IsNullOrWhiteSpace(filter)) - { - extensionList = filter.Split(',').Select(i => i.Trim()).ToList(); - } + extensionList = filter.Split(',').Select(i => i.Trim()).ToList(); + } - var validateParams = new List { extensionList, userInfo.UserID }; - if (!userInfo.IsSuperUser) - { - validateParams.Add(portalId); - } + var validateParams = new List { extensionList, userInfo.UserID }; + if (!userInfo.IsSuperUser) + { + validateParams.Add(portalId); + } - if (!ValidationUtils.ValidationCodeMatched(cryptographyProvider, hostSettings, validateParams, validationCode)) - { - throw new InvalidOperationException("Bad Request"); - } + if (!ValidationUtils.ValidationCodeMatched(cryptographyProvider, hostSettings, validateParams, validationCode)) + { + throw new InvalidOperationException("Bad Request"); + } - var extension = Path.GetExtension(fileName).ValueOrEmpty().Replace(".", string.Empty); - result.FileIconUrl = IconController.GetFileIconUrl(extension); + var extension = Path.GetExtension(fileName).ValueOrEmpty().Replace(".", string.Empty); + result.FileIconUrl = IconController.GetFileIconUrl(extension); - if (!string.IsNullOrEmpty(filter) && !filter.ToLowerInvariant().Contains(extension.ToLowerInvariant())) - { - result.Message = GetLocalizedString("ExtensionNotAllowed"); - return result; - } + if (!string.IsNullOrEmpty(filter) && !filter.ToLowerInvariant().Contains(extension.ToLowerInvariant())) + { + result.Message = GetLocalizedString("ExtensionNotAllowed"); + return result; + } - var folderManager = FolderManager.Instance; - var effectivePortalId = isHostPortal ? Null.NullInteger : portalId; - var folderInfo = folderManager.GetFolder(effectivePortalId, folder); + var folderManager = FolderManager.Instance; + var effectivePortalId = isHostPortal ? Null.NullInteger : portalId; + var folderInfo = folderManager.GetFolder(effectivePortalId, folder); - if (folderInfo == null && IsUserFolder(folder, out var userId)) + if (folderInfo == null && IsUserFolder(folder, out var userId)) + { + var user = UserController.GetUserById(hostSettings, effectivePortalId, userId); + if (user != null) { - var user = UserController.GetUserById(hostSettings, effectivePortalId, userId); - if (user != null) - { - folderInfo = folderManager.GetUserFolder(user); - } + folderInfo = folderManager.GetUserFolder(user); } + } - if (!FolderPermissionController.HasFolderPermission(portalId, folder, "WRITE") - && !FolderPermissionController.HasFolderPermission(portalId, folder, "ADD")) - { - result.Message = GetLocalizedString("NoPermission"); - return result; - } + if (!FolderPermissionController.HasFolderPermission(portalId, folder, "WRITE") + && !FolderPermissionController.HasFolderPermission(portalId, folder, "ADD")) + { + result.Message = GetLocalizedString("NoPermission"); + return result; + } - const bool AlreadyCheckedPermissions = true; - IFileInfo file; - if (!overwrite && FileManager.Instance.FileExists(folderInfo, fileName, true)) + const bool AlreadyCheckedPermissions = true; + IFileInfo file; + if (!overwrite && FileManager.Instance.FileExists(folderInfo, fileName, true)) + { + result.Message = GetLocalizedString("AlreadyExists"); + result.AlreadyExists = true; + file = FileManager.Instance.GetFile(folderInfo, fileName, true); + result.FileId = file.FileId; + } + else + { + file = FileManager.Instance.AddFile(folderInfo, fileName, stream, true, !AlreadyCheckedPermissions, FileContentTypeManager.Instance.GetContentType(Path.GetExtension(fileName)), userInfo.UserID); + if (extract && extension.Equals("zip", StringComparison.OrdinalIgnoreCase)) { - result.Message = GetLocalizedString("AlreadyExists"); - result.AlreadyExists = true; - file = FileManager.Instance.GetFile(folderInfo, fileName, true); - result.FileId = file.FileId; + var destinationFolder = FolderManager.Instance.GetFolder(file.FolderId); + var invalidFiles = new List(); + var filesCount = FileManager.Instance.UnzipFile(file, destinationFolder, invalidFiles); + + var invalidFilesJson = invalidFiles.Count > 0 + ? string.Join(",", invalidFiles.Select(invalidFile => HttpUtility.JavaScriptStringEncode(invalidFile, addDoubleQuotes: true))) + : string.Empty; + result.Prompt = $"{{\"invalidFiles\":[{invalidFilesJson}], \"totalCount\": {filesCount}}}"; } - else - { - file = FileManager.Instance.AddFile(folderInfo, fileName, stream, true, !AlreadyCheckedPermissions, FileContentTypeManager.Instance.GetContentType(Path.GetExtension(fileName)), userInfo.UserID); - if (extract && extension.Equals("zip", StringComparison.OrdinalIgnoreCase)) - { - var destinationFolder = FolderManager.Instance.GetFolder(file.FolderId); - var invalidFiles = new List(); - var filesCount = FileManager.Instance.UnzipFile(file, destinationFolder, invalidFiles); - - var invalidFilesJson = invalidFiles.Count > 0 - ? string.Join(",", invalidFiles.Select(invalidFile => HttpUtility.JavaScriptStringEncode(invalidFile, addDoubleQuotes: true))) - : string.Empty; - result.Prompt = $"{{\"invalidFiles\":[{invalidFilesJson}], \"totalCount\": {filesCount}}}"; - } - result.FileId = file.FileId; - } + result.FileId = file.FileId; + } - fileContent = FileManager.Instance.GetFileContent(file); + fileContent = FileManager.Instance.GetFileContent(file); - var path = GetUrl(result.FileId); - using (reader = new BinaryReader(fileContent)) + var path = GetUrl(result.FileId); + using (reader = new BinaryReader(fileContent)) + { + Size size; + if (IsImage(fileName)) { - Size size; - if (IsImage(fileName)) + try { - try - { - size = ImageHeader.GetDimensions(reader); - } - catch (ArgumentException exc) - { - Logger.Warn("Unable to get image dimensions for image file", exc); - size = new Size(32, 32); - } + size = ImageHeader.GetDimensions(reader); } - else + catch (ArgumentException exc) { + Logger.Warn("Unable to get image dimensions for image file", exc); size = new Size(32, 32); } - - result.Orientation = size.Orientation(); } - - result.Path = result.FileId > 0 ? path : string.Empty; - result.FileName = fileName; - - if (extract && extension.Equals("zip", StringComparison.OrdinalIgnoreCase)) + else { - FileManager.Instance.DeleteFile(file); + size = new Size(32, 32); } - return result; + result.Orientation = size.Orientation(); } - catch (InvalidFileExtensionException) + + result.Path = result.FileId > 0 ? path : string.Empty; + result.FileName = fileName; + + if (extract && extension.Equals("zip", StringComparison.OrdinalIgnoreCase)) { - result.Message = GetLocalizedString("ExtensionNotAllowed"); - return result; + FileManager.Instance.DeleteFile(file); } - catch (Exception exe) + + return result; + } + catch (InvalidFileExtensionException) + { + result.Message = GetLocalizedString("ExtensionNotAllowed"); + return result; + } + catch (Exception exe) + { + Logger.Error(exe); + result.Message = exe.Message; + return result; + } + finally + { + if (reader != null) { - Logger.Error(exe); - result.Message = exe.Message; - return result; + reader.Close(); + reader.Dispose(); } - finally - { - if (reader != null) - { - reader.Close(); - reader.Dispose(); - } - if (fileContent != null) - { - fileContent.Close(); - fileContent.Dispose(); - } + if (fileContent != null) + { + fileContent.Close(); + fileContent.Dispose(); } } + } - private static IPortalInfo[] GetMyPortalGroup() - { - return ( + private static IPortalInfo[] GetMyPortalGroup() + { + return ( from @group in PortalGroupController.Instance.GetPortalGroups().ToArray() select PortalGroupController.Instance.GetPortalsByGroup(@group.PortalGroupId) into portals where portals.Any((IPortalInfo x) => x.PortalId == PortalSettings.Current.PortalId) select portals.Cast().ToArray()) - .FirstOrDefault(); - } + .FirstOrDefault(); + } - private bool IsPortalIdValid(int portalId) + private bool IsPortalIdValid(int portalId) + { + if (this.UserInfo.IsSuperUser) { - if (this.UserInfo.IsSuperUser) - { - return true; - } - - if (this.PortalSettings.PortalId == portalId) - { - return true; - } - - var isAdminUser = PortalSecurity.IsInRole(this.PortalSettings.AdministratorRoleName); - if (!isAdminUser) - { - return false; - } + return true; + } - var myGroup = GetMyPortalGroup(); - return myGroup != null && myGroup.Any(p => p.PortalId == portalId); + if (this.PortalSettings.PortalId == portalId) + { + return true; } - /// A data transfer object with information about a folder. - public class FolderItemDTO + var isAdminUser = PortalSecurity.IsInRole(this.PortalSettings.AdministratorRoleName); + if (!isAdminUser) { - /// Gets or sets the folder ID. - public int FolderId { get; set; } + return false; + } - /// Gets or sets the file filter. - public string FileFilter { get; set; } + var myGroup = GetMyPortalGroup(); + return myGroup != null && myGroup.Any(p => p.PortalId == portalId); + } - /// Gets or sets a value indicating whether to include an entry for an unspecified file. - public bool Required { get; set; } - } + /// A data transfer object with information about a folder. + public class FolderItemDTO + { + /// Gets or sets the folder ID. + public int FolderId { get; set; } - /// A data transfer object with information about a file that has been saved. - public class SavedFileDTO - { - /// Gets or sets the ID of the file. - public string FileId { get; set; } + /// Gets or sets the file filter. + public string FileFilter { get; set; } - /// Gets or sets the path of the file. - public string FilePath { get; set; } - } + /// Gets or sets a value indicating whether to include an entry for an unspecified file. + public bool Required { get; set; } + } - /// A data transfer object with information about an upload by URL request. - public class UploadByUrlDto - { - /// Gets or sets the URL. - public string Url { get; set; } + /// A data transfer object with information about a file that has been saved. + public class SavedFileDTO + { + /// Gets or sets the ID of the file. + public string FileId { get; set; } - /// Gets or sets the destination folder. - public string Folder { get; set; } + /// Gets or sets the path of the file. + public string FilePath { get; set; } + } - /// Gets or sets a value indicating whether to overwrite an existing file. - public bool Overwrite { get; set; } + /// A data transfer object with information about an upload by URL request. + public class UploadByUrlDto + { + /// Gets or sets the URL. + public string Url { get; set; } - /// Gets or sets a value indicating whether to unzip the resulting file. - public bool Unzip { get; set; } + /// Gets or sets the destination folder. + public string Folder { get; set; } - /// Gets or sets the filter. - public string Filter { get; set; } + /// Gets or sets a value indicating whether to overwrite an existing file. + public bool Overwrite { get; set; } - /// Gets or sets a value indicating whether the request is from the host menu. - public bool IsHostMenu { get; set; } + /// Gets or sets a value indicating whether to unzip the resulting file. + public bool Unzip { get; set; } - /// Gets or sets the portal ID. - public int PortalId { get; set; } = -1; + /// Gets or sets the filter. + public string Filter { get; set; } - /// Gets or sets the validation code. - public string ValidationCode { get; set; } - } + /// Gets or sets a value indicating whether the request is from the host menu. + public bool IsHostMenu { get; set; } - /// A data transfer object with information about a file upload. - [DataContract] - public class FileUploadDto - { - /// Gets or sets the file path. - [DataMember(Name = "path")] - public string Path { get; set; } + /// Gets or sets the portal ID. + public int PortalId { get; set; } = -1; + + /// Gets or sets the validation code. + public string ValidationCode { get; set; } + } + + /// A data transfer object with information about a file upload. + [DataContract] + public class FileUploadDto + { + /// Gets or sets the file path. + [DataMember(Name = "path")] + public string Path { get; set; } - /// Gets or sets the image orientation. - [DataMember(Name = "orientation")] - public Orientation Orientation { get; set; } + /// Gets or sets the image orientation. + [DataMember(Name = "orientation")] + public Orientation Orientation { get; set; } - /// Gets or sets a value indicating whether the file already exists. - [DataMember(Name = "alreadyExists")] - public bool AlreadyExists { get; set; } + /// Gets or sets a value indicating whether the file already exists. + [DataMember(Name = "alreadyExists")] + public bool AlreadyExists { get; set; } - /// Gets or sets an error message. - [DataMember(Name = "message")] - public string Message { get; set; } + /// Gets or sets an error message. + [DataMember(Name = "message")] + public string Message { get; set; } - /// Gets or sets the URL of the file type icon. - [DataMember(Name = "fileIconUrl")] - public string FileIconUrl { get; set; } + /// Gets or sets the URL of the file type icon. + [DataMember(Name = "fileIconUrl")] + public string FileIconUrl { get; set; } - /// Gets or sets the ID of the file. - [DataMember(Name = "fileId")] - public int FileId { get; set; } + /// Gets or sets the ID of the file. + [DataMember(Name = "fileId")] + public int FileId { get; set; } - /// Gets or sets the name of the file. - [DataMember(Name = "fileName")] - public string FileName { get; set; } + /// Gets or sets the name of the file. + [DataMember(Name = "fileName")] + public string FileName { get; set; } - /// Gets or sets a JSON string with invalidFiles and totalCount fields. - [DataMember(Name = "prompt")] - public string Prompt { get; set; } - } + /// Gets or sets a JSON string with invalidFiles and totalCount fields. + [DataMember(Name = "prompt")] + public string Prompt { get; set; } } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/ImageHeader.cs b/DNN Platform/DotNetNuke.Web/InternalServices/ImageHeader.cs index a013c7d00e2..5dbcf2637d4 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/ImageHeader.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/ImageHeader.cs @@ -2,184 +2,183 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; + +/// +/// Taken from http://stackoverflow.com/questions/111345/getting-image-dimensions-without-reading-the-entire-file/111349 +/// Minor improvements including supporting unsigned 16-bit integers when decoding Jfif and added logic +/// to load the image using new Bitmap if reading the headers fails. +/// +public static class ImageHeader { - using System; - using System.Collections.Generic; - using System.Drawing; - using System.IO; - using System.Linq; - - /// - /// Taken from http://stackoverflow.com/questions/111345/getting-image-dimensions-without-reading-the-entire-file/111349 - /// Minor improvements including supporting unsigned 16-bit integers when decoding Jfif and added logic - /// to load the image using new Bitmap if reading the headers fails. - /// - public static class ImageHeader - { - private const string ErrorMessage = "Could not recognize image format."; + private const string ErrorMessage = "Could not recognize image format."; - private static readonly Dictionary> ImageFormatDecoders = new Dictionary - > - { - { new byte[] { 0x42, 0x4D }, DecodeBitmap }, - { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, - { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif }, - { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng }, - { new byte[] { 0xff, 0xd8 }, DecodeJfif }, - }; - - /// Gets the dimensions of an image. - /// The path of the image to get the dimensions of. - /// The dimensions of the specified image. - /// The image was of an unrecognised format. - public static Size GetDimensions(string path) + private static readonly Dictionary> ImageFormatDecoders = new Dictionary + > + { + { new byte[] { 0x42, 0x4D }, DecodeBitmap }, + { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, + { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif }, + { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng }, + { new byte[] { 0xff, 0xd8 }, DecodeJfif }, + }; + + /// Gets the dimensions of an image. + /// The path of the image to get the dimensions of. + /// The dimensions of the specified image. + /// The image was of an unrecognised format. + public static Size GetDimensions(string path) + { + Size size; + try { - Size size; - try + using (var binaryReader = new BinaryReader(File.OpenRead(path))) { - using (var binaryReader = new BinaryReader(File.OpenRead(path))) + try { - try - { - size = GetDimensions(binaryReader); - } - catch (ArgumentException e) - { - throw new ArgumentException($"{ErrorMessage} file: '{path}' ", nameof(path), e); - } + size = GetDimensions(binaryReader); } - } - catch (ArgumentException) - { - // do it the old fashioned way - using (var b = new Bitmap(path)) + catch (ArgumentException e) { - size = b.Size; + throw new ArgumentException($"{ErrorMessage} file: '{path}' ", nameof(path), e); } } - - return size; } - - /// Gets the dimensions of an image. - /// The path of the image to get the dimensions of. - /// The dimensions of the specified image. - /// The image was of an unrecognised format. - public static Size GetDimensions(BinaryReader binaryReader) + catch (ArgumentException) { - int maxMagicBytesLength = ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; - var magicBytes = new byte[maxMagicBytesLength]; - for (var i = 0; i < maxMagicBytesLength; i += 1) + // do it the old fashioned way + using (var b = new Bitmap(path)) { - magicBytes[i] = binaryReader.ReadByte(); - foreach (var pair in ImageFormatDecoders) - { - if (StartsWith(magicBytes, pair.Key)) - { - return pair.Value(binaryReader); - } - } + size = b.Size; } - - throw new ArgumentException(ErrorMessage, nameof(binaryReader)); } - private static bool StartsWith(byte[] thisBytes, byte[] thatBytes) + return size; + } + + /// Gets the dimensions of an image. + /// The path of the image to get the dimensions of. + /// The dimensions of the specified image. + /// The image was of an unrecognised format. + public static Size GetDimensions(BinaryReader binaryReader) + { + int maxMagicBytesLength = ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; + var magicBytes = new byte[maxMagicBytesLength]; + for (var i = 0; i < maxMagicBytesLength; i += 1) { - for (var i = 0; i < thatBytes.Length; i += 1) + magicBytes[i] = binaryReader.ReadByte(); + foreach (var pair in ImageFormatDecoders) { - if (thisBytes[i] != thatBytes[i]) + if (StartsWith(magicBytes, pair.Key)) { - return false; + return pair.Value(binaryReader); } } - - return true; } - private static short ReadLittleEndianInt16(BinaryReader binaryReader) - { - var bytes = new byte[sizeof(short)]; - for (var i = 0; i < sizeof(short); i += 1) - { - bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte(); - } - - return BitConverter.ToInt16(bytes, 0); - } + throw new ArgumentException(ErrorMessage, nameof(binaryReader)); + } - private static ushort ReadLittleEndianUInt16(BinaryReader binaryReader) + private static bool StartsWith(byte[] thisBytes, byte[] thatBytes) + { + for (var i = 0; i < thatBytes.Length; i += 1) { - var bytes = new byte[sizeof(ushort)]; - for (var i = 0; i < sizeof(ushort); i += 1) + if (thisBytes[i] != thatBytes[i]) { - bytes[sizeof(ushort) - 1 - i] = binaryReader.ReadByte(); + return false; } - - return BitConverter.ToUInt16(bytes, 0); } - private static int ReadLittleEndianInt32(BinaryReader binaryReader) - { - var bytes = new byte[sizeof(int)]; - for (var i = 0; i < sizeof(int); i += 1) - { - bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte(); - } - - return BitConverter.ToInt32(bytes, 0); - } + return true; + } - private static Size DecodeBitmap(BinaryReader binaryReader) + private static short ReadLittleEndianInt16(BinaryReader binaryReader) + { + var bytes = new byte[sizeof(short)]; + for (var i = 0; i < sizeof(short); i += 1) { - binaryReader.ReadBytes(16); - var width = binaryReader.ReadInt32(); - var height = binaryReader.ReadInt32(); - return new Size(width, height); + bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte(); } - private static Size DecodeGif(BinaryReader binaryReader) + return BitConverter.ToInt16(bytes, 0); + } + + private static ushort ReadLittleEndianUInt16(BinaryReader binaryReader) + { + var bytes = new byte[sizeof(ushort)]; + for (var i = 0; i < sizeof(ushort); i += 1) { - int width = binaryReader.ReadInt16(); - int height = binaryReader.ReadInt16(); - return new Size(width, height); + bytes[sizeof(ushort) - 1 - i] = binaryReader.ReadByte(); } - private static Size DecodePng(BinaryReader binaryReader) + return BitConverter.ToUInt16(bytes, 0); + } + + private static int ReadLittleEndianInt32(BinaryReader binaryReader) + { + var bytes = new byte[sizeof(int)]; + for (var i = 0; i < sizeof(int); i += 1) { - binaryReader.ReadBytes(8); - var width = ReadLittleEndianInt32(binaryReader); - var height = ReadLittleEndianInt32(binaryReader); - return new Size(width, height); + bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte(); } - private static Size DecodeJfif(BinaryReader binaryReader) + return BitConverter.ToInt32(bytes, 0); + } + + private static Size DecodeBitmap(BinaryReader binaryReader) + { + binaryReader.ReadBytes(16); + var width = binaryReader.ReadInt32(); + var height = binaryReader.ReadInt32(); + return new Size(width, height); + } + + private static Size DecodeGif(BinaryReader binaryReader) + { + int width = binaryReader.ReadInt16(); + int height = binaryReader.ReadInt16(); + return new Size(width, height); + } + + private static Size DecodePng(BinaryReader binaryReader) + { + binaryReader.ReadBytes(8); + var width = ReadLittleEndianInt32(binaryReader); + var height = ReadLittleEndianInt32(binaryReader); + return new Size(width, height); + } + + private static Size DecodeJfif(BinaryReader binaryReader) + { + while (binaryReader.ReadByte() == 0xff) { - while (binaryReader.ReadByte() == 0xff) + byte marker = binaryReader.ReadByte(); + short chunkLength = ReadLittleEndianInt16(binaryReader); + if (marker == 0xc0 || marker == 0xc2) { - byte marker = binaryReader.ReadByte(); - short chunkLength = ReadLittleEndianInt16(binaryReader); - if (marker == 0xc0 || marker == 0xc2) - { - binaryReader.ReadByte(); - int height = ReadLittleEndianInt16(binaryReader); - int width = ReadLittleEndianInt16(binaryReader); - return new Size(width, height); - } - - if (chunkLength < 0) - { - var uchunkLength = (ushort)chunkLength; - binaryReader.ReadBytes(uchunkLength - 2); - } - else - { - binaryReader.ReadBytes(chunkLength - 2); - } + binaryReader.ReadByte(); + int height = ReadLittleEndianInt16(binaryReader); + int width = ReadLittleEndianInt16(binaryReader); + return new Size(width, height); } - throw new ArgumentException(ErrorMessage); + if (chunkLength < 0) + { + var uchunkLength = (ushort)chunkLength; + binaryReader.ReadBytes(uchunkLength - 2); + } + else + { + binaryReader.ReadBytes(chunkLength - 2); + } } + + throw new ArgumentException(ErrorMessage); } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/ItemListServiceController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/ItemListServiceController.cs index 398d6fb78b1..958d7046bbd 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/ItemListServiceController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/ItemListServiceController.cs @@ -2,1603 +2,1602 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Runtime.Serialization; +using System.Web.Http; + +using DotNetNuke.Abstractions.Application; +using DotNetNuke.Abstractions.Portals; +using DotNetNuke.Abstractions.Security.Permissions; +using DotNetNuke.Common.Utilities; +using DotNetNuke.Data; +using DotNetNuke.Entities.Content.Taxonomy; +using DotNetNuke.Entities.DataStructures; +using DotNetNuke.Entities.Portals; +using DotNetNuke.Entities.Tabs; +using DotNetNuke.Entities.Users; +using DotNetNuke.Instrumentation; +using DotNetNuke.Security; +using DotNetNuke.Security.Permissions; +using DotNetNuke.Services.FileSystem; +using DotNetNuke.Web.Api; +using DotNetNuke.Web.Common; + +using Microsoft.Extensions.DependencyInjection; + +using Globals = DotNetNuke.Common.Globals; + +/// A web API controller for lists of items. +/// The host settings. +/// The data provider. +/// The portal controller. +/// The application status. +/// The portal group controller. +/// The vocabulary controller. +/// The term controller. +[DnnAuthorize] +public class ItemListServiceController(IHostSettings hostSettings, DataProvider dataProvider, IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController, IVocabularyController vocabularyController, ITermController termController) + : DnnApiController { - using System; - using System.Collections; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Runtime.Serialization; - using System.Web.Http; - - using DotNetNuke.Abstractions.Application; - using DotNetNuke.Abstractions.Portals; - using DotNetNuke.Abstractions.Security.Permissions; - using DotNetNuke.Common.Utilities; - using DotNetNuke.Data; - using DotNetNuke.Entities.Content.Taxonomy; - using DotNetNuke.Entities.DataStructures; - using DotNetNuke.Entities.Portals; - using DotNetNuke.Entities.Tabs; - using DotNetNuke.Entities.Users; - using DotNetNuke.Instrumentation; - using DotNetNuke.Security; - using DotNetNuke.Security.Permissions; - using DotNetNuke.Services.FileSystem; - using DotNetNuke.Web.Api; - using DotNetNuke.Web.Common; - - using Microsoft.Extensions.DependencyInjection; - - using Globals = DotNetNuke.Common.Globals; - - /// A web API controller for lists of items. + private const string PortalPrefix = "P-"; + private const string RootKey = "Root"; + private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(ItemListServiceController)); + private readonly IHostSettings hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly DataProvider dataProvider = dataProvider ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IPortalController portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IApplicationStatusInfo appStatus = appStatus ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IPortalGroupController portalGroupController = portalGroupController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IVocabularyController vocabularyController = vocabularyController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly ITermController termController = termController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + + /// Initializes a new instance of the class. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] + public ItemListServiceController() + : this(null, null, null, null, null, null, null) + { + } + + /// Initializes a new instance of the class. /// The host settings. /// The data provider. /// The portal controller. /// The application status. /// The portal group controller. - /// The vocabulary controller. - /// The term controller. - [DnnAuthorize] - public class ItemListServiceController(IHostSettings hostSettings, DataProvider dataProvider, IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController, IVocabularyController vocabularyController, ITermController termController) - : DnnApiController - { - private const string PortalPrefix = "P-"; - private const string RootKey = "Root"; - private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(ItemListServiceController)); - private readonly IHostSettings hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly DataProvider dataProvider = dataProvider ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IPortalController portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IApplicationStatusInfo appStatus = appStatus ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IPortalGroupController portalGroupController = portalGroupController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IVocabularyController vocabularyController = vocabularyController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly ITermController termController = termController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - - /// Initializes a new instance of the class. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] - public ItemListServiceController() - : this(null, null, null, null, null, null, null) - { - } - - /// Initializes a new instance of the class. - /// The host settings. - /// The data provider. - /// The portal controller. - /// The application status. - /// The portal group controller. - [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IVocabularyController. Scheduled removal in v12.0.0.")] - public ItemListServiceController(IHostSettings hostSettings, DataProvider dataProvider, IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController) - : this(hostSettings, dataProvider, portalController, appStatus, portalGroupController, null, null) - { - } - - /// Gets a list of page descendants. - /// The parent ID. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The search text. - /// The portal ID. - /// Whether to include disabled pages. - /// Whether to include all page types. - /// Whether to include active pages. - /// Whether to include host pages. - /// A semicolon-delimited list of role IDs. - /// Whether disabled pages are not selectable. - /// A response with a list of objects. - [HttpGet] - public HttpResponseMessage GetPageDescendants(string parentId = null, int sortOrder = 0, string searchText = "", int portalId = -1, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) - { - var response = new - { - Success = true, - Items = this.GetPageDescendantsInternal(portalId, parentId, sortOrder, searchText, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles, disabledNotSelectable), - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } - - /// Gets the tree path for a page. - /// The tab ID. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The portal ID. - /// Whether to include disabled pages. - /// Whether to include all page types. - /// Whether to include active pages. - /// Whether to include host pages. - /// A semicolon-delimited list of role IDs. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage GetTreePathForPage(string itemId, int sortOrder = 0, int portalId = -1, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") - { - var response = new - { - Success = true, - Tree = this.GetTreePathForPageInternal(portalId, itemId, sortOrder, false, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } - - /// Sort the given . - /// The tree as JSON. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The search text. - /// The portal ID. - /// Whether to include disabled pages. - /// Whether to include all page types. - /// Whether to include active pages. - /// Whether to include host pages. - /// A semicolon-delimited list of role IDs. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage SortPages(string treeAsJson, int sortOrder = 0, string searchText = "", int portalId = -1, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") - { - var response = new - { - Success = true, - Tree = string.IsNullOrEmpty(searchText) ? this.SortPagesInternal(portalId, treeAsJson, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages) - : this.SearchPagesInternal(portalId, searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } - - /// Sort the given in portal group. - /// The tree as JSON. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The search text. - /// Whether to include disabled pages. - /// Whether to include all page types. - /// Whether to include active pages. - /// Whether to include host pages. - /// A semicolon-delimited list of role IDs. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage SortPagesInPortalGroup(string treeAsJson, int sortOrder = 0, string searchText = "", bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") - { - var response = new - { - Success = true, - Tree = string.IsNullOrEmpty(searchText) ? this.SortPagesInPortalGroupInternal(treeAsJson, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages) - : this.SearchPagesInPortalGroupInternal(treeAsJson, searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } - - /// Gets pages. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The portal ID. - /// Whether to include disabled pages. - /// Whether to include all page types. - /// Whether to include active pages. - /// Whether to include host pages. - /// A semicolon-delimited list of role IDs. - /// Whether disabled pages are not selectable. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage GetPages(int sortOrder = 0, int portalId = -1, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) - { - var response = new - { - Success = true, - Tree = this.GetPagesInternal(portalId, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles, disabledNotSelectable), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } + [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IVocabularyController. Scheduled removal in v12.0.0.")] + public ItemListServiceController(IHostSettings hostSettings, DataProvider dataProvider, IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController) + : this(hostSettings, dataProvider, portalController, appStatus, portalGroupController, null, null) + { + } - /// Gets pages in the current portal group. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage GetPagesInPortalGroup(int sortOrder = 0) + /// Gets a list of page descendants. + /// The parent ID. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The search text. + /// The portal ID. + /// Whether to include disabled pages. + /// Whether to include all page types. + /// Whether to include active pages. + /// Whether to include host pages. + /// A semicolon-delimited list of role IDs. + /// Whether disabled pages are not selectable. + /// A response with a list of objects. + [HttpGet] + public HttpResponseMessage GetPageDescendants(string parentId = null, int sortOrder = 0, string searchText = "", int portalId = -1, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) + { + var response = new { - var response = new - { - Success = true, - Tree = this.GetPagesInPortalGroupInternal(sortOrder), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } - - /// Search pages. - /// The search text. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The portal ID. - /// Whether to include disabled pages. - /// Whether to include all page types. - /// Whether to include active pages. - /// Whether to include host pages. - /// A semicolon-delimited list of role IDs. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage SearchPages(string searchText, int sortOrder = 0, int portalId = -1, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") - { - var response = new - { - Success = true, - Tree = string.IsNullOrEmpty(searchText) ? this.GetPagesInternal(portalId, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles, false) - : this.SearchPagesInternal(portalId, searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } - - /// Gets descendants of a page in the current portal group. - /// The parent page ID. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The search text. - /// Whether to include disabled pages. - /// Whether to include all page types. - /// Whether to include active pages. - /// Whether to include host pages. - /// A semicolon-delimited list of role IDs. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage GetPageDescendantsInPortalGroup(string parentId = null, int sortOrder = 0, string searchText = "", bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") - { - var response = new - { - Success = true, - Items = this.GetPageDescendantsInPortalGroupInternal(parentId, sortOrder, searchText, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } + Success = true, + Items = this.GetPageDescendantsInternal(portalId, parentId, sortOrder, searchText, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles, disabledNotSelectable), + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - /// Gets the portals in the current portal group. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// A response with an object that has sites and portalId fields. - [HttpGet] - public HttpResponseMessage GetPortalsInGroup(int sortOrder = 0) + /// Gets the tree path for a page. + /// The tab ID. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The portal ID. + /// Whether to include disabled pages. + /// Whether to include all page types. + /// Whether to include active pages. + /// Whether to include host pages. + /// A semicolon-delimited list of role IDs. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage GetTreePathForPage(string itemId, int sortOrder = 0, int portalId = -1, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") + { + var response = new { - var sites = this.GetPortalGroup(sortOrder); - var portalId = this.PortalSettings.PortalId; - return this.Request.CreateResponse(HttpStatusCode.OK, new { sites, portalId }); - } + Success = true, + Tree = this.GetTreePathForPageInternal(portalId, itemId, sortOrder, false, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - /// Gets the tree path for a page in the current portal group. - /// The tab ID. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// Whether to include disabled pages. - /// Whether to include all page types. - /// Whether to include active pages. - /// Whether to include host pages. - /// A semicolon-delimited list of role IDs. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage GetTreePathForPageInPortalGroup(string itemId, int sortOrder = 0, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") - { - var response = new - { - Success = true, - Tree = this.GetTreePathForPageInternal(itemId, sortOrder, true, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } - - /// Search pages in the current portal group. - /// The search text. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// Whether to include disabled pages. - /// Whether to include all page types. - /// Whether to include active pages. - /// Whether to include host pages. - /// A semicolon-delimited list of role IDs. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage SearchPagesInPortalGroup(string searchText, int sortOrder = 0, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") - { - var response = new - { - Success = true, - Tree = string.IsNullOrEmpty(searchText) ? this.GetPagesInPortalGroupInternal(sortOrder) - : this.SearchPagesInPortalGroupInternal(searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } + /// Sort the given . + /// The tree as JSON. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The search text. + /// The portal ID. + /// Whether to include disabled pages. + /// Whether to include all page types. + /// Whether to include active pages. + /// Whether to include host pages. + /// A semicolon-delimited list of role IDs. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage SortPages(string treeAsJson, int sortOrder = 0, string searchText = "", int portalId = -1, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") + { + var response = new + { + Success = true, + Tree = string.IsNullOrEmpty(searchText) ? this.SortPagesInternal(portalId, treeAsJson, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages) + : this.SearchPagesInternal(portalId, searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - /// Gets folder descendants. - /// The parent ID. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The search text. - /// The permission key. - /// The portal ID. - /// A response with an object that has an Items fields that's a list of objects. - [HttpGet] - public HttpResponseMessage GetFolderDescendants(string parentId = null, int sortOrder = 0, string searchText = "", string permission = null, int portalId = -1) - { - var response = new - { - Success = true, - Items = this.GetFolderDescendantsInternal(portalId, parentId, sortOrder, searchText, permission), - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } + /// Sort the given in portal group. + /// The tree as JSON. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The search text. + /// Whether to include disabled pages. + /// Whether to include all page types. + /// Whether to include active pages. + /// Whether to include host pages. + /// A semicolon-delimited list of role IDs. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage SortPagesInPortalGroup(string treeAsJson, int sortOrder = 0, string searchText = "", bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") + { + var response = new + { + Success = true, + Tree = string.IsNullOrEmpty(searchText) ? this.SortPagesInPortalGroupInternal(treeAsJson, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages) + : this.SearchPagesInPortalGroupInternal(treeAsJson, searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - /// Get folders. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The permission key. - /// The portal ID. - /// The parent folder ID. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage GetFolders(int sortOrder = 0, string permission = null, int portalId = -1, int parentFolderId = -1) + /// Gets pages. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The portal ID. + /// Whether to include disabled pages. + /// Whether to include all page types. + /// Whether to include active pages. + /// Whether to include host pages. + /// A semicolon-delimited list of role IDs. + /// Whether disabled pages are not selectable. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage GetPages(int sortOrder = 0, int portalId = -1, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) + { + var response = new { - var response = new - { - Success = true, - Tree = this.GetFoldersInternal(portalId, sortOrder, permission, parentFolderId), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } + Success = true, + Tree = this.GetPagesInternal(portalId, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles, disabledNotSelectable), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - /// Sorts the folder in . - /// The tree as JSON. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The search text. - /// The permission key. - /// The portal ID. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage SortFolders(string treeAsJson, int sortOrder = 0, string searchText = "", string permission = null, int portalId = -1) + /// Gets pages in the current portal group. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage GetPagesInPortalGroup(int sortOrder = 0) + { + var response = new { - var response = new - { - Success = true, - Tree = string.IsNullOrEmpty(searchText) ? this.SortFoldersInternal(portalId, treeAsJson, sortOrder, permission) : this.SearchFoldersInternal(portalId, searchText, sortOrder, permission), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } + Success = true, + Tree = this.GetPagesInPortalGroupInternal(sortOrder), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - /// Get the tree path for a folder. - /// The folder ID. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The permission key. - /// The portal ID. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage GetTreePathForFolder(string itemId, int sortOrder = 0, string permission = null, int portalId = -1) + /// Search pages. + /// The search text. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The portal ID. + /// Whether to include disabled pages. + /// Whether to include all page types. + /// Whether to include active pages. + /// Whether to include host pages. + /// A semicolon-delimited list of role IDs. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage SearchPages(string searchText, int sortOrder = 0, int portalId = -1, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") + { + var response = new + { + Success = true, + Tree = string.IsNullOrEmpty(searchText) ? this.GetPagesInternal(portalId, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles, false) + : this.SearchPagesInternal(portalId, searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } + + /// Gets descendants of a page in the current portal group. + /// The parent page ID. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The search text. + /// Whether to include disabled pages. + /// Whether to include all page types. + /// Whether to include active pages. + /// Whether to include host pages. + /// A semicolon-delimited list of role IDs. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage GetPageDescendantsInPortalGroup(string parentId = null, int sortOrder = 0, string searchText = "", bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") + { + var response = new { - var response = new - { - Success = true, - Tree = this.GetTreePathForFolderInternal(itemId, sortOrder, permission), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } + Success = true, + Items = this.GetPageDescendantsInPortalGroupInternal(parentId, sortOrder, searchText, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - /// Search folders. - /// The search text. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The permission key. - /// The portal ID. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage SearchFolders(string searchText, int sortOrder = 0, string permission = null, int portalId = -1) + /// Gets the portals in the current portal group. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// A response with an object that has sites and portalId fields. + [HttpGet] + public HttpResponseMessage GetPortalsInGroup(int sortOrder = 0) + { + var sites = this.GetPortalGroup(sortOrder); + var portalId = this.PortalSettings.PortalId; + return this.Request.CreateResponse(HttpStatusCode.OK, new { sites, portalId }); + } + + /// Gets the tree path for a page in the current portal group. + /// The tab ID. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// Whether to include disabled pages. + /// Whether to include all page types. + /// Whether to include active pages. + /// Whether to include host pages. + /// A semicolon-delimited list of role IDs. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage GetTreePathForPageInPortalGroup(string itemId, int sortOrder = 0, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") + { + var response = new { - var response = new - { - Success = true, - Tree = string.IsNullOrEmpty(searchText) ? this.GetFoldersInternal(portalId, sortOrder, permission) : this.SearchFoldersInternal(portalId, searchText, sortOrder, permission), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } + Success = true, + Tree = this.GetTreePathForPageInternal(itemId, sortOrder, true, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } + + /// Search pages in the current portal group. + /// The search text. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// Whether to include disabled pages. + /// Whether to include all page types. + /// Whether to include active pages. + /// Whether to include host pages. + /// A semicolon-delimited list of role IDs. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage SearchPagesInPortalGroup(string searchText, int sortOrder = 0, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") + { + var response = new + { + Success = true, + Tree = string.IsNullOrEmpty(searchText) ? this.GetPagesInPortalGroupInternal(sortOrder) + : this.SearchPagesInPortalGroupInternal(searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - /// Gets the files. - /// The parent ID. - /// The filter. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The permission key. - /// The portal ID. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage GetFiles(int parentId, string filter, int sortOrder = 0, string permission = null, int portalId = -1) + /// Gets folder descendants. + /// The parent ID. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The search text. + /// The permission key. + /// The portal ID. + /// A response with an object that has an Items fields that's a list of objects. + [HttpGet] + public HttpResponseMessage GetFolderDescendants(string parentId = null, int sortOrder = 0, string searchText = "", string permission = null, int portalId = -1) + { + var response = new { - var response = new - { - Success = true, - Tree = this.GetFilesInternal(portalId, parentId, filter, string.Empty, sortOrder, permission), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } + Success = true, + Items = this.GetFolderDescendantsInternal(portalId, parentId, sortOrder, searchText, permission), + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - /// Sort the files. - /// The parent ID. - /// The filter. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The search text. - /// The permission key. - /// The portal ID. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage SortFiles(int parentId, string filter, int sortOrder = 0, string searchText = "", string permission = null, int portalId = -1) + /// Get folders. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The permission key. + /// The portal ID. + /// The parent folder ID. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage GetFolders(int sortOrder = 0, string permission = null, int portalId = -1, int parentFolderId = -1) + { + var response = new { - var response = new - { - Success = true, - Tree = string.IsNullOrEmpty(searchText) ? this.SortFilesInternal(portalId, parentId, filter, sortOrder, permission) : this.GetFilesInternal(portalId, parentId, filter, searchText, sortOrder, permission), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } + Success = true, + Tree = this.GetFoldersInternal(portalId, sortOrder, permission, parentFolderId), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - /// Search files. - /// The parent ID. - /// The filter. - /// The search text. - /// 1 for A-Z, 2 for Z-A, any other value for no sorting. - /// The permission key. - /// The portal ID. - /// A response with an object with a Tree field containing objects. - [HttpGet] - public HttpResponseMessage SearchFiles(int parentId, string filter, string searchText, int sortOrder = 0, string permission = null, int portalId = -1) + /// Sorts the folder in . + /// The tree as JSON. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The search text. + /// The permission key. + /// The portal ID. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage SortFolders(string treeAsJson, int sortOrder = 0, string searchText = "", string permission = null, int portalId = -1) + { + var response = new { - var response = new - { - Success = true, - Tree = this.GetFilesInternal(portalId, parentId, filter, searchText, sortOrder, permission), - IgnoreRoot = true, - }; - return this.Request.CreateResponse(HttpStatusCode.OK, response); - } + Success = true, + Tree = string.IsNullOrEmpty(searchText) ? this.SortFoldersInternal(portalId, treeAsJson, sortOrder, permission) : this.SearchFoldersInternal(portalId, searchText, sortOrder, permission), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - /// Search users. - /// The search criteria. - /// A response with a list of results (containing id, name, and iconfile fields) or null if there was no search query. - [HttpGet] - public HttpResponseMessage SearchUser(string q) + /// Get the tree path for a folder. + /// The folder ID. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The permission key. + /// The portal ID. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage GetTreePathForFolder(string itemId, int sortOrder = 0, string permission = null, int portalId = -1) + { + var response = new { - try - { - var portalId = PortalController.GetEffectivePortalId(this.portalController, this.appStatus, this.portalGroupController, this.PortalSettings.PortalId); - const int numResults = 5; + Success = true, + Tree = this.GetTreePathForFolderInternal(itemId, sortOrder, permission), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - // GetUsersAdvancedSearch doesn't accept a comma or a single quote in the query so we have to remove them for now. See issue 20224. - q = q.Replace(",", string.Empty).Replace("'", string.Empty); - if (q.Length == 0) - { - return this.Request.CreateResponse(HttpStatusCode.OK, null); - } + /// Search folders. + /// The search text. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The permission key. + /// The portal ID. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage SearchFolders(string searchText, int sortOrder = 0, string permission = null, int portalId = -1) + { + var response = new + { + Success = true, + Tree = string.IsNullOrEmpty(searchText) ? this.GetFoldersInternal(portalId, sortOrder, permission) : this.SearchFoldersInternal(portalId, searchText, sortOrder, permission), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - var results = UserController.Instance.GetUsersBasicSearch(portalId, 0, numResults, "DisplayName", true, "DisplayName", q) - .Select(user => new - { - id = user.UserID, - name = user.DisplayName, - iconfile = UserController.Instance.GetUserProfilePictureUrl(user.UserID, 32, 32), - }).ToList(); + /// Gets the files. + /// The parent ID. + /// The filter. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The permission key. + /// The portal ID. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage GetFiles(int parentId, string filter, int sortOrder = 0, string permission = null, int portalId = -1) + { + var response = new + { + Success = true, + Tree = this.GetFilesInternal(portalId, parentId, filter, string.Empty, sortOrder, permission), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - return this.Request.CreateResponse(HttpStatusCode.OK, results.OrderBy(sr => sr.name)); - } - catch (Exception exc) - { - Logger.Error(exc); - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc); - } - } + /// Sort the files. + /// The parent ID. + /// The filter. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The search text. + /// The permission key. + /// The portal ID. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage SortFiles(int parentId, string filter, int sortOrder = 0, string searchText = "", string permission = null, int portalId = -1) + { + var response = new + { + Success = true, + Tree = string.IsNullOrEmpty(searchText) ? this.SortFilesInternal(portalId, parentId, filter, sortOrder, permission) : this.GetFilesInternal(portalId, parentId, filter, searchText, sortOrder, permission), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - /// Gets terms. - /// The search criteria. - /// Whether to include terms from system vocabularies. - /// Whether to include terms from the tags vocabulary. - /// A response with a list of objects with text and value fields. - [HttpGet] - public HttpResponseMessage GetTerms(string q, bool includeSystem, bool includeTags) + /// Search files. + /// The parent ID. + /// The filter. + /// The search text. + /// 1 for A-Z, 2 for Z-A, any other value for no sorting. + /// The permission key. + /// The portal ID. + /// A response with an object with a Tree field containing objects. + [HttpGet] + public HttpResponseMessage SearchFiles(int parentId, string filter, string searchText, int sortOrder = 0, string permission = null, int portalId = -1) + { + var response = new { - var portalId = PortalSettings.Current.PortalId; + Success = true, + Tree = this.GetFilesInternal(portalId, parentId, filter, searchText, sortOrder, permission), + IgnoreRoot = true, + }; + return this.Request.CreateResponse(HttpStatusCode.OK, response); + } - var terms = new ArrayList(); - var vocabularies = from v in this.vocabularyController.GetVocabularies() - where (v.ScopeType.ScopeType == "Application" - || (v.ScopeType.ScopeType == "Portal" && v.ScopeId == portalId)) - && (!v.IsSystem || includeSystem) - && (v.Name != "Tags" || includeTags) - select v; + /// Search users. + /// The search criteria. + /// A response with a list of results (containing id, name, and iconfile fields) or null if there was no search query. + [HttpGet] + public HttpResponseMessage SearchUser(string q) + { + try + { + var portalId = PortalController.GetEffectivePortalId(this.portalController, this.appStatus, this.portalGroupController, this.PortalSettings.PortalId); + const int numResults = 5; - foreach (var v in vocabularies) + // GetUsersAdvancedSearch doesn't accept a comma or a single quote in the query so we have to remove them for now. See issue 20224. + q = q.Replace(",", string.Empty).Replace("'", string.Empty); + if (q.Length == 0) { - terms.AddRange(new[] - { - from t in this.termController.GetTermsByVocabulary(v.VocabularyId) - where string.IsNullOrEmpty(q) || t.Name.Contains(q, StringComparison.OrdinalIgnoreCase) - select new { text = t.Name, value = t.TermId }, - }); + return this.Request.CreateResponse(HttpStatusCode.OK, null); } - return this.Request.CreateResponse(HttpStatusCode.OK, terms); + var results = UserController.Instance.GetUsersBasicSearch(portalId, 0, numResults, "DisplayName", true, "DisplayName", q) + .Select(user => new + { + id = user.UserID, + name = user.DisplayName, + iconfile = UserController.Instance.GetUserProfilePictureUrl(user.UserID, 32, 32), + }).ToList(); + + return this.Request.CreateResponse(HttpStatusCode.OK, results.OrderBy(sr => sr.name)); + } + catch (Exception exc) + { + Logger.Error(exc); + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc); } + } + + /// Gets terms. + /// The search criteria. + /// Whether to include terms from system vocabularies. + /// Whether to include terms from the tags vocabulary. + /// A response with a list of objects with text and value fields. + [HttpGet] + public HttpResponseMessage GetTerms(string q, bool includeSystem, bool includeTags) + { + var portalId = PortalSettings.Current.PortalId; - private static List GetChildrenOf(IEnumerable tabs, int parentId, List filterTabs = null) + var terms = new ArrayList(); + var vocabularies = from v in this.vocabularyController.GetVocabularies() + where (v.ScopeType.ScopeType == "Application" + || (v.ScopeType.ScopeType == "Portal" && v.ScopeId == portalId)) + && (!v.IsSystem || includeSystem) + && (v.Name != "Tags" || includeTags) + select v; + + foreach (var v in vocabularies) { - return tabs.Where(tab => tab.ParentId == parentId).Select(tab => new ItemDto + terms.AddRange(new[] { - Key = tab.TabID.ToString(CultureInfo.InvariantCulture), - Value = tab.LocalizedTabName, - HasChildren = tab.HasChildren, - Selectable = filterTabs == null || filterTabs.Contains(tab.TabID), - }).ToList(); + from t in this.termController.GetTermsByVocabulary(v.VocabularyId) + where string.IsNullOrEmpty(q) || t.Name.Contains(q, StringComparison.OrdinalIgnoreCase) + select new { text = t.Name, value = t.TermId }, + }); } - private static List GetChildrenOf(IEnumerable tabs, string parentId) + return this.Request.CreateResponse(HttpStatusCode.OK, terms); + } + + private static List GetChildrenOf(IEnumerable tabs, int parentId, List filterTabs = null) + { + return tabs.Where(tab => tab.ParentId == parentId).Select(tab => new ItemDto { - int id; - id = int.TryParse(parentId, out id) ? id : Null.NullInteger; - return GetChildrenOf(tabs, id); - } + Key = tab.TabID.ToString(CultureInfo.InvariantCulture), + Value = tab.LocalizedTabName, + HasChildren = tab.HasChildren, + Selectable = filterTabs == null || filterTabs.Contains(tab.TabID), + }).ToList(); + } - private static void SortPagesRecursively(IEnumerable tabs, NTree treeNode, NTree openedNode, int sortOrder) + private static List GetChildrenOf(IEnumerable tabs, string parentId) + { + int id; + id = int.TryParse(parentId, out id) ? id : Null.NullInteger; + return GetChildrenOf(tabs, id); + } + + private static void SortPagesRecursively(IEnumerable tabs, NTree treeNode, NTree openedNode, int sortOrder) + { + if (openedNode == null) { - if (openedNode == null) - { - return; - } + return; + } - var children = ApplySort(GetChildrenOf(tabs, openedNode.Data.Id), sortOrder).Select(dto => new NTree { Data = dto }).ToList(); - treeNode.Children = children; - if (openedNode.HasChildren()) + var children = ApplySort(GetChildrenOf(tabs, openedNode.Data.Id), sortOrder).Select(dto => new NTree { Data = dto }).ToList(); + treeNode.Children = children; + if (openedNode.HasChildren()) + { + foreach (var openedNodeChild in openedNode.Children) { - foreach (var openedNodeChild in openedNode.Children) + var treeNodeChild = treeNode.Children.Find(child => string.Equals(child.Data.Key, openedNodeChild.Data.Id, StringComparison.OrdinalIgnoreCase)); + if (treeNodeChild == null) { - var treeNodeChild = treeNode.Children.Find(child => string.Equals(child.Data.Key, openedNodeChild.Data.Id, StringComparison.OrdinalIgnoreCase)); - if (treeNodeChild == null) - { - continue; - } - - SortPagesRecursively(tabs, treeNodeChild, openedNodeChild, sortOrder); + continue; } + + SortPagesRecursively(tabs, treeNodeChild, openedNodeChild, sortOrder); } } + } - private static IEnumerable ApplySort(IEnumerable items, int sortOrder) + private static IEnumerable ApplySort(IEnumerable items, int sortOrder) + { + switch (sortOrder) { - switch (sortOrder) - { - case 1: // sort by a-z - return items.OrderBy(item => item.Value).ToList(); - case 2: // sort by z-a - return items.OrderByDescending(item => item.Value).ToList(); - default: // no sort - return items; - } + case 1: // sort by a-z + return items.OrderBy(item => item.Value).ToList(); + case 2: // sort by z-a + return items.OrderByDescending(item => item.Value).ToList(); + default: // no sort + return items; } + } - private static List FilterTabsByRole(IList tabs, string roles, bool disabledNotSelectable) + private static List FilterTabsByRole(IList tabs, string roles, bool disabledNotSelectable) + { + var filterTabs = new List(); + if (!string.IsNullOrEmpty(roles)) { - var filterTabs = new List(); - if (!string.IsNullOrEmpty(roles)) - { - var roleList = roles.Split(';').Select(int.Parse); - - filterTabs.AddRange( - tabs.Where( - t => - t.TabPermissions.Cast() - .Any(p => roleList.Contains(p.RoleId) && p.UserId == Null.NullInteger && p.PermissionKey == "VIEW" && p.AllowAccess)).ToList() - .Where(t => !disabledNotSelectable || !t.DisableLink) - .Select(t => t.TabID)); - } - else - { - filterTabs.AddRange(tabs.Where(t => !disabledNotSelectable || !t.DisableLink).Select(t => t.TabID)); - } + var roleList = roles.Split(';').Select(int.Parse); - return filterTabs; + filterTabs.AddRange( + tabs.Where( + t => + t.TabPermissions.Cast() + .Any(p => roleList.Contains(p.RoleId) && p.UserId == Null.NullInteger && p.PermissionKey == "VIEW" && p.AllowAccess)).ToList() + .Where(t => !disabledNotSelectable || !t.DisableLink) + .Select(t => t.TabID)); } - - private static IEnumerable GetFiles(IFolderInfo parentFolder, string filter, string searchText) + else { - Func searchFunc; - var filterList = string.IsNullOrEmpty(filter) ? null : filter.ToLowerInvariant().Split(',').ToList(); - if (string.IsNullOrEmpty(searchText)) - { - searchFunc = f => filterList == null || filterList.Contains(f.Extension.ToLowerInvariant()); - } - else - { - searchFunc = f => f.FileName.Contains(searchText, StringComparison.OrdinalIgnoreCase) - && (filterList == null || filterList.Contains(f.Extension.ToLowerInvariant())); - } - - return FolderManager.Instance.GetFiles(parentFolder).Where(f => searchFunc(f)); + filterTabs.AddRange(tabs.Where(t => !disabledNotSelectable || !t.DisableLink).Select(t => t.TabID)); } - private NTree GetPagesInPortalGroupInternal(int sortOrder) + return filterTabs; + } + + private static IEnumerable GetFiles(IFolderInfo parentFolder, string filter, string searchText) + { + Func searchFunc; + var filterList = string.IsNullOrEmpty(filter) ? null : filter.ToLowerInvariant().Split(',').ToList(); + if (string.IsNullOrEmpty(searchText)) { - var treeNode = new NTree { Data = new ItemDto { Key = RootKey, }, }; - var portals = this.GetPortalGroup(sortOrder); - treeNode.Children = portals.Select(dto => new NTree { Data = dto, }).ToList(); - return treeNode; + searchFunc = f => filterList == null || filterList.Contains(f.Extension.ToLowerInvariant()); } - - private IEnumerable GetPortalGroup(int sortOrder) + else { - var myGroup = this.GetMyPortalGroup(); - var portals = myGroup.Select(p => new ItemDto - { - Key = PortalPrefix + p.PortalId.ToString(CultureInfo.InvariantCulture), - Value = p.PortalName, - HasChildren = true, - Selectable = false, - }).ToList(); - return ApplySort(portals, sortOrder); + searchFunc = f => f.FileName.Contains(searchText, StringComparison.OrdinalIgnoreCase) + && (filterList == null || filterList.Contains(f.Extension.ToLowerInvariant())); } - private IEnumerable GetMyPortalGroup() + return FolderManager.Instance.GetFiles(parentFolder).Where(f => searchFunc(f)); + } + + private NTree GetPagesInPortalGroupInternal(int sortOrder) + { + var treeNode = new NTree { Data = new ItemDto { Key = RootKey, }, }; + var portals = this.GetPortalGroup(sortOrder); + treeNode.Children = portals.Select(dto => new NTree { Data = dto, }).ToList(); + return treeNode; + } + + private IEnumerable GetPortalGroup(int sortOrder) + { + var myGroup = this.GetMyPortalGroup(); + var portals = myGroup.Select(p => new ItemDto + { + Key = PortalPrefix + p.PortalId.ToString(CultureInfo.InvariantCulture), + Value = p.PortalName, + HasChildren = true, + Selectable = false, + }).ToList(); + return ApplySort(portals, sortOrder); + } + + private IEnumerable GetMyPortalGroup() + { + var groups = PortalGroupController.Instance.GetPortalGroups().ToArray(); + if (groups.Length != 0) { - var groups = PortalGroupController.Instance.GetPortalGroups().ToArray(); - if (groups.Length != 0) - { - return ( + return ( from @group in groups select PortalGroupController.Instance.GetPortalsByGroup(@group.PortalGroupId) into portals where portals.Any((IPortalInfo x) => x.PortalId == PortalSettings.Current.PortalId) select portals.ToArray()) - .FirstOrDefault(); - } + .FirstOrDefault(); + } + + var currentPortal = PortalController.Instance.GetPortal(this.PortalSettings.PortalId); + return new List { currentPortal, }; + } - var currentPortal = PortalController.Instance.GetPortal(this.PortalSettings.PortalId); - return new List { currentPortal, }; + private NTree GetPagesInternal(int portalId, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) + { + if (portalId == -1) + { + portalId = this.GetActivePortalId(); } - private NTree GetPagesInternal(int portalId, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) + var tabs = this.GetPortalPages(portalId, includeDisabled, includeAllTypes, includeActive, includeHostPages); + var sortedTree = new NTree { Data = new ItemDto { Key = RootKey } }; + if (tabs == null) { - if (portalId == -1) - { - portalId = this.GetActivePortalId(); - } + return sortedTree; + } - var tabs = this.GetPortalPages(portalId, includeDisabled, includeAllTypes, includeActive, includeHostPages); - var sortedTree = new NTree { Data = new ItemDto { Key = RootKey } }; - if (tabs == null) - { - return sortedTree; - } + var filterTabs = FilterTabsByRole(tabs, roles, disabledNotSelectable); + var children = ApplySort(GetChildrenOf(tabs, Null.NullInteger, filterTabs), sortOrder).Select(dto => new NTree { Data = dto }).ToList(); + sortedTree.Children = children; + return sortedTree; + } - var filterTabs = FilterTabsByRole(tabs, roles, disabledNotSelectable); - var children = ApplySort(GetChildrenOf(tabs, Null.NullInteger, filterTabs), sortOrder).Select(dto => new NTree { Data = dto }).ToList(); - sortedTree.Children = children; - return sortedTree; + private IEnumerable GetPageDescendantsInPortalGroupInternal(string parentId, int sortOrder, string searchText, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") + { + if (string.IsNullOrEmpty(parentId)) + { + return null; } - private IEnumerable GetPageDescendantsInPortalGroupInternal(string parentId, int sortOrder, string searchText, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "") + int portalId; + int parentIdAsInt; + if (parentId.StartsWith(PortalPrefix, StringComparison.Ordinal)) { - if (string.IsNullOrEmpty(parentId)) + parentIdAsInt = -1; + if (!int.TryParse(parentId.Replace(PortalPrefix, string.Empty), out portalId)) { - return null; + portalId = -1; } - int portalId; - int parentIdAsInt; - if (parentId.StartsWith(PortalPrefix, StringComparison.Ordinal)) + if (!string.IsNullOrEmpty(searchText)) { - parentIdAsInt = -1; - if (!int.TryParse(parentId.Replace(PortalPrefix, string.Empty), out portalId)) - { - portalId = -1; - } - - if (!string.IsNullOrEmpty(searchText)) - { - return this.SearchPagesInternal(portalId, searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles).Children.Select(node => node.Data); - } + return this.SearchPagesInternal(portalId, searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles).Children.Select(node => node.Data); } - else + } + else + { + portalId = -1; + if (!int.TryParse(parentId, out parentIdAsInt)) { - portalId = -1; - if (!int.TryParse(parentId, out parentIdAsInt)) - { - parentIdAsInt = -1; - } + parentIdAsInt = -1; } - - return this.GetPageDescendantsInternal(portalId, parentIdAsInt, sortOrder, searchText, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles); } - private IEnumerable GetPageDescendantsInternal(int portalId, string parentId, int sortOrder, string searchText, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) + return this.GetPageDescendantsInternal(portalId, parentIdAsInt, sortOrder, searchText, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles); + } + + private IEnumerable GetPageDescendantsInternal(int portalId, string parentId, int sortOrder, string searchText, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) + { + int id; + id = int.TryParse(parentId, out id) ? id : Null.NullInteger; + return this.GetPageDescendantsInternal(portalId, id, sortOrder, searchText, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles, disabledNotSelectable); + } + + private IEnumerable GetPageDescendantsInternal(int portalId, int parentId, int sortOrder, string searchText, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) + { + List tabs; + + if (portalId == -1) { - int id; - id = int.TryParse(parentId, out id) ? id : Null.NullInteger; - return this.GetPageDescendantsInternal(portalId, id, sortOrder, searchText, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles, disabledNotSelectable); + portalId = this.GetActivePortalId(parentId); } - - private IEnumerable GetPageDescendantsInternal(int portalId, int parentId, int sortOrder, string searchText, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) + else { - List tabs; - - if (portalId == -1) - { - portalId = this.GetActivePortalId(parentId); - } - else + if (!this.IsPortalIdValid(portalId)) { - if (!this.IsPortalIdValid(portalId)) - { - return new List(); - } + return new List(); } + } - Func searchFunc; - if (string.IsNullOrEmpty(searchText)) - { - searchFunc = _ => true; - } - else + Func searchFunc; + if (string.IsNullOrEmpty(searchText)) + { + searchFunc = _ => true; + } + else + { + searchFunc = page => page.LocalizedTabName.IndexOf(searchText, StringComparison.InvariantCultureIgnoreCase) > -1; + } + + if (portalId > -1) + { + tabs = TabController.GetPortalTabs(this.hostSettings, this.appStatus, portalId, includeActive ? Null.NullInteger : this.PortalSettings.ActiveTab.TabID, false, null, true, false, includeAllTypes, true, false) + .Where(tab => searchFunc(tab) + && tab.ParentId == parentId + && (includeDisabled || !tab.DisableLink) + && (includeAllTypes || tab.TabType == TabType.Normal) + && !tab.IsSystem) + .OrderBy(tab => tab.TabOrder) + .ToList(); + + if (this.PortalSettings.UserInfo.IsSuperUser && includeHostPages) { - searchFunc = page => page.LocalizedTabName.IndexOf(searchText, StringComparison.InvariantCultureIgnoreCase) > -1; + tabs.AddRange(TabController.Instance.GetTabsByPortal(-1).AsList() + .Where(tab => searchFunc(tab) && tab.ParentId == parentId && !tab.IsDeleted && !tab.DisableLink && !tab.IsSystem) + .OrderBy(tab => tab.TabOrder) + .ToList()); } - - if (portalId > -1) + } + else + { + if (this.PortalSettings.UserInfo.IsSuperUser) { - tabs = TabController.GetPortalTabs(this.hostSettings, this.appStatus, portalId, includeActive ? Null.NullInteger : this.PortalSettings.ActiveTab.TabID, false, null, true, false, includeAllTypes, true, false) - .Where(tab => searchFunc(tab) - && tab.ParentId == parentId - && (includeDisabled || !tab.DisableLink) - && (includeAllTypes || tab.TabType == TabType.Normal) - && !tab.IsSystem) + tabs = TabController.Instance.GetTabsByPortal(-1).AsList() + .Where(tab => searchFunc(tab) && tab.ParentId == parentId && !tab.IsDeleted && !tab.DisableLink && !tab.IsSystem) .OrderBy(tab => tab.TabOrder) .ToList(); - - if (this.PortalSettings.UserInfo.IsSuperUser && includeHostPages) - { - tabs.AddRange(TabController.Instance.GetTabsByPortal(-1).AsList() - .Where(tab => searchFunc(tab) && tab.ParentId == parentId && !tab.IsDeleted && !tab.DisableLink && !tab.IsSystem) - .OrderBy(tab => tab.TabOrder) - .ToList()); - } } else { - if (this.PortalSettings.UserInfo.IsSuperUser) - { - tabs = TabController.Instance.GetTabsByPortal(-1).AsList() - .Where(tab => searchFunc(tab) && tab.ParentId == parentId && !tab.IsDeleted && !tab.DisableLink && !tab.IsSystem) - .OrderBy(tab => tab.TabOrder) - .ToList(); - } - else - { - return new List(); - } + return new List(); } + } - var filterTabs = FilterTabsByRole(tabs, roles, disabledNotSelectable); + var filterTabs = FilterTabsByRole(tabs, roles, disabledNotSelectable); - var pages = tabs.Select(tab => new ItemDto - { - Key = tab.TabID.ToString(CultureInfo.InvariantCulture), - Value = tab.LocalizedTabName, - HasChildren = tab.HasChildren, - Selectable = filterTabs.Contains(tab.TabID), - }); + var pages = tabs.Select(tab => new ItemDto + { + Key = tab.TabID.ToString(CultureInfo.InvariantCulture), + Value = tab.LocalizedTabName, + HasChildren = tab.HasChildren, + Selectable = filterTabs.Contains(tab.TabID), + }); - return ApplySort(pages, sortOrder); - } + return ApplySort(pages, sortOrder); + } - private NTree SearchPagesInternal(int portalId, string searchText, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) - { - var tree = new NTree { Data = new ItemDto { Key = RootKey } }; + private NTree SearchPagesInternal(int portalId, string searchText, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = true, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) + { + var tree = new NTree { Data = new ItemDto { Key = RootKey } }; - List tabs; - if (portalId == -1) - { - portalId = this.GetActivePortalId(); - } - else + List tabs; + if (portalId == -1) + { + portalId = this.GetActivePortalId(); + } + else + { + if (!this.IsPortalIdValid(portalId)) { - if (!this.IsPortalIdValid(portalId)) - { - return tree; - } + return tree; } + } - Func searchFunc; - if (string.IsNullOrEmpty(searchText)) - { - searchFunc = _ => true; - } - else - { - searchFunc = page => page.LocalizedTabName.IndexOf(searchText, StringComparison.InvariantCultureIgnoreCase) > -1; - } + Func searchFunc; + if (string.IsNullOrEmpty(searchText)) + { + searchFunc = _ => true; + } + else + { + searchFunc = page => page.LocalizedTabName.IndexOf(searchText, StringComparison.InvariantCultureIgnoreCase) > -1; + } - if (portalId > -1) + if (portalId > -1) + { + tabs = TabController.Instance.GetTabsByPortal(portalId).Where(tab => + (includeActive || tab.Value.TabID != this.PortalSettings.ActiveTab.TabID) + && (includeDisabled || !tab.Value.DisableLink) + && (includeAllTypes || tab.Value.TabType == TabType.Normal) + && searchFunc(tab.Value) + && !tab.Value.IsSystem) + .OrderBy(tab => tab.Value.TabOrder) + .Select(tab => tab.Value) + .ToList(); + + if (this.PortalSettings.UserInfo.IsSuperUser && includeHostPages) { - tabs = TabController.Instance.GetTabsByPortal(portalId).Where(tab => - (includeActive || tab.Value.TabID != this.PortalSettings.ActiveTab.TabID) - && (includeDisabled || !tab.Value.DisableLink) - && (includeAllTypes || tab.Value.TabType == TabType.Normal) - && searchFunc(tab.Value) - && !tab.Value.IsSystem) + tabs.AddRange(TabController.Instance.GetTabsByPortal(-1).Where(tab => !tab.Value.DisableLink && searchFunc(tab.Value) && !tab.Value.IsSystem) .OrderBy(tab => tab.Value.TabOrder) .Select(tab => tab.Value) - .ToList(); - - if (this.PortalSettings.UserInfo.IsSuperUser && includeHostPages) - { - tabs.AddRange(TabController.Instance.GetTabsByPortal(-1).Where(tab => !tab.Value.DisableLink && searchFunc(tab.Value) && !tab.Value.IsSystem) - .OrderBy(tab => tab.Value.TabOrder) - .Select(tab => tab.Value) - .ToList()); - } + .ToList()); } - else - { - if (this.PortalSettings.UserInfo.IsSuperUser) - { - tabs = TabController.Instance.GetTabsByPortal(-1).Where(tab => !tab.Value.DisableLink && searchFunc(tab.Value) && !tab.Value.IsSystem) - .OrderBy(tab => tab.Value.TabOrder) - .Select(tab => tab.Value) - .ToList(); - } - else - { - return tree; - } - } - - var filterTabs = FilterTabsByRole(tabs, roles, disabledNotSelectable); - - var pages = tabs.Select(tab => new ItemDto - { - Key = tab.TabID.ToString(CultureInfo.InvariantCulture), - Value = tab.LocalizedTabName, - HasChildren = false, - Selectable = filterTabs.Contains(tab.TabID), - }); - - tree.Children = ApplySort(pages, sortOrder).Select(dto => new NTree { Data = dto }).ToList(); - return tree; } - - private List GetPortalPages(int portalId, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false) + else { - List tabs = null; - if (portalId == -1) + if (this.PortalSettings.UserInfo.IsSuperUser) { - portalId = this.GetActivePortalId(); + tabs = TabController.Instance.GetTabsByPortal(-1).Where(tab => !tab.Value.DisableLink && searchFunc(tab.Value) && !tab.Value.IsSystem) + .OrderBy(tab => tab.Value.TabOrder) + .Select(tab => tab.Value) + .ToList(); } else { - if (!this.IsPortalIdValid(portalId)) - { - return null; - } + return tree; } + } - if (portalId > -1) - { - tabs = TabController.GetPortalTabs(this.hostSettings, this.appStatus, portalId, includeActive ? Null.NullInteger : this.PortalSettings.ActiveTab.TabID, false, null, true, false, includeAllTypes, true, false) - .Where(t => (!t.DisableLink || includeDisabled) && !t.IsSystem) - .ToList(); + var filterTabs = FilterTabsByRole(tabs, roles, disabledNotSelectable); - if (this.PortalSettings.UserInfo.IsSuperUser && includeHostPages) - { - tabs.AddRange(TabController.Instance.GetTabsByPortal(-1).AsList().Where(t => !t.IsDeleted && !t.DisableLink && !t.IsSystem).ToList()); - } - } - else - { - if (this.PortalSettings.UserInfo.IsSuperUser) - { - tabs = TabController.Instance.GetTabsByPortal(-1).AsList().Where(t => !t.IsDeleted && !t.DisableLink && !t.IsSystem).ToList(); - } - } + var pages = tabs.Select(tab => new ItemDto + { + Key = tab.TabID.ToString(CultureInfo.InvariantCulture), + Value = tab.LocalizedTabName, + HasChildren = false, + Selectable = filterTabs.Contains(tab.TabID), + }); - return tabs; - } + tree.Children = ApplySort(pages, sortOrder).Select(dto => new NTree { Data = dto }).ToList(); + return tree; + } - private NTree SortPagesInternal(int portalId, string treeAsJson, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false) + private List GetPortalPages(int portalId, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false) + { + List tabs = null; + if (portalId == -1) { - var tree = DotNetNuke.Common.Utilities.Json.Deserialize>(treeAsJson); - return this.SortPagesInternal(portalId, tree, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages); + portalId = this.GetActivePortalId(); } - - private NTree SortPagesInternal(int portalId, NTree openedNodesTree, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false) + else { - var pages = this.GetPortalPages(portalId, includeDisabled, includeAllTypes, includeActive, includeHostPages); - var sortedTree = new NTree { Data = new ItemDto { Key = RootKey } }; - if (pages == null) + if (!this.IsPortalIdValid(portalId)) { - return sortedTree; + return null; } - - SortPagesRecursively(pages, sortedTree, openedNodesTree, sortOrder); - return sortedTree; } - private NTree SearchPagesInPortalGroupInternal(string searchText, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false, string roles = "") + if (portalId > -1) { - var treeNode = new NTree { Data = new ItemDto { Key = RootKey } }; - var portals = this.GetPortalGroup(sortOrder); - treeNode.Children = portals.Select(dto => new NTree { Data = dto }).ToList(); - foreach (var child in treeNode.Children) + tabs = TabController.GetPortalTabs(this.hostSettings, this.appStatus, portalId, includeActive ? Null.NullInteger : this.PortalSettings.ActiveTab.TabID, false, null, true, false, includeAllTypes, true, false) + .Where(t => (!t.DisableLink || includeDisabled) && !t.IsSystem) + .ToList(); + + if (this.PortalSettings.UserInfo.IsSuperUser && includeHostPages) { - int portalId; - if (int.TryParse(child.Data.Key.Replace(PortalPrefix, string.Empty), out portalId)) - { - var pageTree = this.SearchPagesInternal(portalId, searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles); - child.Children = pageTree.Children; - } + tabs.AddRange(TabController.Instance.GetTabsByPortal(-1).AsList().Where(t => !t.IsDeleted && !t.DisableLink && !t.IsSystem).ToList()); } - - return treeNode; } - - private NTree SearchPagesInPortalGroupInternal(string treeAsJson, string searchText, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false, string roles = "") + else { - var treeNode = new NTree { Data = new ItemDto { Key = RootKey } }; - var openedNode = DotNetNuke.Common.Utilities.Json.Deserialize>(treeAsJson); - if (openedNode == null) + if (this.PortalSettings.UserInfo.IsSuperUser) { - return treeNode; + tabs = TabController.Instance.GetTabsByPortal(-1).AsList().Where(t => !t.IsDeleted && !t.DisableLink && !t.IsSystem).ToList(); } + } - var portals = this.GetPortalGroup(sortOrder); - treeNode.Children = portals.Select(dto => new NTree { Data = dto }).ToList(); - - if (!openedNode.HasChildren()) - { - return treeNode; - } - - foreach (var openedNodeChild in openedNode.Children) - { - var portalIdString = openedNodeChild.Data.Id; - var treeNodeChild = treeNode.Children.Find(child => string.Equals(child.Data.Key, portalIdString, StringComparison.OrdinalIgnoreCase)); - if (treeNodeChild == null) - { - continue; - } - - int portalId; - if (int.TryParse(treeNodeChild.Data.Key.Replace(PortalPrefix, string.Empty), out portalId)) - { - var pageTree = this.SearchPagesInternal(portalId, searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles); - treeNodeChild.Children = pageTree.Children; - } - } + return tabs; + } - return treeNode; - } + private NTree SortPagesInternal(int portalId, string treeAsJson, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false) + { + var tree = DotNetNuke.Common.Utilities.Json.Deserialize>(treeAsJson); + return this.SortPagesInternal(portalId, tree, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages); + } - private NTree SortPagesInPortalGroupInternal(string treeAsJson, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false) + private NTree SortPagesInternal(int portalId, NTree openedNodesTree, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false) + { + var pages = this.GetPortalPages(portalId, includeDisabled, includeAllTypes, includeActive, includeHostPages); + var sortedTree = new NTree { Data = new ItemDto { Key = RootKey } }; + if (pages == null) { - var tree = DotNetNuke.Common.Utilities.Json.Deserialize>(treeAsJson); - return this.SortPagesInPortalGroupInternal(tree, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages); + return sortedTree; } - private NTree SortPagesInPortalGroupInternal(NTree openedNode, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false) + SortPagesRecursively(pages, sortedTree, openedNodesTree, sortOrder); + return sortedTree; + } + + private NTree SearchPagesInPortalGroupInternal(string searchText, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false, string roles = "") + { + var treeNode = new NTree { Data = new ItemDto { Key = RootKey } }; + var portals = this.GetPortalGroup(sortOrder); + treeNode.Children = portals.Select(dto => new NTree { Data = dto }).ToList(); + foreach (var child in treeNode.Children) { - var treeNode = new NTree { Data = new ItemDto { Key = RootKey } }; - if (openedNode == null) + int portalId; + if (int.TryParse(child.Data.Key.Replace(PortalPrefix, string.Empty), out portalId)) { - return treeNode; + var pageTree = this.SearchPagesInternal(portalId, searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles); + child.Children = pageTree.Children; } + } - var portals = this.GetPortalGroup(sortOrder); - treeNode.Children = portals.Select(dto => new NTree { Data = dto }).ToList(); - if (openedNode.HasChildren()) - { - foreach (var openedNodeChild in openedNode.Children) - { - var portalIdString = openedNodeChild.Data.Id; - var treeNodeChild = treeNode.Children.Find(child => string.Equals(child.Data.Key, portalIdString, StringComparison.OrdinalIgnoreCase)); - if (treeNodeChild == null) - { - continue; - } - - int portalId; - if (!int.TryParse(portalIdString.Replace(PortalPrefix, string.Empty), out portalId)) - { - portalId = -1; - } - - var treeOfPages = this.SortPagesInternal(portalId, openedNodeChild, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages); - treeNodeChild.Children = treeOfPages.Children; - } - } + return treeNode; + } + private NTree SearchPagesInPortalGroupInternal(string treeAsJson, string searchText, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false, string roles = "") + { + var treeNode = new NTree { Data = new ItemDto { Key = RootKey } }; + var openedNode = DotNetNuke.Common.Utilities.Json.Deserialize>(treeAsJson); + if (openedNode == null) + { return treeNode; } - private NTree GetTreePathForPageInternal(int portalId, string itemId, int sortOrder, bool includePortalTree = false, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false, string roles = "") - { - int itemIdAsInt; - if (string.IsNullOrEmpty(itemId) || !int.TryParse(itemId, out itemIdAsInt)) - { - itemIdAsInt = Null.NullInteger; - } + var portals = this.GetPortalGroup(sortOrder); + treeNode.Children = portals.Select(dto => new NTree { Data = dto }).ToList(); - return this.GetTreePathForPageInternal(portalId, itemIdAsInt, sortOrder, includePortalTree, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles); + if (!openedNode.HasChildren()) + { + return treeNode; } - private NTree GetTreePathForPageInternal(string itemId, int sortOrder, bool includePortalTree = false, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false, string roles = "") + foreach (var openedNodeChild in openedNode.Children) { - var tree = new NTree { Data = new ItemDto { Key = RootKey } }; - if (string.IsNullOrEmpty(itemId) || !int.TryParse(itemId, out var itemIdAsInt)) + var portalIdString = openedNodeChild.Data.Id; + var treeNodeChild = treeNode.Children.Find(child => string.Equals(child.Data.Key, portalIdString, StringComparison.OrdinalIgnoreCase)); + if (treeNodeChild == null) { - return tree; + continue; } - var portals = PortalController.GetPortalDictionary(this.hostSettings, this.dataProvider); int portalId; - if (portals.TryGetValue(itemIdAsInt, out var pid)) + if (int.TryParse(treeNodeChild.Data.Key.Replace(PortalPrefix, string.Empty), out portalId)) { - portalId = pid; + var pageTree = this.SearchPagesInternal(portalId, searchText, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles); + treeNodeChild.Children = pageTree.Children; } - else - { - return tree; - } - - return this.GetTreePathForPageInternal(portalId, itemIdAsInt, sortOrder, includePortalTree, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles); } - private NTree GetTreePathForPageInternal(int portalId, int selectedItemId, int sortOrder, bool includePortalTree = false, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) - { - var tree = new NTree { Data = new ItemDto { Key = RootKey } }; - - if (selectedItemId <= 0) - { - return tree; - } - - var pages = this.GetPortalPages(portalId, includeDisabled, includeAllTypes, includeActive, includeHostPages); - - if (pages == null) - { - return tree; - } - - var page = pages.SingleOrDefault(pageInfo => pageInfo.TabID == selectedItemId); + return treeNode; + } - if (page == null) - { - return tree; - } + private NTree SortPagesInPortalGroupInternal(string treeAsJson, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false) + { + var tree = DotNetNuke.Common.Utilities.Json.Deserialize>(treeAsJson); + return this.SortPagesInPortalGroupInternal(tree, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages); + } - var selfTree = new NTree - { - Data = new ItemDto - { - Key = page.TabID.ToString(CultureInfo.InvariantCulture), - Value = page.LocalizedTabName, - HasChildren = page.HasChildren, - Selectable = true, - }, - }; + private NTree SortPagesInPortalGroupInternal(NTree openedNode, int sortOrder, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false) + { + var treeNode = new NTree { Data = new ItemDto { Key = RootKey } }; + if (openedNode == null) + { + return treeNode; + } - var parentId = page.ParentId; - var parentTab = parentId > 0 ? pages.SingleOrDefault(t => t.TabID == parentId) : null; - var filterTabs = FilterTabsByRole(pages, roles, disabledNotSelectable); - while (parentTab != null) + var portals = this.GetPortalGroup(sortOrder); + treeNode.Children = portals.Select(dto => new NTree { Data = dto }).ToList(); + if (openedNode.HasChildren()) + { + foreach (var openedNodeChild in openedNode.Children) { - // load all siblings - var siblingTabs = ApplySort(GetChildrenOf(pages, parentId, filterTabs), sortOrder); - var siblingTabsTree = siblingTabs.Select(t => new NTree { Data = t, }).ToList(); - - // attach the tree - if (selfTree.Children != null) + var portalIdString = openedNodeChild.Data.Id; + var treeNodeChild = treeNode.Children.Find(child => string.Equals(child.Data.Key, portalIdString, StringComparison.OrdinalIgnoreCase)); + if (treeNodeChild == null) { - foreach (var node in siblingTabsTree) - { - if (node.Data.Key == selfTree.Data.Key) - { - node.Children = selfTree.Children; - break; - } - } + continue; } - selfTree = new NTree + int portalId; + if (!int.TryParse(portalIdString.Replace(PortalPrefix, string.Empty), out portalId)) { - Data = new ItemDto - { - Key = parentId.ToString(CultureInfo.InvariantCulture), - Value = parentTab.LocalizedTabName, - HasChildren = true, - Selectable = true, - }, - Children = siblingTabsTree, - }; + portalId = -1; + } - parentId = parentTab.ParentId; - parentTab = parentId > 0 ? pages.SingleOrDefault(t => t.TabID == parentId) : null; + var treeOfPages = this.SortPagesInternal(portalId, openedNodeChild, sortOrder, includeDisabled, includeAllTypes, includeActive, includeHostPages); + treeNodeChild.Children = treeOfPages.Children; } + } - // retain root pages - var rootTabs = ApplySort(GetChildrenOf(pages, Null.NullInteger, filterTabs), sortOrder); - var rootTree = rootTabs.Select(dto => new NTree { Data = dto }).ToList(); + return treeNode; + } - foreach (var node in rootTree) - { - if (node.Data.Key == selfTree.Data.Key) - { - node.Children = selfTree.Children; - break; - } - } + private NTree GetTreePathForPageInternal(int portalId, string itemId, int sortOrder, bool includePortalTree = false, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false, string roles = "") + { + int itemIdAsInt; + if (string.IsNullOrEmpty(itemId) || !int.TryParse(itemId, out itemIdAsInt)) + { + itemIdAsInt = Null.NullInteger; + } - if (includePortalTree) - { - var myGroup = this.GetMyPortalGroup(); - var portalTree = myGroup.Select( - portal => new NTree - { - Data = new ItemDto - { - Key = PortalPrefix + portal.PortalId.ToString(CultureInfo.InvariantCulture), - Value = portal.PortalName, - HasChildren = true, - Selectable = false, - }, - }).ToList(); - - foreach (var node in portalTree) - { - if (node.Data.Key == PortalPrefix + portalId.ToString(CultureInfo.InvariantCulture)) - { - node.Children = rootTree; - break; - } - } + return this.GetTreePathForPageInternal(portalId, itemIdAsInt, sortOrder, includePortalTree, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles); + } + + private NTree GetTreePathForPageInternal(string itemId, int sortOrder, bool includePortalTree = false, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false, string roles = "") + { + var tree = new NTree { Data = new ItemDto { Key = RootKey } }; + if (string.IsNullOrEmpty(itemId) || !int.TryParse(itemId, out var itemIdAsInt)) + { + return tree; + } + + var portals = PortalController.GetPortalDictionary(this.hostSettings, this.dataProvider); + int portalId; + if (portals.TryGetValue(itemIdAsInt, out var pid)) + { + portalId = pid; + } + else + { + return tree; + } - rootTree = portalTree; - } + return this.GetTreePathForPageInternal(portalId, itemIdAsInt, sortOrder, includePortalTree, includeDisabled, includeAllTypes, includeActive, includeHostPages, roles); + } - tree.Children = rootTree; + private NTree GetTreePathForPageInternal(int portalId, int selectedItemId, int sortOrder, bool includePortalTree = false, bool includeDisabled = false, bool includeAllTypes = false, bool includeActive = false, bool includeHostPages = false, string roles = "", bool disabledNotSelectable = false) + { + var tree = new NTree { Data = new ItemDto { Key = RootKey } }; + if (selectedItemId <= 0) + { return tree; } - private NTree GetFoldersInternal(int portalId, int sortOrder, string permissions, int parentFolderId = -1) - { - var tree = new NTree { Data = new ItemDto { Key = RootKey } }; - var children = ApplySort(this.GetFolderDescendantsInternal(portalId, parentFolderId, sortOrder, string.Empty, permissions), sortOrder).Select(dto => new NTree { Data = dto }).ToList(); - tree.Children = children; - foreach (var child in tree.Children) - { - children = ApplySort(this.GetFolderDescendantsInternal(portalId, child.Data.Key, sortOrder, string.Empty, permissions), sortOrder).Select(dto => new NTree { Data = dto }).ToList(); - child.Children = children; - } + var pages = this.GetPortalPages(portalId, includeDisabled, includeAllTypes, includeActive, includeHostPages); + if (pages == null) + { return tree; } - private NTree SortFoldersInternal(int portalId, string treeAsJson, int sortOrder, string permissions) - { - var tree = DotNetNuke.Common.Utilities.Json.Deserialize>(treeAsJson); - return this.SortFoldersInternal(portalId, tree, sortOrder, permissions); - } + var page = pages.SingleOrDefault(pageInfo => pageInfo.TabID == selectedItemId); - private NTree SortFoldersInternal(int portalId, NTree openedNodesTree, int sortOrder, string permissions) + if (page == null) { - var sortedTree = new NTree { Data = new ItemDto { Key = RootKey } }; - this.SortFoldersRecursevely(portalId, sortedTree, openedNodesTree, sortOrder, permissions); - return sortedTree; + return tree; } - private void SortFoldersRecursevely(int portalId, NTree treeNode, NTree openedNode, int sortOrder, string permissions) + var selfTree = new NTree { - if (openedNode == null) + Data = new ItemDto { - return; - } + Key = page.TabID.ToString(CultureInfo.InvariantCulture), + Value = page.LocalizedTabName, + HasChildren = page.HasChildren, + Selectable = true, + }, + }; + + var parentId = page.ParentId; + var parentTab = parentId > 0 ? pages.SingleOrDefault(t => t.TabID == parentId) : null; + var filterTabs = FilterTabsByRole(pages, roles, disabledNotSelectable); + while (parentTab != null) + { + // load all siblings + var siblingTabs = ApplySort(GetChildrenOf(pages, parentId, filterTabs), sortOrder); + var siblingTabsTree = siblingTabs.Select(t => new NTree { Data = t, }).ToList(); - var children = ApplySort(this.GetFolderDescendantsInternal(portalId, openedNode.Data.Id, sortOrder, string.Empty, permissions), sortOrder).Select(dto => new NTree { Data = dto }).ToList(); - treeNode.Children = children; - if (openedNode.HasChildren()) + // attach the tree + if (selfTree.Children != null) { - foreach (var openedNodeChild in openedNode.Children) + foreach (var node in siblingTabsTree) { - var treeNodeChild = treeNode.Children.Find(child => string.Equals(child.Data.Key, openedNodeChild.Data.Id, StringComparison.OrdinalIgnoreCase)); - if (treeNodeChild == null) + if (node.Data.Key == selfTree.Data.Key) { - continue; + node.Children = selfTree.Children; + break; } - - this.SortFoldersRecursevely(portalId, treeNodeChild, openedNodeChild, sortOrder, permissions); } } - } - - private IEnumerable GetFolderDescendantsInternal(int portalId, string parentId, int sortOrder, string searchText, string permission) - { - int id; - id = int.TryParse(parentId, out id) ? id : Null.NullInteger; - return this.GetFolderDescendantsInternal(portalId, id, sortOrder, searchText, permission); - } - private IEnumerable GetFolderDescendantsInternal(int portalId, int parentId, int sortOrder, string searchText, string permission) - { - if (portalId > -1) + selfTree = new NTree { - if (!this.IsPortalIdValid(portalId)) + Data = new ItemDto { - return new List(); - } - } - else - { - portalId = this.GetActivePortalId(); - } + Key = parentId.ToString(CultureInfo.InvariantCulture), + Value = parentTab.LocalizedTabName, + HasChildren = true, + Selectable = true, + }, + Children = siblingTabsTree, + }; - var parentFolder = parentId > -1 ? FolderManager.Instance.GetFolder(parentId) : FolderManager.Instance.GetFolder(portalId, string.Empty); + parentId = parentTab.ParentId; + parentTab = parentId > 0 ? pages.SingleOrDefault(t => t.TabID == parentId) : null; + } - if (parentFolder == null) - { - return new List(); - } + // retain root pages + var rootTabs = ApplySort(GetChildrenOf(pages, Null.NullInteger, filterTabs), sortOrder); + var rootTree = rootTabs.Select(dto => new NTree { Data = dto }).ToList(); - var hasPermission = string.IsNullOrEmpty(permission) ? - this.HasPermission(parentFolder, "BROWSE") || this.HasPermission(parentFolder, "READ") : - this.HasPermission(parentFolder, permission); - if (!hasPermission) + foreach (var node in rootTree) + { + if (node.Data.Key == selfTree.Data.Key) { - return new List(); + node.Children = selfTree.Children; + break; } + } - if (parentId < 1) - { - return new List + if (includePortalTree) + { + var myGroup = this.GetMyPortalGroup(); + var portalTree = myGroup.Select( + portal => new NTree { - new ItemDto + Data = new ItemDto { - Key = parentFolder.FolderID.ToString(CultureInfo.InvariantCulture), - Value = portalId == -1 ? DynamicSharedConstants.HostRootFolder : DynamicSharedConstants.RootFolder, - HasChildren = this.HasChildren(parentFolder, permission), - Selectable = true, + Key = PortalPrefix + portal.PortalId.ToString(CultureInfo.InvariantCulture), + Value = portal.PortalName, + HasChildren = true, + Selectable = false, }, - }; + }).ToList(); + + foreach (var node in portalTree) + { + if (node.Data.Key == PortalPrefix + portalId.ToString(CultureInfo.InvariantCulture)) + { + node.Children = rootTree; + break; + } } - var childrenFolders = this.GetFolderDescendants(parentFolder, searchText, permission); + rootTree = portalTree; + } - var folders = childrenFolders.Select(folder => new ItemDto - { - Key = folder.FolderID.ToString(CultureInfo.InvariantCulture), - Value = folder.FolderName, - HasChildren = this.HasChildren(folder, permission), - Selectable = true, - }); + tree.Children = rootTree; - return ApplySort(folders, sortOrder); + return tree; + } + + private NTree GetFoldersInternal(int portalId, int sortOrder, string permissions, int parentFolderId = -1) + { + var tree = new NTree { Data = new ItemDto { Key = RootKey } }; + var children = ApplySort(this.GetFolderDescendantsInternal(portalId, parentFolderId, sortOrder, string.Empty, permissions), sortOrder).Select(dto => new NTree { Data = dto }).ToList(); + tree.Children = children; + foreach (var child in tree.Children) + { + children = ApplySort(this.GetFolderDescendantsInternal(portalId, child.Data.Key, sortOrder, string.Empty, permissions), sortOrder).Select(dto => new NTree { Data = dto }).ToList(); + child.Children = children; } - private NTree SearchFoldersInternal(int portalId, string searchText, int sortOrder, string permission) + return tree; + } + + private NTree SortFoldersInternal(int portalId, string treeAsJson, int sortOrder, string permissions) + { + var tree = DotNetNuke.Common.Utilities.Json.Deserialize>(treeAsJson); + return this.SortFoldersInternal(portalId, tree, sortOrder, permissions); + } + + private NTree SortFoldersInternal(int portalId, NTree openedNodesTree, int sortOrder, string permissions) + { + var sortedTree = new NTree { Data = new ItemDto { Key = RootKey } }; + this.SortFoldersRecursevely(portalId, sortedTree, openedNodesTree, sortOrder, permissions); + return sortedTree; + } + + private void SortFoldersRecursevely(int portalId, NTree treeNode, NTree openedNode, int sortOrder, string permissions) + { + if (openedNode == null) { - var tree = new NTree { Data = new ItemDto { Key = RootKey } }; + return; + } - if (portalId > -1) + var children = ApplySort(this.GetFolderDescendantsInternal(portalId, openedNode.Data.Id, sortOrder, string.Empty, permissions), sortOrder).Select(dto => new NTree { Data = dto }).ToList(); + treeNode.Children = children; + if (openedNode.HasChildren()) + { + foreach (var openedNodeChild in openedNode.Children) { - if (!this.IsPortalIdValid(portalId)) + var treeNodeChild = treeNode.Children.Find(child => string.Equals(child.Data.Key, openedNodeChild.Data.Id, StringComparison.OrdinalIgnoreCase)); + if (treeNodeChild == null) { - return tree; + continue; } - } - else - { - portalId = this.GetActivePortalId(); - } - var allFolders = this.GetPortalFolders(portalId, searchText, permission); - var folders = allFolders.Select(f => new ItemDto - { - Key = f.FolderID.ToString(CultureInfo.InvariantCulture), - Value = f.FolderName, - HasChildren = false, - Selectable = true, - }); - tree.Children = ApplySort(folders, sortOrder).Select(dto => new NTree { Data = dto }).ToList(); - return tree; + this.SortFoldersRecursevely(portalId, treeNodeChild, openedNodeChild, sortOrder, permissions); + } } + } - private NTree GetTreePathForFolderInternal(string selectedItemId, int sortOrder, string permission) - { - var tree = new NTree { Data = new ItemDto { Key = RootKey } }; + private IEnumerable GetFolderDescendantsInternal(int portalId, string parentId, int sortOrder, string searchText, string permission) + { + int id; + id = int.TryParse(parentId, out id) ? id : Null.NullInteger; + return this.GetFolderDescendantsInternal(portalId, id, sortOrder, searchText, permission); + } - int itemId; - if (string.IsNullOrEmpty(selectedItemId) || !int.TryParse(selectedItemId, out itemId)) + private IEnumerable GetFolderDescendantsInternal(int portalId, int parentId, int sortOrder, string searchText, string permission) + { + if (portalId > -1) + { + if (!this.IsPortalIdValid(portalId)) { - return tree; + return new List(); } + } + else + { + portalId = this.GetActivePortalId(); + } - if (itemId <= 0) - { - return tree; - } + var parentFolder = parentId > -1 ? FolderManager.Instance.GetFolder(parentId) : FolderManager.Instance.GetFolder(portalId, string.Empty); - var folder = FolderManager.Instance.GetFolder(itemId); - if (folder == null) - { - return tree; - } + if (parentFolder == null) + { + return new List(); + } - var hasPermission = string.IsNullOrEmpty(permission) ? - (this.HasPermission(folder, "BROWSE") || this.HasPermission(folder, "READ")) : - this.HasPermission(folder, permission); - if (!hasPermission) - { - return new NTree(); - } + var hasPermission = string.IsNullOrEmpty(permission) ? + this.HasPermission(parentFolder, "BROWSE") || this.HasPermission(parentFolder, "READ") : + this.HasPermission(parentFolder, permission); + if (!hasPermission) + { + return new List(); + } - var selfTree = new NTree + if (parentId < 1) + { + return new List { - Data = new ItemDto + new ItemDto { - Key = folder.FolderID.ToString(CultureInfo.InvariantCulture), - Value = folder.FolderName, - HasChildren = this.HasChildren(folder, permission), + Key = parentFolder.FolderID.ToString(CultureInfo.InvariantCulture), + Value = portalId == -1 ? DynamicSharedConstants.HostRootFolder : DynamicSharedConstants.RootFolder, + HasChildren = this.HasChildren(parentFolder, permission), Selectable = true, }, }; - var parentId = folder.ParentID; - var parentFolder = parentId > 0 ? FolderManager.Instance.GetFolder(parentId) : null; + } - while (parentFolder != null) - { - // load all sibling - var siblingFolders = this.GetFolderDescendants(parentFolder, string.Empty, permission) - .Select(folderInfo => new ItemDto - { - Key = folderInfo.FolderID.ToString(CultureInfo.InvariantCulture), - Value = folderInfo.FolderName, - HasChildren = this.HasChildren(folderInfo, permission), - Selectable = true, - }).ToList(); - siblingFolders = ApplySort(siblingFolders, sortOrder).ToList(); + var childrenFolders = this.GetFolderDescendants(parentFolder, searchText, permission); - var siblingFoldersTree = siblingFolders.Select(f => new NTree { Data = f }).ToList(); + var folders = childrenFolders.Select(folder => new ItemDto + { + Key = folder.FolderID.ToString(CultureInfo.InvariantCulture), + Value = folder.FolderName, + HasChildren = this.HasChildren(folder, permission), + Selectable = true, + }); - // attach the tree - if (selfTree.Children != null) - { - foreach (var node in siblingFoldersTree) - { - if (node.Data.Key == selfTree.Data.Key) - { - node.Children = selfTree.Children; - break; - } - } - } + return ApplySort(folders, sortOrder); + } - selfTree = new NTree - { - Data = new ItemDto - { - Key = parentId.ToString(CultureInfo.InvariantCulture), - Value = parentFolder.FolderName, - HasChildren = true, - Selectable = true, - }, - Children = siblingFoldersTree, - }; + private NTree SearchFoldersInternal(int portalId, string searchText, int sortOrder, string permission) + { + var tree = new NTree { Data = new ItemDto { Key = RootKey } }; - parentId = parentFolder.ParentID; - parentFolder = parentId > 0 ? FolderManager.Instance.GetFolder(parentId) : null; + if (portalId > -1) + { + if (!this.IsPortalIdValid(portalId)) + { + return tree; } + } + else + { + portalId = this.GetActivePortalId(); + } + + var allFolders = this.GetPortalFolders(portalId, searchText, permission); + var folders = allFolders.Select(f => new ItemDto + { + Key = f.FolderID.ToString(CultureInfo.InvariantCulture), + Value = f.FolderName, + HasChildren = false, + Selectable = true, + }); + tree.Children = ApplySort(folders, sortOrder).Select(dto => new NTree { Data = dto }).ToList(); + return tree; + } - selfTree.Data.Value = DynamicSharedConstants.RootFolder; + private NTree GetTreePathForFolderInternal(string selectedItemId, int sortOrder, string permission) + { + var tree = new NTree { Data = new ItemDto { Key = RootKey } }; - tree.Children.Add(selfTree); + int itemId; + if (string.IsNullOrEmpty(selectedItemId) || !int.TryParse(selectedItemId, out itemId)) + { return tree; } - private bool HasPermission(IFolderInfo folder, string permissionKey) + if (itemId <= 0) { - var hasPermission = this.PortalSettings.UserInfo.IsSuperUser; - - if (!hasPermission && folder != null) - { - hasPermission = FolderPermissionController.HasFolderPermission(folder.FolderPermissions, permissionKey); - } - - return hasPermission; + return tree; } - private IEnumerable GetFolderDescendants(IFolderInfo parentFolder, string searchText, string permission) + var folder = FolderManager.Instance.GetFolder(itemId); + if (folder == null) { - Func searchFunc; - if (string.IsNullOrEmpty(searchText)) - { - searchFunc = _ => true; - } - else - { - searchFunc = folder => folder.FolderName.IndexOf(searchText, StringComparison.InvariantCultureIgnoreCase) > -1; - } + return tree; + } - return FolderManager.Instance.GetFolders(parentFolder).Where(folder => - (string.IsNullOrEmpty(permission) ? - this.HasPermission(folder, "BROWSE") || this.HasPermission(folder, "READ") : - this.HasPermission(folder, permission)) && searchFunc(folder)); + var hasPermission = string.IsNullOrEmpty(permission) ? + (this.HasPermission(folder, "BROWSE") || this.HasPermission(folder, "READ")) : + this.HasPermission(folder, permission); + if (!hasPermission) + { + return new NTree(); } - private IEnumerable GetPortalFolders(int portalId, string searchText, string permission) + var selfTree = new NTree { - if (portalId == -1) + Data = new ItemDto { - portalId = this.GetActivePortalId(); - } + Key = folder.FolderID.ToString(CultureInfo.InvariantCulture), + Value = folder.FolderName, + HasChildren = this.HasChildren(folder, permission), + Selectable = true, + }, + }; + var parentId = folder.ParentID; + var parentFolder = parentId > 0 ? FolderManager.Instance.GetFolder(parentId) : null; - Func searchFunc; - if (string.IsNullOrEmpty(searchText)) + while (parentFolder != null) + { + // load all sibling + var siblingFolders = this.GetFolderDescendants(parentFolder, string.Empty, permission) + .Select(folderInfo => new ItemDto + { + Key = folderInfo.FolderID.ToString(CultureInfo.InvariantCulture), + Value = folderInfo.FolderName, + HasChildren = this.HasChildren(folderInfo, permission), + Selectable = true, + }).ToList(); + siblingFolders = ApplySort(siblingFolders, sortOrder).ToList(); + + var siblingFoldersTree = siblingFolders.Select(f => new NTree { Data = f }).ToList(); + + // attach the tree + if (selfTree.Children != null) { - searchFunc = _ => true; + foreach (var node in siblingFoldersTree) + { + if (node.Data.Key == selfTree.Data.Key) + { + node.Children = selfTree.Children; + break; + } + } } - else + + selfTree = new NTree { - searchFunc = folder => folder.FolderName.IndexOf(searchText, StringComparison.InvariantCultureIgnoreCase) > -1; - } + Data = new ItemDto + { + Key = parentId.ToString(CultureInfo.InvariantCulture), + Value = parentFolder.FolderName, + HasChildren = true, + Selectable = true, + }, + Children = siblingFoldersTree, + }; - return FolderManager.Instance.GetFolders(portalId).Where(folder => - (string.IsNullOrEmpty(permission) ? - this.HasPermission(folder, "BROWSE") || this.HasPermission(folder, "READ") : - this.HasPermission(folder, permission)) && searchFunc(folder)); + parentId = parentFolder.ParentID; + parentFolder = parentId > 0 ? FolderManager.Instance.GetFolder(parentId) : null; } - private bool HasChildren(IFolderInfo parentFolder, string permission) + selfTree.Data.Value = DynamicSharedConstants.RootFolder; + + tree.Children.Add(selfTree); + return tree; + } + + private bool HasPermission(IFolderInfo folder, string permissionKey) + { + var hasPermission = this.PortalSettings.UserInfo.IsSuperUser; + + if (!hasPermission && folder != null) { - return FolderManager.Instance.GetFolders(parentFolder).Any(folder => - string.IsNullOrEmpty(permission) ? - this.HasPermission(folder, "BROWSE") || this.HasPermission(folder, "READ") : - this.HasPermission(folder, permission)); + hasPermission = FolderPermissionController.HasFolderPermission(folder.FolderPermissions, permissionKey); } - private NTree GetFilesInternal(int portalId, int parentId, string filter, string searchText, int sortOrder, string permissions) + return hasPermission; + } + + private IEnumerable GetFolderDescendants(IFolderInfo parentFolder, string searchText, string permission) + { + Func searchFunc; + if (string.IsNullOrEmpty(searchText)) { - var tree = new NTree { Data = new ItemDto { Key = RootKey } }; - var children = this.GetFileItemsDto(portalId, parentId, filter, searchText, permissions, sortOrder).Select(dto => new NTree { Data = dto }).ToList(); - tree.Children = children; - return tree; + searchFunc = _ => true; + } + else + { + searchFunc = folder => folder.FolderName.IndexOf(searchText, StringComparison.InvariantCultureIgnoreCase) > -1; } - private NTree SortFilesInternal(int portalId, int parentId, string filter, int sortOrder, string permissions) + return FolderManager.Instance.GetFolders(parentFolder).Where(folder => + (string.IsNullOrEmpty(permission) ? + this.HasPermission(folder, "BROWSE") || this.HasPermission(folder, "READ") : + this.HasPermission(folder, permission)) && searchFunc(folder)); + } + + private IEnumerable GetPortalFolders(int portalId, string searchText, string permission) + { + if (portalId == -1) { - var sortedTree = new NTree { Data = new ItemDto { Key = RootKey } }; - var children = this.GetFileItemsDto(portalId, parentId, filter, string.Empty, permissions, sortOrder).Select(dto => new NTree { Data = dto }).ToList(); - sortedTree.Children = children; - return sortedTree; + portalId = this.GetActivePortalId(); } - private IEnumerable GetFileItemsDto(int portalId, int parentId, string filter, string searchText, string permission, int sortOrder) + Func searchFunc; + if (string.IsNullOrEmpty(searchText)) { - if (portalId > -1) - { - if (!this.IsPortalIdValid(portalId)) - { - return new List(); - } - } - else - { - portalId = this.GetActivePortalId(); - } + searchFunc = _ => true; + } + else + { + searchFunc = folder => folder.FolderName.IndexOf(searchText, StringComparison.InvariantCultureIgnoreCase) > -1; + } - var parentFolder = parentId > -1 ? FolderManager.Instance.GetFolder(parentId) : FolderManager.Instance.GetFolder(portalId, string.Empty); + return FolderManager.Instance.GetFolders(portalId).Where(folder => + (string.IsNullOrEmpty(permission) ? + this.HasPermission(folder, "BROWSE") || this.HasPermission(folder, "READ") : + this.HasPermission(folder, permission)) && searchFunc(folder)); + } - if (parentFolder == null) - { - return new List(); - } + private bool HasChildren(IFolderInfo parentFolder, string permission) + { + return FolderManager.Instance.GetFolders(parentFolder).Any(folder => + string.IsNullOrEmpty(permission) ? + this.HasPermission(folder, "BROWSE") || this.HasPermission(folder, "READ") : + this.HasPermission(folder, permission)); + } - var hasPermission = string.IsNullOrEmpty(permission) ? - this.HasPermission(parentFolder, "BROWSE") || this.HasPermission(parentFolder, "READ") : - this.HasPermission(parentFolder, permission); - if (!hasPermission) - { - return new List(); - } + private NTree GetFilesInternal(int portalId, int parentId, string filter, string searchText, int sortOrder, string permissions) + { + var tree = new NTree { Data = new ItemDto { Key = RootKey } }; + var children = this.GetFileItemsDto(portalId, parentId, filter, searchText, permissions, sortOrder).Select(dto => new NTree { Data = dto }).ToList(); + tree.Children = children; + return tree; + } + + private NTree SortFilesInternal(int portalId, int parentId, string filter, int sortOrder, string permissions) + { + var sortedTree = new NTree { Data = new ItemDto { Key = RootKey } }; + var children = this.GetFileItemsDto(portalId, parentId, filter, string.Empty, permissions, sortOrder).Select(dto => new NTree { Data = dto }).ToList(); + sortedTree.Children = children; + return sortedTree; + } - if (parentId < 1) + private IEnumerable GetFileItemsDto(int portalId, int parentId, string filter, string searchText, string permission, int sortOrder) + { + if (portalId > -1) + { + if (!this.IsPortalIdValid(portalId)) { return new List(); } + } + else + { + portalId = this.GetActivePortalId(); + } - var files = GetFiles(parentFolder, filter, searchText); - - var filesDto = files.Select(f => new ItemDto - { - Key = f.FileId.ToString(CultureInfo.InvariantCulture), - Value = f.FileName, - HasChildren = false, - Selectable = true, - }).ToList(); - - var sortedList = ApplySort(filesDto, sortOrder); + var parentFolder = parentId > -1 ? FolderManager.Instance.GetFolder(parentId) : FolderManager.Instance.GetFolder(portalId, string.Empty); - return sortedList; + if (parentFolder == null) + { + return new List(); } - private bool IsPortalIdValid(int portalId) + var hasPermission = string.IsNullOrEmpty(permission) ? + this.HasPermission(parentFolder, "BROWSE") || this.HasPermission(parentFolder, "READ") : + this.HasPermission(parentFolder, permission); + if (!hasPermission) { - if (this.UserInfo.IsSuperUser) - { - return true; - } - - if (this.PortalSettings.PortalId == portalId) - { - return true; - } - - var isAdminUser = PortalSecurity.IsInRole(this.PortalSettings.AdministratorRoleName); - if (!isAdminUser) - { - return false; - } + return new List(); + } - var mygroup = this.GetMyPortalGroup(); - return mygroup != null && mygroup.Any(p => p.PortalId == portalId); + if (parentId < 1) + { + return new List(); } - private int GetActivePortalId(int pageId) + var files = GetFiles(parentFolder, filter, searchText); + + var filesDto = files.Select(f => new ItemDto { - var page = TabController.Instance.GetTab(pageId, Null.NullInteger, false); - var portalId = page.PortalID; + Key = f.FileId.ToString(CultureInfo.InvariantCulture), + Value = f.FileName, + HasChildren = false, + Selectable = true, + }).ToList(); - if (portalId == Null.NullInteger) - { - portalId = this.GetActivePortalId(); - } + var sortedList = ApplySort(filesDto, sortOrder); - return portalId; - } + return sortedList; + } - private int GetActivePortalId() + private bool IsPortalIdValid(int portalId) + { + if (this.UserInfo.IsSuperUser) { - var portalId = -1; - if (!TabController.CurrentPage.IsSuperTab) - { - portalId = this.PortalSettings.PortalId; - } + return true; + } - return portalId; + if (this.PortalSettings.PortalId == portalId) + { + return true; } - /// A data transfer object with information about an item in a list. - [DataContract] - public class ItemDto + var isAdminUser = PortalSecurity.IsInRole(this.PortalSettings.AdministratorRoleName); + if (!isAdminUser) { - /// Gets or sets the key. - [DataMember(Name = "key")] - public string Key { get; set; } + return false; + } - /// Gets or sets the value. - [DataMember(Name = "value")] - public string Value { get; set; } + var mygroup = this.GetMyPortalGroup(); + return mygroup != null && mygroup.Any(p => p.PortalId == portalId); + } - /// Gets or sets a value indicating whether this item has children. - [DataMember(Name = "hasChildren")] - public bool HasChildren { get; set; } + private int GetActivePortalId(int pageId) + { + var page = TabController.Instance.GetTab(pageId, Null.NullInteger, false); + var portalId = page.PortalID; - /// Gets or sets a value indicating whether this item is selectable. - [DataMember(Name = "selectable")] - public bool Selectable { get; set; } + if (portalId == Null.NullInteger) + { + portalId = this.GetActivePortalId(); } - /// A data transfer object with information about an item with an ID. - [DataContract] - public class ItemIdDto + return portalId; + } + + private int GetActivePortalId() + { + var portalId = -1; + if (!TabController.CurrentPage.IsSuperTab) { - /// Gets or sets the ID. - [DataMember(Name = "id")] - public string Id { get; set; } + portalId = this.PortalSettings.PortalId; } + + return portalId; + } + + /// A data transfer object with information about an item in a list. + [DataContract] + public class ItemDto + { + /// Gets or sets the key. + [DataMember(Name = "key")] + public string Key { get; set; } + + /// Gets or sets the value. + [DataMember(Name = "value")] + public string Value { get; set; } + + /// Gets or sets a value indicating whether this item has children. + [DataMember(Name = "hasChildren")] + public bool HasChildren { get; set; } + + /// Gets or sets a value indicating whether this item is selectable. + [DataMember(Name = "selectable")] + public bool Selectable { get; set; } + } + + /// A data transfer object with information about an item with an ID. + [DataContract] + public class ItemIdDto + { + /// Gets or sets the ID. + [DataMember(Name = "id")] + public string Id { get; set; } } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/LanguageServiceController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/LanguageServiceController.cs index c13b95f964f..09e34f597b5 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/LanguageServiceController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/LanguageServiceController.cs @@ -2,97 +2,96 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Web; - using System.Web.Http; +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web; +using System.Web.Http; + +using DotNetNuke.Abstractions; +using DotNetNuke.Common; +using DotNetNuke.Entities.Tabs; +using DotNetNuke.Services.Localization; +using DotNetNuke.Web.Api; - using DotNetNuke.Abstractions; - using DotNetNuke.Common; - using DotNetNuke.Entities.Tabs; - using DotNetNuke.Services.Localization; - using DotNetNuke.Web.Api; +using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.DependencyInjection; +/// A web API for getting the translation status of a site. +[DnnAuthorize] +public class LanguageServiceController : DnnApiController +{ + private readonly ILocaleController localeController; + private readonly ITabController tabController; - /// A web API for getting the translation status of a site. - [DnnAuthorize] - public class LanguageServiceController : DnnApiController + /// Initializes a new instance of the class. + /// The navigation manager. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IPortalAliasService. Scheduled removal in v12.0.0.")] + public LanguageServiceController(INavigationManager navigationManager) + : this(navigationManager, null, null) { - private readonly ILocaleController localeController; - private readonly ITabController tabController; + this.NavigationManager = navigationManager; + } - /// Initializes a new instance of the class. - /// The navigation manager. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IPortalAliasService. Scheduled removal in v12.0.0.")] - public LanguageServiceController(INavigationManager navigationManager) - : this(navigationManager, null, null) - { - this.NavigationManager = navigationManager; - } + /// Initializes a new instance of the class. + /// The navigation manager. + /// The locale controller. + /// The tab controller. + public LanguageServiceController(INavigationManager navigationManager, ILocaleController localeController, ITabController tabController) + { + this.NavigationManager = navigationManager ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.localeController = localeController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.tabController = tabController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + } - /// Initializes a new instance of the class. - /// The navigation manager. - /// The locale controller. - /// The tab controller. - public LanguageServiceController(INavigationManager navigationManager, ILocaleController localeController, ITabController tabController) - { - this.NavigationManager = navigationManager ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.localeController = localeController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.tabController = tabController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - } + /// Gets the navigation manager. + protected INavigationManager NavigationManager { get; } - /// Gets the navigation manager. - protected INavigationManager NavigationManager { get; } + /// Gets the pages which aren't translated for the . + /// The language code. + /// A response with a list of instances. + [HttpGet] + public HttpResponseMessage GetNonTranslatedPages(string languageCode) + { + var request = HttpContext.Current.Request; + var locale = this.localeController.GetLocale(languageCode); - /// Gets the pages which aren't translated for the . - /// The language code. - /// A response with a list of instances. - [HttpGet] - public HttpResponseMessage GetNonTranslatedPages(string languageCode) + List pages = []; + if (!this.IsDefaultLanguage(locale.Code)) { - var request = HttpContext.Current.Request; - var locale = this.localeController.GetLocale(languageCode); - - List pages = []; - if (!this.IsDefaultLanguage(locale.Code)) + var nonTranslated = from t in this.tabController.GetTabsByPortal(this.PortalSettings.PortalId).WithCulture(locale.Code, false).Values where !t.IsTranslated && !t.IsDeleted select t; + foreach (TabInfo page in nonTranslated) { - var nonTranslated = from t in this.tabController.GetTabsByPortal(this.PortalSettings.PortalId).WithCulture(locale.Code, false).Values where !t.IsTranslated && !t.IsDeleted select t; - foreach (TabInfo page in nonTranslated) + pages.Add(new PageDto() { - pages.Add(new PageDto() - { - Name = page.TabName, - ViewUrl = this.NavigationManager.NavigateURL(page.TabID), - EditUrl = this.NavigationManager.NavigateURL(page.TabID, "Tab", "action=edit", "returntabid=" + this.PortalSettings.ActiveTab.TabID), - }); - } + Name = page.TabName, + ViewUrl = this.NavigationManager.NavigateURL(page.TabID), + EditUrl = this.NavigationManager.NavigateURL(page.TabID, "Tab", "action=edit", "returntabid=" + this.PortalSettings.ActiveTab.TabID), + }); } - - return this.Request.CreateResponse(HttpStatusCode.OK, pages); } - private bool IsDefaultLanguage(string code) - { - return code == this.PortalSettings.DefaultLanguage; - } + return this.Request.CreateResponse(HttpStatusCode.OK, pages); + } - /// A data transfer object with information about a page. - public class PageDto - { - /// Gets or sets the page's name. - public string Name { get; set; } + private bool IsDefaultLanguage(string code) + { + return code == this.PortalSettings.DefaultLanguage; + } - /// Gets or sets the page's view URL. - public string ViewUrl { get; set; } + /// A data transfer object with information about a page. + public class PageDto + { + /// Gets or sets the page's name. + public string Name { get; set; } - /// Gets or sets the page's edit URL. - public string EditUrl { get; set; } - } + /// Gets or sets the page's view URL. + public string ViewUrl { get; set; } + + /// Gets or sets the page's edit URL. + public string EditUrl { get; set; } } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/MessagingServiceController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/MessagingServiceController.cs index d33ac14b8f8..d18acef99a0 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/MessagingServiceController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/MessagingServiceController.cs @@ -2,192 +2,191 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web; +using System.Web.Http; + +using DotNetNuke.Abstractions.Application; +using DotNetNuke.Common; +using DotNetNuke.Common.Internal; +using DotNetNuke.Common.Utilities; +using DotNetNuke.Entities.Portals; +using DotNetNuke.Entities.Users; +using DotNetNuke.Instrumentation; +using DotNetNuke.Security; +using DotNetNuke.Security.Roles; +using DotNetNuke.Services.Social.Messaging; +using DotNetNuke.Services.Social.Messaging.Internal; +using DotNetNuke.Web.Api; + +using Microsoft.Extensions.DependencyInjection; + +/// A web API for messaging. +[DnnAuthorize] +public class MessagingServiceController : DnnApiController { - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Web; - using System.Web.Http; - - using DotNetNuke.Abstractions.Application; - using DotNetNuke.Common; - using DotNetNuke.Common.Internal; - using DotNetNuke.Common.Utilities; - using DotNetNuke.Entities.Portals; - using DotNetNuke.Entities.Users; - using DotNetNuke.Instrumentation; - using DotNetNuke.Security; - using DotNetNuke.Security.Roles; - using DotNetNuke.Services.Social.Messaging; - using DotNetNuke.Services.Social.Messaging.Internal; - using DotNetNuke.Web.Api; - - using Microsoft.Extensions.DependencyInjection; - - /// A web API for messaging. - [DnnAuthorize] - public class MessagingServiceController : DnnApiController + private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(MessagingServiceController)); + private readonly IPortalController portalController; + private readonly IApplicationStatusInfo appStatus; + private readonly IPortalGroupController portalGroupController; + + /// Initializes a new instance of the class. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IPortalController. Scheduled removal in v12.0.0.")] + public MessagingServiceController() + : this(null, null, null) { - private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(MessagingServiceController)); - private readonly IPortalController portalController; - private readonly IApplicationStatusInfo appStatus; - private readonly IPortalGroupController portalGroupController; - - /// Initializes a new instance of the class. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IPortalController. Scheduled removal in v12.0.0.")] - public MessagingServiceController() - : this(null, null, null) - { - } + } - /// Initializes a new instance of the class. - /// The portal controller. - /// The application status. - /// The portal group controller. - public MessagingServiceController(IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController) + /// Initializes a new instance of the class. + /// The portal controller. + /// The application status. + /// The portal group controller. + public MessagingServiceController(IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController) + { + this.portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.appStatus = appStatus ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.portalGroupController = portalGroupController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + } + + /// Gets how long a user needs to wait before being allowed to send a message. + /// A response with an object containing a Value field with the number of seconds. + [HttpGet] + public HttpResponseMessage WaitTimeForNextMessage() + { + try { - this.portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.appStatus = appStatus ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.portalGroupController = portalGroupController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", Value = InternalMessagingController.Instance.WaitTimeForNextMessage(this.UserInfo) }); } - - /// Gets how long a user needs to wait before being allowed to send a message. - /// A response with an object containing a Value field with the number of seconds. - [HttpGet] - public HttpResponseMessage WaitTimeForNextMessage() + catch (Exception exc) { - try - { - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", Value = InternalMessagingController.Instance.WaitTimeForNextMessage(this.UserInfo) }); - } - catch (Exception exc) - { - Logger.Error(exc); - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc); - } + Logger.Error(exc); + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc); } + } - /// Creates a message. - /// Information about the new message. - /// A response with an object containing the ID of the new message. - [ValidateAntiForgeryToken] - [HttpPost] - public HttpResponseMessage Create(CreateDTO postData) + /// Creates a message. + /// Information about the new message. + /// A response with an object containing the ID of the new message. + [ValidateAntiForgeryToken] + [HttpPost] + public HttpResponseMessage Create(CreateDTO postData) + { + try { - try - { - var portalId = PortalController.GetEffectivePortalId(this.portalController, this.appStatus, this.portalGroupController, this.PortalSettings.PortalId); - var roleIdsList = string.IsNullOrEmpty(postData.RoleIds) ? null : postData.RoleIds.FromJson>(); - var userIdsList = string.IsNullOrEmpty(postData.UserIds) ? null : postData.UserIds.FromJson>(); - var fileIdsList = string.IsNullOrEmpty(postData.FileIds) ? null : postData.FileIds.FromJson>(); + var portalId = PortalController.GetEffectivePortalId(this.portalController, this.appStatus, this.portalGroupController, this.PortalSettings.PortalId); + var roleIdsList = string.IsNullOrEmpty(postData.RoleIds) ? null : postData.RoleIds.FromJson>(); + var userIdsList = string.IsNullOrEmpty(postData.UserIds) ? null : postData.UserIds.FromJson>(); + var fileIdsList = string.IsNullOrEmpty(postData.FileIds) ? null : postData.FileIds.FromJson>(); - var roles = roleIdsList is { Count: > 0, } - ? roleIdsList.Select(id => RoleController.Instance.GetRole(portalId, r => r.RoleID == id)).Where(role => role != null).ToList() - : null; + var roles = roleIdsList is { Count: > 0, } + ? roleIdsList.Select(id => RoleController.Instance.GetRole(portalId, r => r.RoleID == id)).Where(role => role != null).ToList() + : null; - List users = null; - if (userIdsList != null) - { - users = userIdsList.Select(id => UserController.Instance.GetUser(portalId, id)).Where(user => user != null).ToList(); - } + List users = null; + if (userIdsList != null) + { + users = userIdsList.Select(id => UserController.Instance.GetUser(portalId, id)).Where(user => user != null).ToList(); + } - var body = HttpUtility.UrlDecode(postData.Body); + var body = HttpUtility.UrlDecode(postData.Body); #pragma warning disable CS0618 // Type or member is obsolete - body = PortalSecurity.Instance.InputFilter(body, PortalSecurity.FilterFlag.NoMarkup); + body = PortalSecurity.Instance.InputFilter(body, PortalSecurity.FilterFlag.NoMarkup); #pragma warning restore CS0618 // Type or member is obsolete - var message = new Message { Subject = HttpUtility.UrlDecode(postData.Subject), Body = body, }; - MessagingController.Instance.SendMessage(message, roles, users, fileIdsList); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", Value = message.MessageID }); - } - catch (Exception exc) - { - Logger.Error(exc); - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc); - } + var message = new Message { Subject = HttpUtility.UrlDecode(postData.Subject), Body = body, }; + MessagingController.Instance.SendMessage(message, roles, users, fileIdsList); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", Value = message.MessageID }); + } + catch (Exception exc) + { + Logger.Error(exc); + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc); } + } - /// Searches users and roles. - /// The search query. - /// A response with a list of results (containing id, name, and iconfile fields) or null if there was no search query. - [HttpGet] - public HttpResponseMessage Search(string q) + /// Searches users and roles. + /// The search query. + /// A response with a list of results (containing id, name, and iconfile fields) or null if there was no search query. + [HttpGet] + public HttpResponseMessage Search(string q) + { + try { - try - { - var portalId = PortalController.GetEffectivePortalId(this.portalController, this.appStatus, this.portalGroupController, this.PortalSettings.PortalId); - var isAdmin = this.UserInfo.IsSuperUser || this.UserInfo.IsInRole("Administrators"); - const int numResults = 10; + var portalId = PortalController.GetEffectivePortalId(this.portalController, this.appStatus, this.portalGroupController, this.PortalSettings.PortalId); + var isAdmin = this.UserInfo.IsSuperUser || this.UserInfo.IsInRole("Administrators"); + const int numResults = 10; - // GetUsersAdvancedSearch doesn't accept a comma or a single quote in the query so we have to remove them for now. See issue 20224. - q = q.Replace(",", string.Empty).Replace("'", string.Empty); - if (q.Length == 0) - { - return this.Request.CreateResponse(HttpStatusCode.OK, null); - } - - var results = UserController.Instance.GetUsersBasicSearch(portalId, 0, numResults, "DisplayName", true, "DisplayName", q) - .Select(user => new - { - id = "user-" + user.UserID, - name = user.DisplayName, - iconfile = UserController.Instance.GetUserProfilePictureUrl(user.UserID, 32, 32), - }).ToList(); - - // Roles should be visible to Administrators or User in the Role. - var roles = RoleController.Instance.GetRolesBasicSearch(portalId, numResults, q); - results.AddRange(from roleInfo in roles - where - isAdmin || - this.UserInfo.Social.Roles.SingleOrDefault(ur => ur.RoleID == roleInfo.RoleID && ur.IsOwner) != null - select new - { - id = "role-" + roleInfo.RoleID, - name = roleInfo.RoleName, - iconfile = TestableGlobals.Instance.ResolveUrl(string.IsNullOrEmpty(roleInfo.IconFile) - ? "~/images/no_avatar.gif" - : this.PortalSettings.HomeDirectory.TrimEnd('/') + "/" + roleInfo.IconFile), - }); - - return this.Request.CreateResponse(HttpStatusCode.OK, results.OrderBy(sr => sr.name)); - } - catch (Exception exc) + // GetUsersAdvancedSearch doesn't accept a comma or a single quote in the query so we have to remove them for now. See issue 20224. + q = q.Replace(",", string.Empty).Replace("'", string.Empty); + if (q.Length == 0) { - Logger.Error(exc); - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc); + return this.Request.CreateResponse(HttpStatusCode.OK, null); } - } - /// A data transfer object with information to create a message. - public class CreateDTO + var results = UserController.Instance.GetUsersBasicSearch(portalId, 0, numResults, "DisplayName", true, "DisplayName", q) + .Select(user => new + { + id = "user-" + user.UserID, + name = user.DisplayName, + iconfile = UserController.Instance.GetUserProfilePictureUrl(user.UserID, 32, 32), + }).ToList(); + + // Roles should be visible to Administrators or User in the Role. + var roles = RoleController.Instance.GetRolesBasicSearch(portalId, numResults, q); + results.AddRange(from roleInfo in roles + where + isAdmin || + this.UserInfo.Social.Roles.SingleOrDefault(ur => ur.RoleID == roleInfo.RoleID && ur.IsOwner) != null + select new + { + id = "role-" + roleInfo.RoleID, + name = roleInfo.RoleName, + iconfile = TestableGlobals.Instance.ResolveUrl(string.IsNullOrEmpty(roleInfo.IconFile) + ? "~/images/no_avatar.gif" + : this.PortalSettings.HomeDirectory.TrimEnd('/') + "/" + roleInfo.IconFile), + }); + + return this.Request.CreateResponse(HttpStatusCode.OK, results.OrderBy(sr => sr.name)); + } + catch (Exception exc) { - /// Gets or sets the message subject. - [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Breaking change")] - [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] - public string Subject; - - /// Gets or sets the message body. - [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Breaking change")] - [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] - public string Body; - - /// Gets or sets the IDs of the roles which are recipients of the message. - [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Breaking change")] - [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] - public string RoleIds; - - /// Gets or sets the IDs of the users which are recipients of the message. - [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Breaking change")] - [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] - public string UserIds; - - /// Gets or sets the IDs of the files which are attached to the message. - [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Breaking change")] - [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] - public string FileIds; + Logger.Error(exc); + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc); } } + + /// A data transfer object with information to create a message. + public class CreateDTO + { + /// Gets or sets the message subject. + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Breaking change")] + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] + public string Subject; + + /// Gets or sets the message body. + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Breaking change")] + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] + public string Body; + + /// Gets or sets the IDs of the roles which are recipients of the message. + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Breaking change")] + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] + public string RoleIds; + + /// Gets or sets the IDs of the users which are recipients of the message. + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Breaking change")] + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] + public string UserIds; + + /// Gets or sets the IDs of the files which are attached to the message. + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Breaking change")] + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Breaking change")] + public string FileIds; + } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/ModuleServiceController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/ModuleServiceController.cs index 07c3bacd439..fdd5d804c62 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/ModuleServiceController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/ModuleServiceController.cs @@ -2,173 +2,172 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web.Http; + +using DotNetNuke.Abstractions.Application; +using DotNetNuke.Abstractions.Portals; +using DotNetNuke.Common; +using DotNetNuke.Data; +using DotNetNuke.Entities.Modules; +using DotNetNuke.Entities.Portals; +using DotNetNuke.Instrumentation; +using DotNetNuke.Web.Api; +using DotNetNuke.Web.Api.Internal; + +using Microsoft.Extensions.DependencyInjection; + +/// A web API controller for module information. +[DnnAuthorize] +public class ModuleServiceController : DnnApiController { - using System; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Web.Http; - - using DotNetNuke.Abstractions.Application; - using DotNetNuke.Abstractions.Portals; - using DotNetNuke.Common; - using DotNetNuke.Data; - using DotNetNuke.Entities.Modules; - using DotNetNuke.Entities.Portals; - using DotNetNuke.Instrumentation; - using DotNetNuke.Web.Api; - using DotNetNuke.Web.Api.Internal; - - using Microsoft.Extensions.DependencyInjection; - - /// A web API controller for module information. - [DnnAuthorize] - public class ModuleServiceController : DnnApiController + private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(ModuleServiceController)); + private readonly IHostSettings hostSettings; + private readonly DataProvider dataProvider; + + /// Initializes a new instance of the class. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] + public ModuleServiceController() + : this(null, null) { - private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(ModuleServiceController)); - private readonly IHostSettings hostSettings; - private readonly DataProvider dataProvider; - - /// Initializes a new instance of the class. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] - public ModuleServiceController() - : this(null, null) + } + + /// Initializes a new instance of the class. + /// The host settings. + /// The data provider. + public ModuleServiceController(IHostSettings hostSettings, DataProvider dataProvider) + { + this.hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.dataProvider = dataProvider ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + } + + /// Gets a value determining whether a module is shareable. + /// The module ID. + /// The tab ID. + /// The portal ID. + /// A response with an object containing Shareable and RequiredWarning fields. + [HttpGet] + [DnnAuthorize(StaticRoles = "Registered Users")] + public HttpResponseMessage GetModuleShareable(int moduleId, int tabId, int portalId = -1) + { + var requiresWarning = false; + if (portalId <= -1) { + var portalDict = PortalController.GetPortalDictionary(this.hostSettings, this.dataProvider); + portalId = portalDict[tabId]; } - - /// Initializes a new instance of the class. - /// The host settings. - /// The data provider. - public ModuleServiceController(IHostSettings hostSettings, DataProvider dataProvider) + else { - this.hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.dataProvider = dataProvider ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + portalId = this.FixPortalId(portalId); } - /// Gets a value determining whether a module is shareable. - /// The module ID. - /// The tab ID. - /// The portal ID. - /// A response with an object containing Shareable and RequiredWarning fields. - [HttpGet] - [DnnAuthorize(StaticRoles = "Registered Users")] - public HttpResponseMessage GetModuleShareable(int moduleId, int tabId, int portalId = -1) + DesktopModuleInfo desktopModule; + if (tabId < 0) { - var requiresWarning = false; - if (portalId <= -1) - { - var portalDict = PortalController.GetPortalDictionary(this.hostSettings, this.dataProvider); - portalId = portalDict[tabId]; - } - else - { - portalId = this.FixPortalId(portalId); - } - - DesktopModuleInfo desktopModule; - if (tabId < 0) - { - desktopModule = DesktopModuleController.GetDesktopModule(this.hostSettings, moduleId, portalId); - } - else - { - var moduleInfo = ModuleController.Instance.GetModule(moduleId, tabId, false); - - desktopModule = moduleInfo.DesktopModule; + desktopModule = DesktopModuleController.GetDesktopModule(this.hostSettings, moduleId, portalId); + } + else + { + var moduleInfo = ModuleController.Instance.GetModule(moduleId, tabId, false); - requiresWarning = moduleInfo.PortalID != this.PortalSettings.PortalId && desktopModule.Shareable == ModuleSharing.Unknown; - } + desktopModule = moduleInfo.DesktopModule; - if (desktopModule == null) - { - var message = $"Cannot find module ID {moduleId} (tab ID {tabId}, portal ID {portalId})"; - Logger.Error(message); - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, message); - } + requiresWarning = moduleInfo.PortalID != this.PortalSettings.PortalId && desktopModule.Shareable == ModuleSharing.Unknown; + } - return this.Request.CreateResponse(HttpStatusCode.OK, new { Shareable = desktopModule.Shareable.ToString(), RequiresWarning = requiresWarning }); + if (desktopModule == null) + { + var message = $"Cannot find module ID {moduleId} (tab ID {tabId}, portal ID {portalId})"; + Logger.Error(message); + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, message); } - /// Moves a module. - /// Information about the move request. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - [DnnPageEditor] - public HttpResponseMessage MoveModule(MoveModuleDTO postData) + return this.Request.CreateResponse(HttpStatusCode.OK, new { Shareable = desktopModule.Shareable.ToString(), RequiresWarning = requiresWarning }); + } + + /// Moves a module. + /// Information about the move request. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnPageEditor] + public HttpResponseMessage MoveModule(MoveModuleDTO postData) + { + var moduleOrder = postData.ModuleOrder; + if (moduleOrder > 0) { - var moduleOrder = postData.ModuleOrder; - if (moduleOrder > 0) + // DNN-7099: the deleted modules won't show in page, so when the module index calculated from client, it will lost the + // index count of deleted modules and will cause order issue. + var deletedModules = ModuleController.Instance.GetTabModules(postData.TabId).Values.Where(m => m.IsDeleted); + foreach (var module in deletedModules) { - // DNN-7099: the deleted modules won't show in page, so when the module index calculated from client, it will lost the - // index count of deleted modules and will cause order issue. - var deletedModules = ModuleController.Instance.GetTabModules(postData.TabId).Values.Where(m => m.IsDeleted); - foreach (var module in deletedModules) + if (module.ModuleOrder < moduleOrder && module.PaneName == postData.Pane) { - if (module.ModuleOrder < moduleOrder && module.PaneName == postData.Pane) - { - moduleOrder += 2; - } + moduleOrder += 2; } } + } - ModuleController.Instance.UpdateModuleOrder(postData.TabId, postData.ModuleId, moduleOrder, postData.Pane); - ModuleController.Instance.UpdateTabModuleOrder(postData.TabId); + ModuleController.Instance.UpdateModuleOrder(postData.TabId, postData.ModuleId, moduleOrder, postData.Pane); + ModuleController.Instance.UpdateTabModuleOrder(postData.TabId); - return this.Request.CreateResponse(HttpStatusCode.OK); - } + return this.Request.CreateResponse(HttpStatusCode.OK); + } - /// Web method that deletes a tab module. - /// This has been introduced for integration testing purposes. - /// delete module dto. - /// Http response message. - [HttpPost] - [ValidateAntiForgeryToken] - [DnnAuthorize(StaticRoles = "Administrators")] - public HttpResponseMessage DeleteModule(DeleteModuleDto deleteModuleDto) - { - ModuleController.Instance.DeleteTabModule(deleteModuleDto.TabId, deleteModuleDto.ModuleId, deleteModuleDto.SoftDelete); + /// Web method that deletes a tab module. + /// This has been introduced for integration testing purposes. + /// delete module dto. + /// Http response message. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnAuthorize(StaticRoles = "Administrators")] + public HttpResponseMessage DeleteModule(DeleteModuleDto deleteModuleDto) + { + ModuleController.Instance.DeleteTabModule(deleteModuleDto.TabId, deleteModuleDto.ModuleId, deleteModuleDto.SoftDelete); - return this.Request.CreateResponse(HttpStatusCode.OK); - } + return this.Request.CreateResponse(HttpStatusCode.OK); + } - private int FixPortalId(int portalId) - { - return this.UserInfo.IsSuperUser && - this.PortalSettings.PortalId != portalId && - PortalController.Instance.GetPortals().OfType().Any(x => x.PortalId == portalId) - ? portalId - : this.PortalSettings.PortalId; - } + private int FixPortalId(int portalId) + { + return this.UserInfo.IsSuperUser && + this.PortalSettings.PortalId != portalId && + PortalController.Instance.GetPortals().OfType().Any(x => x.PortalId == portalId) + ? portalId + : this.PortalSettings.PortalId; + } - /// A data transfer object with information about moving a module. - public class MoveModuleDTO - { - /// Gets or sets the module's ID. - public int ModuleId { get; set; } + /// A data transfer object with information about moving a module. + public class MoveModuleDTO + { + /// Gets or sets the module's ID. + public int ModuleId { get; set; } - /// Gets or sets the module order. - public int ModuleOrder { get; set; } + /// Gets or sets the module order. + public int ModuleOrder { get; set; } - /// Gets or sets the pane name. - public string Pane { get; set; } + /// Gets or sets the pane name. + public string Pane { get; set; } - /// Gets or sets the tab ID. - public int TabId { get; set; } - } + /// Gets or sets the tab ID. + public int TabId { get; set; } + } - /// A data transfer object with information about a request to delete a module. - public class DeleteModuleDto - { - /// Gets or sets the module ID. - public int ModuleId { get; set; } + /// A data transfer object with information about a request to delete a module. + public class DeleteModuleDto + { + /// Gets or sets the module ID. + public int ModuleId { get; set; } - /// Gets or sets the tab ID. - public int TabId { get; set; } + /// Gets or sets the tab ID. + public int TabId { get; set; } - /// Gets or sets a value indicating whether it is a soft or hard delete. - public bool SoftDelete { get; set; } - } + /// Gets or sets a value indicating whether it is a soft or hard delete. + public bool SoftDelete { get; set; } } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/NewUserNotificationServiceController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/NewUserNotificationServiceController.cs index 759a8774047..e965eacc62d 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/NewUserNotificationServiceController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/NewUserNotificationServiceController.cs @@ -2,155 +2,194 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Net; +using System.Net.Http; +using System.Web.Http; + +using DotNetNuke.Abstractions.Application; +using DotNetNuke.Abstractions.Logging; +using DotNetNuke.Common; +using DotNetNuke.Entities; +using DotNetNuke.Entities.Portals; +using DotNetNuke.Entities.Users; +using DotNetNuke.Internal.SourceGenerators; +using DotNetNuke.Security; +using DotNetNuke.Security.Roles; +using DotNetNuke.Services.Localization; +using DotNetNuke.Services.Mail; +using DotNetNuke.Services.Social.Notifications; +using DotNetNuke.Web.Api; + +using Microsoft.Extensions.DependencyInjection; + +/// A web API controller for new user notifications. +/// The role provider. +/// The role controller. +/// The event manager. +/// The portal controller. +/// The user controller. +/// The event logger. +/// The host settings. +[DnnAuthorize] +public partial class NewUserNotificationServiceController(RoleProvider roleProvider, IRoleController roleController, IEventManager eventManager, IPortalController portalController, IUserController userController, IEventLogger eventLogger, IHostSettings hostSettings) + : DnnApiController { - using System; - using System.Net; - using System.Net.Http; - using System.Web.Http; - - using DotNetNuke.Abstractions.Application; - using DotNetNuke.Abstractions.Logging; - using DotNetNuke.Common; - using DotNetNuke.Entities; - using DotNetNuke.Entities.Portals; - using DotNetNuke.Entities.Users; - using DotNetNuke.Security.Roles; - using DotNetNuke.Services.Localization; - using DotNetNuke.Services.Mail; - using DotNetNuke.Services.Social.Notifications; - using DotNetNuke.Web.Api; - - using Microsoft.Extensions.DependencyInjection; - - /// A web API controller for new user notifications. + private readonly RoleProvider roleProvider = roleProvider ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IRoleController roleController = roleController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IEventManager eventManager = eventManager ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IPortalController portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IUserController userController = userController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IEventLogger eventLogger = eventLogger ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IHostSettings hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + + /// Initializes a new instance of the class. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] + public NewUserNotificationServiceController() + : this(null, null, null, null, null, null) + { + } + + /// Initializes a new instance of the class. /// The role provider. /// The role controller. /// The event manager. /// The portal controller. /// The user controller. /// The event logger. - /// The host settings. - [DnnAuthorize] - public class NewUserNotificationServiceController(RoleProvider roleProvider, IRoleController roleController, IEventManager eventManager, IPortalController portalController, IUserController userController, IEventLogger eventLogger, IHostSettings hostSettings) - : DnnApiController + [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] + public NewUserNotificationServiceController(RoleProvider roleProvider, IRoleController roleController, IEventManager eventManager, IPortalController portalController, IUserController userController, IEventLogger eventLogger) + : this(roleProvider, roleController, eventManager, portalController, userController, eventLogger, null) { - private readonly RoleProvider roleProvider = roleProvider ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IRoleController roleController = roleController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IEventManager eventManager = eventManager ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IPortalController portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IUserController userController = userController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IEventLogger eventLogger = eventLogger ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IHostSettings hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - - /// Initializes a new instance of the class. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] - public NewUserNotificationServiceController() - : this(null, null, null, null, null, null) + } + + /// Authorizes a new user. + /// Information about the request. + /// A response indicating success. + [NonAction] + [DnnDeprecated(10, 3, 3, "Use overload taking NotificationRequest")] + public partial HttpResponseMessage Authorize(NotificationDTO postData) + => this.Authorize(postData?.ToNotificationRequest()); + + /// Authorizes a new user. + /// Information about the request. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage Authorize(NotificationRequest requestBody) + { + if (!PortalSecurity.IsInRole(this.PortalSettings.AdministratorRoleName)) { + return this.Request.CreateResponse(HttpStatusCode.Unauthorized); } - /// Initializes a new instance of the class. - /// The role provider. - /// The role controller. - /// The event manager. - /// The portal controller. - /// The user controller. - /// The event logger. - [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] - public NewUserNotificationServiceController(RoleProvider roleProvider, IRoleController roleController, IEventManager eventManager, IPortalController portalController, IUserController userController, IEventLogger eventLogger) - : this(roleProvider, roleController, eventManager, portalController, userController, eventLogger, null) + var user = this.GetUser(requestBody); + if (user == null) { + NotificationsController.Instance.DeleteNotification(requestBody.NotificationId); + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "User not found"); } - /// Authorizes a new user. - /// Information about the request. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage Authorize(NotificationDTO postData) - { - var user = this.GetUser(postData); - if (user == null) - { - NotificationsController.Instance.DeleteNotification(postData.NotificationId); - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "User not found"); - } + user.Membership.Approved = true; + UserController.UpdateUser(this.eventLogger, this.PortalSettings.PortalId, user); - user.Membership.Approved = true; - UserController.UpdateUser(this.eventLogger, this.PortalSettings.PortalId, user); + // Update User Roles if needed + if (!user.IsSuperUser && user.IsInRole("Unverified Users") && this.PortalSettings.UserRegistration == (int)Globals.PortalRegistrationType.VerifiedRegistration) + { + UserController.ApproveUser(this.roleProvider, this.roleController, this.eventManager, this.portalController, this.userController, this.eventLogger, this.PortalSettings, user); + } - // Update User Roles if needed - if (!user.IsSuperUser && user.IsInRole("Unverified Users") && this.PortalSettings.UserRegistration == (int)Globals.PortalRegistrationType.VerifiedRegistration) - { - UserController.ApproveUser(this.roleProvider, this.roleController, this.eventManager, this.portalController, this.userController, this.eventLogger, this.PortalSettings, user); - } + Mail.SendMail(user, MessageType.UserAuthorized, this.PortalSettings); - Mail.SendMail(user, MessageType.UserAuthorized, this.PortalSettings); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); + } - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); + /// Rejects a new user. + /// Information about the request. + /// A response indicating success. + [NonAction] + [DnnDeprecated(10, 3, 3, "Use overload taking NotificationRequest")] + public partial HttpResponseMessage Reject(NotificationDTO postData) + => this.Reject(postData?.ToNotificationRequest()); + + /// Rejects a new user. + /// Information about the request. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage Reject(NotificationRequest requestBody) + { + if (!PortalSecurity.IsInRole(this.PortalSettings.AdministratorRoleName)) + { + return this.Request.CreateResponse(HttpStatusCode.Unauthorized); } - /// Rejects a new user. - /// Information about the request. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage Reject(NotificationDTO postData) + var user = this.GetUser(requestBody); + if (user == null) { - var user = this.GetUser(postData); - if (user == null) - { - NotificationsController.Instance.DeleteNotification(postData.NotificationId); - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "User not found"); - } + NotificationsController.Instance.DeleteNotification(requestBody.NotificationId); + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "User not found"); + } - UserController.RemoveUser(user); + UserController.RemoveUser(user); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); - } + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); + } - /// Sends a verification email to the current user. - /// Information about the request. - /// A response with an object that has a Result field. - /// The user is already verified. - /// The user is not unverified. - [HttpPost] - [DnnAuthorize] - [ValidateAntiForgeryToken] - public HttpResponseMessage SendVerificationMail(NotificationDTO postData) + /// Sends a verification email to the current user. + /// Information about the request. + /// A response with an object that has a Result field. + /// The user is already verified. + /// The user is not unverified. + [NonAction] + [DnnDeprecated(10, 3, 3, "Use overload taking NotificationRequest")] + public partial HttpResponseMessage SendVerificationMail(NotificationDTO postData) + => this.SendVerificationMail(postData?.ToNotificationRequest()); + + /// Sends a verification email to the current user. + /// Information about the request. + /// A response with an object that has a Result field. + /// The user is already verified. + /// The user is not unverified. + [HttpPost] + [DnnAuthorize] + [ValidateAntiForgeryToken] + public HttpResponseMessage SendVerificationMail(NotificationRequest requestBody) + { + if (this.UserInfo.Membership.Approved) { - if (this.UserInfo.Membership.Approved) - { - throw new UserAlreadyVerifiedException(); - } - - if (!this.UserInfo.IsInRole("Unverified Users")) - { - throw new InvalidVerificationCodeException(); - } + throw new UserAlreadyVerifiedException(); + } - var message = Mail.SendMail(this.UserInfo, MessageType.UserRegistrationVerified, this.PortalSettings); - if (string.IsNullOrEmpty(message)) - { - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = Localization.GetSafeJSString("VerificationMailSendSuccessful", Localization.SharedResourceFile), }); - } - else - { - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, message); - } + if (!this.UserInfo.IsInRole("Unverified Users")) + { + throw new InvalidVerificationCodeException(); } - private UserInfo GetUser(NotificationDTO notificationDto) + var message = Mail.SendMail(this.UserInfo, MessageType.UserRegistrationVerified, this.PortalSettings); + if (!string.IsNullOrEmpty(message)) { - var notification = NotificationsController.Instance.GetNotification(notificationDto.NotificationId); + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, message); + } - if (!int.TryParse(notification.Context, out var userId)) + return this.Request.CreateResponse( + HttpStatusCode.OK, + new { - return null; - } + Result = Localization.GetSafeJSString("VerificationMailSendSuccessful", Localization.SharedResourceFile), + }); + } - return UserController.GetUserById(this.hostSettings, this.PortalSettings.PortalId, userId); + private UserInfo GetUser(NotificationRequest requestBody) + { + var notification = NotificationsController.Instance.GetNotification(requestBody.NotificationId); + if (!int.TryParse(notification.Context, out var userId)) + { + return null; } + + return UserController.GetUserById(this.hostSettings, this.PortalSettings.PortalId, userId); } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/NotificationDto.cs b/DNN Platform/DotNetNuke.Web/InternalServices/NotificationDto.cs deleted file mode 100644 index 2388feb4ed9..00000000000 --- a/DNN Platform/DotNetNuke.Web/InternalServices/NotificationDto.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information - -namespace DotNetNuke.Web.InternalServices -{ - /// A data transfer object with information about a notification. - public class NotificationDTO - { - /// Gets or sets the ID of the notification. - public int NotificationId { get; set; } - } -} diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/NotificationRequest.cs b/DNN Platform/DotNetNuke.Web/InternalServices/NotificationRequest.cs new file mode 100644 index 00000000000..043508f0505 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web/InternalServices/NotificationRequest.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information + +namespace DotNetNuke.Web.InternalServices; + +/// A data transfer object with information about a notification. +public class NotificationRequest +{ + /// Gets or sets the ID of the notification. + public int NotificationId { get; set; } +} diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/NotificationsServiceController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/NotificationsServiceController.cs index cbc4e4e9b4e..a3e443a5272 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/NotificationsServiceController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/NotificationsServiceController.cs @@ -2,64 +2,73 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web.Http; + +using DotNetNuke.Instrumentation; +using DotNetNuke.Internal.SourceGenerators; +using DotNetNuke.Services.Social.Messaging.Internal; +using DotNetNuke.Services.Social.Notifications; +using DotNetNuke.Web.Api; + +/// A web API controller for notifications. +[DnnAuthorize] +public partial class NotificationsServiceController : DnnApiController { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Web.Http; + private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(NotificationsServiceController)); - using DotNetNuke.Instrumentation; - using DotNetNuke.Services.Social.Messaging.Internal; - using DotNetNuke.Services.Social.Notifications; - using DotNetNuke.Web.Api; + /// Dismisses a notification. + /// Information about the notification. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnDeprecated(10, 3, 3, "Use overload taking NotificationRequest")] + public partial HttpResponseMessage Dismiss(NotificationDTO postData) + => this.Dismiss(postData?.ToNotificationRequest()); - /// A web API controller for notifications. - [DnnAuthorize] - public class NotificationsServiceController : DnnApiController + /// Dismisses a notification. + /// Information about the notification. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage Dismiss(NotificationRequest requestBody) { - private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(NotificationsServiceController)); - - /// Dismisses a notification. - /// Information about the notification. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage Dismiss(NotificationDTO postData) + try { - try + var recipient = InternalMessagingController.Instance.GetMessageRecipient(requestBody.NotificationId, this.UserInfo.UserID); + if (recipient != null) { - var recipient = InternalMessagingController.Instance.GetMessageRecipient(postData.NotificationId, this.UserInfo.UserID); - if (recipient != null) - { - NotificationsController.Instance.DeleteNotificationRecipient(postData.NotificationId, this.UserInfo.UserID); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success" }); - } - - return this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Unable to dismiss notification"); - } - catch (Exception exc) - { - Logger.Error(exc); - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc); + NotificationsController.Instance.DeleteNotificationRecipient(requestBody.NotificationId, this.UserInfo.UserID); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success" }); } - } - /// Gets toasts for the user. - /// A response with an object that has a Toasts field. - [HttpGet] - public HttpResponseMessage GetToasts() - { - var toasts = NotificationsController.Instance.GetToasts(this.UserInfo); - IList convertedObjects = toasts.Select(this.ToExpandoObject).ToList(); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, Toasts = convertedObjects.Take(3) }); + return this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Unable to dismiss notification"); } - - private object ToExpandoObject(Notification notification) + catch (Exception exc) { - return new { Subject = notification.Subject, Body = notification.Body }; + Logger.Error(exc); + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc); } } + + /// Gets toasts for the user. + /// A response with an object that has a Toasts field. + [HttpGet] + public HttpResponseMessage GetToasts() + { + var toasts = NotificationsController.Instance.GetToasts(this.UserInfo); + IList convertedObjects = toasts.Select(this.ToExpandoObject).ToList(); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Success = true, Toasts = convertedObjects.Take(3) }); + } + + private object ToExpandoObject(Notification notification) + { + return new { Subject = notification.Subject, Body = notification.Body }; + } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/Obsolete/NotificationDto.cs b/DNN Platform/DotNetNuke.Web/InternalServices/Obsolete/NotificationDto.cs new file mode 100644 index 00000000000..4ad88c31004 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web/InternalServices/Obsolete/NotificationDto.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information + +namespace DotNetNuke.Web.InternalServices; + +using DotNetNuke.Internal.SourceGenerators; + +/// A data transfer object with information about a notification. +[DnnDeprecated(10, 3, 3, "Use NotificationRequest")] +public partial class NotificationDTO +{ + /// Gets or sets the ID of the notification. + public int NotificationId { get; set; } + + /// Converts this instance into a . + /// A instance. + public NotificationRequest ToNotificationRequest() + { + return new NotificationRequest + { + NotificationId = this.NotificationId, + }; + } +} diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/Obsolete/PublishPageDto.cs b/DNN Platform/DotNetNuke.Web/InternalServices/Obsolete/PublishPageDto.cs new file mode 100644 index 00000000000..d0c5e1bb202 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web/InternalServices/Obsolete/PublishPageDto.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information + +namespace DotNetNuke.Web.InternalServices; + +using DotNetNuke.Internal.SourceGenerators; + +/// A data transfer object with information about a request to publish the current page. +[DnnDeprecated(10, 3, 3, "Use PublishPageRequest")] +public partial class PublishPageDto +{ + /// Gets or sets a value indicating whether to publish. + public bool Publish { get; set; } + + /// Converts this instance to a . + /// A instance. + public PublishPageRequest ToPublishPageRequest() + { + return new PublishPageRequest + { + Publish = this.Publish, + }; + } +} diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/PageServiceController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/PageServiceController.cs index b4ba2585ceb..9f0fc1c01e0 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/PageServiceController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/PageServiceController.cs @@ -2,170 +2,150 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web.Http; + +using DotNetNuke.Abstractions.Application; +using DotNetNuke.Common; +using DotNetNuke.Common.Utilities; +using DotNetNuke.Entities.Portals; +using DotNetNuke.Entities.Tabs; +using DotNetNuke.Entities.Urls; +using DotNetNuke.Internal.SourceGenerators; +using DotNetNuke.Services.Localization; +using DotNetNuke.Web.Api; +using DotNetNuke.Web.Api.Internal; + +using Microsoft.Extensions.DependencyInjection; + +/// A web API controller for pages. +[DnnAuthorize] +[DnnPageEditor] +public partial class PageServiceController : DnnApiController { - using System; - using System.Globalization; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Web.Http; - - using DotNetNuke.Abstractions.Application; - using DotNetNuke.Common; - using DotNetNuke.Common.Utilities; - using DotNetNuke.Entities.Portals; - using DotNetNuke.Entities.Tabs; - using DotNetNuke.Entities.Urls; - using DotNetNuke.Services.Localization; - using DotNetNuke.Web.Api; - using DotNetNuke.Web.Api.Internal; - - using Microsoft.Extensions.DependencyInjection; - - /// A web API controller for pages. - [DnnAuthorize] - [DnnPageEditor] - public class PageServiceController : DnnApiController + private readonly IPortalController portalController; + private readonly IHostSettings hostSettings; + private readonly IHostSettingsService hostSettingsService; + private int? portalId; + + /// Initializes a new instance of the class. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] + public PageServiceController() + : this(null, null, null) { - private readonly IPortalController portalController; - private readonly IHostSettings hostSettings; - private readonly IHostSettingsService hostSettingsService; - private int? portalId; - - /// Initializes a new instance of the class. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] - public PageServiceController() - : this(null, null, null) - { - } + } - /// Initializes a new instance of the class. - /// The portal controller. - /// The host settings. - /// The host settings service. - public PageServiceController(IPortalController portalController, IHostSettings hostSettings, IHostSettingsService hostSettingsService) - { - this.portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.hostSettingsService = hostSettingsService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - } + /// Initializes a new instance of the class. + /// The portal controller. + /// The host settings. + /// The host settings service. + public PageServiceController(IPortalController portalController, IHostSettings hostSettings, IHostSettingsService hostSettingsService) + { + this.portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.hostSettingsService = hostSettingsService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + } - /// Gets the portal ID. - protected int PortalId + /// Gets the portal ID. + protected int PortalId + { + get { - get + if (!this.portalId.HasValue) { - if (!this.portalId.HasValue) - { - this.portalId = this.PortalSettings.ActiveTab.IsSuperTab ? -1 : this.PortalSettings.PortalId; - } - - return this.portalId.Value; + this.portalId = this.PortalSettings.ActiveTab.IsSuperTab ? -1 : this.PortalSettings.PortalId; } - } - /// Publishes a page. - /// The publish request. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - [DnnPagePermission] - public HttpResponseMessage PublishPage(PublishPageDto dto) - { - var tabId = this.Request.FindTabId(); - - TabPublishingController.Instance.SetTabPublishing(tabId, this.PortalId, dto.Publish); - - return this.Request.CreateResponse(HttpStatusCode.OK); + return this.portalId.Value; } + } - /// Updates a custom URL for a page. - /// The update request. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage UpdateCustomUrl(SaveUrlDto dto) - { - var urlPath = dto.Path.ValueOrEmpty().TrimStart('/'); + /// Publishes a page. + /// The publish request. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnPagePermission] + [DnnDeprecated(10, 3, 3, "Use overload taking PublishPageRequest")] + public partial HttpResponseMessage PublishPage(PublishPageDto dto) + => this.PublishPage(dto?.ToPublishPageRequest()); + + /// Publishes a page. + /// The publish request. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnPagePermission] + public HttpResponseMessage PublishPage(PublishPageRequest requestBody) + { + var tabId = this.Request.FindTabId(); - // Clean Url - var options = UrlRewriterUtils.ExtendOptionsForCustomURLs(UrlRewriterUtils.GetOptionsFromSettings(new FriendlyUrlSettings(this.portalController, this.hostSettings, this.hostSettingsService, this.PortalSettings.PortalId))); + TabPublishingController.Instance.SetTabPublishing(tabId, this.PortalId, requestBody.Publish); - // now clean the path - urlPath = FriendlyUrlController.CleanNameForUrl(urlPath, options, out var modified); - if (modified) - { - return this.Request.CreateResponse( - HttpStatusCode.OK, - new - { - Success = false, - ErrorMessage = Localization.GetString("CustomUrlPathCleaned.Error", Localization.GlobalResourceFile), - SuggestedUrlPath = "/" + urlPath, - }); - } + return this.Request.CreateResponse(HttpStatusCode.OK); + } - // Validate for uniqueness - urlPath = FriendlyUrlController.ValidateUrl(urlPath, -1, this.PortalSettings, out modified); - if (modified) - { - return this.Request.CreateResponse( - HttpStatusCode.OK, - new - { - Success = false, - ErrorMessage = Localization.GetString("UrlPathNotUnique.Error", Localization.GlobalResourceFile), - SuggestedUrlPath = "/" + urlPath, - }); - } + /// Updates a custom URL for a page. + /// The update request. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage UpdateCustomUrl(SaveUrlDto dto) + { + var urlPath = dto.Path.ValueOrEmpty().TrimStart('/'); - var tab = this.PortalSettings.ActiveTab; - var cultureCode = LocaleController.Instance.GetLocales(this.PortalId) - .Where(l => l.Value.KeyID == dto.LocaleKey) - .Select(l => l.Value.Code) - .SingleOrDefault(); + // Clean Url + var options = UrlRewriterUtils.ExtendOptionsForCustomURLs(UrlRewriterUtils.GetOptionsFromSettings(new FriendlyUrlSettings(this.portalController, this.hostSettings, this.hostSettingsService, this.PortalSettings.PortalId))); - if (dto.StatusCodeKey.ToString(CultureInfo.InvariantCulture) == "200") - { - // We need to check if we are updating a current url or creating a new 200 - var tabUrl = tab.TabUrls.SingleOrDefault(t => t.SeqNum == dto.Id - && t.HttpStatus == "200"); - if (tabUrl == null) + // now clean the path + urlPath = FriendlyUrlController.CleanNameForUrl(urlPath, options, out var modified); + if (modified) + { + return this.Request.CreateResponse( + HttpStatusCode.OK, + new { - // Just create Url - tabUrl = new TabUrlInfo - { - TabId = tab.TabID, - SeqNum = dto.Id, - PortalAliasId = dto.SiteAliasKey, - PortalAliasUsage = (PortalAliasUsageType)dto.SiteAliasUsage, - QueryString = dto.QueryString.ValueOrEmpty(), - Url = dto.Path.ValueOrEmpty(), - CultureCode = cultureCode, - HttpStatus = dto.StatusCodeKey.ToString(CultureInfo.InvariantCulture), - IsSystem = dto.IsSystem, // false - }; - TabController.Instance.SaveTabUrl(tabUrl, this.PortalId, true); - } - else + Success = false, + ErrorMessage = Localization.GetString("CustomUrlPathCleaned.Error", Localization.GlobalResourceFile), + SuggestedUrlPath = "/" + urlPath, + }); + } + + // Validate for uniqueness + urlPath = FriendlyUrlController.ValidateUrl(urlPath, -1, this.PortalSettings, out modified); + if (modified) + { + return this.Request.CreateResponse( + HttpStatusCode.OK, + new { - // Change the original 200 url to a redirect - tabUrl.HttpStatus = "301"; - tabUrl.SeqNum = dto.Id; - TabController.Instance.SaveTabUrl(tabUrl, this.PortalId, true); - - // Add new custom url - tabUrl.Url = dto.Path.ValueOrEmpty(); - tabUrl.HttpStatus = "200"; - tabUrl.SeqNum = tab.TabUrls.Max(t => t.SeqNum) + 1; - TabController.Instance.SaveTabUrl(tabUrl, this.PortalId, true); - } - } - else + Success = false, + ErrorMessage = Localization.GetString("UrlPathNotUnique.Error", Localization.GlobalResourceFile), + SuggestedUrlPath = "/" + urlPath, + }); + } + + var tab = this.PortalSettings.ActiveTab; + var cultureCode = LocaleController.Instance.GetLocales(this.PortalId) + .Where(l => l.Value.KeyID == dto.LocaleKey) + .Select(l => l.Value.Code) + .SingleOrDefault(); + + if (dto.StatusCodeKey.ToString(CultureInfo.InvariantCulture) == "200") + { + // We need to check if we are updating a current url or creating a new 200 + var tabUrl = tab.TabUrls.SingleOrDefault(t => t.SeqNum == dto.Id + && t.HttpStatus == "200"); + if (tabUrl == null) { - // Just update the url - var tabUrl = new TabUrlInfo + // Just create Url + tabUrl = new TabUrlInfo { TabId = tab.TabID, SeqNum = dto.Id, @@ -179,13 +159,43 @@ public HttpResponseMessage UpdateCustomUrl(SaveUrlDto dto) }; TabController.Instance.SaveTabUrl(tabUrl, this.PortalId, true); } + else + { + // Change the original 200 url to a redirect + tabUrl.HttpStatus = "301"; + tabUrl.SeqNum = dto.Id; + TabController.Instance.SaveTabUrl(tabUrl, this.PortalId, true); - var response = new + // Add new custom url + tabUrl.Url = dto.Path.ValueOrEmpty(); + tabUrl.HttpStatus = "200"; + tabUrl.SeqNum = tab.TabUrls.Max(t => t.SeqNum) + 1; + TabController.Instance.SaveTabUrl(tabUrl, this.PortalId, true); + } + } + else + { + // Just update the url + var tabUrl = new TabUrlInfo { - Success = true, + TabId = tab.TabID, + SeqNum = dto.Id, + PortalAliasId = dto.SiteAliasKey, + PortalAliasUsage = (PortalAliasUsageType)dto.SiteAliasUsage, + QueryString = dto.QueryString.ValueOrEmpty(), + Url = dto.Path.ValueOrEmpty(), + CultureCode = cultureCode, + HttpStatus = dto.StatusCodeKey.ToString(CultureInfo.InvariantCulture), + IsSystem = dto.IsSystem, // false }; - - return this.Request.CreateResponse(HttpStatusCode.OK, response); + TabController.Instance.SaveTabUrl(tabUrl, this.PortalId, true); } + + var response = new + { + Success = true, + }; + + return this.Request.CreateResponse(HttpStatusCode.OK, response); } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/ProfileServiceController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/ProfileServiceController.cs index 2470bb95865..83aa9239bf8 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/ProfileServiceController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/ProfileServiceController.cs @@ -1,160 +1,159 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web; +using System.Web.Http; + +using DotNetNuke.Abstractions.Application; +using DotNetNuke.Abstractions.Logging; +using DotNetNuke.Common; +using DotNetNuke.Common.Lists; +using DotNetNuke.Common.Utilities; +using DotNetNuke.Entities.Portals; +using DotNetNuke.Entities.Urls; +using DotNetNuke.Entities.Users; +using DotNetNuke.Services.Localization; +using DotNetNuke.Services.Registration; +using DotNetNuke.Web.Api; + +using Microsoft.Extensions.DependencyInjection; + +/// A web API controller for a user's profile. +/// The portal controller. +/// The application status. +/// The portal group controller. +/// The host settings. +/// The host settings service. +/// The event logger. +/// The list controller. +[DnnAuthorize] +public class ProfileServiceController(IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController, IHostSettings hostSettings, IHostSettingsService hostSettingsService, IEventLogger eventLogger, ListController listController) + : DnnApiController { - using System; - using System.Globalization; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Web; - using System.Web.Http; - - using DotNetNuke.Abstractions.Application; - using DotNetNuke.Abstractions.Logging; - using DotNetNuke.Common; - using DotNetNuke.Common.Lists; - using DotNetNuke.Common.Utilities; - using DotNetNuke.Entities.Portals; - using DotNetNuke.Entities.Urls; - using DotNetNuke.Entities.Users; - using DotNetNuke.Services.Localization; - using DotNetNuke.Services.Registration; - using DotNetNuke.Web.Api; - - using Microsoft.Extensions.DependencyInjection; - - /// A web API controller for a user's profile. + private readonly IPortalController portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IApplicationStatusInfo appStatus = appStatus ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IPortalGroupController portalGroupController = portalGroupController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IHostSettings hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IHostSettingsService hostSettingsService = hostSettingsService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly IEventLogger eventLogger = eventLogger ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + private readonly ListController listController = listController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + + /// Initializes a new instance of the class. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IPortalController. Scheduled removal in v12.0.0.")] + public ProfileServiceController() + : this(null, null, null, null, null, null) + { + } + + /// Initializes a new instance of the class. + /// The portal controller. + /// The application status. + /// The portal group controller. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] + public ProfileServiceController(IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController) + : this(portalController, appStatus, portalGroupController, null, null, null) + { + } + + /// Initializes a new instance of the class. /// The portal controller. /// The application status. /// The portal group controller. /// The host settings. /// The host settings service. /// The event logger. - /// The list controller. - [DnnAuthorize] - public class ProfileServiceController(IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController, IHostSettings hostSettings, IHostSettingsService hostSettingsService, IEventLogger eventLogger, ListController listController) - : DnnApiController + [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with ListController. Scheduled removal in v12.0.0.")] + public ProfileServiceController(IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController, IHostSettings hostSettings, IHostSettingsService hostSettingsService, IEventLogger eventLogger) + : this(portalController, appStatus, portalGroupController, hostSettings, hostSettingsService, eventLogger, null) { - private readonly IPortalController portalController = portalController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IApplicationStatusInfo appStatus = appStatus ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IPortalGroupController portalGroupController = portalGroupController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IHostSettings hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IHostSettingsService hostSettingsService = hostSettingsService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly IEventLogger eventLogger = eventLogger ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - private readonly ListController listController = listController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - - /// Initializes a new instance of the class. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IPortalController. Scheduled removal in v12.0.0.")] - public ProfileServiceController() - : this(null, null, null, null, null, null) - { - } + } - /// Initializes a new instance of the class. - /// The portal controller. - /// The application status. - /// The portal group controller. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] - public ProfileServiceController(IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController) - : this(portalController, appStatus, portalGroupController, null, null, null) - { - } + /// Searches a registration profile. + /// The search criteria. + /// A response with a list of objects containing id and name fields. + [HttpGet] + public HttpResponseMessage Search(string q) + { + var results = RegistrationProfileController.Instance.Search(PortalController.GetEffectivePortalId(this.portalController, this.appStatus, this.portalGroupController, this.PortalSettings.PortalId), q); + return this.Request.CreateResponse( + HttpStatusCode.OK, + results.OrderBy(sr => sr) + .Select(field => new { id = field, name = field })); + } - /// Initializes a new instance of the class. - /// The portal controller. - /// The application status. - /// The portal group controller. - /// The host settings. - /// The host settings service. - /// The event logger. - [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with ListController. Scheduled removal in v12.0.0.")] - public ProfileServiceController(IPortalController portalController, IApplicationStatusInfo appStatus, IPortalGroupController portalGroupController, IHostSettings hostSettings, IHostSettingsService hostSettingsService, IEventLogger eventLogger) - : this(portalController, appStatus, portalGroupController, hostSettings, hostSettingsService, eventLogger, null) - { - } + /// Updates a user's vanity URL. + /// The URL. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage UpdateVanityUrl(VanityUrlDTO vanityUrl) + { + // Clean Url + var options = UrlRewriterUtils.GetOptionsFromSettings(new FriendlyUrlSettings(this.portalController, this.hostSettings, this.hostSettingsService, this.PortalSettings.PortalId)); + var cleanUrl = FriendlyUrlController.CleanNameForUrl(vanityUrl.Url, options, out var modified); - /// Searches a registration profile. - /// The search criteria. - /// A response with a list of objects containing id and name fields. - [HttpGet] - public HttpResponseMessage Search(string q) + if (modified) { - var results = RegistrationProfileController.Instance.Search(PortalController.GetEffectivePortalId(this.portalController, this.appStatus, this.portalGroupController, this.PortalSettings.PortalId), q); return this.Request.CreateResponse( HttpStatusCode.OK, - results.OrderBy(sr => sr) - .Select(field => new { id = field, name = field })); + new + { + Result = "warning", + Title = Localization.GetString("CleanWarningTitle", Localization.SharedResourceFile), + Message = Localization.GetString("ProfileUrlCleaned", Localization.SharedResourceFile), + SuggestedUrl = cleanUrl, + }); } - /// Updates a user's vanity URL. - /// The URL. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage UpdateVanityUrl(VanityUrlDTO vanityUrl) - { - // Clean Url - var options = UrlRewriterUtils.GetOptionsFromSettings(new FriendlyUrlSettings(this.portalController, this.hostSettings, this.hostSettingsService, this.PortalSettings.PortalId)); - var cleanUrl = FriendlyUrlController.CleanNameForUrl(vanityUrl.Url, options, out var modified); - - if (modified) - { - return this.Request.CreateResponse( - HttpStatusCode.OK, - new - { - Result = "warning", - Title = Localization.GetString("CleanWarningTitle", Localization.SharedResourceFile), - Message = Localization.GetString("ProfileUrlCleaned", Localization.SharedResourceFile), - SuggestedUrl = cleanUrl, - }); - } - - // Validate for uniqueness - var uniqueUrl = FriendlyUrlController.ValidateUrl(cleanUrl, -1, this.PortalSettings, out modified); - - if (modified) - { - return this.Request.CreateResponse( - HttpStatusCode.OK, - new - { - Result = "warning", - Title = Localization.GetString("DuplicateUrlWarningTitle", Localization.SharedResourceFile), - Message = Localization.GetString("ProfileUrlNotUnique", Localization.SharedResourceFile), - SuggestedUrl = uniqueUrl, - }); - } - - var user = this.PortalSettings.UserInfo; - user.VanityUrl = uniqueUrl; - UserController.UpdateUser(this.eventLogger, this.PortalSettings.PortalId, user); - - DataCache.RemoveCache(string.Format(CultureInfo.InvariantCulture, CacheController.VanityUrlLookupKey, this.PortalSettings.PortalId)); - - // Url is clean and validated so we can update the User - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); - } + // Validate for uniqueness + var uniqueUrl = FriendlyUrlController.ValidateUrl(cleanUrl, -1, this.PortalSettings, out modified); - /// Gets the profile property values. - /// A response with a list of values. - [DnnAuthorize] - [HttpGet] - public HttpResponseMessage ProfilePropertyValues() + if (modified) { - string searchString = HttpContext.Current.Request.Params["SearchString"].NormalizeString(); - string propertyName = HttpContext.Current.Request.Params["PropName"].NormalizeString(); - int portalId = int.Parse(HttpContext.Current.Request.Params["PortalId"], CultureInfo.InvariantCulture); - return this.Request.CreateResponse(HttpStatusCode.OK, Entities.Profile.ProfileController.SearchProfilePropertyValues(this.listController, this.hostSettings, this.portalController, this.appStatus, this.portalGroupController, portalId, propertyName, searchString)); + return this.Request.CreateResponse( + HttpStatusCode.OK, + new + { + Result = "warning", + Title = Localization.GetString("DuplicateUrlWarningTitle", Localization.SharedResourceFile), + Message = Localization.GetString("ProfileUrlNotUnique", Localization.SharedResourceFile), + SuggestedUrl = uniqueUrl, + }); } - /// A data transfer object with information about a vanity URL. - public class VanityUrlDTO - { - /// Gets or sets the URL. - public string Url { get; set; } - } + var user = this.PortalSettings.UserInfo; + user.VanityUrl = uniqueUrl; + UserController.UpdateUser(this.eventLogger, this.PortalSettings.PortalId, user); + + DataCache.RemoveCache(string.Format(CultureInfo.InvariantCulture, CacheController.VanityUrlLookupKey, this.PortalSettings.PortalId)); + + // Url is clean and validated so we can update the User + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); + } + + /// Gets the profile property values. + /// A response with a list of values. + [DnnAuthorize] + [HttpGet] + public HttpResponseMessage ProfilePropertyValues() + { + string searchString = HttpContext.Current.Request.Params["SearchString"].NormalizeString(); + string propertyName = HttpContext.Current.Request.Params["PropName"].NormalizeString(); + int portalId = int.Parse(HttpContext.Current.Request.Params["PortalId"], CultureInfo.InvariantCulture); + return this.Request.CreateResponse(HttpStatusCode.OK, Entities.Profile.ProfileController.SearchProfilePropertyValues(this.listController, this.hostSettings, this.portalController, this.appStatus, this.portalGroupController, portalId, propertyName, searchString)); + } + + /// A data transfer object with information about a vanity URL. + public class VanityUrlDTO + { + /// Gets or sets the URL. + public string Url { get; set; } } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/PublishPageDto.cs b/DNN Platform/DotNetNuke.Web/InternalServices/PublishPageDto.cs deleted file mode 100644 index d5507f61a5a..00000000000 --- a/DNN Platform/DotNetNuke.Web/InternalServices/PublishPageDto.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information - -namespace DotNetNuke.Web.InternalServices -{ - /// A data transfer object with information about a request to publish the current page. - public class PublishPageDto - { - /// Gets or sets a value indicating whether to publish. - public bool Publish { get; set; } - } -} diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/PublishPageRequest.cs b/DNN Platform/DotNetNuke.Web/InternalServices/PublishPageRequest.cs new file mode 100644 index 00000000000..ec059cf04ab --- /dev/null +++ b/DNN Platform/DotNetNuke.Web/InternalServices/PublishPageRequest.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information + +namespace DotNetNuke.Web.InternalServices; + +/// A data transfer object with information about a request to publish the current page. +public class PublishPageRequest +{ + /// Gets or sets a value indicating whether to publish. + public bool Publish { get; set; } +} diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/RelationshipServiceController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/RelationshipServiceController.cs index c5c29a870b6..d5d0c9b98fd 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/RelationshipServiceController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/RelationshipServiceController.cs @@ -2,141 +2,159 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Net; +using System.Net.Http; +using System.Web.Http; + +using DotNetNuke.Abstractions.Application; +using DotNetNuke.Common; +using DotNetNuke.Entities.Users; +using DotNetNuke.Entities.Users.Social; +using DotNetNuke.Instrumentation; +using DotNetNuke.Internal.SourceGenerators; +using DotNetNuke.Services.Localization; +using DotNetNuke.Services.Social.Messaging.Internal; +using DotNetNuke.Services.Social.Notifications; +using DotNetNuke.Web.Api; + +using Microsoft.Extensions.DependencyInjection; + +/// A web API controller for relationships. +/// The host settings. +[DnnAuthorize] +public partial class RelationshipServiceController(IHostSettings hostSettings) + : DnnApiController { - using System; - using System.Net; - using System.Net.Http; - using System.Web.Http; - - using DotNetNuke.Abstractions.Application; - using DotNetNuke.Common; - using DotNetNuke.Entities.Users; - using DotNetNuke.Entities.Users.Social; - using DotNetNuke.Instrumentation; - using DotNetNuke.Services.Localization; - using DotNetNuke.Services.Social.Messaging.Internal; - using DotNetNuke.Services.Social.Notifications; - using DotNetNuke.Web.Api; - - using Microsoft.Extensions.DependencyInjection; - - /// A web API controller for relationships. - /// The host settings. - [DnnAuthorize] - public class RelationshipServiceController(IHostSettings hostSettings) - : DnnApiController + private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(RelationshipServiceController)); + private readonly IHostSettings hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + + /// Initializes a new instance of the class. + [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] + public RelationshipServiceController() + : this(null) { - private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(RelationshipServiceController)); - private readonly IHostSettings hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + } - /// Initializes a new instance of the class. - [Obsolete("Deprecated in DotNetNuke 10.2.4. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] - public RelationshipServiceController() - : this(null) - { - } + /// Accept a friend. + /// The request. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnDeprecated(10, 3, 3, "Use overload taking NotificationRequest")] + public partial HttpResponseMessage AcceptFriend(NotificationDTO postData) + => this.AcceptFriend(postData?.ToNotificationRequest()); + + /// Accept a friend. + /// The request. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage AcceptFriend(NotificationRequest requestBody) + { + var success = false; - /// Accept a friend. - /// The request. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage AcceptFriend(NotificationDTO postData) + try { - var success = false; - - try + var recipient = InternalMessagingController.Instance.GetMessageRecipient(requestBody.NotificationId, this.UserInfo.UserID); + if (recipient != null) { - var recipient = InternalMessagingController.Instance.GetMessageRecipient(postData.NotificationId, this.UserInfo.UserID); - if (recipient != null) + var notification = NotificationsController.Instance.GetNotification(requestBody.NotificationId); + if (int.TryParse(notification.Context, out var userRelationshipId)) { - var notification = NotificationsController.Instance.GetNotification(postData.NotificationId); - if (int.TryParse(notification.Context, out var userRelationshipId)) + var userRelationship = RelationshipController.Instance.GetUserRelationship(userRelationshipId); + if (userRelationship != null) { - var userRelationship = RelationshipController.Instance.GetUserRelationship(userRelationshipId); - if (userRelationship != null) - { - var friend = UserController.GetUserById(this.hostSettings, this.PortalSettings.PortalId, userRelationship.UserId); - FriendsController.Instance.AcceptFriend(friend); - success = true; - } + var friend = UserController.GetUserById(this.hostSettings, this.PortalSettings.PortalId, userRelationship.UserId); + FriendsController.Instance.AcceptFriend(friend); + success = true; } } } - catch (Exception exc) - { - Logger.Error(exc); - } - - if (success) - { - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); - } - - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + } + catch (Exception exc) + { + Logger.Error(exc); } - /// Follow a user who has requested to follow you. - /// The request. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - public HttpResponseMessage FollowBack(NotificationDTO postData) + if (success) { - var success = false; + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); + } + + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + } + + /// Follow a user who has requested to follow you. + /// The request. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [DnnDeprecated(10, 3, 3, "Use overload taking NotificationRequest")] + public partial HttpResponseMessage FollowBack(NotificationDTO postData) + => this.FollowBack(postData?.ToNotificationRequest()); + + /// Follow a user who has requested to follow you. + /// The request. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + public HttpResponseMessage FollowBack(NotificationRequest requestBody) + { + var success = false; - try + try + { + var recipient = InternalMessagingController.Instance.GetMessageRecipient(requestBody.NotificationId, this.UserInfo.UserID); + if (recipient != null) { - var recipient = InternalMessagingController.Instance.GetMessageRecipient(postData.NotificationId, this.UserInfo.UserID); - if (recipient != null) + var notification = NotificationsController.Instance.GetNotification(requestBody.NotificationId); + if (int.TryParse(notification.Context, out var targetUserId)) { - var notification = NotificationsController.Instance.GetNotification(postData.NotificationId); - if (int.TryParse(notification.Context, out var targetUserId)) - { - var targetUser = UserController.GetUserById(this.hostSettings, this.PortalSettings.PortalId, targetUserId); + var targetUser = UserController.GetUserById(this.hostSettings, this.PortalSettings.PortalId, targetUserId); - if (targetUser == null) + if (targetUser == null) + { + var response = new { - var response = new - { - Message = Localization.GetExceptionMessage( - "UserDoesNotExist", - "The user you are trying to follow no longer exists."), - }; - return this.Request.CreateResponse(HttpStatusCode.InternalServerError, response); - } - - FollowersController.Instance.FollowUser(targetUser); - NotificationsController.Instance.DeleteNotificationRecipient(postData.NotificationId, this.UserInfo.UserID); - - success = true; + Message = Localization.GetExceptionMessage( + "UserDoesNotExist", + "The user you are trying to follow no longer exists."), + }; + return this.Request.CreateResponse(HttpStatusCode.InternalServerError, response); } + + FollowersController.Instance.FollowUser(targetUser); + NotificationsController.Instance.DeleteNotificationRecipient(requestBody.NotificationId, this.UserInfo.UserID); + + success = true; } } - catch (UserRelationshipExistsException exc) - { - Logger.Error(exc); - var response = new - { - Message = Localization.GetExceptionMessage( - "AlreadyFollowingUser", - "You are already following this user."), - }; - return this.Request.CreateResponse(HttpStatusCode.InternalServerError, response); - } - catch (Exception exc) - { - Logger.Error(exc); - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc.Message); - } - - if (success) + } + catch (UserRelationshipExistsException exc) + { + Logger.Error(exc); + var response = new { - return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); - } + Message = Localization.GetExceptionMessage( + "AlreadyFollowingUser", + "You are already following this user."), + }; + return this.Request.CreateResponse(HttpStatusCode.InternalServerError, response); + } + catch (Exception exc) + { + Logger.Error(exc); + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc.Message); + } - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); + if (success) + { + return this.Request.CreateResponse(HttpStatusCode.OK, new { Result = "success", }); } + + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "unable to process notification"); } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/SearchServiceController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/SearchServiceController.cs index 67e1925536b..edb1353920e 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/SearchServiceController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/SearchServiceController.cs @@ -2,764 +2,763 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text.RegularExpressions; +using System.Web; +using System.Web.Caching; +using System.Web.Http; + +using DotNetNuke.Abstractions.Application; +using DotNetNuke.Common; +using DotNetNuke.Common.Utilities; +using DotNetNuke.Data; +using DotNetNuke.Entities.Modules; +using DotNetNuke.Entities.Modules.Definitions; +using DotNetNuke.Entities.Tabs; +using DotNetNuke.Entities.Users; +using DotNetNuke.Services.Search.Controllers; +using DotNetNuke.Services.Search.Entities; +using DotNetNuke.Services.Search.Internals; +using DotNetNuke.Web.Api; +using DotNetNuke.Web.InternalServices.Views.Search; + +using Microsoft.Extensions.DependencyInjection; + +/// A web API controller for searching. +[DnnAuthorize(StaticRoles = "Administrators")] +public class SearchServiceController : DnnApiController { - using System; - using System.Collections; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Text.RegularExpressions; - using System.Web; - using System.Web.Caching; - using System.Web.Http; - - using DotNetNuke.Abstractions.Application; - using DotNetNuke.Common; - using DotNetNuke.Common.Utilities; - using DotNetNuke.Data; - using DotNetNuke.Entities.Modules; - using DotNetNuke.Entities.Modules.Definitions; - using DotNetNuke.Entities.Tabs; - using DotNetNuke.Entities.Users; - using DotNetNuke.Services.Search.Controllers; - using DotNetNuke.Services.Search.Entities; - using DotNetNuke.Services.Search.Internals; - using DotNetNuke.Web.Api; - using DotNetNuke.Web.InternalServices.Views.Search; - - using Microsoft.Extensions.DependencyInjection; - - /// A web API controller for searching. - [DnnAuthorize(StaticRoles = "Administrators")] - public class SearchServiceController : DnnApiController + private const string ModuleInfosCacheKey = "ModuleInfos{0}"; + private const CacheItemPriority ModuleInfosCachePriority = CacheItemPriority.AboveNormal; + private const int ModuleInfosCacheTimeOut = 20; + private const string ModuleTitleCacheKey = "SearchModuleTabTitle_{0}"; + private const CacheItemPriority ModuleTitleCachePriority = CacheItemPriority.Normal; + private const int ModuleTitleCacheTimeOut = 20; + private static readonly Regex GroupedBasicViewRegex = new Regex(@"userid(/|\|=)(\d+)", RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private readonly ISearchController searchController; + private readonly IHostSettings hostSettings; + private readonly IHostSettingsService hostSettingsService; + private readonly int htmlModuleDefinitionId; + + /// Initializes a new instance of the class. + /// The search controller. + [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] + public SearchServiceController(ISearchController searchController) + : this(searchController, null, null) { - private const string ModuleInfosCacheKey = "ModuleInfos{0}"; - private const CacheItemPriority ModuleInfosCachePriority = CacheItemPriority.AboveNormal; - private const int ModuleInfosCacheTimeOut = 20; - private const string ModuleTitleCacheKey = "SearchModuleTabTitle_{0}"; - private const CacheItemPriority ModuleTitleCachePriority = CacheItemPriority.Normal; - private const int ModuleTitleCacheTimeOut = 20; - private static readonly Regex GroupedBasicViewRegex = new Regex(@"userid(/|\|=)(\d+)", RegexOptions.IgnoreCase | RegexOptions.Compiled); - - private readonly ISearchController searchController; - private readonly IHostSettings hostSettings; - private readonly IHostSettingsService hostSettingsService; - private readonly int htmlModuleDefinitionId; - - /// Initializes a new instance of the class. - /// The search controller. - [Obsolete("Deprecated in DotNetNuke 10.2.2. Please use overload with IHostSettings. Scheduled removal in v12.0.0.")] - public SearchServiceController(ISearchController searchController) - : this(searchController, null, null) - { - } + } - /// Initializes a new instance of the class. - /// The search controller. - /// The host settings. - /// The host settings service. - public SearchServiceController(ISearchController searchController, IHostSettings hostSettings, IHostSettingsService hostSettingsService) - : this(searchController, hostSettings, hostSettingsService, ModuleDefinitionController.GetModuleDefinitionByFriendlyName("Text/HTML")?.ModuleDefID ?? -1) - { - } + /// Initializes a new instance of the class. + /// The search controller. + /// The host settings. + /// The host settings service. + public SearchServiceController(ISearchController searchController, IHostSettings hostSettings, IHostSettingsService hostSettingsService) + : this(searchController, hostSettings, hostSettingsService, ModuleDefinitionController.GetModuleDefinitionByFriendlyName("Text/HTML")?.ModuleDefID ?? -1) + { + } - /// Initializes a new instance of the class. - /// this constructor is for unit tests. - /// The search controller. - /// The host settings. - /// The host settings service. - /// The ID of the HTML module definition. - internal SearchServiceController(ISearchController searchController, IHostSettings hostSettings, IHostSettingsService hostSettingsService, int htmlModuleDefinitionId) - { - this.searchController = searchController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.hostSettingsService = hostSettingsService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); - this.htmlModuleDefinitionId = htmlModuleDefinitionId; - } + /// Initializes a new instance of the class. + /// this constructor is for unit tests. + /// The search controller. + /// The host settings. + /// The host settings service. + /// The ID of the HTML module definition. + internal SearchServiceController(ISearchController searchController, IHostSettings hostSettings, IHostSettingsService hostSettingsService, int htmlModuleDefinitionId) + { + this.searchController = searchController ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.hostSettings = hostSettings ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.hostSettingsService = hostSettingsService ?? Globals.GetCurrentServiceProvider().GetRequiredService(); + this.htmlModuleDefinitionId = htmlModuleDefinitionId; + } - /// Previews a search. - /// The keywords. - /// The culture. - /// 0 to not use wildcards, any positive integer to force a wildcard search. - /// The portal ID. - /// A response with a list of objects. - [HttpGet] - [AllowAnonymous] - public HttpResponseMessage Preview(string keywords, string culture, int forceWild = 1, int portal = -1) + /// Previews a search. + /// The keywords. + /// The culture. + /// 0 to not use wildcards, any positive integer to force a wildcard search. + /// The portal ID. + /// A response with a list of objects. + [HttpGet] + [AllowAnonymous] + public HttpResponseMessage Preview(string keywords, string culture, int forceWild = 1, int portal = -1) + { + keywords = (keywords ?? string.Empty).Trim(); + var tags = SearchQueryStringParser.Instance.GetTags(keywords, out var cleanedKeywords); + var beginModifiedTimeUtc = SearchQueryStringParser.Instance.GetLastModifiedDate(cleanedKeywords, out cleanedKeywords); + var searchTypes = SearchQueryStringParser.Instance.GetSearchTypeList(keywords, out cleanedKeywords); + + var contentSources = this.GetSearchContentSources(searchTypes); + var settings = this.GetSearchModuleSettings(); + var searchTypeIds = GetSearchTypeIds(settings, contentSources); + var moduleDefIds = GetSearchModuleDefIds(settings, contentSources); + var portalIds = this.GetSearchPortalIds(settings, portal); + + var userSearchTypeId = SearchHelper.Instance.GetSearchTypeByName("user").SearchTypeId; + var userSearchSource = contentSources.FirstOrDefault(s => s.SearchTypeId == userSearchTypeId); + + var results = new List(); + if (portalIds.Count != 0 && searchTypeIds.Count != 0 && + (!string.IsNullOrEmpty(cleanedKeywords) || tags.Count > 0)) { - keywords = (keywords ?? string.Empty).Trim(); - var tags = SearchQueryStringParser.Instance.GetTags(keywords, out var cleanedKeywords); - var beginModifiedTimeUtc = SearchQueryStringParser.Instance.GetLastModifiedDate(cleanedKeywords, out cleanedKeywords); - var searchTypes = SearchQueryStringParser.Instance.GetSearchTypeList(keywords, out cleanedKeywords); - - var contentSources = this.GetSearchContentSources(searchTypes); - var settings = this.GetSearchModuleSettings(); - var searchTypeIds = GetSearchTypeIds(settings, contentSources); - var moduleDefIds = GetSearchModuleDefIds(settings, contentSources); - var portalIds = this.GetSearchPortalIds(settings, portal); - - var userSearchTypeId = SearchHelper.Instance.GetSearchTypeByName("user").SearchTypeId; - var userSearchSource = contentSources.FirstOrDefault(s => s.SearchTypeId == userSearchTypeId); - - var results = new List(); - if (portalIds.Count != 0 && searchTypeIds.Count != 0 && - (!string.IsNullOrEmpty(cleanedKeywords) || tags.Count > 0)) + var query = new SearchQuery { - var query = new SearchQuery - { - KeyWords = cleanedKeywords, - Tags = tags, - PortalIds = portalIds, - SearchTypeIds = searchTypeIds, - ModuleDefIds = moduleDefIds, - BeginModifiedTimeUtc = beginModifiedTimeUtc, - PageIndex = 1, - PageSize = 5, - TitleSnippetLength = 40, - BodySnippetLength = 100, - CultureCode = culture, - WildCardSearch = forceWild > 0, - }; - - try - { - results = this.GetGroupedBasicViews(query, userSearchSource, this.PortalSettings.PortalId); - } - catch (Exception ex) - { - DotNetNuke.Services.Exceptions.Exceptions.LogException(ex); - } + KeyWords = cleanedKeywords, + Tags = tags, + PortalIds = portalIds, + SearchTypeIds = searchTypeIds, + ModuleDefIds = moduleDefIds, + BeginModifiedTimeUtc = beginModifiedTimeUtc, + PageIndex = 1, + PageSize = 5, + TitleSnippetLength = 40, + BodySnippetLength = 100, + CultureCode = culture, + WildCardSearch = forceWild > 0, + }; + + try + { + results = this.GetGroupedBasicViews(query, userSearchSource, this.PortalSettings.PortalId); + } + catch (Exception ex) + { + DotNetNuke.Services.Exceptions.Exceptions.LogException(ex); } - - return this.Request.CreateResponse(HttpStatusCode.OK, results); } - /// Performs a search. - /// The search criteria. - /// The culture. - /// The page index. - /// The page size. - /// A value. - /// A response with an object that has results, totalHits, and more fields. - [HttpGet] - [AllowAnonymous] - public HttpResponseMessage Search(string search, string culture, int pageIndex, int pageSize, int sortOption) + return this.Request.CreateResponse(HttpStatusCode.OK, results); + } + + /// Performs a search. + /// The search criteria. + /// The culture. + /// The page index. + /// The page size. + /// A value. + /// A response with an object that has results, totalHits, and more fields. + [HttpGet] + [AllowAnonymous] + public HttpResponseMessage Search(string search, string culture, int pageIndex, int pageSize, int sortOption) + { + search = (search ?? string.Empty).Trim(); + var tags = SearchQueryStringParser.Instance.GetTags(search, out var cleanedKeywords); + var beginModifiedTimeUtc = SearchQueryStringParser.Instance.GetLastModifiedDate(cleanedKeywords, out cleanedKeywords); + var searchTypes = SearchQueryStringParser.Instance.GetSearchTypeList(cleanedKeywords, out cleanedKeywords); + + var contentSources = this.GetSearchContentSources(searchTypes); + var settings = this.GetSearchModuleSettings(); + var searchTypeIds = GetSearchTypeIds(settings, contentSources); + var moduleDefIds = GetSearchModuleDefIds(settings, contentSources); + var portalIds = this.GetSearchPortalIds(settings, -1); + var userSearchTypeId = SearchHelper.Instance.GetSearchTypeByName("user").SearchTypeId; + var maximumPageSize = this.hostSettingsService.GetInteger("Search_MaxResultPerPage", 100); + + var more = false; + var totalHits = 0; + var results = new List(); + if (portalIds.Count != 0 && searchTypeIds.Count != 0 && + (!string.IsNullOrEmpty(cleanedKeywords) || tags.Any())) { - search = (search ?? string.Empty).Trim(); - var tags = SearchQueryStringParser.Instance.GetTags(search, out var cleanedKeywords); - var beginModifiedTimeUtc = SearchQueryStringParser.Instance.GetLastModifiedDate(cleanedKeywords, out cleanedKeywords); - var searchTypes = SearchQueryStringParser.Instance.GetSearchTypeList(cleanedKeywords, out cleanedKeywords); - - var contentSources = this.GetSearchContentSources(searchTypes); - var settings = this.GetSearchModuleSettings(); - var searchTypeIds = GetSearchTypeIds(settings, contentSources); - var moduleDefIds = GetSearchModuleDefIds(settings, contentSources); - var portalIds = this.GetSearchPortalIds(settings, -1); - var userSearchTypeId = SearchHelper.Instance.GetSearchTypeByName("user").SearchTypeId; - var maximumPageSize = this.hostSettingsService.GetInteger("Search_MaxResultPerPage", 100); - - var more = false; - var totalHits = 0; - var results = new List(); - if (portalIds.Count != 0 && searchTypeIds.Count != 0 && - (!string.IsNullOrEmpty(cleanedKeywords) || tags.Any())) + if (pageSize > maximumPageSize) { - if (pageSize > maximumPageSize) - { - pageSize = maximumPageSize; - } - - var query = new SearchQuery - { - KeyWords = cleanedKeywords, - Tags = tags, - PortalIds = portalIds, - SearchTypeIds = searchTypeIds, - ModuleDefIds = moduleDefIds, - BeginModifiedTimeUtc = beginModifiedTimeUtc, - EndModifiedTimeUtc = beginModifiedTimeUtc > DateTime.MinValue ? DateTime.MaxValue : DateTime.MinValue, - PageIndex = pageIndex, - PageSize = pageSize, - SortField = (SortFields)sortOption, - TitleSnippetLength = 120, - BodySnippetLength = 300, - CultureCode = culture, - WildCardSearch = this.IsWildCardEnabledForModule(), - }; - - try - { - results = this.GetGroupedDetailViews(query, userSearchTypeId, out totalHits, out more).ToList(); - } - catch (Exception ex) - { - DotNetNuke.Services.Exceptions.Exceptions.LogException(ex); - } + pageSize = maximumPageSize; } - return this.Request.CreateResponse(HttpStatusCode.OK, new { results, totalHits, more }); + var query = new SearchQuery + { + KeyWords = cleanedKeywords, + Tags = tags, + PortalIds = portalIds, + SearchTypeIds = searchTypeIds, + ModuleDefIds = moduleDefIds, + BeginModifiedTimeUtc = beginModifiedTimeUtc, + EndModifiedTimeUtc = beginModifiedTimeUtc > DateTime.MinValue ? DateTime.MaxValue : DateTime.MinValue, + PageIndex = pageIndex, + PageSize = pageSize, + SortField = (SortFields)sortOption, + TitleSnippetLength = 120, + BodySnippetLength = 300, + CultureCode = culture, + WildCardSearch = this.IsWildCardEnabledForModule(), + }; + + try + { + results = this.GetGroupedDetailViews(query, userSearchTypeId, out totalHits, out more).ToList(); + } + catch (Exception ex) + { + DotNetNuke.Services.Exceptions.Exceptions.LogException(ex); + } } - /// Add a synonyms group. - /// The synonyms group to add. - /// A response with an object that has Id and DuplicateWord fields. - [HttpPost] - [ValidateAntiForgeryToken] - [SupportedModules("SearchAdmin")] - public HttpResponseMessage AddSynonymsGroup(SynonymsGroupDto synonymsGroup) - { - var synonymsGroupId = SearchHelper.Instance.AddSynonymsGroup(synonymsGroup.Tags, synonymsGroup.PortalId, synonymsGroup.Culture, out var duplicateWord); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Id = synonymsGroupId, DuplicateWord = duplicateWord, }); - } + return this.Request.CreateResponse(HttpStatusCode.OK, new { results, totalHits, more }); + } - /// Update a synonyms group. - /// The synonyms group to update. - /// A response with an object that has Id and DuplicateWord fields. - [HttpPost] - [ValidateAntiForgeryToken] - [SupportedModules("SearchAdmin")] - public HttpResponseMessage UpdateSynonymsGroup(SynonymsGroupDto synonymsGroup) - { - var synonymsGroupId = SearchHelper.Instance.UpdateSynonymsGroup(synonymsGroup.Id, synonymsGroup.Tags, synonymsGroup.PortalId, synonymsGroup.Culture, out var duplicateWord); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Id = synonymsGroupId, DuplicateWord = duplicateWord, }); - } + /// Add a synonyms group. + /// The synonyms group to add. + /// A response with an object that has Id and DuplicateWord fields. + [HttpPost] + [ValidateAntiForgeryToken] + [SupportedModules("SearchAdmin")] + public HttpResponseMessage AddSynonymsGroup(SynonymsGroupDto synonymsGroup) + { + var synonymsGroupId = SearchHelper.Instance.AddSynonymsGroup(synonymsGroup.Tags, synonymsGroup.PortalId, synonymsGroup.Culture, out var duplicateWord); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Id = synonymsGroupId, DuplicateWord = duplicateWord, }); + } - /// Deletes a synonyms group. - /// The synonyms group to delete. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - [SupportedModules("SearchAdmin")] - public HttpResponseMessage DeleteSynonymsGroup(SynonymsGroupDto synonymsGroup) - { - SearchHelper.Instance.DeleteSynonymsGroup(synonymsGroup.Id, synonymsGroup.PortalId, synonymsGroup.Culture); - return this.Request.CreateResponse(HttpStatusCode.OK); - } + /// Update a synonyms group. + /// The synonyms group to update. + /// A response with an object that has Id and DuplicateWord fields. + [HttpPost] + [ValidateAntiForgeryToken] + [SupportedModules("SearchAdmin")] + public HttpResponseMessage UpdateSynonymsGroup(SynonymsGroupDto synonymsGroup) + { + var synonymsGroupId = SearchHelper.Instance.UpdateSynonymsGroup(synonymsGroup.Id, synonymsGroup.Tags, synonymsGroup.PortalId, synonymsGroup.Culture, out var duplicateWord); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Id = synonymsGroupId, DuplicateWord = duplicateWord, }); + } - /// Add search stop words. - /// The stop words to add. - /// A response with an object that has an Id field. - [HttpPost] - [ValidateAntiForgeryToken] - [SupportedModules("SearchAdmin")] - public HttpResponseMessage AddStopWords(StopWordsDto stopWords) - { - var stopWordsId = SearchHelper.Instance.AddSearchStopWords(stopWords.Words, stopWords.PortalId, stopWords.Culture); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Id = stopWordsId, }); - } + /// Deletes a synonyms group. + /// The synonyms group to delete. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [SupportedModules("SearchAdmin")] + public HttpResponseMessage DeleteSynonymsGroup(SynonymsGroupDto synonymsGroup) + { + SearchHelper.Instance.DeleteSynonymsGroup(synonymsGroup.Id, synonymsGroup.PortalId, synonymsGroup.Culture); + return this.Request.CreateResponse(HttpStatusCode.OK); + } - /// Update search stop words. - /// The stop words to update. - /// A response with an object that has an Id field. - [HttpPost] - [ValidateAntiForgeryToken] - [SupportedModules("SearchAdmin")] - public HttpResponseMessage UpdateStopWords(StopWordsDto stopWords) - { - var stopWordsId = SearchHelper.Instance.UpdateSearchStopWords(stopWords.Id, stopWords.Words, stopWords.PortalId, stopWords.Culture); - return this.Request.CreateResponse(HttpStatusCode.OK, new { Id = stopWordsId, }); - } + /// Add search stop words. + /// The stop words to add. + /// A response with an object that has an Id field. + [HttpPost] + [ValidateAntiForgeryToken] + [SupportedModules("SearchAdmin")] + public HttpResponseMessage AddStopWords(StopWordsDto stopWords) + { + var stopWordsId = SearchHelper.Instance.AddSearchStopWords(stopWords.Words, stopWords.PortalId, stopWords.Culture); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Id = stopWordsId, }); + } - /// Delete search stop words. - /// The stop words to delete. - /// A response indicating success. - [HttpPost] - [ValidateAntiForgeryToken] - [SupportedModules("SearchAdmin")] - public HttpResponseMessage DeleteStopWords(StopWordsDto stopWords) - { - SearchHelper.Instance.DeleteSearchStopWords(stopWords.Id, stopWords.PortalId, stopWords.Culture); - return this.Request.CreateResponse(HttpStatusCode.OK); - } + /// Update search stop words. + /// The stop words to update. + /// A response with an object that has an Id field. + [HttpPost] + [ValidateAntiForgeryToken] + [SupportedModules("SearchAdmin")] + public HttpResponseMessage UpdateStopWords(StopWordsDto stopWords) + { + var stopWordsId = SearchHelper.Instance.UpdateSearchStopWords(stopWords.Id, stopWords.Words, stopWords.PortalId, stopWords.Culture); + return this.Request.CreateResponse(HttpStatusCode.OK, new { Id = stopWordsId, }); + } - /// Gets grouped detail views. - /// The search query. - /// The ID of the user search type. - /// The total number of hits. - /// Whether there is more. - /// A list of instances. - internal IEnumerable GetGroupedDetailViews(SearchQuery searchQuery, int userSearchTypeId, out int totalHits, out bool more) - { - var searchResults = this.searchController.SiteSearch(searchQuery); - totalHits = searchResults.TotalHits; - more = totalHits > searchQuery.PageSize * searchQuery.PageIndex; + /// Delete search stop words. + /// The stop words to delete. + /// A response indicating success. + [HttpPost] + [ValidateAntiForgeryToken] + [SupportedModules("SearchAdmin")] + public HttpResponseMessage DeleteStopWords(StopWordsDto stopWords) + { + SearchHelper.Instance.DeleteSearchStopWords(stopWords.Id, stopWords.PortalId, stopWords.Culture); + return this.Request.CreateResponse(HttpStatusCode.OK); + } + + /// Gets grouped detail views. + /// The search query. + /// The ID of the user search type. + /// The total number of hits. + /// Whether there is more. + /// A list of instances. + internal IEnumerable GetGroupedDetailViews(SearchQuery searchQuery, int userSearchTypeId, out int totalHits, out bool more) + { + var searchResults = this.searchController.SiteSearch(searchQuery); + totalHits = searchResults.TotalHits; + more = totalHits > searchQuery.PageSize * searchQuery.PageIndex; - var groups = new List(); - var tabGroups = new Dictionary>(); + var groups = new List(); + var tabGroups = new Dictionary>(); - foreach (var result in searchResults.Results) + foreach (var result in searchResults.Results) + { + ////var key = result.TabId + result.Url; + var key = result.Url; + if (!tabGroups.ContainsKey(key)) { - ////var key = result.TabId + result.Url; - var key = result.Url; - if (!tabGroups.ContainsKey(key)) + tabGroups.Add(key, new List { result }); + } + else + { + // when the result is a user search type, we should only show one result + // and if duplicate, we should also reduce the totalHit number. + if (result.SearchTypeId != userSearchTypeId || + tabGroups[key].All(r => r.Url != result.Url)) { - tabGroups.Add(key, new List { result }); + tabGroups[key].Add(result); } else { - // when the result is a user search type, we should only show one result - // and if duplicate, we should also reduce the totalHit number. - if (result.SearchTypeId != userSearchTypeId || - tabGroups[key].All(r => r.Url != result.Url)) - { - tabGroups[key].Add(result); - } - else - { - totalHits--; - } + totalHits--; } } + } - var showFriendlyTitle = - this.ActiveModule == null || - !this.ActiveModule.ModuleSettings.ContainsKey("ShowFriendlyTitle") || - Convert.ToBoolean(this.ActiveModule.ModuleSettings["ShowFriendlyTitle"], CultureInfo.InvariantCulture); - foreach (var results in tabGroups.Values) - { - var group = new GroupedDetailView(); + var showFriendlyTitle = + this.ActiveModule == null || + !this.ActiveModule.ModuleSettings.ContainsKey("ShowFriendlyTitle") || + Convert.ToBoolean(this.ActiveModule.ModuleSettings["ShowFriendlyTitle"], CultureInfo.InvariantCulture); + foreach (var results in tabGroups.Values) + { + var group = new GroupedDetailView(); - // first entry - var first = results[0]; - @group.Title = showFriendlyTitle ? GetFriendlyTitle(first) : first.Title; - @group.DocumentUrl = first.Url; + // first entry + var first = results[0]; + @group.Title = showFriendlyTitle ? GetFriendlyTitle(first) : first.Title; + @group.DocumentUrl = first.Url; - // Find a different title for multiple entries with same url - if (results.Count > 1) + // Find a different title for multiple entries with same url + if (results.Count > 1) + { + if (first.TabId > 0) { - if (first.TabId > 0) - { - var tab = TabController.Instance.GetTab(first.TabId, first.PortalId, false); - if (tab != null) - { - @group.Title = showFriendlyTitle && !string.IsNullOrEmpty(tab.Title) ? tab.Title : tab.TabName; - } - } - else if (first.ModuleId > 0) + var tab = TabController.Instance.GetTab(first.TabId, first.PortalId, false); + if (tab != null) { - var tabTitle = GetTabTitleFromModuleId(this.hostSettings, first.ModuleId); - if (!string.IsNullOrEmpty(tabTitle)) - { - @group.Title = tabTitle; - } + @group.Title = showFriendlyTitle && !string.IsNullOrEmpty(tab.Title) ? tab.Title : tab.TabName; } } - else if (first.ModuleDefId > 0 && first.ModuleDefId == this.htmlModuleDefinitionId) + else if (first.ModuleId > 0) { - // special handling for Html module var tabTitle = GetTabTitleFromModuleId(this.hostSettings, first.ModuleId); if (!string.IsNullOrEmpty(tabTitle)) { @group.Title = tabTitle; - if (first.Title != "Enter Title" && first.Title != "Text/HTML") - { - @group.Title += $" > {first.Title}"; - } - - first.Title = @group.Title; } } - - foreach (var result in results) + } + else if (first.ModuleDefId > 0 && first.ModuleDefId == this.htmlModuleDefinitionId) + { + // special handling for Html module + var tabTitle = GetTabTitleFromModuleId(this.hostSettings, first.ModuleId); + if (!string.IsNullOrEmpty(tabTitle)) { - var title = showFriendlyTitle ? GetFriendlyTitle(result) : result.Title; - var detail = new DetailedView + @group.Title = tabTitle; + if (first.Title != "Enter Title" && first.Title != "Text/HTML") { - Title = title != null && title.Contains("<") ? HttpUtility.HtmlEncode(title) : title, - DocumentTypeName = InternalSearchController.Instance.GetSearchDocumentTypeDisplayName(result), - DocumentUrl = result.Url, - Snippet = result.Snippet, - Description = result.Description, - DisplayModifiedTime = result.DisplayModifiedTime, - Tags = result.Tags.ToList(), - AuthorProfileUrl = result.AuthorUserId > 0 ? Globals.UserProfileURL(result.AuthorUserId) : string.Empty, - AuthorName = result.AuthorName, - }; - @group.Results.Add(detail); + @group.Title += $" > {first.Title}"; + } + + first.Title = @group.Title; } + } - groups.Add(@group); + foreach (var result in results) + { + var title = showFriendlyTitle ? GetFriendlyTitle(result) : result.Title; + var detail = new DetailedView + { + Title = title != null && title.Contains("<") ? HttpUtility.HtmlEncode(title) : title, + DocumentTypeName = InternalSearchController.Instance.GetSearchDocumentTypeDisplayName(result), + DocumentUrl = result.Url, + Snippet = result.Snippet, + Description = result.Description, + DisplayModifiedTime = result.DisplayModifiedTime, + Tags = result.Tags.ToList(), + AuthorProfileUrl = result.AuthorUserId > 0 ? Globals.UserProfileURL(result.AuthorUserId) : string.Empty, + AuthorName = result.AuthorName, + }; + @group.Results.Add(detail); } - return groups; + groups.Add(@group); } - /// Gets grouped basic views. - /// The query. - /// The user search source. - /// The portal ID. - /// A list of instances. - internal List GetGroupedBasicViews(SearchQuery query, SearchContentSource userSearchSource, int portalId) - { - var results = new List(); - var previews = this.GetBasicViews(query, out _); + return groups; + } - foreach (var preview in previews) + /// Gets grouped basic views. + /// The query. + /// The user search source. + /// The portal ID. + /// A list of instances. + internal List GetGroupedBasicViews(SearchQuery query, SearchContentSource userSearchSource, int portalId) + { + var results = new List(); + var previews = this.GetBasicViews(query, out _); + + foreach (var preview in previews) + { + // if the document type is user, then try to add user pic into preview's custom attributes. + if (userSearchSource != null && preview.DocumentTypeName == userSearchSource.LocalizedName) { - // if the document type is user, then try to add user pic into preview's custom attributes. - if (userSearchSource != null && preview.DocumentTypeName == userSearchSource.LocalizedName) + var match = GroupedBasicViewRegex.Match(preview.DocumentUrl); + if (match.Success) { - var match = GroupedBasicViewRegex.Match(preview.DocumentUrl); - if (match.Success) + var userid = Convert.ToInt32(match.Groups[2].Value, CultureInfo.InvariantCulture); + var user = UserController.Instance.GetUserById(portalId, userid); + if (user != null) { - var userid = Convert.ToInt32(match.Groups[2].Value, CultureInfo.InvariantCulture); - var user = UserController.Instance.GetUserById(portalId, userid); - if (user != null) - { - preview.Attributes.Add("Avatar", user.Profile.PhotoURL); - } + preview.Attributes.Add("Avatar", user.Profile.PhotoURL); } } + } - var groupedResult = results.SingleOrDefault(g => g.DocumentTypeName == preview.DocumentTypeName); - if (groupedResult != null) + var groupedResult = results.SingleOrDefault(g => g.DocumentTypeName == preview.DocumentTypeName); + if (groupedResult != null) + { + if (!groupedResult.Results.Any(r => string.Equals(r.DocumentUrl, preview.DocumentUrl, StringComparison.Ordinal))) { - if (!groupedResult.Results.Any(r => string.Equals(r.DocumentUrl, preview.DocumentUrl, StringComparison.Ordinal))) + groupedResult.Results.Add(new BasicView { - groupedResult.Results.Add(new BasicView - { - Title = preview.Title.Contains("<") ? HttpUtility.HtmlEncode(preview.Title) : preview.Title, - Snippet = preview.Snippet, - Description = preview.Description, - DocumentUrl = preview.DocumentUrl, - Attributes = preview.Attributes, - }); - } - } - else - { - results.Add(new GroupedBasicView(preview)); + Title = preview.Title.Contains("<") ? HttpUtility.HtmlEncode(preview.Title) : preview.Title, + Snippet = preview.Snippet, + Description = preview.Description, + DocumentUrl = preview.DocumentUrl, + Attributes = preview.Attributes, + }); } } - - return results; + else + { + results.Add(new GroupedBasicView(preview)); + } } - /// Gets basic views. - /// The search query. - /// The total number of hits. - /// A sequence of instances. - internal IEnumerable GetBasicViews(SearchQuery searchQuery, out int totalHits) + return results; + } + + /// Gets basic views. + /// The search query. + /// The total number of hits. + /// A sequence of instances. + internal IEnumerable GetBasicViews(SearchQuery searchQuery, out int totalHits) + { + var sResult = this.searchController.SiteSearch(searchQuery); + totalHits = sResult.TotalHits; + var showFriendlyTitle = this.GetBooleanSetting("ShowFriendlyTitle", true); + var showDescription = this.GetBooleanSetting("ShowDescription", true); + var showSnippet = this.GetBooleanSetting("ShowSnippet", true); + var maxDescriptionLength = this.GetIntegerSetting("MaxDescriptionLength", 100); + + return sResult.Results.Select(result => { - var sResult = this.searchController.SiteSearch(searchQuery); - totalHits = sResult.TotalHits; - var showFriendlyTitle = this.GetBooleanSetting("ShowFriendlyTitle", true); - var showDescription = this.GetBooleanSetting("ShowDescription", true); - var showSnippet = this.GetBooleanSetting("ShowSnippet", true); - var maxDescriptionLength = this.GetIntegerSetting("MaxDescriptionLength", 100); - - return sResult.Results.Select(result => + var description = result.Description; + if (!string.IsNullOrEmpty(description) && description.Length > maxDescriptionLength) { - var description = result.Description; - if (!string.IsNullOrEmpty(description) && description.Length > maxDescriptionLength) - { - description = description.Substring(0, maxDescriptionLength) + "..."; - } + description = description.Substring(0, maxDescriptionLength) + "..."; + } - return new BasicView - { - Title = this.GetTitle(result, showFriendlyTitle), - DocumentTypeName = InternalSearchController.Instance.GetSearchDocumentTypeDisplayName(result), - DocumentUrl = result.Url, - Snippet = showSnippet ? result.Snippet : string.Empty, - Description = showDescription ? description : string.Empty, - }; - }); - } + return new BasicView + { + Title = this.GetTitle(result, showFriendlyTitle), + DocumentTypeName = InternalSearchController.Instance.GetSearchDocumentTypeDisplayName(result), + DocumentUrl = result.Url, + Snippet = showSnippet ? result.Snippet : string.Empty, + Description = showDescription ? description : string.Empty, + }; + }); + } + + private static ArrayList GetModulesByDefinition(IHostSettings hostSettings, int portalId, string friendlyName) + { + var cacheKey = string.Format(CultureInfo.InvariantCulture, ModuleInfosCacheKey, portalId); + return CBO.GetCachedObject( + hostSettings, + new CacheItemArgs(cacheKey, ModuleInfosCacheTimeOut, ModuleInfosCachePriority), + _ => CBO.FillCollection(DataProvider.Instance().GetModuleByDefinition(portalId, friendlyName), typeof(ModuleInfo))); + } - private static ArrayList GetModulesByDefinition(IHostSettings hostSettings, int portalId, string friendlyName) + private static List GetSearchTypeIds(Hashtable settings, IEnumerable searchContentSources) + { + var list = new List(); + var configuredList = new List(); + var scopeForFilters = Convert.ToString(settings["ScopeForFilters"], CultureInfo.InvariantCulture); + if (!string.IsNullOrEmpty(scopeForFilters)) { - var cacheKey = string.Format(CultureInfo.InvariantCulture, ModuleInfosCacheKey, portalId); - return CBO.GetCachedObject( - hostSettings, - new CacheItemArgs(cacheKey, ModuleInfosCacheTimeOut, ModuleInfosCachePriority), - _ => CBO.FillCollection(DataProvider.Instance().GetModuleByDefinition(portalId, friendlyName), typeof(ModuleInfo))); + configuredList = scopeForFilters.Split('|').ToList(); } - private static List GetSearchTypeIds(Hashtable settings, IEnumerable searchContentSources) + // check content source in configured list or not + foreach (var contentSource in searchContentSources) { - var list = new List(); - var configuredList = new List(); - var scopeForFilters = Convert.ToString(settings["ScopeForFilters"], CultureInfo.InvariantCulture); - if (!string.IsNullOrEmpty(scopeForFilters)) + if (contentSource.IsPrivate) { - configuredList = scopeForFilters.Split('|').ToList(); + continue; } - // check content source in configured list or not - foreach (var contentSource in searchContentSources) + if (configuredList.Count > 0) { - if (contentSource.IsPrivate) - { - continue; - } - - if (configuredList.Count > 0) - { - if (configuredList.Any(l => l.Contains(contentSource.LocalizedName))) - { - // in configured list - list.Add(contentSource.SearchTypeId); - } - } - else + if (configuredList.Any(l => l.Contains(contentSource.LocalizedName))) { + // in configured list list.Add(contentSource.SearchTypeId); } } + else + { + list.Add(contentSource.SearchTypeId); + } + } + + return list.Distinct().ToList(); + } - return list.Distinct().ToList(); + private static List GetSearchModuleDefIds(Hashtable settings, IEnumerable searchContentSources) + { + var list = new List(); + var configuredList = new List(); + var scopeForFilters = Convert.ToString(settings["ScopeForFilters"], CultureInfo.InvariantCulture); + if (!string.IsNullOrEmpty(scopeForFilters)) + { + configuredList = scopeForFilters.Split('|').ToList(); } - private static List GetSearchModuleDefIds(Hashtable settings, IEnumerable searchContentSources) + // check content source in configured list or not + foreach (var contentSource in searchContentSources) { - var list = new List(); - var configuredList = new List(); - var scopeForFilters = Convert.ToString(settings["ScopeForFilters"], CultureInfo.InvariantCulture); - if (!string.IsNullOrEmpty(scopeForFilters)) + if (contentSource.IsPrivate) { - configuredList = scopeForFilters.Split('|').ToList(); + continue; } - // check content source in configured list or not - foreach (var contentSource in searchContentSources) + if (configuredList.Count > 0) { - if (contentSource.IsPrivate) + if (configuredList.Any(l => l.Contains(contentSource.LocalizedName)) && contentSource.ModuleDefinitionId > 0) { - continue; + // in configured list + list.Add(contentSource.ModuleDefinitionId); } - - if (configuredList.Count > 0) - { - if (configuredList.Any(l => l.Contains(contentSource.LocalizedName)) && contentSource.ModuleDefinitionId > 0) - { - // in configured list - list.Add(contentSource.ModuleDefinitionId); - } - } - else + } + else + { + if (contentSource.ModuleDefinitionId > 0) { - if (contentSource.ModuleDefinitionId > 0) - { - list.Add(contentSource.ModuleDefinitionId); - } + list.Add(contentSource.ModuleDefinitionId); } } - - return list; } - private static string GetFriendlyTitle(SearchResult result) - { - if (result.Keywords.TryGetValue("title", out var title) && !string.IsNullOrEmpty(title)) - { - return title; - } - - return result.Title; - } + return list; + } - private static string GetTabTitleFromModuleId(IHostSettings hostSettings, int moduleId) + private static string GetFriendlyTitle(SearchResult result) + { + if (result.Keywords.TryGetValue("title", out var title) && !string.IsNullOrEmpty(title)) { - // no manual clearing of the cache exists; let it just expire - var cacheKey = string.Format(CultureInfo.InvariantCulture, ModuleTitleCacheKey, moduleId); - - return CBO.GetCachedObject( - hostSettings, - new CacheItemArgs(cacheKey, ModuleTitleCacheTimeOut, ModuleTitleCachePriority, moduleId), - GetTabTitleCallBack); + return title; } - private static object GetTabTitleCallBack(CacheItemArgs cacheItemArgs) - { - var moduleId = (int)cacheItemArgs.ParamList[0]; - var moduleInfo = ModuleController.Instance.GetModule(moduleId, Null.NullInteger, true); - if (moduleInfo != null) - { - var tab = moduleInfo.ParentTab; + return result.Title; + } - return !string.IsNullOrEmpty(tab.Title) ? tab.Title : tab.TabName; - } + private static string GetTabTitleFromModuleId(IHostSettings hostSettings, int moduleId) + { + // no manual clearing of the cache exists; let it just expire + var cacheKey = string.Format(CultureInfo.InvariantCulture, ModuleTitleCacheKey, moduleId); - return string.Empty; - } + return CBO.GetCachedObject( + hostSettings, + new CacheItemArgs(cacheKey, ModuleTitleCacheTimeOut, ModuleTitleCachePriority, moduleId), + GetTabTitleCallBack); + } - private bool IsWildCardEnabledForModule() + private static object GetTabTitleCallBack(CacheItemArgs cacheItemArgs) + { + var moduleId = (int)cacheItemArgs.ParamList[0]; + var moduleInfo = ModuleController.Instance.GetModule(moduleId, Null.NullInteger, true); + if (moduleInfo != null) { - var searchModuleSettings = this.GetSearchModuleSettings(); - var enableWildSearch = true; - if (!string.IsNullOrEmpty(Convert.ToString(searchModuleSettings["EnableWildSearch"], CultureInfo.InvariantCulture))) - { - enableWildSearch = Convert.ToBoolean(searchModuleSettings["EnableWildSearch"], CultureInfo.InvariantCulture); - } + var tab = moduleInfo.ParentTab; - return enableWildSearch; + return !string.IsNullOrEmpty(tab.Title) ? tab.Title : tab.TabName; } - private ModuleInfo GetSearchModule() - { - var arrModules = GetModulesByDefinition(this.hostSettings, this.PortalSettings.PortalId, "Search Results"); - ModuleInfo findModule = null; - if (arrModules.Count > 1) - { - findModule = arrModules.Cast().FirstOrDefault(searchModule => searchModule.CultureCode == this.PortalSettings.CultureCode); - } + return string.Empty; + } - return findModule ?? (arrModules.Count > 0 ? (ModuleInfo)arrModules[0] : null); + private bool IsWildCardEnabledForModule() + { + var searchModuleSettings = this.GetSearchModuleSettings(); + var enableWildSearch = true; + if (!string.IsNullOrEmpty(Convert.ToString(searchModuleSettings["EnableWildSearch"], CultureInfo.InvariantCulture))) + { + enableWildSearch = Convert.ToBoolean(searchModuleSettings["EnableWildSearch"], CultureInfo.InvariantCulture); } - private Hashtable GetSearchModuleSettings() - { - if (this.ActiveModule != null && this.ActiveModule.ModuleDefinition.FriendlyName == "Search Results") - { - return this.ActiveModule.ModuleSettings; - } + return enableWildSearch; + } - var searchModule = this.GetSearchModule(); - return searchModule?.ModuleSettings; + private ModuleInfo GetSearchModule() + { + var arrModules = GetModulesByDefinition(this.hostSettings, this.PortalSettings.PortalId, "Search Results"); + ModuleInfo findModule = null; + if (arrModules.Count > 1) + { + findModule = arrModules.Cast().FirstOrDefault(searchModule => searchModule.CultureCode == this.PortalSettings.CultureCode); } - private bool GetBooleanSetting(string settingName, bool defaultValue) + return findModule ?? (arrModules.Count > 0 ? (ModuleInfo)arrModules[0] : null); + } + + private Hashtable GetSearchModuleSettings() + { + if (this.ActiveModule != null && this.ActiveModule.ModuleDefinition.FriendlyName == "Search Results") { - if (this.PortalSettings == null) - { - return defaultValue; - } + return this.ActiveModule.ModuleSettings; + } - var settings = this.GetSearchModuleSettings(); - if (settings == null || !settings.ContainsKey(settingName)) - { - return defaultValue; - } + var searchModule = this.GetSearchModule(); + return searchModule?.ModuleSettings; + } - return Convert.ToBoolean(settings[settingName], CultureInfo.InvariantCulture); + private bool GetBooleanSetting(string settingName, bool defaultValue) + { + if (this.PortalSettings == null) + { + return defaultValue; } - private int GetIntegerSetting(string settingName, int defaultValue) + var settings = this.GetSearchModuleSettings(); + if (settings == null || !settings.ContainsKey(settingName)) { - if (this.PortalSettings == null) - { - return defaultValue; - } + return defaultValue; + } - var settings = this.GetSearchModuleSettings(); - if (settings == null || !settings.ContainsKey(settingName)) - { - return defaultValue; - } + return Convert.ToBoolean(settings[settingName], CultureInfo.InvariantCulture); + } - var settingValue = Convert.ToString(settings[settingName], CultureInfo.InvariantCulture); - if (!string.IsNullOrEmpty(settingValue) && Regex.IsMatch(settingValue, "^\\d+$")) - { - return Convert.ToInt32(settingValue, CultureInfo.InvariantCulture); - } + private int GetIntegerSetting(string settingName, int defaultValue) + { + if (this.PortalSettings == null) + { + return defaultValue; + } + var settings = this.GetSearchModuleSettings(); + if (settings == null || !settings.ContainsKey(settingName)) + { return defaultValue; } - private List GetSearchPortalIds(Hashtable settings, int portalId) + var settingValue = Convert.ToString(settings[settingName], CultureInfo.InvariantCulture); + if (!string.IsNullOrEmpty(settingValue) && Regex.IsMatch(settingValue, "^\\d+$")) { - var list = new List(); - var scopeForPortals = Convert.ToString(settings["ScopeForPortals"], CultureInfo.InvariantCulture); - if (!string.IsNullOrEmpty(scopeForPortals)) - { - list = scopeForPortals.Split('|').Select(s => Convert.ToInt32(s, CultureInfo.InvariantCulture)).ToList(); - } + return Convert.ToInt32(settingValue, CultureInfo.InvariantCulture); + } - if (portalId == -1) - { - portalId = this.PortalSettings.ActiveTab.PortalID; - } + return defaultValue; + } - if (portalId > -1 && !list.Contains(portalId)) - { - list.Add(portalId); - } + private List GetSearchPortalIds(Hashtable settings, int portalId) + { + var list = new List(); + var scopeForPortals = Convert.ToString(settings["ScopeForPortals"], CultureInfo.InvariantCulture); + if (!string.IsNullOrEmpty(scopeForPortals)) + { + list = scopeForPortals.Split('|').Select(s => Convert.ToInt32(s, CultureInfo.InvariantCulture)).ToList(); + } - // Add Host - var userInfo = this.UserInfo; - if (userInfo.IsSuperUser) - { - list.Add(-1); - } + if (portalId == -1) + { + portalId = this.PortalSettings.ActiveTab.PortalID; + } - return list; + if (portalId > -1 && !list.Contains(portalId)) + { + list.Add(portalId); } - private List GetSearchContentSources(IList typesList) + // Add Host + var userInfo = this.UserInfo; + if (userInfo.IsSuperUser) { - var sources = new List(); - var list = InternalSearchController.Instance.GetSearchContentSourceList(this.PortalSettings.PortalId); + list.Add(-1); + } - if (typesList.Any()) - { - foreach (var contentSources in typesList.Select(t1 => list.Where(src => string.Equals(src.LocalizedName, t1, StringComparison.OrdinalIgnoreCase)))) - { - sources.AddRange(contentSources); - } - } - else + return list; + } + + private List GetSearchContentSources(IList typesList) + { + var sources = new List(); + var list = InternalSearchController.Instance.GetSearchContentSourceList(this.PortalSettings.PortalId); + + if (typesList.Any()) + { + foreach (var contentSources in typesList.Select(t1 => list.Where(src => string.Equals(src.LocalizedName, t1, StringComparison.OrdinalIgnoreCase)))) { - // no types filter specified, add all available content sources - sources.AddRange(list); + sources.AddRange(contentSources); } - - return sources; } + else + { + // no types filter specified, add all available content sources + sources.AddRange(list); + } + + return sources; + } - private string GetTitle(SearchResult result, bool showFriendlyTitle = false) + private string GetTitle(SearchResult result, bool showFriendlyTitle = false) + { + if (result.ModuleDefId > 0 && result.ModuleDefId == this.htmlModuleDefinitionId) { - if (result.ModuleDefId > 0 && result.ModuleDefId == this.htmlModuleDefinitionId) + // special handling for Html module + var tabTitle = GetTabTitleFromModuleId(this.hostSettings, result.ModuleId); + if (!string.IsNullOrEmpty(tabTitle)) { - // special handling for Html module - var tabTitle = GetTabTitleFromModuleId(this.hostSettings, result.ModuleId); - if (!string.IsNullOrEmpty(tabTitle)) + if (result.Title != "Enter Title" && result.Title != "Text/HTML") { - if (result.Title != "Enter Title" && result.Title != "Text/HTML") - { - return $"{tabTitle} > {result.Title}"; - } - - return tabTitle; + return $"{tabTitle} > {result.Title}"; } - } - return showFriendlyTitle ? GetFriendlyTitle(result) : result.Title; + return tabTitle; + } } - /// A data transfer object with information about a synonyms group. - public class SynonymsGroupDto - { - /// Gets or sets the ID. - public int Id { get; set; } + return showFriendlyTitle ? GetFriendlyTitle(result) : result.Title; + } - /// Gets or sets the tags. - public string Tags { get; set; } + /// A data transfer object with information about a synonyms group. + public class SynonymsGroupDto + { + /// Gets or sets the ID. + public int Id { get; set; } - /// Gets or sets the portal ID. - public int PortalId { get; set; } + /// Gets or sets the tags. + public string Tags { get; set; } - /// Gets or sets the culture. - public string Culture { get; set; } - } + /// Gets or sets the portal ID. + public int PortalId { get; set; } - /// A data transfer object with information about stop words. - public class StopWordsDto - { - /// Gets or sets the ID. - public int Id { get; set; } + /// Gets or sets the culture. + public string Culture { get; set; } + } + + /// A data transfer object with information about stop words. + public class StopWordsDto + { + /// Gets or sets the ID. + public int Id { get; set; } - /// Gets or sets the words. - public string Words { get; set; } + /// Gets or sets the words. + public string Words { get; set; } - /// Gets or sets the portal ID. - public int PortalId { get; set; } + /// Gets or sets the portal ID. + public int PortalId { get; set; } - /// Gets or sets the culture. - public string Culture { get; set; } - } + /// Gets or sets the culture. + public string Culture { get; set; } } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/ServiceRouteMapper.cs b/DNN Platform/DotNetNuke.Web/InternalServices/ServiceRouteMapper.cs index 4e8308e2ab1..79bd8c377a3 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/ServiceRouteMapper.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/ServiceRouteMapper.cs @@ -2,23 +2,22 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using DotNetNuke.Web.Api; + +/// A web API service route mapper for internal services. +public class ServiceRouteMapper : IServiceRouteMapper { - using DotNetNuke.Web.Api; + private static readonly string[] Namespaces = ["DotNetNuke.Web.InternalServices",]; - /// A web API service route mapper for internal services. - public class ServiceRouteMapper : IServiceRouteMapper + /// + public void RegisterRoutes(IMapRoute mapRouteManager) { - private static readonly string[] Namespaces = ["DotNetNuke.Web.InternalServices",]; - - /// - public void RegisterRoutes(IMapRoute mapRouteManager) - { - mapRouteManager.MapHttpRoute( - "InternalServices", - "default", - "{controller}/{action}", - Namespaces); - } + mapRouteManager.MapHttpRoute( + "InternalServices", + "default", + "{controller}/{action}", + Namespaces); } } diff --git a/DNN Platform/DotNetNuke.Web/InternalServices/UserFileController.cs b/DNN Platform/DotNetNuke.Web/InternalServices/UserFileController.cs index 86a56d97ecc..034477a39b9 100644 --- a/DNN Platform/DotNetNuke.Web/InternalServices/UserFileController.cs +++ b/DNN Platform/DotNetNuke.Web/InternalServices/UserFileController.cs @@ -2,171 +2,170 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information -namespace DotNetNuke.Web.InternalServices +namespace DotNetNuke.Web.InternalServices; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Net; +using System.Net.Http; +using System.Web.Http; + +using DotNetNuke.Entities.Icons; +using DotNetNuke.Instrumentation; +using DotNetNuke.Services.FileSystem; +using DotNetNuke.Services.Localization; +using DotNetNuke.Web.Api; + +/// A web API controller for user files. +public class UserFileController : DnnApiController { - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Net; - using System.Net.Http; - using System.Web.Http; - - using DotNetNuke.Entities.Icons; - using DotNetNuke.Instrumentation; - using DotNetNuke.Services.FileSystem; - using DotNetNuke.Services.Localization; - using DotNetNuke.Web.Api; - - /// A web API controller for user files. - public class UserFileController : DnnApiController + private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(UserFileController)); + private static readonly char[] FileExtensionSeparator = [',',]; + private static readonly HashSet ImageExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { "jpg", "png", "gif", "jpe", "jpeg", "tiff", }; + private readonly IFolderManager folderManager = FolderManager.Instance; + + /// Gets the items in the user's folder. + /// A response with a list of objects (containing the following fields: id, name, folder, parentId, thumb_url, type, size, modified, and children). + [DnnAuthorize] + [HttpGet] + public HttpResponseMessage GetItems() { - private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(UserFileController)); - private static readonly char[] FileExtensionSeparator = [',',]; - private static readonly HashSet ImageExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { "jpg", "png", "gif", "jpe", "jpeg", "tiff", }; - private readonly IFolderManager folderManager = FolderManager.Instance; - - /// Gets the items in the user's folder. - /// A response with a list of objects (containing the following fields: id, name, folder, parentId, thumb_url, type, size, modified, and children). - [DnnAuthorize] - [HttpGet] - public HttpResponseMessage GetItems() - { - return this.GetItems(null); - } + return this.GetItems(null); + } - /// Gets the items in the user's folder. - /// A comma-delimited list of file extensions. - /// A response with a list of objects (containing the following fields: id, name, folder, parentId, thumb_url, type, size, modified, and children). - [DnnAuthorize] - [HttpGet] - public HttpResponseMessage GetItems(string fileExtensions) + /// Gets the items in the user's folder. + /// A comma-delimited list of file extensions. + /// A response with a list of objects (containing the following fields: id, name, folder, parentId, thumb_url, type, size, modified, and children). + [DnnAuthorize] + [HttpGet] + public HttpResponseMessage GetItems(string fileExtensions) + { + try { - try - { - var userFolder = this.folderManager.GetUserFolder(this.UserInfo); - var extensions = new List(); - - if (!string.IsNullOrEmpty(fileExtensions)) - { - fileExtensions = fileExtensions.ToLowerInvariant(); - extensions.AddRange(fileExtensions.Split(FileExtensionSeparator, StringSplitOptions.RemoveEmptyEntries)); - } + var userFolder = this.folderManager.GetUserFolder(this.UserInfo); + var extensions = new List(); - var folderStructure = new - { - id = userFolder.FolderID, - name = Localization.GetString("UserFolderTitle.Text", Localization.SharedResourceFile), - folder = true, - parentId = 0, - thumb_url = default(string), - type = default(string), - size = default(string), - modified = default(string), - children = this.GetChildren(userFolder, extensions), - }; - - return this.Request.CreateResponse(HttpStatusCode.OK, new List { folderStructure }); - } - catch (Exception exc) + if (!string.IsNullOrEmpty(fileExtensions)) { - Logger.Error(exc); - return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc); + fileExtensions = fileExtensions.ToLowerInvariant(); + extensions.AddRange(fileExtensions.Split(FileExtensionSeparator, StringSplitOptions.RemoveEmptyEntries)); } - } - private static string GetModifiedTime(DateTime dateTime) + var folderStructure = new + { + id = userFolder.FolderID, + name = Localization.GetString("UserFolderTitle.Text", Localization.SharedResourceFile), + folder = true, + parentId = 0, + thumb_url = default(string), + type = default(string), + size = default(string), + modified = default(string), + children = this.GetChildren(userFolder, extensions), + }; + + return this.Request.CreateResponse(HttpStatusCode.OK, new List { folderStructure }); + } + catch (Exception exc) { - return string.Format(CultureInfo.CurrentCulture, "{0:MMM} {0:dd}, {0:yyyy} at {0:t}", dateTime); + Logger.Error(exc); + return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc); } + } + + private static string GetModifiedTime(DateTime dateTime) + { + return string.Format(CultureInfo.CurrentCulture, "{0:MMM} {0:dd}, {0:yyyy} at {0:t}", dateTime); + } + + private static string GetTypeName(IFileInfo file) + { + return file.ContentType == null + ? string.Empty + : (file.ContentType.StartsWith("image/", StringComparison.Ordinal) + ? file.ContentType.Replace("image/", string.Empty) + : (file.Extension != null ? file.Extension.ToLowerInvariant() : string.Empty)); + } + + private static bool IsImageFile(string relativePath) + { + var extension = relativePath.Substring(relativePath.LastIndexOf(".", StringComparison.Ordinal) + 1); + return ImageExtensions.Contains(extension); + } - private static string GetTypeName(IFileInfo file) + private static string GetFileSize(int sizeInBytes) + { + var size = sizeInBytes / 1024; + var biggerThanAMegabyte = size > 1024; + if (biggerThanAMegabyte) { - return file.ContentType == null - ? string.Empty - : (file.ContentType.StartsWith("image/", StringComparison.Ordinal) - ? file.ContentType.Replace("image/", string.Empty) - : (file.Extension != null ? file.Extension.ToLowerInvariant() : string.Empty)); + size = size / 1024; } - private static bool IsImageFile(string relativePath) + return size.ToString(CultureInfo.InvariantCulture) + (biggerThanAMegabyte ? "Mb" : "k"); + } + + private string GetThumbUrl(IFileInfo file) + { + if (IsImageFile(file.RelativePath)) { - var extension = relativePath.Substring(relativePath.LastIndexOf(".", StringComparison.Ordinal) + 1); - return ImageExtensions.Contains(extension); + return FileManager.Instance.GetUrl(file); } - private static string GetFileSize(int sizeInBytes) + var fileIcon = IconController.IconURL("Ext" + file.Extension, "32x32"); + if (!System.IO.File.Exists(this.Request.GetHttpContext().Server.MapPath(fileIcon))) { - var size = sizeInBytes / 1024; - var biggerThanAMegabyte = size > 1024; - if (biggerThanAMegabyte) - { - size = size / 1024; - } - - return size.ToString(CultureInfo.InvariantCulture) + (biggerThanAMegabyte ? "Mb" : "k"); + fileIcon = IconController.IconURL("File", "32x32"); } - private string GetThumbUrl(IFileInfo file) - { - if (IsImageFile(file.RelativePath)) - { - return FileManager.Instance.GetUrl(file); - } + return fileIcon; + } - var fileIcon = IconController.IconURL("Ext" + file.Extension, "32x32"); - if (!System.IO.File.Exists(this.Request.GetHttpContext().Server.MapPath(fileIcon))) - { - fileIcon = IconController.IconURL("File", "32x32"); - } + private List GetChildren(IFolderInfo folder, ICollection extensions) + { + var everything = new List(); - return fileIcon; - } + var folders = this.folderManager.GetFolders(folder); - private List GetChildren(IFolderInfo folder, ICollection extensions) + foreach (var currentFolder in folders) { - var everything = new List(); + everything.Add(new + { + id = currentFolder.FolderID, + name = currentFolder.DisplayName ?? currentFolder.FolderName, + folder = true, + parentId = folder.FolderID, + thumb_url = default(string), + type = default(string), + size = default(string), + modified = default(string), + children = this.GetChildren(currentFolder, extensions), + }); + } - var folders = this.folderManager.GetFolders(folder); + var files = this.folderManager.GetFiles(folder); - foreach (var currentFolder in folders) + foreach (var file in files) + { + // list is empty or contains the file extension in question + if (extensions.Count == 0 || extensions.Contains(file.Extension.ToLowerInvariant())) { everything.Add(new { - id = currentFolder.FolderID, - name = currentFolder.DisplayName ?? currentFolder.FolderName, - folder = true, - parentId = folder.FolderID, - thumb_url = default(string), - type = default(string), - size = default(string), - modified = default(string), - children = this.GetChildren(currentFolder, extensions), + id = file.FileId, + name = file.FileName, + folder = false, + parentId = file.FolderId, + thumb_url = this.GetThumbUrl(file), + type = GetTypeName(file), + size = GetFileSize(file.Size), + modified = GetModifiedTime(file.LastModificationTime), + children = default(List), }); } - - var files = this.folderManager.GetFiles(folder); - - foreach (var file in files) - { - // list is empty or contains the file extension in question - if (extensions.Count == 0 || extensions.Contains(file.Extension.ToLowerInvariant())) - { - everything.Add(new - { - id = file.FileId, - name = file.FileName, - folder = false, - parentId = file.FolderId, - thumb_url = this.GetThumbUrl(file), - type = GetTypeName(file), - size = GetFileSize(file.Size), - modified = GetModifiedTime(file.LastModificationTime), - children = default(List), - }); - } - } - - return everything; } + + return everything; } } diff --git a/DNN Platform/Library/Data/DataProvider.cs b/DNN Platform/Library/Data/DataProvider.cs index dea87b3dad4..2baac1d5103 100644 --- a/DNN Platform/Library/Data/DataProvider.cs +++ b/DNN Platform/Library/Data/DataProvider.cs @@ -18,6 +18,7 @@ namespace DotNetNuke.Data using System.Web.Hosting; using DotNetNuke.Abstractions.Application; + using DotNetNuke.Abstractions.Framework; using DotNetNuke.Common; using DotNetNuke.Common.Utilities; using DotNetNuke.ComponentModel; @@ -889,7 +890,15 @@ public virtual int SaveTabVersionDetail(int tabVersionDetailId, int tabVersionId return this.ExecuteScalar("SaveTabVersionDetail", tabVersionDetailId, tabVersionId, moduleId, moduleVersion, paneName, moduleOrder, action, createdByUserID, modifiedByUserID); } - public virtual void UpdateTab(int tabId, int contentItemId, int portalId, Guid versionGuid, Guid defaultLanguageGuid, Guid localizedVersionGuid, string tabName, bool isVisible, bool disableLink, int parentId, string iconFile, string iconFileLarge, string title, string description, string keyWords, bool isDeleted, string url, string skinSrc, string containerSrc, DateTime startDate, DateTime endDate, int refreshInterval, string pageHeadText, bool isSecure, bool permanentRedirect, float siteMapPriority, int lastModifiedByuserID, string cultureCode, bool isSystem) + [DnnDeprecated(11, 0, 0, "Use UpdateTab with pagePipeline parameter instead.")] +#pragma warning disable SA1601 // Partial elements should be documented + public virtual partial void UpdateTab(int tabId, int contentItemId, int portalId, Guid versionGuid, Guid defaultLanguageGuid, Guid localizedVersionGuid, string tabName, bool isVisible, bool disableLink, int parentId, string iconFile, string iconFileLarge, string title, string description, string keyWords, bool isDeleted, string url, string skinSrc, string containerSrc, DateTime startDate, DateTime endDate, int refreshInterval, string pageHeadText, bool isSecure, bool permanentRedirect, float siteMapPriority, int lastModifiedByuserID, string cultureCode, bool isSystem) +#pragma warning restore SA1601 // Partial elements should be documented + { + this.UpdateTab(tabId, contentItemId, portalId, versionGuid, defaultLanguageGuid, localizedVersionGuid, tabName, isVisible, disableLink, parentId, iconFile, iconFileLarge, title, description, keyWords, isDeleted, url, skinSrc, containerSrc, startDate, endDate, refreshInterval, pageHeadText, isSecure, permanentRedirect, siteMapPriority, lastModifiedByuserID, cultureCode, isSystem, (int)PagePipeline.PageRenderingPipeline.Inherited); + } + + public virtual void UpdateTab(int tabId, int contentItemId, int portalId, Guid versionGuid, Guid defaultLanguageGuid, Guid localizedVersionGuid, string tabName, bool isVisible, bool disableLink, int parentId, string iconFile, string iconFileLarge, string title, string description, string keyWords, bool isDeleted, string url, string skinSrc, string containerSrc, DateTime startDate, DateTime endDate, int refreshInterval, string pageHeadText, bool isSecure, bool permanentRedirect, float siteMapPriority, int lastModifiedByuserID, string cultureCode, bool isSystem, int pagePipeline) { this.ExecuteNonQuery( "UpdateTab", @@ -921,7 +930,8 @@ public virtual void UpdateTab(int tabId, int contentItemId, int portalId, Guid v siteMapPriority, lastModifiedByuserID, this.GetNull(cultureCode), - isSystem); + isSystem, + pagePipeline); } public virtual void UpdateTabOrder(int tabId, int tabOrder, int parentId, int lastModifiedByUserID) @@ -1329,7 +1339,15 @@ public virtual void UpdateModuleDefinition(int moduleDefId, string friendlyName, lastModifiedByUserID); } - public virtual int AddModuleControl(int moduleDefId, string controlKey, string controlTitle, string controlSrc, string iconFile, int controlType, int viewOrder, string helpUrl, bool supportsPartialRendering, bool supportsPopUps, int createdByUserID) + [DnnDeprecated(10, 99, 0, "Use overload with mvcControlClass")] +#pragma warning disable SA1601 // Partial elements should be documented + public virtual partial int AddModuleControl(int moduleDefId, string controlKey, string controlTitle, string controlSrc, string iconFile, int controlType, int viewOrder, string helpUrl, bool supportsPartialRendering, bool supportsPopUps, int createdByUserID) +#pragma warning restore SA1601 // Partial elements should be documented + { + return this.AddModuleControl(moduleDefId, controlKey, controlTitle, controlSrc, null, iconFile, controlType, viewOrder, helpUrl, supportsPartialRendering, supportsPopUps, createdByUserID); + } + + public virtual int AddModuleControl(int moduleDefId, string controlKey, string controlTitle, string controlSrc, string mvcControlClass, string iconFile, int controlType, int viewOrder, string helpUrl, bool supportsPartialRendering, bool supportsPopUps, int createdByUserID) { return this.ExecuteScalar( "AddModuleControl", @@ -1337,6 +1355,7 @@ public virtual int AddModuleControl(int moduleDefId, string controlKey, string c this.GetNull(controlKey), this.GetNull(controlTitle), controlSrc, + this.GetNull(mvcControlClass), this.GetNull(iconFile), controlType, this.GetNull(viewOrder), @@ -1356,7 +1375,13 @@ public virtual IDataReader GetModuleControls() return this.ExecuteReader("GetModuleControls"); } - public virtual void UpdateModuleControl(int moduleControlId, int moduleDefId, string controlKey, string controlTitle, string controlSrc, string iconFile, int controlType, int viewOrder, string helpUrl, bool supportsPartialRendering, bool supportsPopUps, int lastModifiedByUserID) + [DnnDeprecated(10, 99, 0, "Use overload with mvcControlClass")] +#pragma warning disable SA1601 // Partial elements should be documented + public virtual partial void UpdateModuleControl(int moduleControlId, int moduleDefId, string controlKey, string controlTitle, string controlSrc, string iconFile, int controlType, int viewOrder, string helpUrl, bool supportsPartialRendering, bool supportsPopUps, int lastModifiedByUserID) +#pragma warning restore SA1601 // Partial elements should be documented + => this.UpdateModuleControl(moduleControlId, moduleDefId, controlKey, controlTitle, controlSrc, null, iconFile, controlType, viewOrder, helpUrl, supportsPartialRendering, supportsPopUps, lastModifiedByUserID); + + public virtual void UpdateModuleControl(int moduleControlId, int moduleDefId, string controlKey, string controlTitle, string controlSrc, string mvcControlClass, string iconFile, int controlType, int viewOrder, string helpUrl, bool supportsPartialRendering, bool supportsPopUps, int lastModifiedByUserID) { this.ExecuteNonQuery( "UpdateModuleControl", @@ -1365,6 +1390,7 @@ public virtual void UpdateModuleControl(int moduleControlId, int moduleDefId, st this.GetNull(controlKey), this.GetNull(controlTitle), controlSrc, + this.GetNull(mvcControlClass), this.GetNull(iconFile), controlType, this.GetNull(viewOrder), diff --git a/DNN Platform/Library/Entities/Modules/ControlInfo.cs b/DNN Platform/Library/Entities/Modules/ControlInfo.cs index 6f6e3d17c25..d435410a479 100644 --- a/DNN Platform/Library/Entities/Modules/ControlInfo.cs +++ b/DNN Platform/Library/Entities/Modules/ControlInfo.cs @@ -27,6 +27,10 @@ protected ControlInfo() /// A String. public string ControlSrc { get; set; } + /// Gets or sets the Mvc Control Class. + /// A String. + public string MvcControlClass { get; set; } + /// /// Gets or sets a value indicating whether the control support the AJAX /// Update Panel. @@ -42,6 +46,7 @@ protected override void FillInternal(IDataReader dr) base.FillInternal(dr); this.ControlKey = Null.SetNullString(dr["ControlKey"]); this.ControlSrc = Null.SetNullString(dr["ControlSrc"]); + this.MvcControlClass = Null.SetNullString(dr["MvcControlClass"]); this.SupportsPartialRendering = Null.SetNullBoolean(dr["SupportsPartialRendering"]); } @@ -55,6 +60,9 @@ protected void ReadXmlInternal(XmlReader reader) case "controlSrc": this.ControlSrc = reader.ReadElementContentAsString(); break; + case "mvcControlClass": + this.MvcControlClass = reader.ReadElementContentAsString(); + break; case "supportsPartialRendering": string elementvalue = reader.ReadElementContentAsString(); if (!string.IsNullOrEmpty(elementvalue)) @@ -71,6 +79,7 @@ protected void WriteXmlInternal(XmlWriter writer) // write out properties writer.WriteElementString("controlKey", this.ControlKey); writer.WriteElementString("controlSrc", this.ControlSrc); + writer.WriteElementString("mvcControlClass", this.MvcControlClass); writer.WriteElementString("supportsPartialRendering", this.SupportsPartialRendering.ToString()); } } diff --git a/DNN Platform/Library/Entities/Modules/ModuleControlController.cs b/DNN Platform/Library/Entities/Modules/ModuleControlController.cs index ed36766881d..beefc2d8bc3 100644 --- a/DNN Platform/Library/Entities/Modules/ModuleControlController.cs +++ b/DNN Platform/Library/Entities/Modules/ModuleControlController.cs @@ -110,6 +110,7 @@ public static int SaveModuleControl(ModuleControlInfo moduleControl, bool clearC moduleControl.ControlKey, moduleControl.ControlTitle, moduleControl.ControlSrc, + moduleControl.MvcControlClass, moduleControl.IconFile, (int)moduleControl.ControlType, moduleControl.ViewOrder, @@ -127,6 +128,7 @@ public static int SaveModuleControl(ModuleControlInfo moduleControl, bool clearC moduleControl.ControlKey, moduleControl.ControlTitle, moduleControl.ControlSrc, + moduleControl.MvcControlClass, moduleControl.IconFile, (int)moduleControl.ControlType, moduleControl.ViewOrder, diff --git a/DNN Platform/Library/Entities/Modules/ModuleInfo.cs b/DNN Platform/Library/Entities/Modules/ModuleInfo.cs index 271995d0c2e..04d905ba243 100644 --- a/DNN Platform/Library/Entities/Modules/ModuleInfo.cs +++ b/DNN Platform/Library/Entities/Modules/ModuleInfo.cs @@ -839,6 +839,11 @@ public string GetProperty(string propertyName, string format, CultureInfo format propertyNotFound = false; result = PropertyAccess.FormatString(this.ModuleControl.ControlSrc, format); break; + case "mvcControlClass": + isPublic = false; + propertyNotFound = false; + result = PropertyAccess.FormatString(this.ModuleControl.MvcControlClass, format); + break; case "controltitle": propertyNotFound = false; result = PropertyAccess.FormatString(this.ModuleControl.ControlTitle, format); diff --git a/DNN Platform/Library/Entities/Portals/PortalSettings.cs b/DNN Platform/Library/Entities/Portals/PortalSettings.cs index 1886fb6ccb9..aa264c8820a 100644 --- a/DNN Platform/Library/Entities/Portals/PortalSettings.cs +++ b/DNN Platform/Library/Entities/Portals/PortalSettings.cs @@ -9,6 +9,7 @@ namespace DotNetNuke.Entities.Portals using System.Linq; using System.Web; + using DotNetNuke.Abstractions.Framework; using DotNetNuke.Abstractions.Portals; using DotNetNuke.Common; using DotNetNuke.Common.Utilities; diff --git a/DNN Platform/Library/Entities/Portals/PortalSettingsController.cs b/DNN Platform/Library/Entities/Portals/PortalSettingsController.cs index 8ca8cb228b9..cabfa43fe4e 100644 --- a/DNN Platform/Library/Entities/Portals/PortalSettingsController.cs +++ b/DNN Platform/Library/Entities/Portals/PortalSettingsController.cs @@ -12,11 +12,13 @@ namespace DotNetNuke.Entities.Portals using System.Linq; using DotNetNuke.Abstractions.Application; + using DotNetNuke.Abstractions.Framework; using DotNetNuke.Collections; using DotNetNuke.Common; using DotNetNuke.Common.Utilities; using DotNetNuke.Entities.Modules; using DotNetNuke.Entities.Tabs; + using DotNetNuke.Framework; using DotNetNuke.Security; using DotNetNuke.Services.Localization; using DotNetNuke.UI.Skins; @@ -125,7 +127,7 @@ public virtual PortalSettings.PortalAliasMapping GetPortalAliasMappingMode(int p return aliasMapping; } - /// + /// public virtual IList GetTabModules(PortalSettings portalSettings) { return portalSettings.ActiveTab.Modules.Cast().Select(m => m).ToList(); @@ -183,13 +185,13 @@ public virtual void LoadPortalSettings(PortalSettings portalSettings) var settings = PortalController.Instance.GetPortalSettings(portalSettings.PortalId); portalSettings.Registration = new RegistrationSettings(settings); - var clientResourcesSettings = new ClientResourceSettings(); + var clientResourcesSettings = new Web.Client.ClientResourceSettings(); bool overridingDefaultSettings = clientResourcesSettings.IsOverridingDefaultSettingsEnabled(portalSettings.PortalId); int crmVersion; if (overridingDefaultSettings) { - int? globalVersion = new ClientResourceSettings().GetVersion(portalSettings.PortalId); + int? globalVersion = new Web.Client.ClientResourceSettings().GetVersion(portalSettings.PortalId); crmVersion = globalVersion ?? default(int); } else @@ -280,7 +282,6 @@ public virtual void LoadPortalSettings(PortalSettings portalSettings) portalSettings.DataConsentDelayMeasurement = setting; setting = settings.GetValueOrDefault("AllowedExtensionsWhitelist", this.hostSettingsService.GetString("DefaultEndUserExtensionWhitelist")); portalSettings.AllowedExtensionsWhitelist = new FileExtensionWhitelist(setting); - setting = settings.GetValueOrDefault("CspHeaderMode", "OFF"); switch (setting.ToUpperInvariant()) { diff --git a/DNN Platform/Library/Entities/Tabs/TabController.cs b/DNN Platform/Library/Entities/Tabs/TabController.cs index e1ccc65539c..536f243cd04 100644 --- a/DNN Platform/Library/Entities/Tabs/TabController.cs +++ b/DNN Platform/Library/Entities/Tabs/TabController.cs @@ -150,7 +150,8 @@ public static void CopyDesignToChildren(IEventLogger eventLogger, TabInfo parent tab.SiteMapPriority, UserController.Instance.GetCurrentUserInfo().UserID, tab.CultureCode, - tab.IsSystem); + tab.IsSystem, + (int)tab.PagePipeline); UpdateTabVersion(tab.TabID); @@ -2124,7 +2125,8 @@ public void UpdateTab(TabInfo updatedTab) updatedTab.SiteMapPriority, UserController.Instance.GetCurrentUserInfo().UserID, updatedTab.CultureCode, - updatedTab.IsSystem); + updatedTab.IsSystem, + (int)updatedTab.PagePipeline); // Update Tags List terms = updatedTab.Terms; diff --git a/DNN Platform/Library/Entities/Tabs/TabInfo.cs b/DNN Platform/Library/Entities/Tabs/TabInfo.cs index 72bd06b0763..95bdc1c9e52 100644 --- a/DNN Platform/Library/Entities/Tabs/TabInfo.cs +++ b/DNN Platform/Library/Entities/Tabs/TabInfo.cs @@ -18,6 +18,7 @@ namespace DotNetNuke.Entities.Tabs using System.Xml.Serialization; using DotNetNuke.Abstractions.Application; + using DotNetNuke.Abstractions.Framework; using DotNetNuke.Collections.Internal; using DotNetNuke.Common; using DotNetNuke.Common.Internal; @@ -27,6 +28,7 @@ namespace DotNetNuke.Entities.Tabs using DotNetNuke.Entities.Portals; using DotNetNuke.Entities.Tabs.TabVersions; using DotNetNuke.Entities.Users; + using DotNetNuke.Framework; using DotNetNuke.Security.Permissions; using DotNetNuke.Services.Exceptions; using DotNetNuke.Services.FileSystem; @@ -525,6 +527,10 @@ public CacheLevel Cacheability [XmlElement("localizedVersionGuid")] public Guid LocalizedVersionGuid { get; set; } + /// Gets or sets the rendering pipeline of the page. + [XmlElement("pagepipeline")] + public PagePipeline.PageRenderingPipeline PagePipeline { get; set; } + /// Gets or sets a collection of the modules on this page. /// An of . [XmlIgnore] @@ -855,6 +861,10 @@ public string GetProperty(string propertyName, string format, CultureInfo format propertyNotFound = false; result = PropertyAccess.FormatString(this.SiteMapPriority.ToString(formatProvider), format); break; + case "pagepipeline": + propertyNotFound = false; + result = this.PagePipeline.ToString(); + break; } if (!isPublic && currentScope != Scope.Debug) @@ -985,6 +995,7 @@ public override void Fill(IDataReader dr) this.BreadCrumbs = null; this.Modules = null; this.IsSystem = Null.SetNullBoolean(dr["IsSystem"]); + this.PagePipeline = (PagePipeline.PageRenderingPipeline)Null.SetNullInteger(dr["PagePipeline"]); } /// Gets the URL for the given . diff --git a/DNN Platform/Library/Framework/MvcPipeline/MvcPipelineSettings.cs b/DNN Platform/Library/Framework/MvcPipeline/MvcPipelineSettings.cs new file mode 100644 index 00000000000..6fcb1e51695 --- /dev/null +++ b/DNN Platform/Library/Framework/MvcPipeline/MvcPipelineSettings.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information + +namespace DotNetNuke.Framework.MvcPipeline +{ + using DotNetNuke.Abstractions.Framework; + using DotNetNuke.Entities.Modules.Settings; + + internal class MvcPipelineSettings + { + [PortalSetting] + public PagePipeline.PortalRenderingPipeline DefaultPagePipeline { get; set; } + } +} diff --git a/DNN Platform/Library/Framework/MvcPipeline/MvcPipelineSettingsRepository.cs b/DNN Platform/Library/Framework/MvcPipeline/MvcPipelineSettingsRepository.cs new file mode 100644 index 00000000000..6cd72780359 --- /dev/null +++ b/DNN Platform/Library/Framework/MvcPipeline/MvcPipelineSettingsRepository.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information + +namespace DotNetNuke.Framework.MvcPipeline +{ + using DotNetNuke.Abstractions.Application; + using DotNetNuke.Abstractions.Portals; + using DotNetNuke.Entities.Modules; + using DotNetNuke.Entities.Modules.Settings; + using DotNetNuke.Entities.Portals; + + internal class MvcPipelineSettingsRepository : SettingsRepository + { + private readonly IPortalSettings portalSettings; + + public MvcPipelineSettingsRepository( + IModuleController moduleController, + IHostSettings hostSettings, + IHostSettingsService hostSettingsService, + IPortalController portalController, + IPortalSettings portalSettings) + : base(moduleController, hostSettings, hostSettingsService, portalController) + { + this.portalSettings = portalSettings; + } + + public MvcPipelineSettings GetSettings() + { + return this.GetSettings(this.portalSettings.PortalId); + } + + public void SaveSettings(MvcPipelineSettings settings) + { + this.SaveSettings(this.portalSettings.PortalId, settings); + } + } +} diff --git a/DNN Platform/Library/Security/Permissions/PermissionProvider.cs b/DNN Platform/Library/Security/Permissions/PermissionProvider.cs index 0dee7eb3568..5fa73dec4b2 100644 --- a/DNN Platform/Library/Security/Permissions/PermissionProvider.cs +++ b/DNN Platform/Library/Security/Permissions/PermissionProvider.cs @@ -425,7 +425,7 @@ public virtual bool HasModuleAccess(SecurityAccessLevel accessLevel, string perm bool isAuthorized = false; UserInfo userInfo = UserController.Instance.GetCurrentUserInfo(); TabInfo tab = TabController.Instance.GetTab(moduleConfiguration.TabID, moduleConfiguration.PortalID, false); - if (userInfo != null && userInfo.IsSuperUser) + if (userInfo is { IsSuperUser: true, }) { isAuthorized = true; } @@ -447,7 +447,7 @@ public virtual bool HasModuleAccess(SecurityAccessLevel accessLevel, string perm isAuthorized = TabPermissionController.CanAddContentToPage(tab); break; case SecurityAccessLevel.Edit: - if (!((moduleConfiguration.IsShared && moduleConfiguration.IsShareableViewOnly) && TabPermissionController.CanAddContentToPage(tab))) + if (!(moduleConfiguration.IsShared && moduleConfiguration.IsShareableViewOnly && TabPermissionController.CanAddContentToPage(tab))) { if (string.IsNullOrEmpty(permissionKey)) { @@ -457,14 +457,7 @@ public virtual bool HasModuleAccess(SecurityAccessLevel accessLevel, string perm if (TabPermissionController.CanAddContentToPage(tab)) { // Need to check for Deny Edit at the Module Level - if (permissionKey == "CONTENT") - { - isAuthorized = !this.IsDeniedModulePermission(moduleConfiguration, permissionKey); - } - else - { - isAuthorized = true; - } + isAuthorized = !this.IsDeniedModulePermission(moduleConfiguration, permissionKey); } else { diff --git a/DNN Platform/Library/Services/Installer/Writers/ModulePackageWriter.cs b/DNN Platform/Library/Services/Installer/Writers/ModulePackageWriter.cs index 4cef70e114c..925c8cfccd0 100644 --- a/DNN Platform/Library/Services/Installer/Writers/ModulePackageWriter.cs +++ b/DNN Platform/Library/Services/Installer/Writers/ModulePackageWriter.cs @@ -138,7 +138,7 @@ private static void ProcessControls(XPathNavigator controlNav, string moduleFold controlSrc = controlSrc.Replace('\\', '/'); moduleControl.ControlSrc = controlSrc; - + moduleControl.MvcControlClass = Util.ReadElement(controlNav, "mvcControlClass"); moduleControl.IconFile = Util.ReadElement(controlNav, "iconfile"); string controlType = Util.ReadElement(controlNav, "type"); diff --git a/DNN Platform/Library/Services/Upgrade/Upgrade.cs b/DNN Platform/Library/Services/Upgrade/Upgrade.cs index ccabbbc129b..36c44d554e0 100644 --- a/DNN Platform/Library/Services/Upgrade/Upgrade.cs +++ b/DNN Platform/Library/Services/Upgrade/Upgrade.cs @@ -241,10 +241,23 @@ public static TabInfo AddHostPage(string tabName, string description, string tab /// The icon file. /// The type of control. /// The vieworder for this module. - public static void AddModuleControl(int moduleDefId, string controlKey, string controlTitle, string controlSrc, string iconFile, SecurityAccessLevel controlType, int viewOrder) + [DnnDeprecated(10, 99, 0, "Use overload with mvcControlClass")] + public static partial void AddModuleControl(int moduleDefId, string controlKey, string controlTitle, string controlSrc, string iconFile, SecurityAccessLevel controlType, int viewOrder) + => AddModuleControl(moduleDefId, controlKey, controlTitle, controlSrc, Null.NullString, iconFile, controlType, viewOrder); + + /// AddModuleControl adds a new Module Control to the system. + /// The Module Definition Id. + /// The key for this control in the Definition. + /// The title of this control. + /// The source of ths control. + /// The MVC control class of this control. + /// The icon file. + /// The type of control. + /// The vieworder for this module. + public static void AddModuleControl(int moduleDefId, string controlKey, string controlTitle, string controlSrc, string mvcControlClass, string iconFile, SecurityAccessLevel controlType, int viewOrder) { // Call Overload with HelpUrl = Null.NullString - AddModuleControl(moduleDefId, controlKey, controlTitle, controlSrc, iconFile, controlType, viewOrder, Null.NullString); + AddModuleControl(moduleDefId, controlKey, controlTitle, controlSrc, mvcControlClass, iconFile, controlType, viewOrder, Null.NullString); } /// AddModuleDefinition adds a new Core Module Definition to the system. @@ -2134,16 +2147,17 @@ protected static bool IsLanguageEnabled(int portalid, string code) /// The key for this control in the Definition. /// The title of this control. /// Te source of ths control. + /// The mvc control class. /// The icon file. /// The type of control. /// The vieworder for this module. /// The Help Url. - private static void AddModuleControl(int moduleDefId, string controlKey, string controlTitle, string controlSrc, string iconFile, SecurityAccessLevel controlType, int viewOrder, string helpURL) + private static void AddModuleControl(int moduleDefId, string controlKey, string controlTitle, string controlSrc, string mvcControlClass, string iconFile, SecurityAccessLevel controlType, int viewOrder, string helpURL) { - AddModuleControl(moduleDefId, controlKey, controlTitle, controlSrc, iconFile, controlType, viewOrder, helpURL, false); + AddModuleControl(moduleDefId, controlKey, controlTitle, controlSrc, mvcControlClass, iconFile, controlType, viewOrder, helpURL, false); } - private static void AddModuleControl(int moduleDefId, string controlKey, string controlTitle, string controlSrc, string iconFile, SecurityAccessLevel controlType, int viewOrder, string helpURL, bool supportsPartialRendering) + private static void AddModuleControl(int moduleDefId, string controlKey, string controlTitle, string controlSrc, string mvcControlClass, string iconFile, SecurityAccessLevel controlType, int viewOrder, string helpURL, bool supportsPartialRendering) { DnnInstallLogger.InstallLogInfo(Localization.GetString("LogStart", Localization.GlobalResourceFile) + "AddModuleControl:" + moduleDefId); @@ -2161,6 +2175,7 @@ private static void AddModuleControl(int moduleDefId, string controlKey, string ControlKey = controlKey, ControlTitle = controlTitle, ControlSrc = controlSrc, + MvcControlClass = mvcControlClass, ControlType = controlType, ViewOrder = viewOrder, IconFile = iconFile, diff --git a/DNN Platform/Library/Startup.cs b/DNN Platform/Library/Startup.cs index 0b45945a739..f90b212c291 100644 --- a/DNN Platform/Library/Startup.cs +++ b/DNN Platform/Library/Startup.cs @@ -41,6 +41,7 @@ namespace DotNetNuke using DotNetNuke.Entities.Users; using DotNetNuke.Framework; using DotNetNuke.Framework.JavaScriptLibraries; + using DotNetNuke.Framework.MvcPipeline; using DotNetNuke.Framework.Reflections; using DotNetNuke.Instrumentation; using DotNetNuke.Prompt; @@ -200,6 +201,10 @@ public void ConfigureServices(IServiceCollection services) services.AddTransient(); services.AddTransient(); RegisterModuleInjectionFilters(services); + + services.AddScoped(serviceProvider => PortalController.Instance.GetCurrentSettings()); + services.AddScoped(); + services.AddScoped(serviceProvider => serviceProvider.GetRequiredService().GetSettings()); } private static void RegisterModuleInjectionFilters(IServiceCollection services) diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/package.json b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/package.json index efa2d4a6ab2..b75dc8d4819 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/package.json +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/package.json @@ -28,17 +28,17 @@ }, "license": "MIT", "devDependencies": { - "@dnncommunity/dnn-elements": "^0.29.2", - "@eslint/js": "^9.39.4", - "@stencil/core": "^4.43.3", - "@stencil/eslint-plugin": "^1.2.0", + "@dnncommunity/dnn-elements": "^0.29.3", + "@eslint/js": "^10.0.1", + "@stencil/core": "^4.43.5", + "@stencil/eslint-plugin": "^1.3.1", "@stencil/sass": "^3.2.3", "@stencil/store": "^2.2.2", - "@types/node": "^24.9.0", - "@typescript-eslint/utils": "^8.57.2", - "eslint": "^10.2.1", - "jiti": "^2.6.1", + "@types/node": "^25.9.2", + "@typescript-eslint/utils": "^8.61.1", + "eslint": "^10.5.0", + "jiti": "^2.7.0", "typescript": "^5.9.3", - "typescript-eslint": "^8.57.2" + "typescript-eslint": "^8.61.1" } } diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/dnn-rm-folder-list.scss b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/dnn-rm-folder-list.scss index 02f3a669491..79efe3fe025 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/dnn-rm-folder-list.scss +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/dnn-rm-folder-list.scss @@ -5,10 +5,13 @@ } button{ - padding:0; - margin:0; - background-color: transparent; + display: flex; + align-items: center; border: none; + padding: 0; + margin: 0; + background-color: transparent; + gap: 0.5em; cursor: pointer; } diff --git a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/dnn-rm-folder-list.tsx b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/dnn-rm-folder-list.tsx index 222fcc15671..8bdd86ff5cb 100644 --- a/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/dnn-rm-folder-list.tsx +++ b/DNN Platform/Modules/ResourceManager/ResourceManager.Web/src/components/dnn-rm-folder-list/dnn-rm-folder-list.tsx @@ -45,7 +45,8 @@ export class DnnRmFolderList { state.pageSize, state.sortField, state.sortOrder); - this.rootItem = await this.itemsClient.getFolderItem(state.settings.HomeFolderId) + this.rootItem = await this.itemsClient.getFolderItem(state.settings.HomeFolderId); + this.rootItem.iconUrl = await this.itemsClient.getFolderIconUrl(state.settings.HomeFolderId); } catch (error) { alert(error); } @@ -88,6 +89,13 @@ export class DnnRmFolderList { this.rootItemContextMenu.open(e as PointerEvent).catch(console.error); }} > + {/* reusing code from dnn-rm-folder-list-item.tsx */} + {this.rootItem?.iconUrl != null && this.rootItem.iconUrl.length > 0 + ? + {state.settings.HomeFolderName} + : + + } {state.settings.HomeFolderName} this.rootItemContextMenu = el} diff --git a/DNN Platform/Modules/Samples/Dnn.ContactList.SpaReact/package.json b/DNN Platform/Modules/Samples/Dnn.ContactList.SpaReact/package.json index 729dee88c45..00c0a0a105a 100644 --- a/DNN Platform/Modules/Samples/Dnn.ContactList.SpaReact/package.json +++ b/DNN Platform/Modules/Samples/Dnn.ContactList.SpaReact/package.json @@ -2,7 +2,7 @@ "name": "dnn.contactlist.spareact", "version": "1.0.0", "type": "module", - "packageManager": "yarn@4.12.0", + "packageManager": "yarn@4.16.0+sha512.5374c94eb4ef6aa8188fb112f20c1aa6569f248d676c5e576e1fd2a1a4d8d87a96df65d9dfe1c2a0252cbe38bda46cf18d955005b81b43cc7607a5c9d56fd2b6", "scripts": { "dev": "vite", "build": "tsc && vite build --mode production", @@ -12,13 +12,13 @@ "dependencies": { "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router": "^7.13.1" + "react-router": "^7.18.0" }, "devDependencies": { - "@types/react": "^18.3.5", - "@types/react-dom": "^18.3.0", - "@vitejs/plugin-react": "^6.0.1", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.2", "typescript": "^5.9.3", - "vite": "^8.0.2" + "vite": "^8.0.16" } } diff --git a/DNN Platform/Skins/Aperture/package.json b/DNN Platform/Skins/Aperture/package.json index e0b91261fcb..404e4824de7 100644 --- a/DNN Platform/Skins/Aperture/package.json +++ b/DNN Platform/Skins/Aperture/package.json @@ -14,21 +14,21 @@ "normalize.css": "^8.0.1" }, "devDependencies": { - "@types/browser-sync": "^2.29.0", - "@types/node": "^24.9.0", + "@types/browser-sync": "^2.29.1", + "@types/node": "^25.9.2", "@types/postcss-import": "^14.0.3", "browser-sync": "^3.0.4", - "chokidar": "^4.0.3", - "cssnano": "^7.1.3", + "chokidar": "^5.0.0", + "cssnano": "^8.0.2", "glob": "^13.0.6", - "postcss": "^8.5.8", + "postcss": "^8.5.15", "postcss-banner": "^4.0.1", "postcss-cli": "^11.0.1", "postcss-import": "^16.1.1", - "sass-embedded": "^1.98.0", - "tsx": "^4.21.0", + "sass-embedded": "^1.100.0", + "tsx": "^4.22.4", "typescript": "^5.9.3", - "zip-lib": "^1.3.1" + "zip-lib": "^1.4.0" }, "browserslist": [ "last 2 versions", diff --git a/DNN Platform/Website/DotNetNuke.Website.csproj b/DNN Platform/Website/DotNetNuke.Website.csproj index f8e658b25ab..74d879a75fa 100644 --- a/DNN Platform/Website/DotNetNuke.Website.csproj +++ b/DNN Platform/Website/DotNetNuke.Website.csproj @@ -3272,6 +3272,10 @@ + + + + diff --git a/DNN Platform/Website/Providers/DataProviders/SqlDataProvider/10.03.03.SqlDataProvider b/DNN Platform/Website/Providers/DataProviders/SqlDataProvider/10.03.03.SqlDataProvider new file mode 100644 index 00000000000..4412108fcf0 --- /dev/null +++ b/DNN Platform/Website/Providers/DataProviders/SqlDataProvider/10.03.03.SqlDataProvider @@ -0,0 +1,9 @@ +/************************************************************/ +/***** SqlDataProvider *****/ +/***** *****/ +/***** *****/ +/***** Note: To manually execute this script you must *****/ +/***** perform a search and replace operation *****/ +/***** for {databaseOwner} and {objectQualifier} *****/ +/***** *****/ +/************************************************************/ \ No newline at end of file diff --git a/DNN Platform/Website/Providers/DataProviders/SqlDataProvider/10.99.00.SqlDataProvider b/DNN Platform/Website/Providers/DataProviders/SqlDataProvider/10.99.00.SqlDataProvider new file mode 100644 index 00000000000..5719928a896 --- /dev/null +++ b/DNN Platform/Website/Providers/DataProviders/SqlDataProvider/10.99.00.SqlDataProvider @@ -0,0 +1,313 @@ +/************************************************************/ +/* MVC Pipeline */ +/************************************************************/ + +/* Add MvcControlClass Column to ModuleControls Table */ +/*****************************************************/ + +IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME='{objectQualifier}ModuleControls' AND COLUMN_NAME='MvcControlClass') + BEGIN + -- Add new Column + ALTER TABLE {databaseOwner}{objectQualifier}ModuleControls + ADD MvcControlClass [nvarchar] (256) NULL + END +GO + +/* Update AddModuleControl */ +/***************************/ + +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'{databaseOwner}[{objectQualifier}AddModuleControl]') AND type in (N'P', N'PC')) + DROP PROCEDURE {databaseOwner}[{objectQualifier}AddModuleControl] +GO + +CREATE PROCEDURE {databaseOwner}[{objectQualifier}AddModuleControl] + + @ModuleDefID int, + @ControlKey nvarchar(50), + @ControlTitle nvarchar(50), + @ControlSrc nvarchar(256), + @MvcControlClass nvarchar(256) = null, + @IconFile nvarchar(100), + @ControlType int, + @ViewOrder int, + @HelpUrl nvarchar(200), + @SupportsPartialRendering bit, + @SupportsPopUps bit, + @CreatedByUserID int + +AS + INSERT INTO {databaseOwner}{objectQualifier}ModuleControls ( + ModuleDefID, + ControlKey, + ControlTitle, + ControlSrc, + MvcControlClass, + IconFile, + ControlType, + ViewOrder, + HelpUrl, + SupportsPartialRendering, + SupportsPopUps, + CreatedByUserID, + CreatedOnDate, + LastModifiedByUserID, + LastModifiedOnDate + ) + VALUES ( + @ModuleDefID, + @ControlKey, + @ControlTitle, + @ControlSrc, + @MvcControlClass, + @IconFile, + @ControlType, + @ViewOrder, + @HelpUrl, + @SupportsPartialRendering, + @SupportsPopUps, + @CreatedByUserID, + getdate(), + @CreatedByUserID, + getdate() + ) + + SELECT SCOPE_IDENTITY() +GO + +/* Update UpdateModuleControl */ +/******************************/ + +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'{databaseOwner}[{objectQualifier}UpdateModuleControl]') AND type in (N'P', N'PC')) + DROP PROCEDURE {databaseOwner}[{objectQualifier}UpdateModuleControl] +GO + +CREATE PROCEDURE {databaseOwner}[{objectQualifier}UpdateModuleControl] + @ModuleControlId int, + @ModuleDefID int, + @ControlKey nvarchar(50), + @ControlTitle nvarchar(50), + @ControlSrc nvarchar(256), + @MvcControlClass nvarchar(256) = null, + @IconFile nvarchar(100), + @ControlType int, + @ViewOrder int, + @HelpUrl nvarchar(200), + @SupportsPartialRendering bit, + @SupportsPopUps bit, + @LastModifiedByUserID int + +AS + UPDATE {databaseOwner}{objectQualifier}ModuleControls + SET + ModuleDefId = @ModuleDefId, + ControlKey = @ControlKey, + ControlTitle = @ControlTitle, + ControlSrc = @ControlSrc, + MvcControlClass = @MvcControlClass, + IconFile = @IconFile, + ControlType = @ControlType, + ViewOrder = ViewOrder, + HelpUrl = @HelpUrl, + SupportsPartialRendering = @SupportsPartialRendering, + SupportsPopUps = @SupportsPopUps, + LastModifiedByUserID = @LastModifiedByUserID, + LastModifiedOnDate = getdate() + WHERE ModuleControlId = @ModuleControlId +GO + +/* Update Terms and privacy */ +/************************************************/ + +UPDATE {databaseOwner}{objectQualifier}ModuleControls + SET + MvcControlClass = 'DotNetNuke.Common.Controls.PrivacyControl, DotNetNuke.Website' + WHERE ControlSrc = 'Admin/Portal/Privacy.ascx' + +UPDATE {databaseOwner}{objectQualifier}ModuleControls + SET + MvcControlClass = 'DotNetNuke.Common.Controls.TermsControl, DotNetNuke.Website' + WHERE ControlSrc = 'Admin/Portal/Terms.ascx' + +/* Add PagePipeline Column to Tabs Table */ +/*****************************************************/ + +IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME='{objectQualifier}Tabs' AND COLUMN_NAME='PagePipeline') + BEGIN + -- Add new Column + ALTER TABLE {databaseOwner}{objectQualifier}Tabs + ADD PagePipeline [int] NULL + END +GO + +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +ALTER VIEW {databaseOwner}[{objectQualifier}vw_Tabs] +AS + SELECT + T.TabID, + T.TabOrder, + T.PortalID, + T.TabName, + T.ParentId, + T.[Level], + T.TabPath, + T.UniqueId, + T.VersionGuid, + T.DefaultLanguageGuid, + T.LocalizedVersionGuid, + T.IsVisible, + T.HasBeenPublished, + Case when t.IconFile LIKE 'fileid=%' + then (SELECT IsNull(Folder, '') + [FileName] FROM {databaseOwner}[{objectQualifier}vw_Files] + WHERE fileid = CAST(SUBSTRING(t.IconFile, 8, 10) AS Int)) + else Coalesce(t.IconFile,'') + end as IconFile + , + Case when t.IconFileLarge LIKE 'fileid=%' + then (SELECT IsNull(Folder, '') + [FileName] FROM {databaseOwner}[{objectQualifier}vw_Files] + WHERE fileid = CAST(SUBSTRING(t.IconFileLarge, 8, 10) AS Int)) + else Coalesce(t.IconFileLarge,'') + end as IconFileLarge + ,T.DisableLink, + T.Title, + T.Description, + T.KeyWords, + T.IsDeleted, + T.SkinSrc, + T.ContainerSrc, + T.StartDate, + T.EndDate, + T.Url, + CASE WHEN {databaseOwner}{objectQualifier}HasChildTab(T.TabID) = 1 THEN 'true' ELSE 'false' END AS HasChildren, + T.RefreshInterval, + T.PageHeadText, + T.IsSecure, + T.PermanentRedirect, + T.SiteMapPriority, + CI.ContentItemID, + CI.[Content], + CI.ContentTypeID, + CI.ModuleID, + CI.ContentKey, + CI.Indexed, + CI.StateID, + T.CultureCode, + T.CreatedByUserID, + T.CreatedOnDate, + T.LastModifiedByUserID, + T.LastModifiedOnDate, + T.IsSystem, + T.PagePipeline + FROM {databaseOwner}[{objectQualifier}Tabs] AS T + LEFT JOIN {databaseOwner}[{objectQualifier}ContentItems] AS CI ON T.ContentItemID = CI.ContentItemID +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwner}[{objectQualifier}UpdateTab]') AND OBJECTPROPERTY(id, N'IsProcedure') = 1) +BEGIN + DROP PROCEDURE {databaseOwner} [{objectQualifier}UpdateTab] +END +GO + +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +CREATE PROCEDURE {databaseOwner}[{objectQualifier}UpdateTab] + @TabId Int, + @ContentItemID Int, + @PortalId Int, + @VersionGuid UniqueIdentifier, + @DefaultLanguageGuid UniqueIdentifier, + @LocalizedVersionGuid UniqueIdentifier, + @TabName nVarChar(200), + @IsVisible Bit, + @DisableLink Bit, + @ParentId Int, + @IconFile nVarChar(255), + @IconFileLarge nVarChar(255), + @Title nVarChar(200), + @Description nVarChar(500), + @KeyWords nVarChar(500), + @IsDeleted Bit, + @Url nVarChar(255), + @SkinSrc nVarChar(200), + @ContainerSrc nVarChar(200), + @StartDate DateTime, + @EndDate DateTime, + @RefreshInterval Int, + @PageHeadText nVarChar(max), + @IsSecure Bit, + @PermanentRedirect Bit, + @SiteMapPriority Float, + @LastModifiedByUserID Int, + @CultureCode nVarChar( 10), + @IsSystem Bit, + @PagePipeline Int +AS +BEGIN + DECLARE @OldParentId Int + SET @OldParentId = (SELECT ParentId FROM {databaseOwner}[{objectQualifier}Tabs] WHERE TabID = @TabId) + + DECLARE @TabOrder Int + SET @TabOrder = (SELECT TabOrder FROM {databaseOwner}[{objectQualifier}Tabs] WHERE TabID = @TabId) + + -- Get New TabOrder + DECLARE @NewTabOrder Int + SET @NewTabOrder = (SELECT MAX(TabOrder) FROM {databaseOwner}[{objectQualifier}Tabs] WHERE (ParentId = @ParentId OR (ParentId IS NULL AND @ParentId IS NULL))) + IF @NewTabOrder IS NULL + SET @NewTabOrder = 1 + ELSE + SET @NewTabOrder = @NewTabOrder + 2 + + UPDATE {databaseOwner}[{objectQualifier}Tabs] + SET + ContentItemID = @ContentItemID, + PortalId = @PortalId, + VersionGuid = @VersionGuid, + DefaultLanguageGuid = @DefaultLanguageGuid, + LocalizedVersionGuid = @LocalizedVersionGuid, + TabName = @TabName, + IsVisible = @IsVisible, + DisableLink = @DisableLink, + ParentId = @ParentId, + IconFile = @IconFile, + IconFileLarge = @IconFileLarge, + Title = @Title, + Description = @Description, + KeyWords = @KeyWords, + IsDeleted = @IsDeleted, + Url = @Url, + SkinSrc = @SkinSrc, + ContainerSrc = @ContainerSrc, + StartDate = @StartDate, + EndDate = @EndDate, + RefreshInterval = @RefreshInterval, + PageHeadText = @PageHeadText, + IsSecure = @IsSecure, + PermanentRedirect = @PermanentRedirect, + SiteMapPriority = @SiteMapPriority, + LastModifiedByUserID = @LastModifiedByUserID, + LastModifiedOnDate = getdate(), + CultureCode = @CultureCode, + IsSystem = @IsSystem, + PagePipeline = @PagePipeline + WHERE TabId = @TabId; + + IF (@OldParentId <> @ParentId) BEGIN + -- update TabOrder of Tabs with same original Parent + UPDATE {databaseOwner}[{objectQualifier}Tabs] + SET TabOrder = TabOrder - 2 + WHERE (ParentId = @OldParentId) + AND TabOrder > @TabOrder + + -- Update Tab with new TabOrder + UPDATE {databaseOwner}[{objectQualifier}Tabs] + SET TabOrder = @NewTabOrder + WHERE TabID = @TabId + END /* IF */ + + EXEC {databaseOwner}{objectQualifier}BuildTabLevelAndPath @TabId, 1 +END /* Procedure */ +GO diff --git a/DNN Platform/Website/admin/Skins/DnnCssInclude.ascx.cs b/DNN Platform/Website/admin/Skins/DnnCssInclude.ascx.cs index ca68ec6162e..8fbf4dec8af 100644 --- a/DNN Platform/Website/admin/Skins/DnnCssInclude.ascx.cs +++ b/DNN Platform/Website/admin/Skins/DnnCssInclude.ascx.cs @@ -11,71 +11,110 @@ namespace DotNetNuke.UI.Skins.Controls /// A control which causes CSS to be included on the page. public partial class DnnCssInclude : SkinObjectBase { - public CssMediaType CssMedia { get; set; } = CssMediaType.None; + public CssMediaType CssMedia + { + get => Enum.TryParse(this.ctlInclude.CssMedia, out var media) ? media : CssMediaType.None; + set => this.ctlInclude.CssMedia = value.ToString().ToLowerInvariant(); + } - public string FilePath { get; set; } + public string FilePath + { + get => this.ctlInclude.FilePath; + set => this.ctlInclude.FilePath = value; + } - public string PathNameAlias { get; set; } + public string PathNameAlias + { + get => this.ctlInclude.PathNameAlias; + set => this.ctlInclude.PathNameAlias = value; + } - public int Priority { get; set; } + public int Priority + { + get => this.ctlInclude.Priority; + set => this.ctlInclude.Priority = value; + } - public bool AddTag { get; set; } + public bool AddTag + { + get => this.ctlInclude.AddTag; + set => this.ctlInclude.AddTag = value; + } - public string Name { get; set; } + public string Name + { + get => this.ctlInclude.Name; + set => this.ctlInclude.Name = value; + } - public string Version { get; set; } + public string Version + { + get => this.ctlInclude.Version; + set => this.ctlInclude.Version = value; + } - public bool ForceVersion { get; set; } + public bool ForceVersion + { + get => this.ctlInclude.ForceVersion; + set => this.ctlInclude.ForceVersion = value; + } - public string ForceProvider { get; set; } + public string ForceProvider + { + get => this.ctlInclude.ForceProvider; + set => this.ctlInclude.ForceProvider = value; + } [Obsolete("Deprecated in DotNetNuke 10.2.0. Bundling is no longer supported, there is no replacement within DNN for this functionality. Scheduled removal in v12.0.0.")] public bool ForceBundle { get; set; } /// Gets or sets the CDN URL of the resource. - public string CdnUrl { get; set; } + public string CdnUrl + { + get => this.ctlInclude.CdnUrl; + set => this.ctlInclude.CdnUrl = value; + } /// Gets or sets a value indicating whether to render the blocking attribute. - public bool Blocking { get; set; } + public bool Blocking + { + get => this.ctlInclude.Blocking; + set => this.ctlInclude.Blocking = value; + } /// Gets or sets the integrity hash of the resource. - public string Integrity { get; set; } + public string Integrity + { + get => this.ctlInclude.Integrity; + set => this.ctlInclude.Integrity = value; + } /// Gets or sets the value of the crossorigin attribute. - public CrossOrigin CrossOrigin { get; set; } + public CrossOrigin CrossOrigin + { + get => this.ctlInclude.CrossOrigin; + set => this.ctlInclude.CrossOrigin = value; + } /// Gets or sets the value of the fetchpriority attribute. - public FetchPriority FetchPriority { get; set; } + public FetchPriority FetchPriority + { + get => this.ctlInclude.FetchPriority; + set => this.ctlInclude.FetchPriority = value; + } /// Gets or sets the value of the referrerpolicy attribute. - public ReferrerPolicy ReferrerPolicy { get; set; } + public ReferrerPolicy ReferrerPolicy + { + get => this.ctlInclude.ReferrerPolicy; + set => this.ctlInclude.ReferrerPolicy = value; + } /// Gets or sets a value indicating whether the client resource should be preloaded. - public bool Preload { get; set; } - - /// - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - this.ctlInclude.AddTag = this.AddTag; - this.ctlInclude.FilePath = this.FilePath; - this.ctlInclude.ForceProvider = this.ForceProvider; - this.ctlInclude.ForceVersion = this.ForceVersion; - this.ctlInclude.Name = this.Name; - this.ctlInclude.PathNameAlias = this.PathNameAlias; - this.ctlInclude.Priority = this.Priority; - this.ctlInclude.Version = this.Version; - this.ctlInclude.CdnUrl = this.CdnUrl; - this.ctlInclude.Blocking = this.Blocking; - this.ctlInclude.Integrity = this.Integrity; - this.ctlInclude.CrossOrigin = this.CrossOrigin; - this.ctlInclude.FetchPriority = this.FetchPriority; - this.ctlInclude.ReferrerPolicy = this.ReferrerPolicy; - this.ctlInclude.Preload = this.Preload; - if (this.CssMedia != CssMediaType.None) - { - this.ctlInclude.CssMedia = this.CssMedia.ToString().ToLowerInvariant(); - } + public bool Preload + { + get => this.ctlInclude.Preload; + set => this.ctlInclude.Preload = value; } } } diff --git a/DNN Platform/Website/admin/Skins/DnnJsInclude.ascx.cs b/DNN Platform/Website/admin/Skins/DnnJsInclude.ascx.cs index d887fad321c..0f294b385cb 100644 --- a/DNN Platform/Website/admin/Skins/DnnJsInclude.ascx.cs +++ b/DNN Platform/Website/admin/Skins/DnnJsInclude.ascx.cs @@ -10,73 +10,118 @@ namespace DotNetNuke.UI.Skins.Controls /// A control which causes JavaScript to be included on the page. public partial class DnnJsInclude : SkinObjectBase { - public string FilePath { get; set; } + public string FilePath + { + get => this.ctlInclude.FilePath; + set => this.ctlInclude.FilePath = value; + } - public string PathNameAlias { get; set; } + public string PathNameAlias + { + get => this.ctlInclude.PathNameAlias; + set => this.ctlInclude.PathNameAlias = value; + } - public int Priority { get; set; } + public int Priority + { + get => this.ctlInclude.Priority; + set => this.ctlInclude.Priority = value; + } - public bool AddTag { get; set; } + public bool AddTag + { + get => this.ctlInclude.AddTag; + set => this.ctlInclude.AddTag = value; + } - public string Name { get; set; } + public string Name + { + get => this.ctlInclude.Name; + set => this.ctlInclude.Name = value; + } - public string Version { get; set; } + public string Version + { + get => this.ctlInclude.Version; + set => this.ctlInclude.Version = value; + } - public bool ForceVersion { get; set; } + public bool ForceVersion + { + get => this.ctlInclude.ForceVersion; + set => this.ctlInclude.ForceVersion = value; + } - public string ForceProvider { get; set; } + public string ForceProvider + { + get => this.ctlInclude.ForceProvider; + set => this.ctlInclude.ForceProvider = value; + } [Obsolete("Deprecated in DotNetNuke 10.2.0. Bundling is no longer supported, there is no replacement within DNN for this functionality. Scheduled removal in v12.0.0.")] public bool ForceBundle { get; set; } /// Gets or sets the CDN URL of the resource. - public string CdnUrl { get; set; } + public string CdnUrl + { + get => this.ctlInclude.CdnUrl; + set => this.ctlInclude.CdnUrl = value; + } /// Gets or sets a value indicating whether to render the blocking attribute. - public bool Blocking { get; set; } + public bool Blocking + { + get => this.ctlInclude.Blocking; + set => this.ctlInclude.Blocking = value; + } /// Gets or sets the integrity hash of the resource. - public string Integrity { get; set; } + public string Integrity + { + get => this.ctlInclude.Integrity; + set => this.ctlInclude.Integrity = value; + } /// Gets or sets the value of the crossorigin attribute. - public CrossOrigin CrossOrigin { get; set; } + public CrossOrigin CrossOrigin + { + get => this.ctlInclude.CrossOrigin; + set => this.ctlInclude.CrossOrigin = value; + } /// Gets or sets the value of the fetchpriority attribute. - public FetchPriority FetchPriority { get; set; } + public FetchPriority FetchPriority + { + get => this.ctlInclude.FetchPriority; + set => this.ctlInclude.FetchPriority = value; + } /// Gets or sets the value of the referrerpolicy attribute. - public ReferrerPolicy ReferrerPolicy { get; set; } + public ReferrerPolicy ReferrerPolicy + { + get => this.ctlInclude.ReferrerPolicy; + set => this.ctlInclude.ReferrerPolicy = value; + } /// - public bool Async { get; set; } + public bool Async + { + get => this.ctlInclude.Async; + set => this.ctlInclude.Async = value; + } /// - public bool Defer { get; set; } + public bool Defer + { + get => this.ctlInclude.Defer; + set => this.ctlInclude.Defer = value; + } /// - public bool NoModule { get; set; } - - /// - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - this.ctlInclude.AddTag = this.AddTag; - this.ctlInclude.FilePath = this.FilePath; - this.ctlInclude.ForceProvider = this.ForceProvider; - this.ctlInclude.ForceVersion = this.ForceVersion; - this.ctlInclude.Name = this.Name; - this.ctlInclude.PathNameAlias = this.PathNameAlias; - this.ctlInclude.Priority = this.Priority; - this.ctlInclude.Version = this.Version; - this.ctlInclude.CdnUrl = this.CdnUrl; - this.ctlInclude.Blocking = this.Blocking; - this.ctlInclude.Integrity = this.Integrity; - this.ctlInclude.CrossOrigin = this.CrossOrigin; - this.ctlInclude.FetchPriority = this.FetchPriority; - this.ctlInclude.ReferrerPolicy = this.ReferrerPolicy; - this.ctlInclude.Async = this.Async; - this.ctlInclude.Defer = this.Defer; - this.ctlInclude.NoModule = this.NoModule; + public bool NoModule + { + get => this.ctlInclude.NoModule; + set => this.ctlInclude.NoModule = value; } } } diff --git a/Directory.Packages.props b/Directory.Packages.props index afcb82c9b3e..86b5787d23b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -59,6 +59,7 @@ + diff --git a/Dnn.AdminExperience/ClientSide/AdminLogs.Web/package.json b/Dnn.AdminExperience/ClientSide/AdminLogs.Web/package.json index 3f56964fb8f..81d7d465bb5 100644 --- a/Dnn.AdminExperience/ClientSide/AdminLogs.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/AdminLogs.Web/package.json @@ -10,17 +10,17 @@ }, "devDependencies": { "@dnnsoftware/dnn-react-common": "10.3.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "array.prototype.find": "2.2.3", "array.prototype.findindex": "2.2.4", "create-react-class": "^15.7.0", "es6-object-assign": "1.1.0", - "eslint": "10.2.1", + "eslint": "10.5.0", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "less": "4.6.4", + "globals": "^17.6.0", + "less": "4.6.6", "prop-types": "15.8.1", "react": "^16.14.0", "react-click-outside": "^3.0.1", @@ -37,7 +37,7 @@ "typescript": "^5.9.3" }, "dependencies": { - "dompurify": "^3.3.3", - "html-react-parser": "^5.2.17" + "dompurify": "^3.4.10", + "html-react-parser": "^6.1.3" } } diff --git a/Dnn.AdminExperience/ClientSide/Bundle.Web/package.json b/Dnn.AdminExperience/ClientSide/Bundle.Web/package.json index a4788074956..f096da58408 100644 --- a/Dnn.AdminExperience/ClientSide/Bundle.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/Bundle.Web/package.json @@ -9,16 +9,16 @@ "lint": "eslint --fix" }, "devDependencies": { - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "create-react-class": "^15.7.0", - "dayjs": "^1.11.20", + "dayjs": "^1.11.21", "es6-promise": "4.2.8", - "eslint": "10.2.1", + "eslint": "10.5.0", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "less": "4.6.4", + "globals": "^17.6.0", + "less": "4.6.6", "prop-types": "^15.8.1", "react": "^16.14.0", "react-collapse": "5.1.1", @@ -30,7 +30,7 @@ "react-motion": "0.5.2", "react-redux": "8.1.3", "react-tabs": "3.2.3", - "react-tooltip": "4.5.1", + "react-tooltip": "^6.0.8", "react-widgets": "^5.8.6", "redux": "4.2.1", "redux-devtools": "3.7.0", diff --git a/Dnn.AdminExperience/ClientSide/Dnn.React.Common/package.json b/Dnn.AdminExperience/ClientSide/Dnn.React.Common/package.json index 53e92c65054..431e528bb61 100644 --- a/Dnn.AdminExperience/ClientSide/Dnn.React.Common/package.json +++ b/Dnn.AdminExperience/ClientSide/Dnn.React.Common/package.json @@ -25,9 +25,9 @@ "build-storybook": "storybook build" }, "dependencies": { - "dayjs": "^1.11.20", - "dompurify": "^3.3.3", - "html-react-parser": "^5.2.17", + "dayjs": "^1.11.21", + "dompurify": "^3.4.10", + "html-react-parser": "^6.1.3", "interact.js": "^1.2.8", "raw-loader": "4.0.2", "react-accessible-tooltip": "^2.0.3", @@ -39,41 +39,41 @@ "react-scrollbar": "^0.5.6", "react-slider": "2.0.6", "react-tabs": "3.2.3", - "react-tooltip": "^4.5.1", + "react-tooltip": "^6.0.8", "react-widgets": "^5.8.6", "redux-undo": "^1.0.0-beta9", "scroll": "^3.0.1", "throttle-debounce": "^5.0.2" }, "devDependencies": { - "@eslint/compat": "^2.0.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@eslint/compat": "^2.1.0", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "@rsbuild/plugin-svgr": "^1.3.1", - "@storybook/addon-docs": "10.4.1", - "@storybook/addon-onboarding": "10.3.3", + "@storybook/addon-docs": "10.4.6", + "@storybook/addon-onboarding": "10.4.6", "create-react-class": "^15.7.0", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.8", "enzyme-to-json": "^3.6.2", - "eslint": "^10.2.1", - "eslint-import-resolver-node": "^0.3.9", + "eslint": "^10.5.0", + "eslint-import-resolver-node": "^0.4.0", "eslint-plugin-filenames": "^1.3.2", "eslint-plugin-import": "^2.32.0", - "eslint-plugin-jest": "^29.15.0", + "eslint-plugin-jest": "^29.15.2", "eslint-plugin-react": "7.37.5", - "eslint-plugin-storybook": "10.3.3", - "less": "4.6.4", - "nanoid": "^5.1.7", + "eslint-plugin-storybook": "10.4.6", + "less": "4.6.6", + "nanoid": "^5.1.11", "prop-types": "^15.8.1", "react": "^16.14.0", "react-addons-test-utils": "^15.6.2", "react-dom": "^16.14.0", "react-hot-loader": "^4.13.1", "react-test-renderer": "^17.0.2", - "storybook": "10.3.3", - "storybook-react-rsbuild": "^3.3.2", + "storybook": "10.4.6", + "storybook-react-rsbuild": "^3.3.4", "typescript": "^5.9.3" }, "peerDependencies": { diff --git a/Dnn.AdminExperience/ClientSide/Dnn.React.Common/src/TextOverflowWrapperNew/index.jsx b/Dnn.AdminExperience/ClientSide/Dnn.React.Common/src/TextOverflowWrapperNew/index.jsx index 18932af0479..6b329933797 100644 --- a/Dnn.AdminExperience/ClientSide/Dnn.React.Common/src/TextOverflowWrapperNew/index.jsx +++ b/Dnn.AdminExperience/ClientSide/Dnn.React.Common/src/TextOverflowWrapperNew/index.jsx @@ -1,82 +1,77 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; -import ReactTooltip from "react-tooltip"; +import { Tooltip as ReactTooltip } from "react-tooltip"; import "./style.less"; const generateID = () => "a" + Math.random().toString(36).substr(2, 10); class TextOverflowWrapperNew extends Component { + constructor() { + super(); + this.state = { + isTooltipActive: false, + id: generateID(), + }; + } - constructor() { - super(); - this.state = { - isTooltipActive: false, - id: generateID() - }; - } + showTooltip() { + this.setState({ + isTooltipActive: true, + }); + } - showTooltip() { - this.setState({ - isTooltipActive: true - }); - } + hideTooltip() { + this.setState({ + isTooltipActive: false, + }); + } - hideTooltip() { - this.setState({ - isTooltipActive: false - }); - } + render() { + const { props, state } = this; - render() { - const { props, state } = this; + const hotspotStyles = { + marginLeft: "20px", + backgroundColor: "transparent", + position: "absolute", + top: 0, + left: 0, + height: "20px", + width: "200px", + zIndex: 10000, + }; - const hotspotStyles = { - marginLeft:"20px", - backgroundColor:"transparent", - position:"absolute", - top:0, - left:0, - height: "20px", - width: "200px", - zIndex: 10000 - }; - - return ( -
-
-
- -
- {props.text} -
-
-
- ); - } + return ( +
+
+ +
{props.text}
+
+
+ ); + } } TextOverflowWrapperNew.propTypes = { - text: PropTypes.string, - hotspotStyles: PropTypes.object, - type: PropTypes.string, - place: PropTypes.string, - effect: PropTypes.string, - offset: PropTypes.number, - multiline: PropTypes.bool, - className: PropTypes.string, - border: PropTypes.bool + text: PropTypes.string, + hotspotStyles: PropTypes.object, + type: PropTypes.string, + place: PropTypes.string, + float: PropTypes.bool, + offset: PropTypes.number, + className: PropTypes.string, + border: PropTypes.bool, }; export default TextOverflowWrapperNew; diff --git a/Dnn.AdminExperience/ClientSide/Extensions.Web/package.json b/Dnn.AdminExperience/ClientSide/Extensions.Web/package.json index 2a55b8306b2..396010c1da5 100644 --- a/Dnn.AdminExperience/ClientSide/Extensions.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/Extensions.Web/package.json @@ -10,15 +10,15 @@ }, "devDependencies": { "@dnnsoftware/dnn-react-common": "10.3.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "@rsbuild/plugin-svgr": "^1.3.1", "create-react-class": "^15.7.0", - "eslint": "10.2.1", + "eslint": "10.5.0", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "less": "4.6.4", + "globals": "^17.6.0", + "less": "4.6.6", "prop-types": "^15.8.1", "react": "^16.14.0", "react-dom": "^16.14.0", @@ -26,7 +26,7 @@ "typescript": "^5.9.3" }, "dependencies": { - "dompurify": "^3.3.3", - "html-react-parser": "^5.2.17" + "dompurify": "^3.4.10", + "html-react-parser": "^6.1.3" } } diff --git a/Dnn.AdminExperience/ClientSide/Extensions.Web/src/components/EditExtension/CustomSettings/Module/ModuleDefinitions/ModuleDefinitionRow/Controls/ControlFields.jsx b/Dnn.AdminExperience/ClientSide/Extensions.Web/src/components/EditExtension/CustomSettings/Module/ModuleDefinitions/ModuleDefinitionRow/Controls/ControlFields.jsx index 6a2e4d5b21a..a5ee6e2a22a 100644 --- a/Dnn.AdminExperience/ClientSide/Extensions.Web/src/components/EditExtension/CustomSettings/Module/ModuleDefinitions/ModuleDefinitionRow/Controls/ControlFields.jsx +++ b/Dnn.AdminExperience/ClientSide/Extensions.Web/src/components/EditExtension/CustomSettings/Module/ModuleDefinitions/ModuleDefinitionRow/Controls/ControlFields.jsx @@ -65,6 +65,13 @@ class ControlFields extends Component { error={props.triedToSave && props.error.source} onSelect={this.onSelect.bind(this, "source")} /> + +
diff --git a/Dnn.AdminExperience/ClientSide/Licensing.Web/package.json b/Dnn.AdminExperience/ClientSide/Licensing.Web/package.json index 705c2f6de5c..4930aa07549 100644 --- a/Dnn.AdminExperience/ClientSide/Licensing.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/Licensing.Web/package.json @@ -10,17 +10,17 @@ }, "devDependencies": { "@dnnsoftware/dnn-react-common": "10.3.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "@rsbuild/plugin-svgr": "^1.3.1", "array.prototype.find": "2.2.3", "array.prototype.findindex": "2.2.4", "es6-object-assign": "1.1.0", - "eslint": "10.2.1", + "eslint": "10.5.0", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "less": "4.6.4", + "globals": "^17.6.0", + "less": "4.6.6", "prop-types": "^15.8.1", "react": "^16.14.0", "react-dom": "^16.14.0", diff --git a/Dnn.AdminExperience/ClientSide/Pages.Web/package.json b/Dnn.AdminExperience/ClientSide/Pages.Web/package.json index ccdd3ae9648..dc77b0ae61c 100644 --- a/Dnn.AdminExperience/ClientSide/Pages.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/Pages.Web/package.json @@ -19,20 +19,20 @@ }, "devDependencies": { "@dnnsoftware/dnn-react-common": "10.3.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "@rsbuild/plugin-svgr": "^1.3.1", - "@types/knockout": "^3.4.77", + "@types/knockout": "^3.4.79", "@types/redux": "3.6.31", "create-react-class": "^15.7.0", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.8", - "eslint": "^10.2.1", + "eslint": "^10.5.0", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "jest": "^30.3.0", - "less": "4.6.4", + "globals": "^17.6.0", + "jest": "^30.4.2", + "less": "4.6.6", "lodash": "4.18.1", "react-custom-scrollbars": "4.2.1", "react-dom": "^16.14.0", @@ -44,9 +44,9 @@ "typescript": "^5.9.3" }, "dependencies": { - "dayjs": "^1.11.20", - "dompurify": "^3.3.3", - "html-react-parser": "^5.2.17", + "dayjs": "^1.11.21", + "dompurify": "^3.4.10", + "html-react-parser": "^6.1.3", "promise": "^8.3.0", "prop-types": "^15.8.1", "react": "^16.14.0", diff --git a/Dnn.AdminExperience/ClientSide/Pages.Web/src/components/More/More.jsx b/Dnn.AdminExperience/ClientSide/Pages.Web/src/components/More/More.jsx index 5709657e6bb..59cce2882fd 100644 --- a/Dnn.AdminExperience/ClientSide/Pages.Web/src/components/More/More.jsx +++ b/Dnn.AdminExperience/ClientSide/Pages.Web/src/components/More/More.jsx @@ -21,6 +21,19 @@ class More extends Component { onChangeField(key, event.target.value); } + onPagePipelineSelected(option) { + const {onChangeField} = this.props; + onChangeField("pagePipeline", option.value); + } + + getPagePipelineOptions() { + const options = []; + options.push({ value: -1, label: Localization.get("Inherited") }); + options.push({ value: 0, label: Localization.get("WebForms") }); + options.push({ value: 1, label: Localization.get("Mvc") }); + return options; + } + onCacheProviderSelected(option) { this.props.onChangeField("cacheProvider", option.value); if (!this.props.page.cacheProvider && option.value) { @@ -210,12 +223,33 @@ class More extends Component { options={this.getWorkflowOptions()} label={page.workflowId ? this.getWorkflowOptions().find(x => x.value === page.workflowId).label : Localization.get("Workflow")} value={page.workflowId !== "" && page.workflowId} - onSelect={this.onWorkflowDropDownChange.bind(this)} + onSelect={this.onWorkflowDropDownChange.bind(this)} withBorder={true} />} } + + } +
+ {Localization.get("PipelineSettings")} +
+ + + + + + + +
); } diff --git a/Dnn.AdminExperience/ClientSide/Pages.Web/src/services/pageService.js b/Dnn.AdminExperience/ClientSide/Pages.Web/src/services/pageService.js index 4565633c985..08a4ff87418 100644 --- a/Dnn.AdminExperience/ClientSide/Pages.Web/src/services/pageService.js +++ b/Dnn.AdminExperience/ClientSide/Pages.Web/src/services/pageService.js @@ -93,6 +93,7 @@ const PageService = function () { page.iconFile = null; page.iconFileLarge = null; page.sitemapPriority = 0.5; + page.pagePipeline = ""; return page; }); }; diff --git a/Dnn.AdminExperience/ClientSide/Prompt.Web/package.json b/Dnn.AdminExperience/ClientSide/Prompt.Web/package.json index 195930225cc..4416ab58967 100644 --- a/Dnn.AdminExperience/ClientSide/Prompt.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/Prompt.Web/package.json @@ -11,8 +11,8 @@ }, "devDependencies": { "@dnnsoftware/dnn-react-common": "10.3.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "@rsbuild/plugin-svgr": "^1.3.1", "array.prototype.find": "2.2.3", @@ -21,11 +21,11 @@ "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.8", "es6-object-assign": "1.1.0", - "eslint": "10.2.1", + "eslint": "10.5.0", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "jest": "^30.3.0", - "less": "4.6.4", + "globals": "^17.6.0", + "jest": "^30.4.2", + "less": "4.6.6", "localization": "^1.0.2", "prop-types": "^15.8.1", "react": "^16.14.0", @@ -45,7 +45,7 @@ "typescript": "^5.9.3" }, "dependencies": { - "dompurify": "^3.3.3", - "html-react-parser": "^5.2.17" + "dompurify": "^3.4.10", + "html-react-parser": "^6.1.3" } } diff --git a/Dnn.AdminExperience/ClientSide/Roles.Web/package.json b/Dnn.AdminExperience/ClientSide/Roles.Web/package.json index 014ca7f17f2..0d88f4aea10 100644 --- a/Dnn.AdminExperience/ClientSide/Roles.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/Roles.Web/package.json @@ -10,18 +10,18 @@ }, "devDependencies": { "@dnnsoftware/dnn-react-common": "10.3.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "@rsbuild/plugin-svgr": "^1.3.1", "array.prototype.find": "^2.2.3", "array.prototype.findindex": "^2.2.4", "create-react-class": "^15.7.0", "es6-object-assign": "^1.1.0", - "eslint": "10.2.1", + "eslint": "10.5.0", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "less": "4.6.4", + "globals": "^17.6.0", + "less": "4.6.6", "prop-types": "^15.8.1", "react": "^16.14.0", "react-click-outside": "^3.0.1", diff --git a/Dnn.AdminExperience/ClientSide/Security.Web/package.json b/Dnn.AdminExperience/ClientSide/Security.Web/package.json index 5f36d995d69..70744c92326 100644 --- a/Dnn.AdminExperience/ClientSide/Security.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/Security.Web/package.json @@ -10,15 +10,15 @@ }, "devDependencies": { "@dnnsoftware/dnn-react-common": "10.3.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "@rsbuild/plugin-svgr": "^1.3.1", "create-react-class": "^15.7.0", - "eslint": "10.2.1", + "eslint": "10.5.0", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "less": "4.6.4", + "globals": "^17.6.0", + "less": "4.6.6", "prop-types": "^15.8.1", "react": "^16.14.0", "react-dom": "^16.14.0", @@ -30,7 +30,7 @@ "redux-thunk": "^2.4.2" }, "dependencies": { - "dompurify": "^3.3.3", - "html-react-parser": "^5.2.17" + "dompurify": "^3.4.10", + "html-react-parser": "^6.1.3" } } diff --git a/Dnn.AdminExperience/ClientSide/Seo.Web/package.json b/Dnn.AdminExperience/ClientSide/Seo.Web/package.json index 9cee947bd42..b4dbd76a520 100644 --- a/Dnn.AdminExperience/ClientSide/Seo.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/Seo.Web/package.json @@ -10,17 +10,17 @@ }, "devDependencies": { "@dnnsoftware/dnn-react-common": "10.3.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "array.prototype.find": "2.2.3", "array.prototype.findindex": "2.2.4", "create-react-class": "^15.7.0", "es6-object-assign": "1.1.0", - "eslint": "10.2.1", + "eslint": "10.5.0", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "less": "4.6.4", + "globals": "^17.6.0", + "less": "4.6.6", "prop-types": "^15.8.1", "react": "^16.14.0", "react-dom": "^16.14.0", diff --git a/Dnn.AdminExperience/ClientSide/Servers.Web/package.json b/Dnn.AdminExperience/ClientSide/Servers.Web/package.json index 0215fc390e8..1d7739a6b2b 100644 --- a/Dnn.AdminExperience/ClientSide/Servers.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/Servers.Web/package.json @@ -10,19 +10,19 @@ }, "devDependencies": { "@dnnsoftware/dnn-react-common": "10.3.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "@rsbuild/plugin-svgr": "^1.3.1", - "@types/react": "^19.2.2", - "@types/react-dom": "^19.2.2", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", "@types/redux": "^3.6.31", "create-react-class": "^15.7.0", - "dayjs": "^1.11.20", - "eslint": "10.2.1", + "dayjs": "^1.11.21", + "eslint": "10.5.0", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "less": "4.6.4", + "globals": "^17.6.0", + "less": "4.6.6", "prop-types": "^15.8.1", "react": "^16.14.0", "react-dom": "^16.14.0", @@ -30,8 +30,8 @@ "typescript": "^5.9.3" }, "dependencies": { - "dompurify": "^3.3.3", - "html-react-parser": "^5.2.17", + "dompurify": "^3.4.10", + "html-react-parser": "^6.1.3", "react-custom-scrollbars": "^4.2.1" } } diff --git a/Dnn.AdminExperience/ClientSide/SiteGroups.Web/package.json b/Dnn.AdminExperience/ClientSide/SiteGroups.Web/package.json index 1d515993027..bfeec2f665a 100644 --- a/Dnn.AdminExperience/ClientSide/SiteGroups.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/SiteGroups.Web/package.json @@ -10,14 +10,14 @@ }, "devDependencies": { "@dnnsoftware/dnn-react-common": "10.3.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "create-react-class": "^15.7.0", - "eslint": "10.2.1", + "eslint": "10.5.0", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "less": "4.6.4", + "globals": "^17.6.0", + "less": "4.6.6", "prop-types": "^15.8.1", "react": "^16.14.0", "react-dom": "^16.14.0", diff --git a/Dnn.AdminExperience/ClientSide/SiteImportExport.Web/package.json b/Dnn.AdminExperience/ClientSide/SiteImportExport.Web/package.json index 1c228c6219f..2f67cd4775c 100644 --- a/Dnn.AdminExperience/ClientSide/SiteImportExport.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/SiteImportExport.Web/package.json @@ -10,15 +10,15 @@ }, "devDependencies": { "@dnnsoftware/dnn-react-common": "10.3.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "@rsbuild/plugin-svgr": "^1.3.1", "create-react-class": "^15.7.0", - "eslint": "10.2.1", + "eslint": "10.5.0", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "less": "4.6.4", + "globals": "^17.6.0", + "less": "4.6.6", "localization": "^1.0.2", "prop-types": "^15.8.1", "rc-progress": "^4.0.0", @@ -37,7 +37,7 @@ "typescript": "^5.9.3" }, "dependencies": { - "dompurify": "^3.3.3", - "html-react-parser": "^5.2.17" + "dompurify": "^3.4.10", + "html-react-parser": "^6.1.3" } } diff --git a/Dnn.AdminExperience/ClientSide/SiteSettings.Web/package.json b/Dnn.AdminExperience/ClientSide/SiteSettings.Web/package.json index 670b7979a38..e64ec5e9245 100644 --- a/Dnn.AdminExperience/ClientSide/SiteSettings.Web/package.json +++ b/Dnn.AdminExperience/ClientSide/SiteSettings.Web/package.json @@ -12,19 +12,19 @@ }, "devDependencies": { "@dnnsoftware/dnn-react-common": "10.3.3", - "@rsbuild/core": "^1.7.3", - "@rsbuild/plugin-less": "^1.6.2", + "@rsbuild/core": "^1.7.5", + "@rsbuild/plugin-less": "^1.6.4", "@rsbuild/plugin-react": "^1.4.6", "@rsbuild/plugin-svgr": "^1.3.1", "array.prototype.find": "2.2.3", "array.prototype.findindex": "2.2.4", "create-react-class": "^15.7.0", - "eslint": "10.2.1", - "eslint-plugin-jest": "^29.15.0", + "eslint": "10.5.0", + "eslint-plugin-jest": "^29.15.2", "eslint-plugin-react": "7.37.5", - "globals": "^17.4.0", - "jest": "^30.3.0", - "less": "4.6.4", + "globals": "^17.6.0", + "jest": "^30.4.2", + "less": "4.6.6", "prop-types": "^15.8.1", "react": "^16.14.0", "react-custom-scrollbars": "4.2.1", @@ -38,7 +38,7 @@ "typescript": "^5.9.3" }, "dependencies": { - "dompurify": "^3.3.3", - "html-react-parser": "^5.2.17" + "dompurify": "^3.4.10", + "html-react-parser": "^6.1.3" } } diff --git a/Dnn.AdminExperience/ClientSide/SiteSettings.Web/src/components/moreSettings/index.jsx b/Dnn.AdminExperience/ClientSide/SiteSettings.Web/src/components/moreSettings/index.jsx index 1aa15772014..41ef93b2be2 100644 --- a/Dnn.AdminExperience/ClientSide/SiteSettings.Web/src/components/moreSettings/index.jsx +++ b/Dnn.AdminExperience/ClientSide/SiteSettings.Web/src/components/moreSettings/index.jsx @@ -286,7 +286,15 @@ class MoreSettingsPanelBody extends Component { } return options; } - + + getPagePipelineOptions() { + const options = []; + options.push({ value: "0", label: resx.get("WebForms") }); + options.push({ value: "1", label: resx.get("Mvc") }); + options.push({ value: "2", label: resx.get("Auto") }); + return options; + } + onDropDownChange(key, option) { let { state, props } = this; let otherSettings = Object.assign({}, state.otherSettings); @@ -302,7 +310,7 @@ class MoreSettingsPanelBody extends Component { ); } - + render() { const { props, state } = this; let htmlEditor = isHost ? ( @@ -519,6 +527,25 @@ class MoreSettingsPanelBody extends Component { }
} +
+ {resx.get("PipelineSettings")} +
+ + +
+
+
+ +
+