diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 00000000000..0c5ddee146d --- /dev/null +++ b/.cursorignore @@ -0,0 +1,30 @@ +node_modules/ +.env +.env.local +.env.development.local +.env.test.local +.env.production.local +.DS_Store +.idea/ +.vscode/ +.gitignore +.git/ +.gitignore + +.vs/ + +build/ +build.* +.yarn/ +Artifacts/ +DotNetNuke.Internal.SourceGenerators/ +Install/ +tools/ +Website/ +.nuget/ +.github/ +Packages/ +Refs/ +tools/ +Dnn Platform/node_modules/ +Dnn Platform/Tests \ No newline at end of file diff --git a/DNN Platform/DotNetNuke.Abstractions/Portals/IPortalSettings.cs b/DNN Platform/DotNetNuke.Abstractions/Portals/IPortalSettings.cs index 0f96d8eedd1..d959e5cfd6d 100644 --- a/DNN Platform/DotNetNuke.Abstractions/Portals/IPortalSettings.cs +++ b/DNN Platform/DotNetNuke.Abstractions/Portals/IPortalSettings.cs @@ -365,5 +365,8 @@ public interface IPortalSettings /// Gets a value indicating whether to display the dropdowns to quickly add a moduel to the page in the edit bar. bool ShowQuickModuleAddMenu { get; } + + /// Gets the pipeline type for the portal. + string PagePipeline { get; } } } diff --git a/DNN Platform/DotNetNuke.Abstractions/Portals/PagePipelineConstants.cs b/DNN Platform/DotNetNuke.Abstractions/Portals/PagePipelineConstants.cs new file mode 100644 index 00000000000..85f3d4008ea --- /dev/null +++ b/DNN Platform/DotNetNuke.Abstractions/Portals/PagePipelineConstants.cs @@ -0,0 +1,18 @@ +// 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.Portals +{ + /// + /// Provides constants for the page pipelines used in the DNN platform, + /// such as WebForms and MVC. + /// + public static class PagePipelineConstants + { + /// WebForms. + public const string WebForms = "webforms"; + + /// MVC. + public const string Mvc = "mvc"; + } +} diff --git a/DNN Platform/DotNetNuke.ContentSecurityPolicy/README.md b/DNN Platform/DotNetNuke.ContentSecurityPolicy/README.md new file mode 100644 index 00000000000..5fbe0b22543 --- /dev/null +++ b/DNN Platform/DotNetNuke.ContentSecurityPolicy/README.md @@ -0,0 +1,278 @@ +# DotNetNuke.ContentSecurityPolicy + +This library provides a comprehensive Content Security Policy (CSP) implementation for DotNetNuke Platform. It offers a fluent API for building, managing, and generating CSP headers to enhance web application security by preventing various types of attacks such as Cross-Site Scripting (XSS) and data injection attacks. + +## Overview + +Content Security Policy (CSP) is a security standard that helps prevent cross-site scripting (XSS), clickjacking, and other code injection attacks by declaring which dynamic resources are allowed to load. This library provides a robust implementation for managing CSP directives in DotNetNuke applications. + +## Core Interface: IContentSecurityPolicy + +The `IContentSecurityPolicy` interface is the main entry point for managing Content Security Policy directives. It provides access to various source contributors and methods for configuring security policies. + +### Key Features + +- **Cryptographic Nonce Generation**: Automatically generates secure nonce values for CSP policies +- **Multiple Source Types**: Support for various CSP source types (self, inline, eval, host, scheme, nonce, hash, etc.) +- **Directive Management**: Comprehensive support for all major CSP directives +- **Fluent API**: Easy-to-use method chaining for building policies +- **Reporting**: Built-in support for CSP violation reporting + +## Supported Directives + +The interface provides access to the following CSP directive contributors: + +### Source-based Directives +- **DefaultSource**: Fallback for other source directives (`default-src`) +- **ScriptSource**: Controls script execution (`script-src`) +- **StyleSource**: Controls stylesheet loading (`style-src`) +- **ImgSource**: Controls image sources (`img-src`) +- **ConnectSource**: Controls AJAX, WebSocket, and EventSource connections (`connect-src`) +- **FontSource**: Controls font loading (`font-src`) +- **ObjectSource**: Controls plugins and embedded objects (`object-src`) +- **MediaSource**: Controls audio and video elements (`media-src`) +- **FrameSource**: Controls iframe sources (`frame-src`) +- **FrameAncestors**: Controls embedding in frames (`frame-ancestors`) +- **FormAction**: Controls form submission targets (`form-action`) +- **BaseUriSource**: Controls base URI restrictions (`base-uri`) + +### Document Directives +- **Plugin Types**: Restrict allowed plugin types +- **Sandbox**: Apply sandbox restrictions +- **Upgrade Insecure Requests**: Automatically upgrade HTTP to HTTPS + +### Reporting +- **Report Endpoints**: Configure violation reporting endpoints +- **Report To**: Specify report destination + +## Usage Examples + +### Basic Setup + +```csharp +// Create a new CSP instance +IContentSecurityPolicy csp = new ContentSecurityPolicy(); + +// Configure basic security policy +csp.DefaultSource.AddSelf(); +csp.ScriptSource.AddSelf().AddNonce(csp.Nonce); +csp.StyleSource.AddSelf().AddInline(); +csp.ImgSource.AddSelf().AddScheme("data:"); +``` + +### Advanced Configuration + +```csharp +// Configure script sources with strict security +csp.ScriptSource + .AddSelf() + .AddHost("cdn.example.com") + .AddNonce(csp.Nonce) + .AddStrictDynamic(); + +// Configure style sources +csp.StyleSource + .AddSelf() + .AddHost("fonts.googleapis.com") + .AddHash("sha256-abc123..."); + +// Configure frame restrictions +csp.FrameAncestors.AddNone(); // Prevent embedding in frames + +// Configure form actions +csp.AddFormAction(CspSourceType.Self, null); +csp.AddFormAction(CspSourceType.Host, "secure.example.com"); +``` + +### Working with Nonces + +```csharp +// Get the cryptographically secure nonce +string nonce = csp.Nonce; + +// Use nonce in script tags +// + +// Nonce is automatically applied when using AddNonce() +csp.ScriptSource.AddNonce(nonce); +``` + +### Removing Sources + +```csharp +// Remove unsafe script sources +csp.RemoveScriptSources(CspSourceType.Inline); +csp.RemoveScriptSources(CspSourceType.Eval); +``` + +### Reporting Configuration + +```csharp +// Configure violation reporting +csp.AddReportEndpoint("default", "/api/csp-reports"); +csp.AddReportTo("default"); +``` + +### Generating Policy Headers + +```csharp +// Generate the complete CSP header value +string cspHeader = csp.GeneratePolicy(); +// Result: "default-src 'self'; script-src 'self' 'nonce-abc123'; style-src 'self' 'unsafe-inline'" + +// Generate reporting endpoints header +string reportingHeader = csp.GenerateReportingEndpoints(); +``` + +## Source Types + +The library supports all standard CSP source types through the `CspSourceType` enumeration: + +- **Host**: Specific domains (e.g., `example.com`, `*.example.com`) +- **Scheme**: Protocol schemes (e.g., `https:`, `data:`, `blob:`) +- **Self**: Same origin (`'self'`) +- **Inline**: Inline scripts/styles (`'unsafe-inline'`) +- **Eval**: Dynamic code evaluation (`'unsafe-eval'`) +- **Nonce**: Cryptographic nonce (`'nonce-abc123'`) +- **Hash**: Cryptographic hash (`'sha256-abc123'`) +- **None**: Block all sources (`'none'`) +- **StrictDynamic**: Enable strict dynamic loading (`'strict-dynamic'`) + +## Security Best Practices + +1. **Avoid `'unsafe-inline'`**: Use nonces or hashes instead +2. **Avoid `'unsafe-eval'`**: Prevents code injection attacks +3. **Use `'strict-dynamic'`**: For modern browsers with script loading +4. **Implement reporting**: Monitor CSP violations +5. **Start restrictive**: Begin with strict policies and relax as needed +6. **Test thoroughly**: Ensure all legitimate resources load correctly + +## Implementation Details + +### Key Classes + +- **ContentSecurityPolicy**: Main implementation of `IContentSecurityPolicy` +- **SourceCspContributor**: Manages source-based directives +- **CspSource**: Represents individual CSP sources +- **CspDirectiveType**: Enumeration of all CSP directive types +- **CspSourceType**: Enumeration of all CSP source types + +### Thread Safety + +The CSP implementation is designed to be used within a single request context. For multi-threaded scenarios, create separate instances per thread or request. + +### Performance Considerations + +- Nonce generation uses cryptographically secure random number generation +- Policy generation is optimized for minimal string operations +- Source deduplication prevents duplicate entries + +## Class Diagram + +```mermaid +classDiagram + class IContentSecurityPolicy { + <> + +string Nonce + +SourceCspContributor DefaultSource + +SourceCspContributor ScriptSource + +SourceCspContributor StyleSource + +SourceCspContributor ImgSource + +SourceCspContributor ConnectSource + +SourceCspContributor FontSource + +SourceCspContributor ObjectSource + +SourceCspContributor MediaSource + +SourceCspContributor FrameSource + +SourceCspContributor FrameAncestors + +SourceCspContributor FormAction + +SourceCspContributor BaseUriSource + +RemoveScriptSources(CspSourceType) void + +AddPluginTypes(string) void + +AddSandboxDirective(string) void + +AddFormAction(CspSourceType, string) void + +AddFrameAncestors(CspSourceType, string) void + +AddReportEndpoint(string, string) void + +AddReportTo(string) void + +GeneratePolicy() string + +GenerateReportingEndpoints() string + +UpgradeInsecureRequests() void + } + + class ContentSecurityPolicy { + + } + + class BaseCspContributor { + <> + +Guid Id + +CspDirectiveType DirectiveType + +GenerateDirective()* string + } + + class SourceCspContributor { + -List~CspSource~ Sources + +bool InlineForBackwardCompatibility + +AddInline() SourceCspContributor + +AddSelf() SourceCspContributor + +AddSourceEval() SourceCspContributor + +AddHost(string) SourceCspContributor + +AddScheme(string) SourceCspContributor + +AddNonce(string) SourceCspContributor + +AddHash(string) SourceCspContributor + +AddNone() SourceCspContributor + +AddStrictDynamic() SourceCspContributor + +AddSource(CspSource) SourceCspContributor + +RemoveSources(CspSourceType) void + +GenerateDirective() string + +GetSourcesByType(CspSourceType) IEnumerable~CspSource~ + } + + class ReportingCspContributor { + -List~string~ reportingEndpoints + +AddReportingEndpoint(string) void + +GenerateDirective() string + } + + class ReportingEndpointContributor { + -List~ReportingEndpoint~ reportingEndpoints + +AddReportingEndpoint(string, string) void + +GenerateDirective() string + } + + class CspSource { + +CspSourceType Type + +string Value + +CspSource(CspSourceType, string) + +ToString() string + -ValidateSource(CspSourceType, string) string + -ValidateHostSource(string) string + -ValidateSchemeSource(string) string + -ValidateNonceSource(string) string + -ValidateHashSource(string) string + -IsBase64String(string) bool + } + + + + IContentSecurityPolicy <|.. ContentSecurityPolicy : implements + BaseCspContributor <|-- SourceCspContributor : extends + BaseCspContributor <|-- DocumentCspContributor : extends + BaseCspContributor <|-- ReportingCspContributor : extends + BaseCspContributor <|-- ReportingEndpointContributor : extends + ContentSecurityPolicy *-- BaseCspContributor : contains + SourceCspContributor *-- CspSource : contains + + + +``` + +## Integration with DotNetNuke + +This library integrates seamlessly with the DotNetNuke Platform's security infrastructure and can be used within: + +- Custom modules +- Skin implementations +- HTTP modules and handlers +- API controllers +- Page rendering pipelines + diff --git a/DNN Platform/DotNetNuke.Web.Mvc/Framework/Controllers/DnnController.cs b/DNN Platform/DotNetNuke.Web.Mvc/Framework/Controllers/DnnController.cs index e63963b03e3..8264d66dc72 100644 --- a/DNN Platform/DotNetNuke.Web.Mvc/Framework/Controllers/DnnController.cs +++ b/DNN Platform/DotNetNuke.Web.Mvc/Framework/Controllers/DnnController.cs @@ -150,7 +150,7 @@ protected override void Initialize(RequestContext requestContext) { base.Initialize(requestContext); - if (requestContext.RouteData.Values.ContainsKey("mvcpage")) + if (requestContext.RouteData != null && requestContext.RouteData.Values.ContainsKey("mvcpage")) { var values = requestContext.RouteData.Values; var moduleContext = new ModuleInstanceContext(); @@ -168,26 +168,12 @@ protected override void Initialize(RequestContext requestContext) moduleContext.Configuration = moduleInfo; - /* - var desktopModule = DesktopModuleControllerAdapter.Instance.GetDesktopModule(moduleInfo.DesktopModuleID, moduleInfo.PortalID); - var moduleRequestContext = new ModuleRequestContext - { - HttpContext = httpContext, - ModuleContext = moduleContext, - ModuleApplication = new ModuleApplication(this.RequestContext, DisableMvcResponseHeader) - { - ModuleName = desktopModule.ModuleName, - FolderPath = desktopModule.FolderName, - }, - }; - */ - this.ModuleContext = new ModuleInstanceContext() { Configuration = moduleInfo }; this.LocalResourceFile = string.Format( "~/DesktopModules/MVC/{0}/{1}/{2}.resx", moduleInfo.DesktopModule.FolderName, Localization.LocalResourceDirectory, - this.RouteData.Values["ControllerName"]); + this.RouteData.Values["controller"]); var moduleApplication = new ModuleApplication(requestContext, true) { @@ -196,8 +182,6 @@ protected override void Initialize(RequestContext requestContext) }; moduleApplication.Init(); - // var viewEngines = new ViewEngineCollection(); - // viewEngines.Add(new ModuleDelegatingViewEngine()); this.ViewEngineCollectionEx = moduleApplication.ViewEngines; } diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Containers/SkinHelpers.Content.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Containers/SkinHelpers.Content.cs index b4c6f389a84..d968c614ad8 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Containers/SkinHelpers.Content.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Containers/SkinHelpers.Content.cs @@ -5,27 +5,19 @@ namespace DotNetNuke.Web.MvcPipeline.Containers { using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; using System.Web; using System.Web.Mvc; - using System.Web.Mvc.Html; - using System.Web.Routing; + using DotNetNuke.Common; - using DotNetNuke.Common.Internal; - using DotNetNuke.Common.Utilities; - using DotNetNuke.Entities.Portals; - using DotNetNuke.Framework; using DotNetNuke.Framework.JavaScriptLibraries; + using DotNetNuke.Security.Permissions; using DotNetNuke.Web.Client.ClientResourceManagement; + using DotNetNuke.Web.MvcPipeline; using DotNetNuke.Web.MvcPipeline.Framework.JavascriptLibraries; using DotNetNuke.Web.MvcPipeline.Models; public static partial class SkinHelpers { - private const string ExcludedRouteValues = "mid,ctl,popup,tabid,language"; - public static IHtmlString Content(this HtmlHelper htmlHelper) { var model = htmlHelper.ViewData.Model; @@ -45,6 +37,7 @@ public static IHtmlString Content(this HtmlHelper htmlHelper) MvcJavaScript.RequestRegistration(CommonJs.DnnPlugins); if (model.EditMode && model.ModuleConfiguration.ModuleID > 0) { + // render module actions moduleContentPaneDiv.InnerHtml += htmlHelper.Control("ModuleActions", model.ModuleConfiguration); } @@ -64,106 +57,19 @@ public static IHtmlString Content(this HtmlHelper htmlHelper) var moduleDiv = new TagBuilder("div"); moduleDiv.AddCssClass(model.ModuleHost.CssClass); - - /* - if (model.ModuleConfiguration.ModuleControl.ControlSrc.StartsWith("DesktopModules/RazorModules")) - { - var controlFolder = Path.GetDirectoryName(model.ModuleConfiguration.ModuleControl.ControlSrc); - var controlFileNameWithoutExtension = Path.GetFileNameWithoutExtension(model.ModuleConfiguration.ModuleControl.ControlSrc); - var srcPhysicalPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, controlFolder, "_" + controlFileNameWithoutExtension + ".cshtml"); - var scriptFile = Path.Combine("~/" + controlFolder, "Views/", "_" + controlFileNameWithoutExtension + ".cshtml"); - if (File.Exists(srcPhysicalPath)) - { - try - { - moduleDiv.InnerHtml += htmlHelper.Partial(scriptFile, model.ModuleConfiguration); - } - catch (Exception ex2) - { - throw new Exception($"Error : {ex2.Message} ( razor : {scriptFile}, module : {model.ModuleConfiguration.ModuleID})", ex2); - } - } - else - { - throw new Exception($"Error : Razor file dous not exist ( razor : {scriptFile}, module : {model.ModuleConfiguration.ModuleID})"); - - // moduleDiv.InnerHtml += $"Error : {ex.Message} (Controller : {model.ControllerName}, Action : {model.ActionName}, module : {model.ModuleConfiguration.ModuleTitle}) {ex.StackTrace}"; - } - } - */ - - var controlSrcParts = model.ModuleConfiguration.ModuleControl.ControlSrc.Split('/'); - - var controlFolder = controlSrcParts[0]; - var controlFileNameWithoutExtension = Path.GetFileNameWithoutExtension(controlSrcParts[2]); - var srcPhysicalPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, controlFolder, "Partials", controlFileNameWithoutExtension + ".cshtml"); - if (File.Exists(srcPhysicalPath)) - { - var scriptFile = Path.Combine("~/" + controlFolder, "Partials", controlFileNameWithoutExtension + ".cshtml").Replace("\\", "/"); - try - { - moduleDiv.InnerHtml += htmlHelper.Partial(scriptFile, model.ModuleConfiguration); - } - catch (Exception ex2) - { - throw new Exception($"Error : {ex2.Message} ( razor : {scriptFile}, module : {model.ModuleConfiguration.ModuleID})", ex2); - } - } - else - { - moduleDiv.InnerHtml += htmlHelper.Control(model.ModuleConfiguration); - } - - /* + // render module control try { - // module moduleDiv.InnerHtml += htmlHelper.Control(model.ModuleConfiguration); } - catch (HttpException ex) + catch (Exception ex) { - var scriptFolder = Path.GetDirectoryName(model.ModuleConfiguration.ModuleControl.ControlSrc); - var fileRoot = Path.GetFileNameWithoutExtension(model.ModuleConfiguration.ModuleControl.ControlSrc); - var srcPhysicalPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, scriptFolder, "_" + fileRoot + ".cshtml"); - var scriptFile = Path.Combine("~/" + scriptFolder, "Views/", "_" + fileRoot + ".cshtml"); - if (File.Exists(srcPhysicalPath)) + if (TabPermissionController.CanAdminPage()) { - try - { - moduleDiv.InnerHtml += htmlHelper.Partial(scriptFile, model.ModuleConfiguration); - } - catch (Exception ex2) - { - throw new Exception($"Error : {ex2.Message} ( razor : {scriptFile}, module : {model.ModuleConfiguration.ModuleID})", ex2); - } - } - else - { - // moduleDiv.InnerHtml += $"Error : {ex.Message} (Controller : {model.ControllerName}, Action : {model.ActionName}, module : {model.ModuleConfiguration.ModuleTitle}) {ex.StackTrace}"; - throw new Exception($"Error : {ex.Message} (Controller : {model.ControllerName}, Action : {model.ActionName}, module : {model.ModuleConfiguration.ModuleID})", ex); + moduleDiv.InnerHtml += "
Error loading module: " + ex.Message + "
"; } } - catch (Exception ex) - { - // moduleDiv.InnerHtml += $"Error : {ex.Message} (Controller : {model.ControllerName}, Action : {model.ActionName}, module : {model.ModuleConfiguration.ModuleTitle}) {ex.StackTrace}"; - throw new Exception($"Error : {ex.Message} (Controller : {model.ControllerName}, Action : {model.ActionName}, module : {model.ModuleConfiguration.ModuleID})", ex); - } - */ - - string[] routeValues = { $"moduleId={model.ModuleConfiguration.ModuleID}", $"controller={controlSrcParts[1]}", $"action={controlFileNameWithoutExtension}" }; - - var request = htmlHelper.ViewContext.HttpContext.Request; - var req = request.Params; - var isMyRoute = req["MODULEID"] != null && req["CONTROLLER"] != null && int.TryParse(req["MODULEID"], out var moduleId) && moduleId == model.ModuleConfiguration.ModuleID; - - var url = isMyRoute ? - request.Url.ToString() : - TestableGlobals.Instance.NavigateURL(model.ModuleConfiguration.TabID, TestableGlobals.Instance.IsHostTab(model.ModuleConfiguration.TabID), model.PortalSettings, string.Empty, routeValues); - - moduleContentPaneDiv.InnerHtml += $"
"; - moduleContentPaneDiv.InnerHtml += htmlHelper.AntiForgeryToken().ToHtmlString(); moduleContentPaneDiv.InnerHtml += moduleDiv.ToString(); - moduleContentPaneDiv.InnerHtml += "
"; if (!string.IsNullOrEmpty(model.Footer)) { moduleContentPaneDiv.InnerHtml += model.Footer; diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/DnnPageController.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/DnnPageController.cs index 248d67bc965..e7c750b514b 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/DnnPageController.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/DnnPageController.cs @@ -5,24 +5,14 @@ namespace DotNetNuke.Web.MvcPipeline.Controllers { using System.Web.Mvc; - using System.Web.Routing; using DotNetNuke.Entities.Portals; - using DotNetNuke.Entities.Tabs; - using DotNetNuke.Services.Localization; - using DotNetNuke.Web.MvcPipeline.Framework; public abstract class DnnPageController : Controller, IMvcController { /// Initializes a new instance of the class. protected DnnPageController() { - // this.ActionInvoker = new ResultCapturingActionInvoker(); - } - - public TabInfo ActivePage - { - get { return (this.PortalSettings == null) ? null : this.PortalSettings.ActiveTab; } } public PortalSettings PortalSettings @@ -32,46 +22,5 @@ public PortalSettings PortalSettings return PortalController.Instance.GetCurrentPortalSettings(); } } - - /* - public ActionResult ResultOfLastExecute - { - get - { - var actionInvoker = this.ActionInvoker as ResultCapturingActionInvoker; - return (actionInvoker != null) ? actionInvoker.ResultOfLastInvoke : null; - } - } - */ - /* - public new UserInfo User - { - get { return (this.PortalSettings == null) ? null : this.PortalSettings.UserInfo; } - } - - public new DnnUrlHelper Url { get; set; } - */ - public string LocalResourceFile { get; set; } - - public string LocalizeString(string key) - { - return Localization.GetString(key, this.LocalResourceFile); - } - - /// - protected override void Initialize(RequestContext requestContext) - { - base.Initialize(requestContext); - - // this.Url = new DnnUrlHelper(requestContext, this); - } - - public static void RegisterAjaxScript(ControllerContext context) - { - if (MvcServicesFrameworkInternal.Instance.IsAjaxScriptSupportRequired) - { - MvcServicesFrameworkInternal.Instance.RegisterAjaxScript(context); - } - } } } diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleControllerBase.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleControllerBase.cs index 8e0c64a1f1b..513e02ed915 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleControllerBase.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleControllerBase.cs @@ -11,7 +11,7 @@ namespace DotNetNuke.Web.MvcPipeline.Controllers using DotNetNuke.Entities.Portals; using DotNetNuke.Entities.Users; using DotNetNuke.Web.MvcPipeline.Routing; - using DotNetNuke.Web.MvcPipeline.UI.Utilities; + using DotNetNuke.Web.MvcPipeline.Utils; public class ModuleControllerBase : Controller, IMvcController { diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleSettingsController.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleSettingsController.cs index d47782639b6..48723f94854 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleSettingsController.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleSettingsController.cs @@ -2,33 +2,25 @@ // 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.Website.Controllers +namespace DotNetNuke.Web.MvcPipeline.Controllers { using System; using System.Collections.Generic; - using System.IO; using System.Linq; using System.Text; using System.Web.Mvc; using DotNetNuke.Abstractions; - using DotNetNuke.Common; using DotNetNuke.Common.Utilities; - using DotNetNuke.ContentSecurityPolicy; using DotNetNuke.Entities.Host; using DotNetNuke.Entities.Modules; using DotNetNuke.Entities.Modules.Definitions; using DotNetNuke.Entities.Portals; using DotNetNuke.Entities.Tabs; - using DotNetNuke.Framework.JavaScriptLibraries; using DotNetNuke.Security; using DotNetNuke.Security.Permissions; - using DotNetNuke.Services.Localization; using DotNetNuke.UI.Skins; - using DotNetNuke.Web.Client.ClientResourceManagement; - using DotNetNuke.Web.MvcPipeline.Controllers; using DotNetNuke.Web.MvcPipeline.Models; - using Microsoft.Extensions.DependencyInjection; public class ModuleSettingsController : ModuleControllerBase { diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleSettingsViewController.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleSettingsViewController.cs index 95280befffd..784d1f95af0 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleSettingsViewController.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleSettingsViewController.cs @@ -2,7 +2,7 @@ // 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.Website.Controllers +namespace DotNetNuke.Web.MvcPipeline.Controllers { using System; using System.Collections.Generic; diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleViewControllerBase.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleViewControllerBase.cs index d715ad2614d..bd36e44e34d 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleViewControllerBase.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Controllers/ModuleViewControllerBase.cs @@ -19,7 +19,7 @@ namespace DotNetNuke.Web.MvcPipeline.Controllers using DotNetNuke.Services.Localization; using DotNetNuke.UI.Modules; using DotNetNuke.Web.MvcPipeline.Models; - using DotNetNuke.Web.MvcPipeline.UI.Utilities; + using DotNetNuke.Web.MvcPipeline.Utils; public abstract class ModuleViewControllerBase : Controller, IMvcController { @@ -199,7 +199,7 @@ public string LocalResourceFile string fileRoot; if (string.IsNullOrEmpty(this.localResourceFile)) { - fileRoot = Path.Combine(this.ControlPath, Localization.LocalResourceDirectory + "/" + this.ID); + fileRoot = "~/DesktopModules/" + this.FolderName + "/" + Localization.LocalResourceDirectory + "/" + this.ResourceName; } else { @@ -215,8 +215,20 @@ public string LocalResourceFile } } - public abstract string ControlPath { get; } - public abstract string ID { get; } + public virtual string FolderName + { + get + { + return this.moduleContext.Configuration.DesktopModule.FolderName; + } + } + public virtual string ResourceName + { + get + { + return this.GetType().Name.Replace("ViewController", ""); + } + } public string EditUrl() { @@ -268,7 +280,7 @@ public virtual ActionResult Invoke(ControlViewModel input) { this.moduleContext = new ModuleInstanceContext(); var activeModule = ModuleController.Instance.GetModule(input.ModuleId, input.TabId, false); - + if (activeModule.ModuleControlId != input.ModuleControlId) { activeModule = activeModule.Clone(); @@ -294,6 +306,6 @@ protected ActionResult PartialView(ModuleInfo module, string viewName, object mo { return this.View(MvcUtils.GetControlViewName(module, viewName), model); } - + } } diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/DotNetNuke.Web.MvcPipeline.csproj b/DNN Platform/DotNetNuke.Web.MvcPipeline/DotNetNuke.Web.MvcPipeline.csproj index 5c0c5b1c956..b75b1cc79d1 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/DotNetNuke.Web.MvcPipeline.csproj +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/DotNetNuke.Web.MvcPipeline.csproj @@ -1,4 +1,4 @@ - + DotNetNuke.Web.MvcPipeline net48 diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/JavascriptLibraries/MvcJavaScript.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/JavascriptLibraries/MvcJavaScript.cs index 989f6da77e8..3070555ce68 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/JavascriptLibraries/MvcJavaScript.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/JavascriptLibraries/MvcJavaScript.cs @@ -169,12 +169,10 @@ public static void RegisterClientReference(ControllerContext page, ClientAPI.Cli break; } - // MvcClientResourceManager.RegisterScript(page, ClientAPI.ScriptPath + "MicrosoftAjax.js", 10); MvcClientResourceManager.RegisterScript(page, ClientAPI.ScriptPath + "mvc.js", 11); MvcClientResourceManager.RegisterScript(page, ClientAPI.ScriptPath + "dnn.js", 12); HttpContextSource.Current.Items.Add(LegacyPrefix + "dnn.js", true); - // page.ClientScript.RegisterClientScriptBlock(page.GetType(), "dnn.js", string.Empty); if (!ClientAPI.BrowserSupportsFunctionality(ClientAPI.ClientFunctionality.SingleCharDelimiters)) { MvcClientAPI.RegisterClientVariable("__scdoff", "1", true); diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/HtmlHelpers.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/HtmlHelpers.cs index 2d93074aa7c..9e98bae681e 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/HtmlHelpers.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/HtmlHelpers.cs @@ -5,181 +5,60 @@ namespace DotNetNuke.Web.MvcPipeline { using System; - using System.Net.Http; + using System.IO; + using System.Linq; using System.Web; using System.Web.Helpers; using System.Web.Mvc; - using System.Web.Mvc.Html; - using System.Web.Routing; - using DotNetNuke.Common.Utilities; using DotNetNuke.Entities.Modules; using DotNetNuke.Framework; - using DotNetNuke.Services.Localization; - using DotNetNuke.UI.Modules; - - // using DotNetNuke.Framework.Models; - using DotNetNuke.Collections; + using DotNetNuke.Framework.JavaScriptLibraries; + using DotNetNuke.Web.Client.ClientResourceManagement; using DotNetNuke.Web.MvcPipeline.Framework; - using DotNetNuke.Web.MvcPipeline.Models; - using DotNetNuke.Web.MvcPipeline.UI.Utilities; - using System.Linq; - using System.Reflection; - using System.IO; + using DotNetNuke.Web.MvcPipeline.Framework.JavascriptLibraries; + using DotNetNuke.Web.MvcPipeline.ModuleControl.Resources; + using DotNetNuke.Web.MvcPipeline.Utils; public static partial class HtmlHelpers { - - private const string ExcludedQueryStringParams = "tabid,mid,ctl,language,popup,action,controller"; - private const string ExcludedRouteValues = "mid,ctl,popup"; - - public static IHtmlString ViewComponent(this HtmlHelper htmlHelper, string controllerName, object model) - { - return htmlHelper.Action("Invoke", controllerName, model); - } - - public static IHtmlString Control(this HtmlHelper htmlHelper, string controlSrc, object model) - { - try - { - return htmlHelper.Action( - "Invoke", - MvcUtils.GetControlControllerName(controlSrc), - model); - } - catch (Exception ex) - { - throw new Exception($"{ex.Message} - {MvcUtils.GetControlControllerName(controlSrc)} - Invoke", ex); - } - } - public static IHtmlString Control(this HtmlHelper htmlHelper, string controlSrc, ModuleInfo module) { - string controllerName= string.Empty; - string actionName = string.Empty; - try + var moduleControl = MvcUtils.GetModuleControl(module, controlSrc); + // moduleControl.ViewContext = htmlHelper.ViewContext; + if (moduleControl is IResourcable) { - var area = module.DesktopModule.FolderName; - if (controlSrc.EndsWith(".mvc", System.StringComparison.OrdinalIgnoreCase)) + var resourcable = (IResourcable)moduleControl; + if (resourcable.ModuleResources.StyleSheets != null) { - var controlKey = module.ModuleControl.ControlKey; - var segments = controlSrc.Replace(".mvc", string.Empty).Split('/'); - - var localResourceFile = string.Format( - "~/DesktopModules/MVC/{0}/{1}/{2}.resx", - module.DesktopModule.FolderName, - Localization.LocalResourceDirectory, - segments[0]); - - RouteValueDictionary values = new RouteValueDictionary - { - { "mvcpage", true }, - { "ModuleId", module.ModuleID }, - { "TabId", module.TabID }, - { "ModuleControlId", module.ModuleControlId }, - { "PanaName", module.PaneName }, - { "ContainerSrc", module.ContainerSrc }, - { "ContainerPath", module.ContainerPath }, - { "IconFile", module.IconFile }, - { "area", area } - }; - - var queryString = htmlHelper.ViewContext.HttpContext.Request.QueryString; - - if (string.IsNullOrEmpty(controlKey)) + foreach (var styleSheet in resourcable.ModuleResources.StyleSheets) { - controlKey = queryString.GetValueOrDefault("ctl", string.Empty); + MvcClientResourceManager.RegisterStyleSheet(htmlHelper.ViewContext.Controller.ControllerContext, styleSheet.FilePath, styleSheet.Priority, styleSheet.HtmlAttributes); } - - var moduleId = Null.NullInteger; - if (queryString["moduleid"] != null) - { - int.TryParse(queryString["moduleid"], out moduleId); - } - - string routeNamespace = string.Empty; - string routeControllerName; - string routeActionName; - if (segments.Length == 3) - { - routeNamespace = segments[0]; - routeControllerName = segments[1]; - routeActionName = segments[2]; - } - else + } + if (resourcable.ModuleResources.Scripts != null) + { + foreach (var javaScript in resourcable.ModuleResources.Scripts) { - routeControllerName = segments[0]; - routeActionName = segments[1]; + MvcClientResourceManager.RegisterScript(htmlHelper.ViewContext.Controller.ControllerContext, javaScript.FilePath, javaScript.Priority, javaScript.HtmlAttributes); } - - if (moduleId != module.ModuleID && string.IsNullOrEmpty(controlKey)) - { - // Set default routeData for module that is not the "selected" module - actionName = routeActionName; - controllerName = routeControllerName; - - // routeData.Values.Add("controller", controllerName); - // routeData.Values.Add("action", actionName); - - if (!string.IsNullOrEmpty(routeNamespace)) - { - // routeData.DataTokens.Add("namespaces", new string[] { routeNamespace }); - } - } - else + } + if (resourcable.ModuleResources.Libraries != null) + { + foreach (var lib in resourcable.ModuleResources.Libraries) { - var control = ModuleControlController.GetModuleControlByControlKey(controlKey, module.ModuleDefID); - actionName = queryString.GetValueOrDefault("action", routeActionName); - controllerName = queryString.GetValueOrDefault("controller", routeControllerName); - - // values.Add("controller", controllerName); - // values.Add("action", actionName); - - foreach (var param in queryString.AllKeys) - { - if (!ExcludedQueryStringParams.Split(',').ToList().Contains(param.ToLowerInvariant())) - { - if (!values.ContainsKey(param)) - { - values.Add(param, queryString[param]); - } - } - } - - if (!string.IsNullOrEmpty(routeNamespace)) - { - // routeData.DataTokens.Add("namespaces", new string[] { routeNamespace }); - } + MvcJavaScript.RequestRegistration(lib); } - - return htmlHelper.Action( - actionName, - controllerName, - values); } - else + if (resourcable.ModuleResources.AjaxScript) { - controllerName= MvcUtils.GetControlControllerName(controlSrc); - actionName = "Invoke"; - return htmlHelper.Action( - actionName, - controllerName, - new - { - ModuleId = module.ModuleID, - TabId = module.TabID, - ModuleControlId = module.ModuleControlId, - PanaName = module.PaneName, - ContainerSrc = module.ContainerSrc, - ContainerPath = module.ContainerPath, - IconFile = module.IconFile, - area= area, - }); + ServicesFramework.Instance.RequestAjaxScriptSupport(); + } + if (resourcable.ModuleResources.AjaxAntiForgery) + { + ServicesFramework.Instance.RequestAjaxAntiForgerySupport(); } } - catch (Exception ex) - { - throw new Exception($"{ex.Message} - {controlSrc} - {controllerName} - {actionName}", ex); - } + return moduleControl.Html(htmlHelper); } public static IHtmlString Control(this HtmlHelper htmlHelper, ModuleInfo module) @@ -204,10 +83,8 @@ public static IHtmlString RegisterAjaxScriptIfRequired(this HtmlHelper htmlHelpe public static IHtmlString AntiForgeryIfRequired(this HtmlHelper htmlHelper) { - // ServicesFramework.Instance.RequestAjaxAntiForgerySupport(); // add also jquery if (ServicesFrameworkInternal.Instance.IsAjaxAntiForgerySupportRequired) { - // var antiForgery = AntiForgery.GetHtml().ToHtmlString(); return AntiForgery.GetHtml(); } diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/ContainerModelFactory.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/ContainerModelFactory.cs similarity index 76% rename from DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/ContainerModelFactory.cs rename to DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/ContainerModelFactory.cs index 167cf9e9148..3719ddd581c 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/ContainerModelFactory.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/ContainerModelFactory.cs @@ -2,7 +2,7 @@ // 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.MvcPipeline.Framework +namespace DotNetNuke.Web.MvcPipeline.ModelFactories { using System; @@ -30,64 +30,18 @@ public ContainerModel CreateContainerModel(ModuleInfo configuration, PortalSetti private ContainerModel ProcessModule(ContainerModel container, PortalSettings portalSettings) { - /* - if (this.tracelLogger.IsDebugEnabled) - { - this.tracelLogger.Debug($"Container.ProcessModule Start (TabId:{this.PortalSettings.ActiveTab.TabID},ModuleID: {this.ModuleConfiguration.ModuleDefinition.DesktopModuleID}): Module FriendlyName: '{this.ModuleConfiguration.ModuleDefinition.FriendlyName}')"); - } - */ // Process Content Pane Attributes container = this.ProcessContentPane(container, portalSettings); - // always add the actions menu as the first item in the content pane. - /* - if (this.InjectActionMenu && !ModuleHost.IsViewMode(this.ModuleConfiguration, this.PortalSettings) && this.Request.QueryString["dnnprintmode"] != "true") - { - MvcJavaScript.RequestRegistration(CommonJs.DnnPlugins); - this.ContentPane.Controls.Add(this.LoadControl(this.PortalSettings.DefaultModuleActionMenu)); - - // register admin.css - MvcClientResourceManager.RegisterAdminStylesheet(this.Page, Globals.HostPath + "admin.css"); - } - */ - // Process Module Header container = this.ProcessHeader(container); - // Try to load the module control - // container.moduleHost = new ModuleHostModel(this.ModuleConfiguration, this.ParentSkin, this); - // container.moduleHost.OnPreRender(); - - /* - if (this.tracelLogger.IsDebugEnabled) - { - this.tracelLogger.Debug($"Container.ProcessModule Info (TabId:{this.PortalSettings.ActiveTab.TabID},ModuleID: {this.ModuleConfiguration.ModuleDefinition.DesktopModuleID}): ControlPane.Controls.Add(ModuleHost:{this.moduleHost.ID})"); - } - - this.ContentPane.Controls.Add(this.ModuleHost); - */ - // Process Module Footer container = this.ProcessFooter(container); - /* - // Process the Action Controls - if (this.ModuleHost != null && this.ModuleControl != null) - { - this.ProcessChildControls(this); - } - */ - // Add Module Stylesheets container = this.ProcessStylesheets(container, container.ModuleHost != null); - /* - if (this.tracelLogger.IsDebugEnabled) - { - this.tracelLogger.Debug($"Container.ProcessModule End (TabId:{this.PortalSettings.ActiveTab.TabID},ModuleID: {this.ModuleConfiguration.ModuleDefinition.DesktopModuleID}): Module FriendlyName: '{this.ModuleConfiguration.ModuleDefinition.FriendlyName}')"); - } - */ - return container; } @@ -100,12 +54,12 @@ private ContainerModel ProcessContentPane(ContainerModel container, PortalSettin container = this.SetBorder(container); // display visual indicator if module is only visible to administrators - string viewRoles = container.ModuleConfiguration.InheritViewPermissions + var viewRoles = container.ModuleConfiguration.InheritViewPermissions ? TabPermissionController.GetTabPermissions(container.ModuleConfiguration.TabID, container.ModuleConfiguration.PortalID).ToString("VIEW") : container.ModuleConfiguration.ModulePermissions.ToString("VIEW"); - string pageEditRoles = TabPermissionController.GetTabPermissions(container.ModuleConfiguration.TabID, container.ModuleConfiguration.PortalID).ToString("EDIT"); - string moduleEditRoles = container.ModuleConfiguration.ModulePermissions.ToString("EDIT"); + var pageEditRoles = TabPermissionController.GetTabPermissions(container.ModuleConfiguration.TabID, container.ModuleConfiguration.PortalID).ToString("EDIT"); + var moduleEditRoles = container.ModuleConfiguration.ModulePermissions.ToString("EDIT"); viewRoles = viewRoles.Replace(";", string.Empty).Trim().ToLowerInvariant(); pageEditRoles = pageEditRoles.Replace(";", string.Empty).Trim().ToLowerInvariant(); @@ -195,10 +149,10 @@ private ContainerModel ProcessStylesheets(ContainerModel container, bool include // process the base class module properties if (includeModuleCss) { - string controlSrc = container.ModuleConfiguration.ModuleControl.ControlSrc; - string folderName = container.ModuleConfiguration.DesktopModule.FolderName; + var controlSrc = container.ModuleConfiguration.ModuleControl.ControlSrc; + var folderName = container.ModuleConfiguration.DesktopModule.FolderName; - string stylesheet = string.Empty; + var stylesheet = string.Empty; if (string.IsNullOrEmpty(folderName) == false) { if (controlSrc.EndsWith(".mvc")) diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/IContainerModelFactory.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/IContainerModelFactory.cs similarity index 90% rename from DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/IContainerModelFactory.cs rename to DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/IContainerModelFactory.cs index 59e155483d1..2a59e03e4b8 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/IContainerModelFactory.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/IContainerModelFactory.cs @@ -2,7 +2,7 @@ // 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.MvcPipeline.Framework +namespace DotNetNuke.Web.MvcPipeline.ModelFactories { using DotNetNuke.Entities.Modules; using DotNetNuke.Entities.Portals; diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/IPageModelFactory.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/IPageModelFactory.cs similarity index 88% rename from DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/IPageModelFactory.cs rename to DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/IPageModelFactory.cs index 2b764cd76c7..8053207baf3 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/IPageModelFactory.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/IPageModelFactory.cs @@ -2,7 +2,7 @@ // 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.MvcPipeline.Framework +namespace DotNetNuke.Web.MvcPipeline.ModelFactories { using DotNetNuke.Web.MvcPipeline.Controllers; using DotNetNuke.Web.MvcPipeline.Models; diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/IPaneModelFactory.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/IPaneModelFactory.cs similarity index 91% rename from DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/IPaneModelFactory.cs rename to DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/IPaneModelFactory.cs index c2f3dbe9a70..46be767d049 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/IPaneModelFactory.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/IPaneModelFactory.cs @@ -2,7 +2,7 @@ // 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.MvcPipeline.Framework +namespace DotNetNuke.Web.MvcPipeline.ModelFactories { using DotNetNuke.Entities.Modules; using DotNetNuke.Entities.Portals; diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/ISkinModelFactory.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/ISkinModelFactory.cs similarity index 88% rename from DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/ISkinModelFactory.cs rename to DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/ISkinModelFactory.cs index be29eaf77ca..3183943c37a 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/ISkinModelFactory.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/ISkinModelFactory.cs @@ -2,7 +2,7 @@ // 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.MvcPipeline.Framework +namespace DotNetNuke.Web.MvcPipeline.ModelFactories { using DotNetNuke.Web.MvcPipeline.Controllers; using DotNetNuke.Web.MvcPipeline.Models; diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/PageModelFactory.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/PageModelFactory.cs similarity index 67% rename from DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/PageModelFactory.cs rename to DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/PageModelFactory.cs index 81b1c8e3a5a..af66d157f6c 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/PageModelFactory.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/PageModelFactory.cs @@ -2,7 +2,7 @@ // 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.MvcPipeline.Framework +namespace DotNetNuke.Web.MvcPipeline.ModelFactories { using System; using System.IO; @@ -60,41 +60,41 @@ public PageModelFactory( this.hostSettings = hostSettings; } - public PageModel CreatePageModel(DnnPageController page) + public PageModel CreatePageModel(DnnPageController controller) { - var ctl = page.Request.QueryString["ctl"] != null ? page.Request.QueryString["ctl"] : string.Empty; + var ctl = controller.Request.QueryString["ctl"] != null ? controller.Request.QueryString["ctl"] : string.Empty; var pageModel = new PageModel { IsEditMode = Globals.IsEditMode(), AntiForgery = AntiForgery.GetHtml().ToHtmlString(), - PortalId = page.PortalSettings.PortalId, - TabId = page.PortalSettings.ActiveTab.TabID, + PortalId = controller.PortalSettings.PortalId, + TabId = controller.PortalSettings.ActiveTab.TabID, Language = Thread.CurrentThread.CurrentCulture.Name, ContentSecurityPolicy = this.contentSecurityPolicy, NavigationManager = this.navigationManager, - FavIconLink = FavIcon.GetHeaderLink(this.hostSettings, page.PortalSettings.PortalId), + FavIconLink = FavIcon.GetHeaderLink(hostSettings, controller.PortalSettings.PortalId), }; - if (page.PortalSettings.ActiveTab.PageHeadText != Null.NullString && !Globals.IsAdminControl()) + if (controller.PortalSettings.ActiveTab.PageHeadText != Null.NullString && !Globals.IsAdminControl()) { - pageModel.PageHeadText = page.PortalSettings.ActiveTab.PageHeadText; + pageModel.PageHeadText = controller.PortalSettings.ActiveTab.PageHeadText; } - if (!string.IsNullOrEmpty(page.PortalSettings.PageHeadText)) + if (!string.IsNullOrEmpty(controller.PortalSettings.PageHeadText)) { - pageModel.PortalHeadText = page.PortalSettings.PageHeadText; + pageModel.PortalHeadText = controller.PortalSettings.PageHeadText; } // set page title if (UrlUtils.InPopUp()) { - var strTitle = new StringBuilder(page.PortalSettings.PortalName); - var slaveModule = DotNetNuke.UI.UIUtilities.GetSlaveModule(page.PortalSettings.ActiveTab.TabID); + var strTitle = new StringBuilder(controller.PortalSettings.PortalName); + var slaveModule = DotNetNuke.UI.UIUtilities.GetSlaveModule(controller.PortalSettings.ActiveTab.TabID); // Skip is popup is just a tab (no slave module) if (slaveModule.DesktopModuleID != Null.NullInteger) { var control = this.moduleControlPipeline.CreateModuleControl(slaveModule) as IModuleControl; - string extension = Path.GetExtension(slaveModule.ModuleControl.ControlSrc.ToLowerInvariant()); + var extension = Path.GetExtension(slaveModule.ModuleControl.ControlSrc.ToLowerInvariant()); switch (extension) { case ".mvc": @@ -119,12 +119,12 @@ public PageModel CreatePageModel(DnnPageController page) var title = Localization.LocalizeControlTitle(control); - strTitle.Append(string.Concat(" > ", page.PortalSettings.ActiveTab.LocalizedTabName)); + strTitle.Append(string.Concat(" > ", controller.PortalSettings.ActiveTab.LocalizedTabName)); strTitle.Append(string.Concat(" > ", title)); } else { - strTitle.Append(string.Concat(" > ", page.PortalSettings.ActiveTab.LocalizedTabName)); + strTitle.Append(string.Concat(" > ", controller.PortalSettings.ActiveTab.LocalizedTabName)); } // Set to page @@ -133,15 +133,15 @@ public PageModel CreatePageModel(DnnPageController page) else { // If tab is named, use that title, otherwise build it out via breadcrumbs - if (!string.IsNullOrEmpty(page.PortalSettings.ActiveTab.Title)) + if (!string.IsNullOrEmpty(controller.PortalSettings.ActiveTab.Title)) { - pageModel.Title = page.PortalSettings.ActiveTab.Title; + pageModel.Title = controller.PortalSettings.ActiveTab.Title; } else { // Elected for SB over true concatenation here due to potential for long nesting depth - var strTitle = new StringBuilder(page.PortalSettings.PortalName); - foreach (TabInfo tab in page.PortalSettings.ActiveTab.BreadCrumbs) + var strTitle = new StringBuilder(controller.PortalSettings.PortalName); + foreach (TabInfo tab in controller.PortalSettings.ActiveTab.BreadCrumbs) { strTitle.Append(string.Concat(" > ", tab.TabName)); } @@ -153,9 +153,9 @@ public PageModel CreatePageModel(DnnPageController page) // set the background image if there is one selected if (!UrlUtils.InPopUp()) { - if (!string.IsNullOrEmpty(page.PortalSettings.BackgroundFile)) + if (!string.IsNullOrEmpty(controller.PortalSettings.BackgroundFile)) { - var fileInfo = this.GetBackgroundFileInfo(page.PortalSettings); + var fileInfo = this.GetBackgroundFileInfo(controller.PortalSettings); pageModel.BackgroundUrl = FileManager.Instance.GetUrl(fileInfo); // ((HtmlGenericControl)this.FindControl("Body")).Attributes["style"] = string.Concat("background-image: url('", url, "')"); @@ -164,39 +164,39 @@ public PageModel CreatePageModel(DnnPageController page) // META Refresh // Only autorefresh the page if we are in VIEW-mode and if we aren't displaying some module's subcontrol. - if (page.PortalSettings.ActiveTab.RefreshInterval > 0 && Personalization.GetUserMode() == PortalSettings.Mode.View && string.IsNullOrEmpty(ctl)) + if (controller.PortalSettings.ActiveTab.RefreshInterval > 0 && Personalization.GetUserMode() == PortalSettings.Mode.View && string.IsNullOrEmpty(ctl)) { - pageModel.MetaRefresh = page.PortalSettings.ActiveTab.RefreshInterval.ToString(); + pageModel.MetaRefresh = controller.PortalSettings.ActiveTab.RefreshInterval.ToString(); } // META description - if (!string.IsNullOrEmpty(page.PortalSettings.ActiveTab.Description)) + if (!string.IsNullOrEmpty(controller.PortalSettings.ActiveTab.Description)) { - pageModel.Description = page.PortalSettings.ActiveTab.Description; + pageModel.Description = controller.PortalSettings.ActiveTab.Description; } else { - pageModel.Description = page.PortalSettings.Description; + pageModel.Description = controller.PortalSettings.Description; } // META keywords - if (!string.IsNullOrEmpty(page.PortalSettings.ActiveTab.KeyWords)) + if (!string.IsNullOrEmpty(controller.PortalSettings.ActiveTab.KeyWords)) { - pageModel.KeyWords = page.PortalSettings.ActiveTab.KeyWords; + pageModel.KeyWords = controller.PortalSettings.ActiveTab.KeyWords; } else { - pageModel.KeyWords = page.PortalSettings.KeyWords; + pageModel.KeyWords = controller.PortalSettings.KeyWords; } // META copyright - if (!string.IsNullOrEmpty(page.PortalSettings.FooterText)) + if (!string.IsNullOrEmpty(controller.PortalSettings.FooterText)) { - pageModel.Copyright = page.PortalSettings.FooterText.Replace("[year]", DateTime.Now.Year.ToString()); + pageModel.Copyright = controller.PortalSettings.FooterText.Replace("[year]", DateTime.Now.Year.ToString()); } else { - pageModel.Copyright = string.Concat("Copyright (c) ", DateTime.Now.Year, " by ", page.PortalSettings.PortalName); + pageModel.Copyright = string.Concat("Copyright (c) ", DateTime.Now.Year, " by ", controller.PortalSettings.PortalName); } // META generator @@ -204,13 +204,13 @@ public PageModel CreatePageModel(DnnPageController page) // META Robots - hide it inside popups and if PageHeadText of current tab already contains a robots meta tag if (!UrlUtils.InPopUp() && - !(HeaderTextRegex.IsMatch(page.PortalSettings.ActiveTab.PageHeadText) || - HeaderTextRegex.IsMatch(page.PortalSettings.PageHeadText))) + !(HeaderTextRegex.IsMatch(controller.PortalSettings.ActiveTab.PageHeadText) || + HeaderTextRegex.IsMatch(controller.PortalSettings.PageHeadText))) { var allowIndex = true; - if ((page.PortalSettings.ActiveTab.TabSettings.ContainsKey("AllowIndex") && - bool.TryParse(page.PortalSettings.ActiveTab.TabSettings["AllowIndex"].ToString(), out allowIndex) && - !allowIndex) || ctl == "Login" || ctl == "Register") + if (controller.PortalSettings.ActiveTab.TabSettings.ContainsKey("AllowIndex") && + bool.TryParse(controller.PortalSettings.ActiveTab.TabSettings["AllowIndex"].ToString(), out allowIndex) && + !allowIndex || ctl == "Login" || ctl == "Register") { pageModel.MetaRobots = "NOINDEX, NOFOLLOW"; } @@ -223,21 +223,21 @@ public PageModel CreatePageModel(DnnPageController page) // NonProduction Label Injection if (this.applicationInfo.Status != Abstractions.Application.ReleaseMode.Stable && Host.DisplayBetaNotice && !UrlUtils.InPopUp()) { - string versionString = + var versionString = $" ({this.applicationInfo.Status} Version: {this.applicationInfo.Version})"; pageModel.Title += versionString; } - pageModel.Skin = this.skinModelFactory.CreateSkinModel(page); + pageModel.Skin = this.skinModelFactory.CreateSkinModel(controller); return pageModel; } private IFileInfo GetBackgroundFileInfo(PortalSettings portalSettings) { - string cacheKey = string.Format(Common.Utilities.DataCache.PortalCacheKey, portalSettings.PortalId, "BackgroundFile"); + var cacheKey = string.Format(DataCache.PortalCacheKey, portalSettings.PortalId, "BackgroundFile"); var file = CBO.GetCachedObject( - new CacheItemArgs(cacheKey, Common.Utilities.DataCache.PortalCacheTimeOut, Common.Utilities.DataCache.PortalCachePriority, portalSettings.PortalId, portalSettings.BackgroundFile), + new CacheItemArgs(cacheKey, DataCache.PortalCacheTimeOut, DataCache.PortalCachePriority, portalSettings.PortalId, portalSettings.BackgroundFile), this.GetBackgroundFileInfoCallBack); return file; diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/PaneModelFactory.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/PaneModelFactory.cs similarity index 69% rename from DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/PaneModelFactory.cs rename to DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/PaneModelFactory.cs index aa3b9ab7496..da8acc18fb7 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/PaneModelFactory.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/PaneModelFactory.cs @@ -2,7 +2,7 @@ // 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.MvcPipeline.Framework +namespace DotNetNuke.Web.MvcPipeline.ModelFactories { using System; using System.IO; @@ -40,8 +40,8 @@ public PaneModel InjectModule(PaneModel pane, ModuleInfo module, PortalSettings // this.PaneControl.Controls.Add(this.containerWrapperControl); // inject module classes - string classFormatString = "DnnModule DnnModule-{0} DnnModule-{1}"; - string sanitizedModuleName = Null.NullString; + var classFormatString = "DnnModule DnnModule-{0} DnnModule-{1}"; + var sanitizedModuleName = Null.NullString; if (!string.IsNullOrEmpty(module.DesktopModule.ModuleName)) { @@ -62,79 +62,10 @@ public PaneModel InjectModule(PaneModel pane, ModuleInfo module, PortalSettings } // Load container control - ContainerModel container = this.LoadModuleContainer(module, portalSettings); + var container = this.LoadModuleContainer(module, portalSettings); // Add Container to Dictionary pane.Containers.Add(container.ID, container); - - // hide anything of type ActionsMenu - as we're injecting our own menu now. - /* - container.InjectActionMenu = container.Controls.OfType().Count() == 0; - if (!container.InjectActionMenu) - { - foreach (var actionControl in container.Controls.OfType()) - { - if (actionControl is ActionsMenu) - { - Control control = actionControl as Control; - if (control != null) - { - control.Visible = false; - container.InjectActionMenu = true; - } - } - } - } - - if (Globals.IsLayoutMode() && Globals.IsAdminControl() == false) - { - // provide Drag-N-Drop capabilities - var dragDropContainer = new Panel(); - Control title = container.FindControl("dnnTitle"); - - // Assume that the title control is named dnnTitle. If this becomes an issue we could loop through the controls looking for the title type of skin object - dragDropContainer.ID = container.ID + "_DD"; - this.containerWrapperControl.Controls.Add(dragDropContainer); - - // inject the container into the page pane - this triggers the Pre_Init() event for the user control - dragDropContainer.Controls.Add(container); - - if (title != null) - { - if (title.Controls.Count > 0) - { - title = title.Controls[0]; - } - } - - // enable drag and drop - if (title != null) - { - // The title ID is actually the first child so we need to make sure at least one child exists - DNNClientAPI.EnableContainerDragAndDrop(title, dragDropContainer, module.ModuleID); - ClientAPI.RegisterPostBackEventHandler(this.PaneControl, "MoveToPane", this.ModuleMoveToPanePostBack, false); - } - } - else - { - this.containerWrapperControl.Controls.Add(container); - if (Globals.IsAdminControl()) - { - this.containerWrapperControl.Attributes["class"] += " DnnModule-Admin"; - } - } - */ - - // Attach Module to Container - // container.SetModuleConfiguration(module); - - // display collapsible page panes - /* - if (this.PaneControl.Visible == false) - { - this.PaneControl.Visible = true; - } - */ } catch (ThreadAbortException) { @@ -160,12 +91,6 @@ public PaneModel InjectModule(PaneModel pane, ModuleInfo module, PortalSettings public PaneModel ProcessPane(PaneModel pane) { - /* - // remove excess skin non-validating attributes - this.PaneControl.Attributes.Remove("ContainerType"); - this.PaneControl.Attributes.Remove("ContainerName"); - this.PaneControl.Attributes.Remove("ContainerSrc"); - */ if (Globals.IsLayoutMode()) { /* @@ -187,22 +112,11 @@ public PaneModel ProcessPane(PaneModel pane) this.PaneControl.Controls.AddAt(0, ctlLabel); */ } + /* else { - /* - if (this.PaneControl.Visible == false && TabPermissionController.CanAddContentToPage()) - { - this.PaneControl.Visible = true; - }*/ if (this.CanCollapsePane(pane)) { - // This pane has no controls so set the width to 0 - /* - if (this.PaneControl.Attributes["style"] != null) - { - this.PaneControl.Attributes.Remove("style"); - } - */ pane.CssClass += " DNNEmptyPane"; } @@ -211,10 +125,9 @@ public PaneModel ProcessPane(PaneModel pane) { pane.CssClass += " dnnSortable"; - // this call also checks for permission } } - + */ return pane; } @@ -224,33 +137,13 @@ private bool CanCollapsePane(PaneModel pane) // This preserves the integrity of the HTML syntax so we don't have to set // the visiblity of a pane to false. Setting the visibility of a pane to // false where there are colspans and rowspans can render the skin incorrectly. - bool canCollapsePane = true; + var canCollapsePane = true; if (pane.Containers.Count > 0) { canCollapsePane = false; } - /* - else if (this.PaneControl.Controls.Count == 1) - { - // Pane contains 1 control - canCollapsePane = false; - var literal = this.PaneControl.Controls[0] as LiteralControl; - if (literal != null) - { - // Check if the literal control is just whitespace - if so we can collapse panes - if (string.IsNullOrEmpty(HtmlUtils.StripWhiteSpace(literal.Text, false))) - { - canCollapsePane = true; - } - } - } - else if (this.PaneControl.Controls.Count > 1) - { - // Pane contains more than 1 control - canCollapsePane = false; - } - */ + return canCollapsePane; } @@ -261,14 +154,14 @@ private bool IsVesionableModule(ModuleInfo moduleInfo) return false; } - object controller = DotNetNuke.Framework.Reflection.CreateObject(moduleInfo.DesktopModule.BusinessControllerClass, string.Empty); + var controller = DotNetNuke.Framework.Reflection.CreateObject(moduleInfo.DesktopModule.BusinessControllerClass, string.Empty); return controller is IVersionable; } private ContainerModel LoadContainerFromCookie(HttpRequest request, PortalSettings portalSettings) { ContainerModel container = null; - HttpCookie cookie = request.Cookies["_ContainerSrc" + portalSettings.PortalId]; + var cookie = request.Cookies["_ContainerSrc" + portalSettings.PortalId]; if (cookie != null) { if (!string.IsNullOrEmpty(cookie.Value)) @@ -387,7 +280,7 @@ private ContainerModel LoadContainerByPath(string containerPath, ModuleInfo modu try { - string containerSrc = containerPath; + var containerSrc = containerPath; if (containerPath.IndexOf(Globals.ApplicationPath, StringComparison.InvariantCultureIgnoreCase) != -1) { containerPath = containerPath.Remove(0, Globals.ApplicationPath.Length); diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/SkinModelFactory.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/SkinModelFactory.cs similarity index 90% rename from DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/SkinModelFactory.cs rename to DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/SkinModelFactory.cs index 5fee7b2d3e6..8cbc72fd9ce 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Framework/SkinModelFactory.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModelFactories/SkinModelFactory.cs @@ -2,7 +2,7 @@ // 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.MvcPipeline.Framework +namespace DotNetNuke.Web.MvcPipeline.ModelFactories { using System; using System.IO; @@ -54,7 +54,7 @@ public SkinModelFactory(INavigationManager navigationManager, IPaneModelFactory public SkinModel CreateSkinModel(DnnPageController page) { SkinModel skin = null; - string skinSource = Null.NullString; + var skinSource = Null.NullString; if (page.PortalSettings.EnablePopUps && UrlUtils.InPopUp()) { @@ -79,11 +79,6 @@ public SkinModel CreateSkinModel(DnnPageController page) // set skin path page.PortalSettings.ActiveTab.SkinPath = SkinController.FormatSkinPath(skinSource); - - // set skin id to an explicit short name to reduce page payload and make it standards compliant - /* - skin.ID = "dnn"; - */ } else { @@ -97,7 +92,7 @@ public SkinModel CreateSkinModel(DnnPageController page) // load user skin ( based on cookie ) if (skin == null) { - HttpCookie skinCookie = page.Request.Cookies["_SkinSrc" + page.PortalSettings.PortalId]; + var skinCookie = page.Request.Cookies["_SkinSrc" + page.PortalSettings.PortalId]; if (skinCookie != null) { if (!string.IsNullOrEmpty(skinCookie.Value)) @@ -112,7 +107,6 @@ public SkinModel CreateSkinModel(DnnPageController page) if (skin == null) { // DNN-6170 ensure skin value is culture specific - // skinSource = Globals.IsAdminSkin() ? SkinController.FormatSkinSrc(page.page.PortalSettings.DefaultAdminSkin, page.page.PortalSettings) : page.page.PortalSettings.ActiveTab.SkinSrc; skinSource = Globals.IsAdminSkin() ? PortalController.GetPortalSetting("DefaultAdminSkin", page.PortalSettings.PortalId, Host.DefaultPortalSkin, page.PortalSettings.CultureCode) : page.PortalSettings.ActiveTab.SkinSrc; if (!string.IsNullOrEmpty(skinSource)) { @@ -130,11 +124,6 @@ public SkinModel CreateSkinModel(DnnPageController page) // set skin path page.PortalSettings.ActiveTab.SkinPath = SkinController.FormatSkinPath(skinSource); - - // set skin id to an explicit short name to reduce page payload and make it standards compliant - /* - skin.ID = "dnn"; - */ } if (page.PortalSettings.ActiveTab.DisableLink) @@ -181,32 +170,20 @@ private SkinModel LoadSkin(DnnPageController page, string skinPath) SkinModel ctlSkin = null; try { - string skinSrc = skinPath; + var skinSrc = skinPath; if (skinPath.IndexOf(Globals.ApplicationPath, StringComparison.OrdinalIgnoreCase) != -1) { skinPath = skinPath.Remove(0, Globals.ApplicationPath.Length); } - /* - ctlSkin = ControlUtilities.LoadControl(page, skinPath); - */ ctlSkin = new SkinModel(); - ctlSkin.SkinSrc = skinSrc; - // call databind so that any server logic in the skin is executed - /* - ctlSkin.DataBind(); - */ - // Load the Panes this.LoadPanes(page.PortalSettings); // Load the Module Control(s) - bool success = Globals.IsAdminControl() ? this.ProcessSlaveModule(page.PortalSettings, ctlSkin) : this.ProcessMasterModules(page.PortalSettings, ctlSkin); - /* - this.ProcessMasterModules(); - */ + var success = Globals.IsAdminControl() ? this.ProcessSlaveModule(page.PortalSettings, ctlSkin) : this.ProcessMasterModules(page.PortalSettings, ctlSkin); // Load the Control Panel this.InjectControlPanel(ctlSkin, page.Request); @@ -228,15 +205,13 @@ private SkinModel LoadSkin(DnnPageController page, string skinPath) } } */ - if (!TabPermissionController.CanAdminPage() && !success) + if (!success && !TabPermissionController.CanAdminPage()) { // only display the warning to non-administrators (administrators will see the errors) this.AddPageMessage(ctlSkin, Localization.GetString("ModuleLoadWarning.Error"), string.Format(Localization.GetString("ModuleLoadWarning.Text"), page.PortalSettings.Email), ModuleMessage.ModuleMessageType.YellowWarning); } /* - this.InvokeSkinEvents(SkinEventType.OnSkinInit); - if (HttpContext.Current != null && HttpContext.Current.Items.Contains(OnInitMessage)) { var messageType = ModuleMessage.ModuleMessageType.YellowWarning; @@ -255,10 +230,9 @@ private SkinModel LoadSkin(DnnPageController page, string skinPath) // Process the Panes attributes foreach (var key in ctlSkin.Panes.Keys) { - /*ctlSkin.Panes[key] =*/ this.paneModelFactory.ProcessPane(ctlSkin.Panes[key]); + this.paneModelFactory.ProcessPane(ctlSkin.Panes[key]); } - // this.InvokeSkinEvents(SkinEventType.OnSkinPreRender); var isSpecialPageMode = UrlUtils.InPopUp() || page.Request.QueryString["dnnprintmode"] == "true"; if (TabPermissionController.CanAddContentToPage() && Globals.IsEditMode() && !isSpecialPageMode) { @@ -286,17 +260,6 @@ private SkinModel LoadSkin(DnnPageController page, string skinPath) sb.AppendLine(" } (jQuery));"); var script = sb.ToString(); - /* - if (ScriptManager.GetCurrent(this.Page) != null) - { - // respect MS AJAX - ScriptManager.RegisterStartupScript(this.Page, this.GetType(), "DragAndDrop", script, true); - } - else - { - this.Page.ClientScript.RegisterStartupScript(this.GetType(), "DragAndDrop", script, true); - } - */ MvcClientAPI.RegisterStartupScript("DragAndDrop", script); } } @@ -390,7 +353,7 @@ private void LoadPanes(PortalSettings portalSettings) private bool ProcessMasterModules(PortalSettings portalSettings, SkinModel skin) { - bool success = true; + var success = true; if (TabPermissionController.CanViewPage()) { // We need to ensure that Content Item exists since in old versions Content Items are not needed for tabs @@ -423,7 +386,7 @@ private bool ProcessMasterModules(PortalSettings portalSettings, SkinModel skin) // check portal expiry date if (!this.CheckExpired(portalSettings)) { - if ((portalSettings.ActiveTab.StartDate < DateTime.Now && portalSettings.ActiveTab.EndDate > DateTime.Now) || TabPermissionController.CanAdminPage() || Globals.IsLayoutMode()) + if (portalSettings.ActiveTab.StartDate < DateTime.Now && portalSettings.ActiveTab.EndDate > DateTime.Now || TabPermissionController.CanAdminPage() || Globals.IsLayoutMode()) { foreach (var objModule in PortalSettingsController.Instance().GetTabModules(portalSettings)) { @@ -452,7 +415,7 @@ private bool ProcessMasterModules(PortalSettings portalSettings, SkinModel skin) var redirectUrl = Globals.AccessDeniedURL(Localization.GetString("TabAccess.Error")); // Current locale will use default if did'nt find any - Locale currentLocale = LocaleController.Instance.GetCurrentLocale(portalSettings.PortalId); + var currentLocale = LocaleController.Instance.GetCurrentLocale(portalSettings.PortalId); if (portalSettings.ContentLocalizationEnabled && TabController.CurrentPage.CultureCode != currentLocale.Code) { @@ -541,7 +504,7 @@ private bool ProcessModule(PortalSettings portalSettings, SkinModel skin, Module // We need to ensure that Content Item exists since in old versions Content Items are not needed for modules this.EnsureContentItemForModule(module); - PaneModel pane = this.GetPane(skin, module); + var pane = this.GetPane(skin, module); if (pane != null) { @@ -562,7 +525,7 @@ private bool ProcessModule(PortalSettings portalSettings, SkinModel skin, Module private PaneModel GetPane(SkinModel skin, ModuleInfo module) { PaneModel pane; - bool found = skin.Panes.TryGetValue(module.PaneName.ToLowerInvariant(), out pane); + var found = skin.Panes.TryGetValue(module.PaneName.ToLowerInvariant(), out pane); if (!found) { @@ -581,7 +544,7 @@ private void HandleAccesDenied(bool v) private bool CheckExpired(PortalSettings portalSettings) { - bool blnExpired = false; + var blnExpired = false; if (portalSettings.ExpiryDate != Null.NullDate) { if (Convert.ToDateTime(portalSettings.ExpiryDate) < DateTime.Now && !Globals.IsHostTab(portalSettings.ActiveTab.TabID)) @@ -665,14 +628,14 @@ private void AddPageMessage(SkinModel skin, string heading, string message, Modu { if (!string.IsNullOrEmpty(message)) { - ModuleMessageModel moduleMessage = this.GetModuleMessage(heading, message, moduleMessageType, iconSrc); + var moduleMessage = this.GetModuleMessage(heading, message, moduleMessageType, iconSrc); skin.ModuleMessages.Insert(0, moduleMessage); } } private bool InjectModule(PortalSettings portalSettings, PaneModel pane, ModuleInfo module) { - bool bSuccess = true; + var bSuccess = true; // try to inject the module into the pane try @@ -707,9 +670,9 @@ private bool InjectModule(PortalSettings portalSettings, PaneModel pane, ModuleI private IFileInfo GetPageStylesheetFileInfo(string styleSheet, int portalId) { - string cacheKey = string.Format(Common.Utilities.DataCache.PortalCacheKey, portalId, "PageStylesheet" + styleSheet); + var cacheKey = string.Format(DataCache.PortalCacheKey, portalId, "PageStylesheet" + styleSheet); var file = CBO.GetCachedObject( - new CacheItemArgs(cacheKey, Common.Utilities.DataCache.PortalCacheTimeOut, Common.Utilities.DataCache.PortalCachePriority, styleSheet, portalId), + new CacheItemArgs(cacheKey, DataCache.PortalCacheTimeOut, DataCache.PortalCachePriority, styleSheet, portalId), this.GetPageStylesheetInfoCallBack); return file; @@ -731,7 +694,7 @@ private string GetCssVariablesStylesheet(int portalId, Abstractions.Portals.IPor DataCache.PortalCachePriority, portalStyles, homeSystemDirectory); - string filePath = CBO.GetCachedObject(cacheArgs, this.GetCssVariablesStylesheetCallback); + var filePath = CBO.GetCachedObject(cacheArgs, this.GetCssVariablesStylesheetCallback); return filePath; } diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Models/ContainerModel.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Models/ContainerModel.cs index 4cca7dda7f4..108914a4dc5 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Models/ContainerModel.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Models/ContainerModel.cs @@ -94,14 +94,6 @@ public string ControllerName } } - public string RazorFile - { - get - { - return this.moduleConfiguration.ModuleControl.ControlSrc.Replace(".ascx", string.Empty); - } - } - public string ContainerRazorFile { get diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/DefaultMvcModuleControlBase.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/DefaultMvcModuleControlBase.cs new file mode 100644 index 00000000000..152ef132023 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/DefaultMvcModuleControlBase.cs @@ -0,0 +1,294 @@ +// 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.MvcPipeline.ModuleControl +{ + using System; + using System.Collections; + using System.IO; + using System.Web; + using System.Web.Mvc; + using System.Web.Mvc.Html; + using System.Web.UI; + using DotNetNuke.Common; + using DotNetNuke.Entities.Modules; + using DotNetNuke.Entities.Modules.Actions; + using DotNetNuke.Entities.Portals; + using DotNetNuke.Entities.Users; + using DotNetNuke.Instrumentation; + using DotNetNuke.Services.Localization; + using DotNetNuke.UI.Modules; + using DotNetNuke.Web.Client; + using DotNetNuke.Web.Client.ClientResourceManagement; + + public abstract class DefaultMvcModuleControlBase : IMvcModuleControl, IDisposable + { + private readonly Lazy serviceScopeContainer = new Lazy(ServiceScopeContainer.GetRequestOrCreateScope); + private string localResourceFile; + private ModuleInstanceContext moduleContext; + + public ModuleInfo ModuleConfiguration + { + get + { + return ModuleContext.Configuration; + } + } + + public int TabId + { + get + { + return this.ModuleContext.TabId; + } + } + + public int ModuleId + { + get + { + return this.ModuleContext.ModuleId; + } + } + + public int TabModuleId + { + get + { + return this.ModuleContext.TabModuleId; + } + } + + public bool IsHostMenu + { + get + { + return Globals.IsHostTab(this.PortalSettings.ActiveTab.TabID); + } + } + + public PortalSettings PortalSettings + { + get + { + return PortalController.Instance.GetCurrentPortalSettings(); + } + } + + public int PortalId + { + get + { + return this.ModuleContext.PortalId; + } + } + + public UserInfo UserInfo + { + get + { + return this.PortalSettings.UserInfo; + } + } + + public int UserId + { + get + { + return this.PortalSettings.UserId; + } + } + + public PortalAliasInfo PortalAlias + { + get + { + return this.PortalSettings.PortalAlias; + } + } + + public Hashtable Settings + { + get + { + return this.ModuleContext.Settings; + } + } + + /// Gets the underlying base control for this ModuleControl. + /// A String. + public Control Control { get; set; } + + /// Gets the Name for this control. + /// A String. + public virtual string ControlName + { + get + { + return Path.GetFileNameWithoutExtension(this.ModuleConfiguration.ModuleControl.ControlSrc); + + } + } + + /// Gets or Sets the Path for this control (used primarily for UserControls). + /// A String. + public virtual string ControlPath + { + get + { + if (this.ModuleConfiguration.DesktopModule == null) + { + return Path.GetDirectoryName(this.ModuleConfiguration.ModuleControl.ControlSrc); + } + else + { + return "DesktopModules/" + this.ModuleConfiguration.DesktopModule.FolderName; + } + } + } + + /// Gets the Module Context for this control. + /// A ModuleInstanceContext. + public ModuleInstanceContext ModuleContext + { + get + { + if (this.moduleContext == null) + { + this.moduleContext = new ModuleInstanceContext(this); + } + + return this.moduleContext; + } + } + + public ModuleActionCollection Actions + { + get + { + return this.ModuleContext.Actions; + } + + set + { + this.ModuleContext.Actions = value; + } + } + + public string HelpURL + { + get + { + return this.ModuleContext.HelpURL; + } + + set + { + this.ModuleContext.HelpURL = value; + } + } + + + /// Gets or sets the local resource file for this control. + /// A String. + public string LocalResourceFile + { + get + { + string fileRoot; + if (string.IsNullOrEmpty(this.localResourceFile)) + { + fileRoot = Path.Combine(this.ControlPath, Localization.LocalResourceDirectory + "/" + this.ControlName); + } + else + { + fileRoot = this.localResourceFile; + } + + return fileRoot; + } + + set + { + this.localResourceFile = value; + } + } + + /// + /// Gets the Dependency Provider to resolve registered + /// services with the container. + /// + /// + /// The Dependency Service. + /// + protected IServiceProvider DependencyProvider => this.serviceScopeContainer.Value.ServiceScope.ServiceProvider; + + public string EditUrl(string keyName, string keyValue) + { + return this.EditUrl(keyName, keyValue, string.Empty); + } + + public string EditUrl(string keyName, string keyValue, string controlKey) + { + var parameters = new string[] { }; + return this.EditUrl(keyName, keyValue, controlKey, parameters); + } + + public string EditUrl(string keyName, string keyValue, string controlKey, params string[] additionalParameters) + { + var parameters = this.GetParameters(controlKey, additionalParameters); + return this.moduleContext.EditUrl(keyName, keyValue, controlKey, parameters); + } + + private string[] GetParameters(string controlKey, string[] additionalParameters) + { + if (this.moduleContext.Configuration.ModuleDefinition.ModuleControls.ContainsKey(controlKey)) + { + var moduleControl = this.moduleContext.Configuration.ModuleDefinition.ModuleControls[controlKey]; + if (!string.IsNullOrEmpty(moduleControl.MvcControlClass)) + { + var parameters = new string[1 + additionalParameters.Length]; + parameters[0] = "mvcpage=yes"; + Array.Copy(additionalParameters, 0, parameters, 1, additionalParameters.Length); + return parameters; + } + } + + return additionalParameters; + } + + public string EditUrl(int tabID, string controlKey, bool pageRedirect, params string[] additionalParameters) + { + var parameters = this.GetParameters(controlKey, additionalParameters); + return this.ModuleContext.NavigateUrl(tabID, controlKey, pageRedirect, parameters); + } + + public int GetNextActionID() + { + return this.ModuleContext.GetNextActionID(); + } + + /// + public void Dispose() + { + // base.Dispose(); + if (this.serviceScopeContainer.IsValueCreated) + { + this.serviceScopeContainer.Value.Dispose(); + } + } + + protected string LocalizeString(string key) + { + return Localization.GetString(key, this.LocalResourceFile); + } + + protected string LocalizeSafeJsString(string key) + { + return Localization.GetSafeJSString(key, this.LocalResourceFile); + } + + public abstract IHtmlString Html(HtmlHelper htmlHelper); + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Demo/DemoModuleControl.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Demo/DemoModuleControl.cs new file mode 100644 index 00000000000..bc8a0d1b2ac --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Demo/DemoModuleControl.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DotNetNuke.Web.MvcPipeline.ModuleControl.Razor; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl.Demo +{ + public class DemoModuleControl : RazorModuleControlBase + { + public override string ControlName => "DemoModuleControl"; + + public override string ControlPath => "DesktopModules/Demo"; + + public override IRazorModuleResult Invoke() + { + if (Request.QueryString["view"] == "Terms") + { + return Terms(); + } + else if (Request.QueryString["view"] == "Privacy") + { + return Privacy(); + } + else + { + return View("~/admin/Portal/Views/Terms.cshtml", "Hello from DemoModuleControl - Default view"); + } + } + + private IRazorModuleResult Privacy() + { + return View("~/admin/Portal/Views/Privacy.cshtml", "Hello from DemoModuleControl - Privacy view"); + } + + private IRazorModuleResult Terms() + { + return View("~/Views/Default/Terms.cshtml", "Hello from DemoModuleControl - Terms view"); + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/IMvcModuleControl.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/IMvcModuleControl.cs new file mode 100644 index 00000000000..20b481f7ca6 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/IMvcModuleControl.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using System.Web.UI; +using DotNetNuke.Entities.Modules; +using DotNetNuke.UI.Modules; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl +{ + public interface IMvcModuleControl : IModuleControl + { + //public ViewContext ViewContext { get; set; } + + /// Gets the control Html + IHtmlString Html(HtmlHelper htmlHelper); + + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/ModuleControlBase.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/ModuleControlBase.cs deleted file mode 100644 index 830315f9d63..00000000000 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/ModuleControlBase.cs +++ /dev/null @@ -1,440 +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.MvcPipeline.ModuleControl -{ - using System; - using System.Collections; - using System.IO; - using System.Web.UI; - - using DotNetNuke.Common; - using DotNetNuke.Entities.Modules; - using DotNetNuke.Entities.Modules.Actions; - using DotNetNuke.Entities.Portals; - using DotNetNuke.Entities.Users; - using DotNetNuke.Instrumentation; - using DotNetNuke.Services.Localization; - using DotNetNuke.UI.Modules; - - public class ModuleControlBase : IModuleControl, IDisposable - { - /* - protected static readonly Regex FileInfoRegex = new Regex( - @"\.([a-z]{2,3}\-[0-9A-Z]{2,4}(-[A-Z]{2})?)(\.(Host|Portal-\d+))?\.resx$", - RegexOptions.IgnoreCase | RegexOptions.Compiled, - TimeSpan.FromSeconds(1)); - */ - - private readonly ILog tracelLogger = LoggerSource.Instance.GetLogger("DNN.Trace"); - private readonly Lazy serviceScopeContainer = new Lazy(ServiceScopeContainer.GetRequestOrCreateScope); - private string localResourceFile; - private ModuleInstanceContext moduleContext; - - /* - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public Control ContainerControl - { - get - { - return Globals.FindControlRecursive(this, "ctr" + this.ModuleId); - } - } - */ - - public bool IsHostMenu - { - get - { - return Globals.IsHostTab(this.PortalSettings.ActiveTab.TabID); - } - } - - public PortalSettings PortalSettings - { - get - { - return PortalController.Instance.GetCurrentPortalSettings(); - } - } - - /// - /// Gets a value indicating whether the EditMode property is used to determine whether the user is in the - /// Administrator role - /// Cache. - /// - public bool EditMode - { - get - { - return this.ModuleContext.EditMode; - } - } - - public bool IsEditable - { - get - { - return this.ModuleContext.IsEditable; - } - } - - public int PortalId - { - get - { - return this.ModuleContext.PortalId; - } - } - - public int TabId - { - get - { - return this.ModuleContext.TabId; - } - } - - public UserInfo UserInfo - { - get - { - return this.PortalSettings.UserInfo; - } - } - - public int UserId - { - get - { - return this.PortalSettings.UserId; - } - } - - public PortalAliasInfo PortalAlias - { - get - { - return this.PortalSettings.PortalAlias; - } - } - - public Hashtable Settings - { - get - { - return this.ModuleContext.Settings; - } - } - - /// Gets the underlying base control for this ModuleControl. - /// A String. - public Control Control - { - get - { - return null; - } - } - - public string ID { get; set; } - - /// Gets or Sets the Path for this control (used primarily for UserControls). - /// A String. - public string ControlPath { get; set; } - - /// Gets the Name for this control. - /// A String. - public string ControlName - { - get - { - return this.GetType().Name.Replace("_", "."); - } - } - - /// Gets the Module Context for this control. - /// A ModuleInstanceContext. - public ModuleInstanceContext ModuleContext - { - get - { - if (this.moduleContext == null) - { - this.moduleContext = new ModuleInstanceContext(this); - } - - return this.moduleContext; - } - } - - /* - // CONVERSION: Remove obsoleted methods (FYI some core modules use these, such as Links) - - /// - /// Gets the CacheDirectory property is used to return the location of the "Cache" - /// Directory for the Module. - /// - [Obsolete("Deprecated in DotNetNuke 7.0.0. Please use ModuleController.CacheDirectory(). Scheduled removal in v11.0.0.")] - public string CacheDirectory - { - get - { - return PortalController.Instance.GetCurrentPortalSettings().HomeDirectoryMapPath + "Cache"; - } - } - - /// - /// Gets the CacheFileName property is used to store the FileName for this Module's - /// Cache. - /// - [Obsolete("Deprecated in DotNetNuke 7.0.0. Please use ModuleController.CacheFileName(TabModuleID). Scheduled removal in v11.0.0.")] - public string CacheFileName - { - get - { - string strCacheKey = "TabModule:"; - strCacheKey += this.TabModuleId + ":"; - strCacheKey += Thread.CurrentThread.CurrentUICulture.ToString(); - return PortalController.Instance.GetCurrentPortalSettings().HomeDirectoryMapPath + "Cache" + "\\" + Globals.CleanFileName(strCacheKey) + ".resources"; - } - } - - [Obsolete("Deprecated in DotNetNuke 7.0.0. Please use ModuleController.CacheKey(TabModuleID). Scheduled removal in v11.0.0.")] - public string CacheKey - { - get - { - string strCacheKey = "TabModule:"; - strCacheKey += this.TabModuleId + ":"; - strCacheKey += Thread.CurrentThread.CurrentUICulture.ToString(); - return strCacheKey; - } - } - */ - - public ModuleActionCollection Actions - { - get - { - return this.ModuleContext.Actions; - } - - set - { - this.ModuleContext.Actions = value; - } - } - - public string HelpURL - { - get - { - return this.ModuleContext.HelpURL; - } - - set - { - this.ModuleContext.HelpURL = value; - } - } - - public ModuleInfo ModuleConfiguration - { - get - { - return this.ModuleContext.Configuration; - } - - set - { - this.ModuleContext.Configuration = value; - } - } - - public int TabModuleId - { - get - { - return this.ModuleContext.TabModuleId; - } - - set - { - this.ModuleContext.TabModuleId = value; - } - } - - public int ModuleId - { - get - { - return this.ModuleContext.ModuleId; - } - - set - { - this.ModuleContext.ModuleId = value; - } - } - - /// Gets or sets the local resource file for this control. - /// A String. - public string LocalResourceFile - { - get - { - string fileRoot; - if (string.IsNullOrEmpty(this.localResourceFile)) - { - fileRoot = Path.Combine(this.ControlPath, Localization.LocalResourceDirectory + "/" + this.ID); - } - else - { - fileRoot = this.localResourceFile; - } - - return fileRoot; - } - - set - { - this.localResourceFile = value; - } - } - - /// - /// Gets the Dependency Provider to resolve registered - /// services with the container. - /// - /// - /// The Dependency Service. - /// - protected IServiceProvider DependencyProvider => this.serviceScopeContainer.Value.ServiceScope.ServiceProvider; - - public string EditUrl() - { - return this.ModuleContext.EditUrl(); - } - - public string EditUrl(string controlKey) - { - return this.ModuleContext.EditUrl(controlKey); - } - - public string EditUrl(string keyName, string keyValue) - { - return this.ModuleContext.EditUrl(keyName, keyValue); - } - - public string EditUrl(string keyName, string keyValue, string controlKey) - { - return this.ModuleContext.EditUrl(keyName, keyValue, controlKey); - } - - public string EditUrl(string keyName, string keyValue, string controlKey, params string[] additionalParameters) - { - return this.ModuleContext.EditUrl(keyName, keyValue, controlKey, additionalParameters); - } - - public string EditUrl(int tabID, string controlKey, bool pageRedirect, params string[] additionalParameters) - { - return this.ModuleContext.NavigateUrl(tabID, controlKey, pageRedirect, additionalParameters); - } - - public int GetNextActionID() - { - return this.ModuleContext.GetNextActionID(); - } - - /// - public void Dispose() - { - // base.Dispose(); - if (this.serviceScopeContainer.IsValueCreated) - { - this.serviceScopeContainer.Value.Dispose(); - } - } - - /* - [DnnDeprecated(7, 0, 0, "Please use ModuleController.CacheFileName(TabModuleID)", RemovalVersion = 11)] - public partial string GetCacheFileName(int tabModuleId) - { - string strCacheKey = "TabModule:"; - strCacheKey += tabModuleId + ":"; - strCacheKey += Thread.CurrentThread.CurrentUICulture.ToString(); - return PortalController.Instance.GetCurrentPortalSettings().HomeDirectoryMapPath + "Cache" + "\\" + Globals.CleanFileName(strCacheKey) + ".resources"; - } - - [DnnDeprecated(7, 0, 0, "Please use ModuleController.CacheKey(TabModuleID)", RemovalVersion = 11)] - public partial string GetCacheKey(int tabModuleId) - { - string strCacheKey = "TabModule:"; - strCacheKey += tabModuleId + ":"; - strCacheKey += Thread.CurrentThread.CurrentUICulture.ToString(); - return strCacheKey; - } - - [DnnDeprecated(7, 0, 0, "Please use ModuleController.SynchronizeModule(ModuleId)", RemovalVersion = 11)] - public partial void SynchronizeModule() - { - ModuleController.SynchronizeModule(this.ModuleId); - } - */ - protected void OnInit() - { - if (this.tracelLogger.IsDebugEnabled) - { - this.tracelLogger.Debug($"PortalModuleBase.OnInit Start (TabId:{this.PortalSettings.ActiveTab.TabID},ModuleId:{this.ModuleId}): {this.GetType()}"); - } - - // base.OnInit(e); - if (this.tracelLogger.IsDebugEnabled) - { - this.tracelLogger.Debug($"PortalModuleBase.OnInit End (TabId:{this.PortalSettings.ActiveTab.TabID},ModuleId:{this.ModuleId}): {this.GetType()}"); - } - } - - protected void OnLoad() - { - if (this.tracelLogger.IsDebugEnabled) - { - this.tracelLogger.Debug($"PortalModuleBase.OnLoad Start (TabId:{this.PortalSettings.ActiveTab.TabID},ModuleId:{this.ModuleId}): {this.GetType()}"); - } - - // base.OnLoad(e); - if (this.tracelLogger.IsDebugEnabled) - { - this.tracelLogger.Debug($"PortalModuleBase.OnLoad End (TabId:{this.PortalSettings.ActiveTab.TabID},ModuleId:{this.ModuleId}): {this.GetType()}"); - } - } - - /// - /// Helper method that can be used to add an ActionEventHandler to the Skin for this - /// Module Control. - /// - protected void AddActionHandler(ActionEventHandler e) - { - /* - UI.Skins.Skin parentSkin = UI.Skins.Skin.GetParentSkin(this); - if (parentSkin != null) - { - parentSkin.RegisterModuleActionEvent(this.ModuleId, e); - } - */ - } - - protected string LocalizeString(string key) - { - return Localization.GetString(key, this.LocalResourceFile); - } - - protected string LocalizeSafeJsString(string key) - { - return Localization.GetSafeJSString(key, this.LocalResourceFile); - } - } -} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/MvcModuleControl.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/MvcModuleControl.cs new file mode 100644 index 00000000000..5ab7f61af1e --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/MvcModuleControl.cs @@ -0,0 +1,225 @@ +// 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.MvcPipeline.ModuleControl +{ + using System; + using System.Collections; + using System.IO; + using System.Linq; + using System.Web; + using System.Web.Mvc; + using System.Web.Mvc.Html; + using System.Web.Routing; + using System.Web.UI; + + using DotNetNuke.Common; + using DotNetNuke.Common.Utilities; + using DotNetNuke.Entities.Modules; + using DotNetNuke.Entities.Modules.Actions; + using DotNetNuke.Entities.Portals; + using DotNetNuke.Entities.Users; + using DotNetNuke.Instrumentation; + using DotNetNuke.Services.Localization; + using DotNetNuke.UI.Containers; + using DotNetNuke.UI.Modules; + using DotNetNuke.Collections; + using DotNetNuke.Common.Internal; + + public class MvcModuleControl : DefaultMvcModuleControlBase + { + + private const string ExcludedQueryStringParams = "tabid,mid,ctl,language,popup,action,controller"; + private const string ExcludedRouteValues = "mid,ctl,popup"; + + public override string ControlName + { + get + { + return RouteActionName; + } + } + + + /// Gets or Sets the Path for this control (used primarily for UserControls). + /// A String. + public override string ControlPath + { + get + { + return string.Format("~/DesktopModules/MVC/{0}", ModuleConfiguration.DesktopModule.FolderName); + } + } + + /// Gets or sets the action name for the MVC route. + /// A String. + public string RouteActionName + { + get + { + var segments = GetSegments(); + if (segments.Length < 2) + { + return ""; + } + if (segments.Length == 3) + { + return segments[2]; + } + else + { + return segments[1]; + } + } + } + + /// Gets or sets the controller name for the MVC route. + /// A String. + public string RouteControllerName + { + get + { + var segments = GetSegments(); + if (segments.Length < 2) + { + return string.Empty; + } + return segments[1]; + } + } + + public string RouteNamespace + { + get + { + var segments = GetSegments(); + if (segments.Length < 1) + { + return string.Empty; + } + return segments[0]; + } + } + + public string[] GetSegments() + { + return this.ModuleConfiguration.ModuleControl.ControlSrc.Replace(".mvc", string.Empty).Split('/'); + } + + public override IHtmlString Html(HtmlHelper htmlHelper) + { + var module = this.ModuleConfiguration; + var controlSrc = module.ModuleControl.ControlSrc; + var area = module.DesktopModule.FolderName; + if (!controlSrc.EndsWith(".mvc", System.StringComparison.OrdinalIgnoreCase)) + { + throw new Exception("The controlSrc is not a MVC control: " + controlSrc); + } + + var segments = GetSegments(); + if (segments.Length < 2) + { + throw new Exception("The controlSrc is not a MVC control: " + controlSrc); + } + + string controllerName = string.Empty; + string actionName = string.Empty; + var controlKey = module.ModuleControl.ControlKey; + + + this.LocalResourceFile = string.Format( + "~/DesktopModules/MVC/{0}/{1}/{2}.resx", + module.DesktopModule.FolderName, + Localization.LocalResourceDirectory, + RouteActionName); + + RouteValueDictionary values = new RouteValueDictionary + { + { "mvcpage", true }, + { "ModuleId", module.ModuleID }, + { "TabId", module.TabID }, + { "ModuleControlId", module.ModuleControlId }, + { "PanaName", module.PaneName }, + { "ContainerSrc", module.ContainerSrc }, + { "ContainerPath", module.ContainerPath }, + { "IconFile", module.IconFile }, + { "area", area } + }; + + var queryString = htmlHelper.ViewContext.HttpContext.Request.QueryString; + + if (string.IsNullOrEmpty(controlKey)) + { + controlKey = queryString.GetValueOrDefault("ctl", string.Empty); + } + + var moduleId = Null.NullInteger; + if (queryString["moduleid"] != null) + { + int.TryParse(queryString["moduleid"], out moduleId); + } + + if (moduleId != module.ModuleID && string.IsNullOrEmpty(controlKey)) + { + // Set default routeData for module that is not the "selected" module + actionName = RouteActionName; + controllerName = RouteControllerName; + + // routeData.Values.Add("controller", controllerName); + // routeData.Values.Add("action", actionName); + + if (!string.IsNullOrEmpty(RouteNamespace)) + { + // routeData.DataTokens.Add("namespaces", new string[] { routeNamespace }); + } + } + else + { + var control = ModuleControlController.GetModuleControlByControlKey(controlKey, module.ModuleDefID); + actionName = queryString.GetValueOrDefault("action", RouteActionName); + controllerName = queryString.GetValueOrDefault("controller", RouteControllerName); + + // values.Add("controller", controllerName); + // values.Add("action", actionName); + + foreach (var param in queryString.AllKeys) + { + if (!ExcludedQueryStringParams.Split(',').ToList().Contains(param.ToLowerInvariant())) + { + if (!values.ContainsKey(param)) + { + values.Add(param, queryString[param]); + } + } + } + + if (!string.IsNullOrEmpty(RouteNamespace)) + { + // routeData.DataTokens.Add("namespaces", new string[] { routeNamespace }); + } + } + + string[] routeValues = { $"moduleId={ModuleConfiguration.ModuleID}", $"controller={RouteControllerName}", $"action={RouteActionName}" }; + + var request = htmlHelper.ViewContext.HttpContext.Request; + var req = request.Params; + var isMyRoute = req["MODULEID"] != null && req["CONTROLLER"] != null && int.TryParse(req["MODULEID"], out var modId) && modId == ModuleConfiguration.ModuleID; + + var url = isMyRoute ? + request.Url.ToString() : + TestableGlobals.Instance.NavigateURL(ModuleConfiguration.TabID, TestableGlobals.Instance.IsHostTab(ModuleConfiguration.TabID), PortalSettings, string.Empty, routeValues); + + var formTag = new TagBuilder("form"); + formTag.Attributes.Add("action", url); + formTag.Attributes.Add("method", "post"); + formTag.InnerHtml += htmlHelper.AntiForgeryToken().ToHtmlString(); + + formTag.InnerHtml += htmlHelper.Action( + actionName, + controllerName, + values); + return new MvcHtmlString(formTag.ToString()); + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/MvcModuleControlExtensions.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/MvcModuleControlExtensions.cs new file mode 100644 index 00000000000..11c65d32e9d --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/MvcModuleControlExtensions.cs @@ -0,0 +1,175 @@ +// 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.MvcPipeline.ModuleControl +{ + using System; + using System.Collections.Generic; + using System.Web; + using System.Web.Mvc; + using DotNetNuke.Entities.Modules; + using DotNetNuke.Services.Localization; + using DotNetNuke.Web.Client; + using DotNetNuke.Web.Client.ClientResourceManagement; + using DotNetNuke.Web.MvcPipeline.ModuleControl; + + /// + /// Extension methods for IMvcModuleControl interface. + /// + public static class MvcModuleControlExtensions + { + + /// + /// Gets a localized string for the module control. + /// + /// The module control instance. + /// The resource key. + /// The localized string. + public static string LocalizeString(this IMvcModuleControl moduleControl, string key) + { + if (moduleControl == null) + { + throw new ArgumentNullException(nameof(moduleControl)); + } + + if (string.IsNullOrEmpty(key)) + { + return string.Empty; + } + + return Localization.GetString(key, moduleControl.LocalResourceFile); + } + + /// + /// Gets a localized string formatted for safe JavaScript usage. + /// + /// The module control instance. + /// The resource key. + /// The JavaScript-safe localized string. + public static string LocalizeSafeJsString(this IMvcModuleControl moduleControl, string key) + { + if (moduleControl == null) + { + throw new ArgumentNullException(nameof(moduleControl)); + } + + if (string.IsNullOrEmpty(key)) + { + return string.Empty; + } + + return Localization.GetSafeJSString(key, moduleControl.LocalResourceFile); + } + + /// + /// Gets an edit URL for the module. + /// + /// The module control instance. + /// The control key for the edit page. + /// The edit URL. + public static string EditUrl(this IMvcModuleControl moduleControl, string controlKey = "") + { + if (moduleControl == null) + { + throw new ArgumentNullException(nameof(moduleControl)); + } + + return string.IsNullOrEmpty(controlKey) + ? moduleControl.ModuleContext.EditUrl() + : moduleControl.ModuleContext.EditUrl(controlKey); + } + + /// + /// Gets an edit URL for the module with specific parameters. + /// + /// The module control instance. + /// The parameter key name. + /// The parameter key value. + /// The control key for the edit page. + /// The edit URL with parameters. + public static string EditUrl(this IMvcModuleControl moduleControl, string keyName, string keyValue, string controlKey = "") + { + if (moduleControl == null) + { + throw new ArgumentNullException(nameof(moduleControl)); + } + + return string.IsNullOrEmpty(controlKey) + ? moduleControl.ModuleContext.EditUrl(keyName, keyValue) + : moduleControl.ModuleContext.EditUrl(keyName, keyValue, controlKey); + } + + /// + /// Gets a module setting value with type conversion. + /// + /// The type to convert the setting to. + /// The module control instance. + /// The setting name. + /// The default value if setting is not found or conversion fails. + /// The setting value converted to the specified type. + public static T GetModuleSetting(this IMvcModuleControl moduleControl, string settingName, T defaultValue = default) + { + if (moduleControl == null) + { + throw new ArgumentNullException(nameof(moduleControl)); + } + + if (string.IsNullOrEmpty(settingName)) + { + return defaultValue; + } + + var settings = moduleControl.ModuleContext.Settings; + if (settings == null || !settings.ContainsKey(settingName)) + { + return defaultValue; + } + + try + { + var settingValue = settings[settingName]?.ToString(); + if (string.IsNullOrEmpty(settingValue)) + { + return defaultValue; + } + + return (T)Convert.ChangeType(settingValue, typeof(T)); + } + catch (Exception) + { + return defaultValue; + } + } + + /// + /// Checks if the current user is in edit mode for the module. + /// + /// The module control instance. + /// True if in edit mode; otherwise, false. + public static bool EditMode(this IMvcModuleControl moduleControl) + { + if (moduleControl == null) + { + throw new ArgumentNullException(nameof(moduleControl)); + } + + return moduleControl.ModuleContext.EditMode; + } + + /// + /// Checks if the module is editable by the current user. + /// + /// The module control instance. + /// True if editable; otherwise, false. + public static bool IsEditable(this IMvcModuleControl moduleControl) + { + if (moduleControl == null) + { + throw new ArgumentNullException(nameof(moduleControl)); + } + + return moduleControl.ModuleContext.IsEditable; + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/README.md b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/README.md new file mode 100644 index 00000000000..b7e0405112c --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/README.md @@ -0,0 +1,321 @@ +# MVC Module Control Implementation + +## Overview + +The MVC Module Control implementation provides a modern alternative to DNN's traditional WebForms-based module rendering pipeline. This new system enables DNN modules to leverage ASP.NET MVC architecture while maintaining compatibility with the existing DNN framework. + +## Problem Statement + +DNN Platform has historically relied on the WebForms pipeline accessed through `/default.aspx`. As outlined in [GitHub issue #6679](https://github.com/dnnsoftware/Dnn.Platform/issues/6679). + +## Solution: Hybrid Pipeline Architecture + +The MVC Pipeline introduces a dual-rendering mechanism: + +1. **Legacy Pipeline**: Traditional WebForms through `/default.aspx` +2. **New MVC Pipeline**: Modern MVC rendering through `/DesktopModules/Default/Page/{tabId}/{locale}` + +### Module Pipeline Support Matrix + +Based on the GitHub issue specifications, modules can support different pipeline patterns: + +| WebForms Support | MVC Module Support | SPA Module Support | +|------------------|--------------------|--------------------| +| Custom Control + Razor view | Use generic Control + Custom MVC Controller as child controller (shared with WebForms pipeline) | Use generic Control + return directly the html (shared with WebForms pipeline) | | +| Render Razor Partial | The generic control redirects to the controller defined in Control Src |The generic Control renders the html file defined in Control Src | | + +### Module Control Class Configuration + +Modules specify their MVC compatibility through: +- **MVC Control Class**: Defined in module control settings and module manifest +- **Interface Implementation**: Must implement `IMvcModuleControl` +- **Optional Interfaces**: Can implement `IActionable` for unified action handling +- **Pipeline Detection**: System can determine module compatibility and show appropriate messages + +## Class Diagram + +```mermaid +classDiagram + %% Interfaces + class IMvcModuleControl { + <> + +Html(helper) IHtmlString + } + + class IModuleControl { + <> + + +string ControlPath + +string ControlName + +ModuleInstanceContext ModuleContext + +string LocalResourceFile + } + + + %% Concrete Classes + class MvcModuleControl { + +Html(helper) IHtmlString + } + note for MvcModuleControl "MVC controller" + + class SpaModuleControl { + +Html(helper) IHtmlString + } + note for SpaModuleControl "Html with tokens" + + %% Abstract Classes + + class DefaultMvcModuleControlBase { + +Html(helper) IHtmlString + } + + class RazorModuleControlBase { + + +Invoke() IRazorModuleResult + } + note for RazorModuleControlBase "Razor view from model" + + %% Relationships + IMvcModuleControl ..|> IModuleControl : extends + + DefaultMvcModuleControlBase ..|> IMvcModuleControl : implements + + MvcModuleControl --|> DefaultMvcModuleControlBase : extends + RazorModuleControlBase --|> DefaultMvcModuleControlBase : extends + SpaModuleControl --|> DefaultMvcModuleControlBase : extends + + +``` + +## Core Components + +### 1. IMvcModuleControl Interface + +```csharp +public interface IMvcModuleControl : IModuleControl +{ + IHtmlString Html(HtmlHelper htmlHelper); +} +``` + +The base interface that all MVC module controls must implement, extending the standard `IModuleControl` with MVC-specific rendering capabilities. This interface enables: + +- **Pipeline Compatibility Detection**: The system can determine if a module supports the MVC pipeline +- **Unified Rendering**: The `Html()` method provides access to `HtmlHelper` with information about HttpContext, controller, and page model +- **Flexible Rendering Options**: Modules can use HTML helpers to render content (Razor partials, child action controllers, or other helpers) + +### 2. DefaultMvcModuleControlBase + +The abstract base class that provides common functionality for all MVC module controls: + +- **Dependency Injection**: Integrated service provider access +- **Module Context**: Access to DNN module configuration and settings +- **Portal Context**: Portal settings, user information, and localization +- **Resource Management**: Localization helpers and resource file management +- **URL Generation**: Helper methods for creating edit URLs + +**Key Features:** +- Service scoped dependency injection +- Automatic resource file path resolution +- Portal and user context access +- Module settings management +- Edit URL generation with MVC support + +### 3. Module Control Implementations + +#### MvcModuleControl +The standard MVC module control for traditional MVC controllers and actions. + +**Features:** +- Parses `.mvc` control source to extract controller and action names +- Supports routing with namespaces: `{namespace}/{controller}/{action}` +- Automatic query string parameter mapping +- Route value dictionary construction for MVC action execution +- Localization resource file resolution + +**Control Source Format:** +``` +{namespace}/{controller}/{action}.mvc +``` + +#### SpaModuleControl +Specialized control for Single Page Applications. + +**Features:** +- HTML5 file rendering with token replacement +- Automatic CSS and JavaScript file inclusion +- File existence caching for performance +- Support for HTML5 module token system +- Content caching with file dependency tracking + +**Supported Files:** +- `.html` or custom HTML5 files +- Automatic `.css` file inclusion (same name) +- Automatic `.js` file inclusion (same name) + +#### RazorModuleControlBase +Abstract base for modules using Razor view rendering. +This use MVC 5 razor views. +Recomended for Weforms control migrations +Folows the ViewComponent patern of .net Core for easy future trasition to .net Core + +**Features:** +- Direct Razor view rendering +- Model binding support +- Custom view context management +- Flexible view name resolution +- Request/Response context integration + +**Usage Pattern:** +```csharp +public class MyModuleControl : RazorModuleControlBase +{ + public override IRazorModuleResult Invoke() + { + var model = GetMyModel(); + return View("MyView", model); + } +} +``` + +### 4. Extension Methods (MvcModuleControlExtensions) + +Provides convenient extension methods for all MVC module controls: + +- **Localization**: `LocalizeString()`, `LocalizeSafeJsString()` +- **URL Generation**: `EditUrl()` with various overloads +- **Settings Access**: `GetModuleSetting()` with type conversion +- **State Checking**: `EditMode()`, `IsEditable()` + +### 5. Resource Management + +#### IResourcable Interface +Modules can implement this interface to automatically manage CSS and JavaScript resources. + +#### ModuleResources System +- Automatic resource registration +- Priority-based loading +- File existence validation +- Caching for performance +- Independent of the pipeline + +### 6. Utilities + +#### MvcModuleControlRenderer +Provides rendering capabilities for Razor-based module controls outside of the normal MVC pipeline. + +#### MvcViewEngine +A powerful utility class for rendering MVC views to strings outside of the standard MVC request pipeline. This class is essential for the MVC module control system as it enables view rendering in non-controller contexts. + +**Core Methods:** +```csharp +// Render full view with layout +string html = MvcViewEngine.RenderView("~/Views/MyView.cshtml", model); + +// Render partial view without layout +string partial = MvcViewEngine.RenderPartialView("~/Views/_MyPartial.cshtml", model); + +// Render HtmlHelper delegates +string html = MvcViewEngine.RenderHtmlHelperToString(helper => + helper.Action("MyAction", "MyController"), model); +``` + +## Demo Implementation + +The Demo folder includes demonstration classes that show practical implementation examples: + +### WrapperModule.cs +A WebForms-compatible module that bridges to the MVC pipeline, demonstrating how to integrate MVC module controls within the traditional DNN WebForms infrastructure. + +**Key Features:** +- **Hybrid Bridge Pattern**: Inherits from `PortalModuleBase` to maintain WebForms compatibility +- **MVC Integration**: Uses `MvcUtils.CreateModuleControl()` to instantiate MVC module controls +- **MvcViewEngine Integration**: Demonstrates `MvcViewEngine.RenderHtmlHelperToString()` usage +- **Interface Support**: Handles `IActionable` and `IResourcable` interfaces automatically +- **Lifecycle Management**: Proper ASP.NET control lifecycle implementation + +**Implementation Pattern:** +```csharp +public class WrapperModule : PortalModuleBase, IActionable +{ + protected override void OnInit(EventArgs e) + { + // Create MVC module control + var mc = MvcUtils.CreateModuleControl(this.ModuleConfiguration); + + // Render using MvcViewEngine + html = MvcViewEngine.RenderHtmlHelperToString(helper => mc.Html(helper)); + + // Handle optional interfaces + if (mc is IActionable actionable) + this.ModuleActions = actionable.ModuleActions; + + if (mc is IResourcable resourcable) + resourcable.RegisterResources(this.Page); + } +} +``` + +### DemoModuleControl.cs +A concrete implementation of `RazorModuleControlBase` showing how to create custom MVC module controls with dynamic view selection. + +**Key Features:** +- **Dynamic View Routing**: Uses query string parameters to determine which view to render +- **Multiple View Support**: Demonstrates rendering different views based on user input +- **Custom View Paths**: Shows how to specify custom view file locations +- **Model Passing**: Illustrates passing data models to views + +**Implementation Example:** +```csharp +public class DemoModuleControl : RazorModuleControlBase +{ + public override IRazorModuleResult Invoke() + { + // Dynamic view selection based on query parameters + switch (Request.QueryString["view"]) + { + case "Terms": + return View("~/Views/Default/Terms.cshtml", "Terms content"); + case "Privacy": + return View("~/admin/Portal/Views/Privacy.cshtml", "Privacy content"); + default: + return View("~/admin/Portal/Views/Terms.cshtml", "Default content"); + } + } +} +``` + +**Usage Scenarios:** +- **Migration**: Use WrapperModule to run MVC controls within WebForms pages +- **Development Reference**: DemoModuleControl shows best practices for Razor module implementation +- **Integration Patterns**: Demonstrates how to handle multiple interfaces (`IActionable`, `IResourcable`) +- **View Management**: Shows flexible view path configuration and model binding + +**Bridge Pattern Benefits:** +The WrapperModule demonstrates the bridge pattern that allows: +- Gradual migration from WebForms to MVC +- Using MVC controls within existing WebForms infrastructure +- Maintaining compatibility with existing DNN module architecture +- Automatic handling of module actions and resource registration + +## Key Benefits + +This implementation provides several key advantages: + +### 1. Modern Development Experience +- MVC pattern for better separation of concerns +- Dependency injection support +- Testable architecture +- Familiar development patterns for modern .NET developers + +### 2. Gradual Migration Path +- Hybrid architecture allows coexistence of WebForms and MVC +- Module-by-module migration strategy +- Backward compatibility maintained + +### 3. Pipeline Transparency & Compatibility +- **Unified Interface Implementation**: MVC modules can implement `IActionable` in a unified way +- **No Legacy Modifications**: No modifications required to the existing module pipeline +- **Custom Module Patterns**: Open to custom module control patterns + + diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Razor/IRazorModuleResult.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Razor/IRazorModuleResult.cs new file mode 100644 index 00000000000..dee80710ad9 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Razor/IRazorModuleResult.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using DotNetNuke.Web.MvcPipeline.Utils; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl.Razor +{ + public interface IRazorModuleResult + { + IHtmlString Execute(HtmlHelper htmlHelper); + string ViewName { get; } + object Model { get; } + + /// + /// Gets or sets the . + /// + ViewDataDictionary ViewData { get; } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Razor/IViewRenderer.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Razor/IViewRenderer.cs new file mode 100644 index 00000000000..53358d4255c --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Razor/IViewRenderer.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl.Razor +{ + public interface IViewRenderer + { + string RenderViewToString(string viewPath, object model = null); + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Razor/RazorModuleViewContext.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Razor/RazorModuleViewContext.cs new file mode 100644 index 00000000000..979de5f5211 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Razor/RazorModuleViewContext.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl.Razor +{ + public class RazorModuleViewContext + { + public HttpContextBase HttpContext { get; internal set; } + + /// + /// Gets the . + /// + /// + /// This is an alias for ViewContext.ViewData. + /// + public ViewDataDictionary ViewData { get; internal set; } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Razor/ViewRazorModuleResult.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Razor/ViewRazorModuleResult.cs new file mode 100644 index 00000000000..b38d1baa5de --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Razor/ViewRazorModuleResult.cs @@ -0,0 +1,32 @@ +using System; +using System.Web; +using System.Web.Mvc; +using System.Web.Mvc.Html; +using DotNetNuke.Web.MvcPipeline.Utils; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl.Razor +{ + public class ViewRazorModuleResult : IRazorModuleResult + { + public ViewRazorModuleResult(string viewName, object model, ViewDataDictionary ViewData) + { + this.ViewName = viewName; + this.Model = model; + this.ViewData = ViewData ; + } + + public string ViewName { get; private set; } + public object Model { get; private set; } + + /// + /// Gets or sets the . + /// + public ViewDataDictionary ViewData { get; private set; } + + public IHtmlString Execute(HtmlHelper htmlHelper) + { + return htmlHelper.Partial(ViewName, Model, ViewData); + } + + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/RazorModuleControlBase.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/RazorModuleControlBase.cs new file mode 100644 index 00000000000..0a9abff2cde --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/RazorModuleControlBase.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using System.Web.Mvc.Html; +using System.Web.Routing; +using DotNetNuke.Web.MvcPipeline.ModuleControl.Razor; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl +{ + public abstract class RazorModuleControlBase : DefaultMvcModuleControlBase + { + private RazorModuleViewContext _viewContext; + public override IHtmlString Html(HtmlHelper htmlHelper) + { + this.ViewContext.HttpContext = htmlHelper.ViewContext.HttpContext; + this.ViewContext.ViewData = new ViewDataDictionary(htmlHelper.ViewData); + this.ViewContext.ViewData["ModuleContext"] = this.ModuleContext; + this.ViewContext.ViewData["ModuleId"] = this.ModuleId; + this.ViewContext.ViewData["LocalResourceFile"] = this.LocalResourceFile; + var res = this.Invoke(); + return res.Execute(htmlHelper); + } + + protected virtual string DefaultViewName + { + get + { + return "~/" + this.ControlPath.Replace('\\', '/').Trim('/') + "/Views/" + this.ControlName + ".cshtml"; + } + } + + public abstract IRazorModuleResult Invoke(); + + public IRazorModuleResult View() + { + return View(null); + } + + public IRazorModuleResult View(string viewName) + { + return View(viewName, null); + } + + public IRazorModuleResult View(object model) + { + return View(null, model); + } + public IRazorModuleResult View(string viewName, object model) + { + if (string.IsNullOrEmpty(viewName)) + { + viewName= this.DefaultViewName; + } + return new ViewRazorModuleResult(viewName, model, ViewData); + } + + public RazorModuleViewContext ViewContext + { + get + { + // This should run only for the ViewComponent unit test scenarios. + if (_viewContext == null) + { + _viewContext = new RazorModuleViewContext(); + } + + return _viewContext; + } + set + { + if (value == null) + { + throw new ArgumentNullException(); + } + + _viewContext = value; + } + } + + /// + /// Gets the . + /// + public HttpContextBase HttpContext => ViewContext.HttpContext; + + /// + /// Gets the . + /// + public HttpRequestBase Request => ViewContext.HttpContext.Request; + + /// + /// Gets the . + /// + public ViewDataDictionary ViewData => ViewContext.ViewData; + + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/IResourcable.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/IResourcable.cs new file mode 100644 index 00000000000..cafb7e91cf6 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/IResourcable.cs @@ -0,0 +1,11 @@ +// 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.MvcPipeline.ModuleControl.Resources +{ + public interface IResourcable + { + ModuleResources ModuleResources {get;} + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/ModuleResources.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/ModuleResources.cs new file mode 100644 index 00000000000..2a66652ad7f --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/ModuleResources.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl.Resources +{ + public class ModuleResources + { + public List StyleSheets { get; set; } = new List(); + public List Scripts { get; set; } = new List(); + public List Libraries { get; set; } = new List(); + public bool AjaxScript { get; set; } + public bool AjaxAntiForgery { get; set; } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/ModuleScript.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/ModuleScript.cs new file mode 100644 index 00000000000..499a5ba4d8e --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/ModuleScript.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using DotNetNuke.Web.Client; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl.Resources +{ + public class ModuleScript + { + public string FilePath { get; set; } + public FileOrder.Js Priority { get; set; } = FileOrder.Js.DefaultPriority; + public IDictionary HtmlAttributes { get; set; } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/ModuleStyleSheet.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/ModuleStyleSheet.cs new file mode 100644 index 00000000000..6b6e579181d --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/ModuleStyleSheet.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using DotNetNuke.Web.Client; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl.Resources +{ + public class ModuleStyleSheet + { + public string FilePath { get; set; } + public FileOrder.Css Priority { get; set; } = FileOrder.Css.DefaultPriority; + public IDictionary HtmlAttributes { get; set; } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/ResourceableExtensions.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/ResourceableExtensions.cs new file mode 100644 index 00000000000..2b6628eb4f6 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Resources/ResourceableExtensions.cs @@ -0,0 +1,53 @@ +// 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 + +using System.Web.UI; +using DotNetNuke.Framework; +using DotNetNuke.Framework.JavaScriptLibraries; +using DotNetNuke.Web.Client.ClientResourceManagement; +using DotNetNuke.Web.MvcPipeline.ModuleControl.Resources; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl +{ + + + /// + /// Extension methods for IResourceable interface. + /// + public static class ResourceableExtensions + { + public static void RegisterResources(this IResourcable resourcable, Page page) + { + if (resourcable.ModuleResources.StyleSheets != null) + { + foreach (var styleSheet in resourcable.ModuleResources.StyleSheets) + { + ClientResourceManager.RegisterStyleSheet(page, styleSheet.FilePath, styleSheet.Priority, styleSheet.HtmlAttributes); + } + } + if (resourcable.ModuleResources.Scripts != null) + { + foreach (var javaScript in resourcable.ModuleResources.Scripts) + { + ClientResourceManager.RegisterScript(page, javaScript.FilePath, javaScript.Priority, javaScript.HtmlAttributes); + } + } + if (resourcable.ModuleResources.Libraries != null) + { + foreach (var lib in resourcable.ModuleResources.Libraries) + { + JavaScript.RequestRegistration(lib); + } + } + if (resourcable.ModuleResources.AjaxScript) + { + ServicesFramework.Instance.RequestAjaxScriptSupport(); + } + if (resourcable.ModuleResources.AjaxAntiForgery) + { + ServicesFramework.Instance.RequestAjaxAntiForgerySupport(); + } + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/SpaModuleControl.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/SpaModuleControl.cs new file mode 100644 index 00000000000..c862c837341 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/SpaModuleControl.cs @@ -0,0 +1,138 @@ +// 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.MvcPipeline.ModuleControl +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Web; + using System.Web.Mvc; + using System.Web.Mvc.Html; + using System.Web.Routing; + using System.Web.UI; + using DotNetNuke.Abstractions.Modules; + using DotNetNuke.Collections; + using DotNetNuke.Common; + using DotNetNuke.Common.Utilities; + using DotNetNuke.Entities.Modules; + using DotNetNuke.Entities.Modules.Actions; + using DotNetNuke.Entities.Portals; + using DotNetNuke.Entities.Users; + using DotNetNuke.Framework; + using DotNetNuke.Instrumentation; + using DotNetNuke.Services.Cache; + using DotNetNuke.Services.Localization; + using DotNetNuke.UI.Containers; + using DotNetNuke.UI.Modules; + using DotNetNuke.UI.Modules.Html5; + using DotNetNuke.Web.Client; + using DotNetNuke.Web.Client.ClientResourceManagement; + using DotNetNuke.Web.MvcPipeline.ModuleControl.Resources; + using DotNetNuke.Web.MvcPipeline.Tokens; + using Microsoft.Extensions.DependencyInjection; + + public class SpaModuleControl : DefaultMvcModuleControlBase, IResourcable + { + private readonly IBusinessControllerProvider businessControllerProvider; + + public SpaModuleControl(): base() + { + this.businessControllerProvider = Globals.DependencyProvider.GetRequiredService(); + } + + public SpaModuleControl(IBusinessControllerProvider businessControllerProvider) : base() + { + this.businessControllerProvider = businessControllerProvider; + } + + public string html5File => ModuleConfiguration.ModuleControl.ControlSrc; + + public ModuleResources ModuleResources + { + get + { + var resource = new ModuleResources() + { + // Register for Services Framework + AjaxScript = true + }; + + if (!string.IsNullOrEmpty(this.html5File)) + { + // Check if css file exists + var cssFile = Path.ChangeExtension(this.html5File, ".css"); + if (this.FileExists(cssFile)) + { + resource.StyleSheets.Add(new ModuleStyleSheet() + { + FilePath = "/" + cssFile, + Priority = FileOrder.Css.DefaultPriority, + }); + } + } + + if (!string.IsNullOrEmpty(this.html5File)) + { + // Check if js file exists + var jsFile = Path.ChangeExtension(this.html5File, ".js"); + if (this.FileExists(jsFile)) + { + resource.Scripts.Add(new ModuleScript() + { + FilePath = "/" + jsFile, + Priority = FileOrder.Js.DefaultPriority, + }); + } + } + return resource; + } + } + public override IHtmlString Html(HtmlHelper htmlHelper) + { + var fileContent = string.Empty; + if (!string.IsNullOrEmpty(this.html5File)) + { + fileContent = this.GetFileContent(this.html5File); + var ModuleActions = new ModuleActionCollection(); + var tokenReplace = new MvcHtml5ModuleTokenReplace(htmlHelper.ViewContext, this.businessControllerProvider, this.html5File, this.ModuleContext, ModuleActions); + fileContent = tokenReplace.ReplaceEnvironmentTokens(fileContent); + } + + return new HtmlString(HttpUtility.HtmlDecode(fileContent)); + } + + private static string GetFileContentInternal(string filepath) + { + using (var reader = new StreamReader(filepath)) + { + return reader.ReadToEnd(); + } + } + + private string GetFileContent(string filepath) + { + var cacheKey = string.Format(DataCache.SpaModulesContentHtmlFileCacheKey, filepath); + var absoluteFilePath = HttpContext.Current.Server.MapPath("/" + filepath); + var cacheItemArgs = new CacheItemArgs(cacheKey, DataCache.SpaModulesHtmlFileTimeOut, DataCache.SpaModulesHtmlFileCachePriority) + { + CacheDependency = new DNNCacheDependency(absoluteFilePath), + }; + return CBO.GetCachedObject(cacheItemArgs, c => GetFileContentInternal(absoluteFilePath)); + } + + private bool FileExists(string filepath) + { + var cacheKey = string.Format(DataCache.SpaModulesFileExistsCacheKey, filepath); + return CBO.GetCachedObject( + new CacheItemArgs( + cacheKey, + DataCache.SpaModulesHtmlFileTimeOut, + DataCache.SpaModulesHtmlFileCachePriority), + c => File.Exists(HttpContext.Current.Server.MapPath("/" + filepath))); + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Utils/MvcModuleControlRenderer.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Utils/MvcModuleControlRenderer.cs new file mode 100644 index 00000000000..12dd21f8f0d --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/Utils/MvcModuleControlRenderer.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.UI; +using DotNetNuke.Entities.Modules; +using DotNetNuke.Framework; +using DotNetNuke.Framework.JavaScriptLibraries; +using DotNetNuke.UI.Modules; +using DotNetNuke.Web.Client.ClientResourceManagement; +using DotNetNuke.Web.MvcPipeline.Framework.JavascriptLibraries; +using DotNetNuke.Web.MvcPipeline.ModuleControl.Resources; +using DotNetNuke.Web.MvcPipeline.Utils; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl.Utils +{ + public class MvcModuleControlRenderer where T : RazorModuleControlBase, new() + { + private readonly T moduleControl; + + public MvcModuleControlRenderer(Control control, ModuleInstanceContext moduleContext) + { + this.moduleControl = new T(); + moduleControl.ModuleContext.Configuration = moduleContext.Configuration; + moduleControl.ViewContext.HttpContext = new System.Web.HttpContextWrapper(System.Web.HttpContext.Current); + //moduleControl.Control = control; + } + + // Fix for CS0149: Replace the invalid constructor chaining syntax with a proper constructor call + public MvcModuleControlRenderer(PortalModuleBase control) + : this(control, control.ModuleContext) + { + } + + public string RenderToString() + { + var renderer = new MvcViewEngine(); + var res = moduleControl.Invoke(); + return renderer.RenderViewToString(res.ViewName, res.Model); + } + + public T ModuleControl => this.moduleControl; + + + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/WebForms/WrapperModule.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/WebForms/WrapperModule.cs new file mode 100644 index 00000000000..dd76ebd31eb --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/ModuleControl/WebForms/WrapperModule.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc.Html; +using System.Web.UI; +using System.Web.UI.WebControls; +using DotNetNuke.Entities.Modules; +using DotNetNuke.Entities.Modules.Actions; +using DotNetNuke.Instrumentation; +using DotNetNuke.Services.Installer.Log; +using DotNetNuke.UI.Skins; +using DotNetNuke.UI.Skins.Controls; +using DotNetNuke.Web.MvcPipeline.ModuleControl.Resources; +using DotNetNuke.Web.MvcPipeline.UI.Utilities; +using DotNetNuke.Web.MvcPipeline.Utils; + +namespace DotNetNuke.Web.MvcPipeline.ModuleControl.WebForms +{ + + public class WrapperModule : PortalModuleBase, IActionable + { + private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(WrapperModule)); + + private string html = string.Empty; + + public ModuleActionCollection ModuleActions { get; private set; } = new ModuleActionCollection(); + + // Make sure child controls are created when needed + protected override void CreateChildControls() + { + Controls.Clear(); + Controls.Add(new LiteralControl(html)); + // important so ASP.NET tracks the created controls across postbacks + ChildControlsCreated = true; + base.CreateChildControls(); + } + + // ensure child controls exist early in page lifecycle + protected override void OnInit(EventArgs e) + { + base.OnInit(e); + try + { + var mc = MvcUtils.CreateModuleControl(this.ModuleConfiguration); + html = MvcViewEngine.RenderHtmlHelperToString(helper => mc.Html(helper)); + if (mc is IActionable) + { + var moduleControl = (IActionable)mc; + this.ModuleActions = moduleControl.ModuleActions; + } + if (mc is IResourcable) + { + var moduleControl = (IResourcable)mc; + moduleControl.RegisterResources(this.Page); + } + } + catch (Exception ex) + { + Logger.Error(ex); + Skin.AddModuleMessage(this, "An error occurred while loading the module. Please contact the site administrator.", ModuleMessage.ModuleMessageType.RedError); + html = "
" + ex.Message + "
"; + } + EnsureChildControls(); + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Modules/ModuleHelpers.TextEditor.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Modules/ModuleHelpers.TextEditor.cs index f78b1b5105f..3133a54924c 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Modules/ModuleHelpers.TextEditor.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Modules/ModuleHelpers.TextEditor.cs @@ -2,7 +2,7 @@ // 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.MvcPipeline.Containers +namespace DotNetNuke.Web.MvcPipeline.Modules { using System; using System.Collections.Generic; @@ -126,7 +126,10 @@ private static void LoadAllSettings(ViewContext page, string id) // Set Current Mode to Module Instance currentEditorSettings.SettingMode = SettingsMode.ModuleInstance; */ - if (page.IsChildAction) + + if (!page.HttpContext.Request.IsAjaxRequest()) + + //if (page.IsChildAction) { MvcClientResourceManager.RegisterStyleSheet(page, Globals.ResolveUrl("~/Providers/HtmlEditorProviders/DNNConnect.CKE/css/CKEditorToolBars.css")); MvcClientResourceManager.RegisterStyleSheet(page, Globals.ResolveUrl("~/Providers/HtmlEditorProviders/DNNConnect.CKE/css/CKEditorOverride.css")); diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Modules/ModuleHelpers.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Modules/ModuleHelpers.cs index d1ac82a2320..0b9e116a295 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Modules/ModuleHelpers.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Modules/ModuleHelpers.cs @@ -2,7 +2,7 @@ // 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.Mvc.Page +namespace DotNetNuke.Web.MvcPipeline.Modules { using System; using System.Collections.Generic; @@ -11,14 +11,101 @@ namespace DotNetNuke.Web.Mvc.Page using System.Threading.Tasks; using System.Web; using System.Web.Mvc; - + using System.Web.Mvc.Html; using DotNetNuke.Services.Localization; + using DotNetNuke.UI.Modules; - public static class ModuleHelpers + public static partial class ModuleHelpers { public static IHtmlString LocalizeString(this HtmlHelper htmlHelper, string key, string localResourceFile) { return MvcHtmlString.Create(Localization.GetString(key, localResourceFile)); } + + public static IHtmlString LocalizeString(this HtmlHelper htmlHelper, string key) + { + if (htmlHelper.ViewContext.ViewData["LocalResourceFile"] == null) + { + throw new InvalidOperationException("The LocalResourceFile must be set in the ViewData to use this helper."); + } + var localResourceFile = (string)htmlHelper.ViewContext.ViewData["LocalResourceFile"]; + return MvcHtmlString.Create(Localization.GetString(key, localResourceFile)); + } + + public static IHtmlString EditUrl(this HtmlHelper htmlHelper) + { + if (htmlHelper.ViewContext.ViewData["ModuleContext"] == null) + { + throw new InvalidOperationException("The ModuleContext must be set in the ViewData to use this helper."); + } + var moduleContext = (ModuleInstanceContext)htmlHelper.ViewContext.ViewData["ModuleContext"]; + return MvcHtmlString.Create(moduleContext.EditUrl()); + } + + public static IHtmlString EditUrl(this HtmlHelper htmlHelper, string controlKey) + { + if (htmlHelper.ViewContext.ViewData["ModuleContext"] == null) + { + throw new InvalidOperationException("The ModuleContext must be set in the ViewData to use this helper."); + } + var moduleContext = (ModuleInstanceContext)htmlHelper.ViewContext.ViewData["ModuleContext"]; + return MvcHtmlString.Create(moduleContext.EditUrl( controlKey)); + } + + public static IHtmlString EditUrl(this HtmlHelper htmlHelper, string keyName, string keyValue) + { + if (htmlHelper.ViewContext.ViewData["ModuleContext"] == null) + { + throw new InvalidOperationException("The ModuleContext must be set in the ViewData to use this helper."); + } + var moduleContext = (ModuleInstanceContext)htmlHelper.ViewContext.ViewData["ModuleContext"]; + return MvcHtmlString.Create(moduleContext.EditUrl(keyName, keyValue)); + } + + public static IHtmlString EditUrl(this HtmlHelper htmlHelper, string keyName, string keyValue, string controlKey) + { + if (htmlHelper.ViewContext.ViewData["ModuleContext"] == null) + { + throw new InvalidOperationException("The ModuleContext must be set in the ViewData to use this helper."); + } + var moduleContext = (ModuleInstanceContext)htmlHelper.ViewContext.ViewData["ModuleContext"]; + return MvcHtmlString.Create(moduleContext.EditUrl(keyName, keyValue, controlKey)); + } + + public static IHtmlString EditUrl(this HtmlHelper htmlHelper, string keyName, string keyValue, string controlKey, params string[] additionalParameters) + { + if (htmlHelper.ViewContext.ViewData["ModuleContext"] == null) + { + throw new InvalidOperationException("The ModuleContext must be set in the ViewData to use this helper."); + } + var moduleContext = (ModuleInstanceContext)htmlHelper.ViewContext.ViewData["ModuleContext"]; + return MvcHtmlString.Create(moduleContext.EditUrl(keyName, keyValue, controlKey, additionalParameters)); + } + + public static MvcHtmlString ModulePartial(this HtmlHelper htmlHelper, string partialViewName, object model = null) + { + if (htmlHelper.ViewContext.ViewData["ModuleContext"] == null) + { + throw new InvalidOperationException("The ModuleContext must be set in the ViewData to use this helper."); + } + var moduleContext = (ModuleInstanceContext)htmlHelper.ViewContext.ViewData["ModuleContext"]; + + var viewPath = string.Format("~/DesktopModules/{0}/Views/{1}.cshtml", moduleContext.Configuration.DesktopModule.FolderName, partialViewName); + + return htmlHelper.Partial(viewPath, model); + } + + public static MvcHtmlString ModulePartial(this HtmlHelper htmlHelper, string partialViewName, object model, ViewDataDictionary dic) + { + if (htmlHelper.ViewContext.ViewData["ModuleContext"] == null) + { + throw new InvalidOperationException("The ModuleContext must be set in the ViewData to use this helper."); + } + var moduleContext = (ModuleInstanceContext)htmlHelper.ViewContext.ViewData["ModuleContext"]; + + var viewPath = string.Format("~/DesktopModules/{0}/Views/{1}.cshtml", moduleContext.Configuration.DesktopModule.FolderName, partialViewName); + + return htmlHelper.Partial(viewPath, model, dic); + } } } diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/PermissionTriStateHelper.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Modules/PermissionTriStateHelper.cs similarity index 98% rename from DNN Platform/DotNetNuke.Web.MvcPipeline/PermissionTriStateHelper.cs rename to DNN Platform/DotNetNuke.Web.MvcPipeline/Modules/PermissionTriStateHelper.cs index 57d058d6e91..c43322bc872 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/PermissionTriStateHelper.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Modules/PermissionTriStateHelper.cs @@ -2,7 +2,7 @@ // 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.MvcPipeline +namespace DotNetNuke.Web.MvcPipeline.Modules { using System.Web.Mvc; @@ -104,7 +104,7 @@ public static string GetInitScript() LookupScriptValues(out grantImagePath, out denyImagePath, out nullImagePath, out lockImagePath, out grantAltText, out denyAltText, out nullAltText); - string script = + var script = string.Format( @"jQuery(document).ready( function() {{ diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Skins/SkinHelpers.Logo.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Skins/SkinHelpers.Logo.cs index ae0170dd365..4126a330e61 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Skins/SkinHelpers.Logo.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Skins/SkinHelpers.Logo.cs @@ -39,6 +39,7 @@ public static IHtmlString Logo(this HtmlHelper helper, string borderW tbImage.Attributes.Add("alt", portalSettings.PortalName); TagBuilder tbLink = new TagBuilder("a"); + tbLink.GenerateId("dnn_dnnLOGO_"); if (!string.IsNullOrEmpty(linkCssClass)) { tbLink.AddCssClass(linkCssClass); diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Skins/SkinHelpers.Pane.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Skins/SkinHelpers.Pane.cs index 3b196662a62..b3f6155029f 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Skins/SkinHelpers.Pane.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Skins/SkinHelpers.Pane.cs @@ -88,9 +88,11 @@ public static IHtmlString Pane(this HtmlHelper htmlHelper, string id, paneDiv.AddCssClass("EditBarEmptyPane"); } } - + if (model.IsEditMode) { + // Add support for drag and drop + paneDiv.AddCssClass(" dnnSortable"); editDiv.InnerHtml += paneDiv.ToString(); return MvcHtmlString.Create(editDiv.ToString()); } diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Skins/SkinHelpers.User.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Skins/SkinHelpers.User.cs index 97fc67b1e13..87802350afa 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Skins/SkinHelpers.User.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Skins/SkinHelpers.User.cs @@ -51,32 +51,56 @@ public static IHtmlString User(this HtmlHelper helper, string cssClas if (legacyMode) { - if (portalSettings.UserRegistration == (int)Globals.PortalRegistrationType.NoRegistration || - (portalSettings.Users > portalSettings.UserQuota && portalSettings.UserQuota != 0)) - { - return MvcHtmlString.Empty; - } - var registerLink = new TagBuilder("a"); - registerLink.AddCssClass("dnnRegisterLink"); - if (!string.IsNullOrEmpty(cssClass)) + if (helper.ViewContext.HttpContext.Request.IsAuthenticated == false) { - registerLink.AddCssClass(cssClass); - } + if (portalSettings.UserRegistration == (int)Globals.PortalRegistrationType.NoRegistration || + (portalSettings.Users > portalSettings.UserQuota && portalSettings.UserQuota != 0)) + { + return MvcHtmlString.Empty; + } - registerLink.Attributes.Add("rel", "nofollow"); - registerLink.InnerHtml = registerText; - registerLink.Attributes.Add("href", !string.IsNullOrEmpty(url) ? url : Globals.RegisterURL(HttpUtility.UrlEncode(navigationManager.NavigateURL()), Null.NullString)); + var registerLink = new TagBuilder("a"); + registerLink.AddCssClass("dnnRegisterLink"); + if (!string.IsNullOrEmpty(cssClass)) + { + registerLink.AddCssClass(cssClass); + } - string registerScript = string.Empty; - if (portalSettings.EnablePopUps && portalSettings.RegisterTabId == Null.NullInteger && !AuthenticationController.HasSocialAuthenticationEnabled(null)) + registerLink.Attributes.Add("rel", "nofollow"); + registerLink.InnerHtml = registerText; + registerLink.Attributes.Add("href", !string.IsNullOrEmpty(url) ? url : Globals.RegisterURL(HttpUtility.UrlEncode(navigationManager.NavigateURL()), Null.NullString)); + + string registerScript = string.Empty; + if (portalSettings.EnablePopUps && portalSettings.RegisterTabId == Null.NullInteger && !AuthenticationController.HasSocialAuthenticationEnabled(null)) + { + // var clickEvent = "return " + UrlUtils.PopUpUrl(registerLink.Attributes["href"], portalSettings, true, false, 600, 950); + // registerLink.Attributes.Add("onclick", clickEvent); + registerScript = GetRegisterScript(registerLink.Attributes["href"], nonce); + } + return new MvcHtmlString(registerLink.ToString() + registerScript); + } + else { - // var clickEvent = "return " + UrlUtils.PopUpUrl(registerLink.Attributes["href"], portalSettings, true, false, 600, 950); - // registerLink.Attributes.Add("onclick", clickEvent); - registerScript = GetRegisterScript(registerLink.Attributes["href"], nonce); + var userInfo = UserController.Instance.GetCurrentUserInfo(); + if (userInfo.UserID != -1) + { + var userDisplayText = userInfo.DisplayName; + var userDisplayTextUrl = Globals.UserProfileURL(userInfo.UserID); + var userDisplayTextToolTip = Localization.GetString("VisitMyProfile", userResourceFile); + var userLink = new TagBuilder("a"); + userLink.AddCssClass("dnnUserLink"); + if (!string.IsNullOrEmpty(cssClass)) + { + userLink.AddCssClass(cssClass); + } + userLink.Attributes.Add("href", userDisplayTextUrl); + userLink.Attributes.Add("title", userDisplayTextToolTip); + userLink.InnerHtml = userDisplayText; + return new MvcHtmlString(userLink.ToString()); + } + return MvcHtmlString.Empty; } - - return new MvcHtmlString(registerLink.ToString() + registerScript); } else { diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Startup.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Startup.cs index 3926e6547d2..81dbecf2bc4 100644 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/Startup.cs +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Startup.cs @@ -9,7 +9,7 @@ namespace DotNetNuke.Web.MvcPipeline using DotNetNuke.ContentSecurityPolicy; using DotNetNuke.DependencyInjection; using DotNetNuke.Web.Mvc.Extensions; - using DotNetNuke.Web.MvcPipeline.Framework; + using DotNetNuke.Web.MvcPipeline.ModelFactories; using DotNetNuke.Web.MvcPipeline.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcCssPropertyAccess.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcCssPropertyAccess.cs new file mode 100644 index 00000000000..540d2701b74 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcCssPropertyAccess.cs @@ -0,0 +1,53 @@ +// 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 + +// ReSharper disable once CheckNamespace +namespace DotNetNuke.Web.MvcPipeline.Tokens +{ + using System; + using System.Web.Mvc; + + using DotNetNuke.Entities.Users; + using DotNetNuke.Services.Tokens; + using DotNetNuke.Web.Client; + using DotNetNuke.Web.Client.ClientResourceManagement; + + /// Property Access implementation for CSS registration in MVC context. + public class MvcCssPropertyAccess : JsonPropertyAccess + { + private readonly ControllerContext controllerContext; + + /// Initializes a new instance of the class. + /// The controller context. + public MvcCssPropertyAccess(ControllerContext controllerContext) + { + this.controllerContext = controllerContext; + } + + /// + protected override string ProcessToken(StylesheetDto model, UserInfo accessingUser, Scope accessLevel) + { + if (string.IsNullOrEmpty(model.Path)) + { + throw new ArgumentException("The Css token must specify a path or property."); + } + + if (model.Priority == 0) + { + model.Priority = (int)FileOrder.Css.DefaultPriority; + } + + if (string.IsNullOrEmpty(model.Provider)) + { + MvcClientResourceManager.RegisterStyleSheet(this.controllerContext, model.Path, model.Priority); + } + else + { + MvcClientResourceManager.RegisterStyleSheet(this.controllerContext, model.Path, model.Priority, model.Provider); + } + + return string.Empty; + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcHtml5ModuleTokenReplace.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcHtml5ModuleTokenReplace.cs new file mode 100644 index 00000000000..356f9475ba0 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcHtml5ModuleTokenReplace.cs @@ -0,0 +1,74 @@ +// 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.MvcPipeline.Tokens +{ + using System.Web.Mvc; + + using DotNetNuke.Abstractions.Modules; + using DotNetNuke.Common; + using DotNetNuke.Entities.Modules; + using DotNetNuke.Entities.Modules.Actions; + using DotNetNuke.Services.Personalization; + using DotNetNuke.Services.Tokens; + using DotNetNuke.UI.Modules; + using DotNetNuke.UI.Modules.Html5; + + using Microsoft.Extensions.DependencyInjection; + + /// A for MVC HTML5 modules. + public class MvcHtml5ModuleTokenReplace : MvcHtmlTokenReplace + { + /// Initializes a new instance of the class. + /// The controller context in which the module is rendering. + /// The path to the module's HTML file. + /// The module context. + /// The module actions collection. + public MvcHtml5ModuleTokenReplace( + ControllerContext controllerContext, + string html5File, + ModuleInstanceContext moduleContext, + ModuleActionCollection moduleActions) + : this(controllerContext, null, html5File, moduleContext, moduleActions) + { + } + + /// Initializes a new instance of the class. + /// The controller context in which the module is rendering. + /// The business controller provider. + /// The path to the module's HTML file. + /// The module context. + /// The module actions collection. + public MvcHtml5ModuleTokenReplace( + ControllerContext controllerContext, + IBusinessControllerProvider businessControllerProvider, + string html5File, + ModuleInstanceContext moduleContext, + ModuleActionCollection moduleActions) + : base(controllerContext) + { + this.AccessingUser = moduleContext.PortalSettings.UserInfo; + this.DebugMessages = Personalization.GetUserMode() != Entities.Portals.PortalSettings.Mode.View; + this.ModuleId = moduleContext.ModuleId; + this.PortalSettings = moduleContext.PortalSettings; + + this.AddPropertySource("moduleaction", new ModuleActionsPropertyAccess(moduleContext, moduleActions)); + this.AddPropertySource("resx", new ModuleLocalizationPropertyAccess(moduleContext, html5File)); + this.AddPropertySource("modulecontext", new ModuleContextPropertyAccess(moduleContext)); + this.AddPropertySource("request", new MvcRequestPropertyAccess(controllerContext)); + + // DNN-7750 + businessControllerProvider ??= Globals.DependencyProvider.GetRequiredService(); + var customTokenProvider = businessControllerProvider.GetInstance(moduleContext); + if (customTokenProvider != null) + { + var tokens = customTokenProvider.GetTokens(controllerContext?.HttpContext?.Handler as System.Web.UI.Page, moduleContext); + foreach (var token in tokens) + { + this.AddPropertySource(token.Key, token.Value); + } + } + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcHtmlTokenReplace.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcHtmlTokenReplace.cs new file mode 100644 index 00000000000..719f5dd7b57 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcHtmlTokenReplace.cs @@ -0,0 +1,26 @@ +// 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.MvcPipeline.Tokens +{ + using System.Web.Mvc; + + using DotNetNuke.Services.Tokens; + using DotNetNuke.UI.Modules.Html5; + + /// A for MVC HTML modules. + public class MvcHtmlTokenReplace : TokenReplace + { + /// Initializes a new instance of the class. + /// The controller context in which the module is rendering. + public MvcHtmlTokenReplace(ControllerContext controllerContext) + : base(Scope.DefaultSettings) + { + this.AddPropertySource("css", new MvcCssPropertyAccess(controllerContext)); + this.AddPropertySource("js", new MvcJavaScriptPropertyAccess(controllerContext)); + this.AddPropertySource("javascript", new MvcJavaScriptPropertyAccess(controllerContext)); + this.AddPropertySource("antiforgerytoken", new AntiForgeryTokenPropertyAccess()); + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcJavaScriptPropertyAccess.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcJavaScriptPropertyAccess.cs new file mode 100644 index 00000000000..00b5dfc75a2 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcJavaScriptPropertyAccess.cs @@ -0,0 +1,92 @@ +// 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 + +// ReSharper disable once CheckNamespace +namespace DotNetNuke.Web.MvcPipeline.Tokens +{ + using System; + using System.Web.Mvc; + + using DotNetNuke.Entities.Users; + using DotNetNuke.Framework.JavaScriptLibraries; + using DotNetNuke.Services.Tokens; + using DotNetNuke.Web.Client; + using DotNetNuke.Web.Client.ClientResourceManagement; + + /// Property Access implementation for JavaScript registration in MVC context. + public class MvcJavaScriptPropertyAccess : JsonPropertyAccess + { + private readonly ControllerContext controllerContext; + + /// Initializes a new instance of the class. + /// The controller context. + public MvcJavaScriptPropertyAccess(ControllerContext controllerContext) + { + this.controllerContext = controllerContext; + } + + /// + protected override string ProcessToken(JavaScriptDto model, UserInfo accessingUser, Scope accessLevel) + { + if (string.IsNullOrEmpty(model.JsName) && string.IsNullOrEmpty(model.Path)) + { + throw new ArgumentException("If the jsname property is not specified then the JavaScript token must specify a path."); + } + + if (model.Priority == 0) + { + model.Priority = (int)FileOrder.Js.DefaultPriority; + } + + if (string.IsNullOrEmpty(model.Path)) + { + RegisterInstalledLibrary(model); + } + else + { + MvcClientResourceManager.RegisterScript( + this.controllerContext, + model.Path, + model.Priority, + model.Provider ?? string.Empty, + model.JsName ?? string.Empty, + model.Version ?? string.Empty, + model.HtmlAttributes); + } + + return string.Empty; + } + + private static void RegisterInstalledLibrary(JavaScriptDto model) + { + Version version = null; + var specific = SpecificVersion.Latest; + if (!string.IsNullOrEmpty(model.Version)) + { + version = new Version(model.Version); + + if (!string.IsNullOrEmpty(model.Specific)) + { + switch (model.Specific) + { + case "Exact": + specific = SpecificVersion.Exact; + break; + case "LatestMajor": + specific = SpecificVersion.LatestMajor; + break; + case "LatestMinor": + specific = SpecificVersion.LatestMinor; + break; + default: + specific = SpecificVersion.Latest; + break; + } + } + } + + JavaScript.RequestRegistration(model.JsName, version, specific); + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcRequestPropertyAccess.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcRequestPropertyAccess.cs new file mode 100644 index 00000000000..e1caa8506a6 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Tokens/MvcRequestPropertyAccess.cs @@ -0,0 +1,53 @@ +// 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.MvcPipeline.Tokens +{ + using System.Globalization; + using System.Web.Mvc; + + using DotNetNuke.Entities.Users; + using DotNetNuke.Services.Tokens; + + /// Replaces tokens related to the current HTTP request in MVC context. + public class MvcRequestPropertyAccess : IPropertyAccess + { + private readonly ControllerContext controllerContext; + + /// Initializes a new instance of the class. + /// The controller context. + public MvcRequestPropertyAccess(ControllerContext controllerContext) + { + this.controllerContext = controllerContext; + } + + /// + public virtual CacheLevel Cacheability + { + get { return CacheLevel.notCacheable; } + } + + /// + public string GetProperty(string propertyName, string format, CultureInfo formatProvider, UserInfo accessingUser, Scope accessLevel, ref bool propertyNotFound) + { + var request = this.controllerContext.HttpContext.Request; + + switch (propertyName.ToLowerInvariant()) + { + case "querystring": + return request.QueryString.ToString(); + case "applicationpath": + return request.ApplicationPath; + case "relativeapppath": + // RelativeAppPath is like ApplicationPath, but will always end with a forward slash (/) + return request.ApplicationPath.EndsWith("/") + ? request.ApplicationPath + : $"{request.ApplicationPath}/"; + } + + propertyNotFound = true; + return string.Empty; + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/UI/Utilities/MvcUtils.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/UI/Utilities/MvcUtils.cs deleted file mode 100644 index 0011d6ed503..00000000000 --- a/DNN Platform/DotNetNuke.Web.MvcPipeline/UI/Utilities/MvcUtils.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace DotNetNuke.Web.MvcPipeline.UI.Utilities -{ - using System.IO; - - using DotNetNuke.Entities.Modules; - - public class MvcUtils - { - public static string GetControlViewName(ModuleInfo module) - { - return GetControlViewName(module, Path.GetFileNameWithoutExtension(module.ModuleControl.ControlSrc)); - } - - public static string GetControlViewName(ModuleInfo module, string viewName) - { - return "~/" + Path.GetDirectoryName(module.ModuleControl.ControlSrc) + "/Views/" + viewName + ".cshtml"; - } - - public static string GetControlControllerName(ModuleInfo module) - { - return GetControlControllerName(module.ModuleControl.ControlSrc); - } - - public static string GetControlControllerName(string controlSrc) - { - if (controlSrc.StartsWith("DesktopModules")) - { - // return controlSrc.Replace("DesktopModules/", string.Empty).Replace("/", string.Empty).Replace(".ascx", string.Empty) + "View"; - return Path.GetFileNameWithoutExtension(controlSrc) + "View"; - } - else - { - return Path.GetFileNameWithoutExtension(controlSrc) + "View"; - } - } - } -} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Utils/MvcUtils.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Utils/MvcUtils.cs new file mode 100644 index 00000000000..9319492ded6 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Utils/MvcUtils.cs @@ -0,0 +1,115 @@ +namespace DotNetNuke.Web.MvcPipeline.Utils +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Runtime.InteropServices; + using System.Security.AccessControl; + using System.Web.UI; + using DotNetNuke.Common; + using DotNetNuke.Entities.Modules; + using DotNetNuke.Entities.Modules.Actions; + using DotNetNuke.Framework; + using DotNetNuke.Web.MvcPipeline.ModuleControl; + + public class MvcUtils + { + public static string GetControlViewName(ModuleInfo module) + { + return GetControlViewName(module, Path.GetFileNameWithoutExtension(module.ModuleControl.ControlSrc)); + } + + public static string GetControlViewName(ModuleInfo module, string viewName) + { + return "~/" + Path.GetDirectoryName(module.ModuleControl.ControlSrc) + "/Views/" + viewName + ".cshtml"; + } + + public static string GetControlControllerName(ModuleInfo module) + { + return GetControlControllerName(module.ModuleControl.ControlSrc); + } + + public static string GetControlControllerName(string controlSrc) + { + if (controlSrc.StartsWith("DesktopModules")) + { + // return controlSrc.Replace("DesktopModules/", string.Empty).Replace("/", string.Empty).Replace(".ascx", string.Empty) + "View"; + return Path.GetFileNameWithoutExtension(controlSrc) + "View"; + } + else + { + return Path.GetFileNameWithoutExtension(controlSrc) + "View"; + } + } + + static private IDictionary _moduleClasses = new Dictionary() { + { "ModuleActions", "DotNetNuke.Web.MvcWebsite.Controls.ModuleActionsControl, DotNetNuke.Web.MvcWebsite" }, + // { "Admin/Portal/Terms.ascx", "DotNetNuke.Web.MvcWebsite.Controls.TermsControl, DotNetNuke.Web.MvcWebsite" }, + // { "Admin/Portal/Privacy.ascx", "DotNetNuke.Web.MvcWebsite.Controls.PrivacyControl, DotNetNuke.Web.MvcWebsite" } + }; + + public static IMvcModuleControl CreateModuleControl(ModuleInfo module) + { + return GetModuleControl(module, module.ModuleControl.ControlSrc); + } + + public static IMvcModuleControl GetModuleControl(ModuleInfo module, string controlSrc) + { + IMvcModuleControl control; + if (_moduleClasses.ContainsKey(controlSrc)) + { + var controlClass = _moduleClasses[controlSrc]; + try + { + var controller = Reflection.CreateObject(Globals.DependencyProvider, controlClass, controlClass); + control = controller as IMvcModuleControl; + } + catch (Exception ex) + { + throw new Exception("Could not create instance of " + controlClass, ex); + } + } + else + { + //if (module.DesktopModule == null) + //{ + // throw new Exception("No DesktopModule is not defined for the module " + module.ModuleTitle); + //} + if (!string.IsNullOrEmpty(module.ModuleControl.MvcControlClass)) + { + var controlClass = module.ModuleControl.MvcControlClass; + try + { + var obj = Reflection.CreateObject(Globals.DependencyProvider, controlClass, controlClass); + if (obj is IMvcModuleControl) + { + control = obj as IMvcModuleControl; + } + else + { + throw new Exception("Mvc Control needs to implement IMvcModuleControl : " + controlClass); + } + } + catch (Exception ex) + { + throw new Exception("Could not create instance of " + controlClass, ex); + } + } + //else if (controlSrc.EndsWith(".mvc", System.StringComparison.OrdinalIgnoreCase)) + //{ + // control = new MvcModuleControl(); + //} + else if (controlSrc.EndsWith(".html", System.StringComparison.OrdinalIgnoreCase)) + { + control = new SpaModuleControl(); + } + else + { + throw new Exception("The module control dous not support the MVC pipeline : " + module.ModuleTitle + " " + module.ModuleControl.ControlTitle); + } + } + control.ModuleContext.Configuration = module; + return control; + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcPipeline/Utils/MvcViewEngine.cs b/DNN Platform/DotNetNuke.Web.MvcPipeline/Utils/MvcViewEngine.cs new file mode 100644 index 00000000000..0909ea4ba7f --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcPipeline/Utils/MvcViewEngine.cs @@ -0,0 +1,464 @@ +using System; +using System.Web; +using System.Web.Mvc; +using System.IO; +using System.Web.Routing; + + +namespace DotNetNuke.Web.MvcPipeline.Utils +{ + /// + /// Class that renders MVC views to a string using the + /// standard MVC View Engine to render the view. + /// + /// Requires that ASP.NET HttpContext is present to + /// work, but works outside of the context of MVC + /// credits to https://weblog.west-wind.com/posts/2012/may/30/rendering-aspnet-mvc-views-to-string + /// + public class MvcViewEngine + { + /// + /// Required Controller Context + /// + protected ControllerContext Context { get; set; } + + /// + /// Initializes the ViewRenderer with a Context. + /// + /// + /// If you are running within the context of an ASP.NET MVC request pass in + /// the controller's context. + /// Only leave out the context if no context is otherwise available. + /// + public MvcViewEngine(ControllerContext controllerContext = null) + { + // Create a known controller from HttpContext if no context is passed + if (controllerContext == null) + { + if (HttpContext.Current != null) + controllerContext = CreateController().ControllerContext; + else + throw new InvalidOperationException( + "ViewRenderer must run in the context of an ASP.NET " + + "Application and requires HttpContext.Current to be present."); + } + Context = controllerContext; + } + + /// + /// Renders a full MVC view to a string. Will render with the full MVC + /// View engine including running _ViewStart and merging into _Layout + /// + /// + /// The path to the view to render. Either in same controller, shared by + /// name or as fully qualified ~/ path including extension + /// + /// The model to render the view with + /// String of the rendered view or null on error + public string RenderViewToString(string viewPath, object model = null) + { + return RenderViewToStringInternal(viewPath, model, false); + } + + /// + /// Renders a full MVC view to a writer. Will render with the full MVC + /// View engine including running _ViewStart and merging into _Layout + /// + /// + /// The path to the view to render. Either in same controller, shared by + /// name or as fully qualified ~/ path including extension + /// + /// The model to render the view with + /// String of the rendered view or null on error + public void RenderView(string viewPath, object model, TextWriter writer) + { + RenderViewToWriterInternal(viewPath, writer, model, false); + } + + + /// + /// Renders a partial MVC view to string. Use this method to render + /// a partial view that doesn't merge with _Layout and doesn't fire + /// _ViewStart. + /// + /// + /// The path to the view to render. Either in same controller, shared by + /// name or as fully qualified ~/ path including extension + /// + /// The model to pass to the viewRenderer + /// String of the rendered view or null on error + public string RenderPartialViewToString(string viewPath, object model = null) + { + return RenderViewToStringInternal(viewPath, model, true); + } + + /// + /// Renders a partial MVC view to given Writer. Use this method to render + /// a partial view that doesn't merge with _Layout and doesn't fire + /// _ViewStart. + /// + /// + /// The path to the view to render. Either in same controller, shared by + /// name or as fully qualified ~/ path including extension + /// + /// The model to pass to the viewRenderer + /// Writer to render the view to + public void RenderPartialView(string viewPath, object model, TextWriter writer) + { + RenderViewToWriterInternal(viewPath, writer, model, true); + } + + /// + /// Renders a full MVC view to a writer. Will render with the full MVC + /// View engine including running _ViewStart and merging into _Layout + /// + /// + /// The path to the view to render. Either in same controller, shared by + /// name or as fully qualified ~/ path including extension + /// + /// The model to pass to the viewRenderer + /// Active Controller context + /// String of the rendered view or null on error + public static string RenderView(string viewPath, object model = null, + ControllerContext controllerContext = null) + { + MvcViewEngine renderer = new MvcViewEngine(controllerContext); + return renderer.RenderViewToString(viewPath, model); + } + + /// + /// Renders a full MVC view to a writer. Will render with the full MVC + /// View engine including running _ViewStart and merging into _Layout + /// + /// + /// The path to the view to render. Either in same controller, shared by + /// name or as fully qualified ~/ path including extension + /// + /// The model to pass to the viewRenderer + /// Writer to render the view to + /// Active Controller context + /// String of the rendered view or null on error + public static void RenderView(string viewPath, TextWriter writer, object model, + ControllerContext controllerContext) + { + MvcViewEngine renderer = new MvcViewEngine(controllerContext); + renderer.RenderView(viewPath, model, writer); + } + + /// + /// Renders a full MVC view to a writer. Will render with the full MVC + /// View engine including running _ViewStart and merging into _Layout + /// + /// + /// The path to the view to render. Either in same controller, shared by + /// name or as fully qualified ~/ path including extension + /// + /// The model to pass to the viewRenderer + /// Active Controller context + /// optional out parameter that captures an error message instead of throwing + /// String of the rendered view or null on error + public static string RenderView(string viewPath, object model, + ControllerContext controllerContext, + out string errorMessage) + { + errorMessage = null; + try + { + MvcViewEngine renderer = new MvcViewEngine(controllerContext); + return renderer.RenderViewToString(viewPath, model); + } + catch (Exception ex) + { + errorMessage = ex.GetBaseException().Message; + } + return null; + } + + /// + /// Renders a full MVC view to a writer. Will render with the full MVC + /// View engine including running _ViewStart and merging into _Layout + /// + /// + /// The path to the view to render. Either in same controller, shared by + /// name or as fully qualified ~/ path including extension + /// + /// The model to pass to the viewRenderer + /// Active Controller context + /// Writer to render the view to + /// optional out parameter that captures an error message instead of throwing + /// String of the rendered view or null on error + public static void RenderView(string viewPath, object model, TextWriter writer, + ControllerContext controllerContext, + out string errorMessage) + { + errorMessage = null; + try + { + MvcViewEngine renderer = new MvcViewEngine(controllerContext); + renderer.RenderView(viewPath, model, writer); + } + catch (Exception ex) + { + errorMessage = ex.GetBaseException().Message; + } + } + + + /// + /// Renders a partial MVC view to string. Use this method to render + /// a partial view that doesn't merge with _Layout and doesn't fire + /// _ViewStart. + /// + /// + /// The path to the view to render. Either in same controller, shared by + /// name or as fully qualified ~/ path including extension + /// + /// The model to pass to the viewRenderer + /// Active controller context + /// String of the rendered view or null on error + public static string RenderPartialView(string viewPath, object model = null, + ControllerContext controllerContext = null) + { + MvcViewEngine renderer = new MvcViewEngine(controllerContext); + return renderer.RenderPartialViewToString(viewPath, model); + } + + /// + /// Renders a partial MVC view to string. Use this method to render + /// a partial view that doesn't merge with _Layout and doesn't fire + /// _ViewStart. + /// + /// + /// The path to the view to render. Either in same controller, shared by + /// name or as fully qualified ~/ path including extension + /// + /// The model to pass to the viewRenderer + /// Active controller context + /// Text writer to render view to + /// optional output parameter to receive an error message on failure + public static void RenderPartialView(string viewPath, TextWriter writer, object model = null, + ControllerContext controllerContext = null) + { + MvcViewEngine renderer = new MvcViewEngine(controllerContext); + renderer.RenderPartialView(viewPath, model, writer); + } + + /// + /// Renders an HtmlHelper delegate to a string. This method creates a temporary view context + /// and captures the output of the HtmlHelper function. + /// + /// A function that takes an HtmlHelper and returns MvcHtmlString or IHtmlString + /// The model to attach to the view data + /// Active controller context (optional) + /// String representation of the rendered HTML helper output + public static string RenderHtmlHelperToString(Func htmlHelperFunc, object model = null, ControllerContext controllerContext = null) + { + if (htmlHelperFunc == null) + throw new ArgumentNullException(nameof(htmlHelperFunc)); + + MvcViewEngine renderer = new MvcViewEngine(controllerContext); + return renderer.RenderHtmlHelperToStringInternal(htmlHelperFunc, model); + } + + /// + /// Renders an HtmlHelper delegate to a string with error handling. This method creates a temporary view context + /// and captures the output of the HtmlHelper function. + /// + /// A function that takes an HtmlHelper and returns MvcHtmlString or IHtmlString + /// The model to attach to the view data + /// Active controller context (optional) + /// Output parameter that captures any error message instead of throwing + /// String representation of the rendered HTML helper output or null on error + public static string RenderHtmlHelperToString(Func htmlHelperFunc, object model, ControllerContext controllerContext, out string errorMessage) + { + errorMessage = null; + try + { + return RenderHtmlHelperToString(htmlHelperFunc, model, controllerContext); + } + catch (Exception ex) + { + errorMessage = ex.GetBaseException().Message; + return null; + } + } + + + /// + /// Internal method that handles rendering of either partial or + /// or full views. + /// + /// + /// The path to the view to render. Either in same controller, shared by + /// name or as fully qualified ~/ path including extension + /// + /// Model to render the view with + /// Determines whether to render a full or partial view + /// Text writer to render view to + protected void RenderViewToWriterInternal(string viewPath, TextWriter writer, object model = null, bool partial = false) + { + // first find the ViewEngine for this view + ViewEngineResult viewEngineResult = null; + if (partial) + viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath); + else + viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null); + + if (viewEngineResult == null) + throw new FileNotFoundException(); + + // get the view and attach the model to view data + var view = viewEngineResult.View; + Context.Controller.ViewData.Model = model; + + var ctx = new ViewContext(Context, view, + Context.Controller.ViewData, + Context.Controller.TempData, + writer); + view.Render(ctx, writer); + } + + /// + /// Internal method that handles rendering of either partial or + /// or full views. + /// + /// + /// The path to the view to render. Either in same controller, shared by + /// name or as fully qualified ~/ path including extension + /// + /// Model to render the view with + /// Determines whether to render a full or partial view + /// String of the rendered view + private string RenderViewToStringInternal(string viewPath, object model, + bool partial = false) + { + // first find the ViewEngine for this view + ViewEngineResult viewEngineResult = null; + if (partial) + viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath); + else + viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null); + + if (viewEngineResult == null || viewEngineResult.View == null) + throw new FileNotFoundException("ViewCouldNotBeFound"); + + // get the view and attach the model to view data + var view = viewEngineResult.View; + Context.Controller.ViewData.Model = model; + + string result = null; + + using (var sw = new StringWriter()) + { + var ctx = new ViewContext(Context, view, + Context.Controller.ViewData, + Context.Controller.TempData, + sw); + view.Render(ctx, sw); + result = sw.ToString(); + } + + return result; + } + + /// + /// Internal method that handles rendering of HtmlHelper delegate to a string. + /// + /// A function that takes an HtmlHelper and returns MvcHtmlString or IHtmlString + /// Model to attach to the view data + /// String representation of the rendered HTML helper output + private string RenderHtmlHelperToStringInternal(Func htmlHelperFunc, object model = null) + { + // Set the model to view data + Context.Controller.ViewData.Model = model; + + string result = null; + + using (var sw = new StringWriter()) + { + // Create a view data dictionary for the HtmlHelper + var viewDataContainer = new ViewDataContainer(Context.Controller.ViewData); + + // Create the HtmlHelper with the proper context + var htmlHelper = new HtmlHelper( + new ViewContext(Context, new FakeView(), Context.Controller.ViewData, Context.Controller.TempData, sw), + viewDataContainer); + + // Execute the HtmlHelper function and capture the result + var htmlResult = htmlHelperFunc(htmlHelper); + result = htmlResult?.ToString() ?? string.Empty; + } + + return result; + } + + + /// + /// Creates an instance of an MVC controller from scratch + /// when no existing ControllerContext is present + /// + /// Type of the controller to create + /// Controller for T + /// thrown if HttpContext not available + public static T CreateController(RouteData routeData = null, params object[] parameters) + where T : Controller, new() + { + // create a disconnected controller instance + T controller = (T) Activator.CreateInstance(typeof (T), parameters); + + // get context wrapper from HttpContext if available + HttpContextBase wrapper = null; + if (HttpContext.Current != null) + wrapper = new HttpContextWrapper(System.Web.HttpContext.Current); + else + throw new InvalidOperationException( + "Can't create Controller Context if no active HttpContext instance is available."); + + if (routeData == null) + routeData = new RouteData(); + + // add the controller routing if not existing + if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller")) + routeData.Values.Add("controller", controller.GetType().Name + .ToLower() + .Replace("controller", "")); + + controller.ControllerContext = new ControllerContext(wrapper, routeData, controller); + return controller; + } + + } + + /// + /// Empty MVC Controller instance used to + /// instantiate and provide a new ControllerContext + /// for the ViewRenderer + /// + public class EmptyController : Controller + { + } + + /// + /// Simple ViewDataContainer implementation for HtmlHelper usage + /// + internal class ViewDataContainer : IViewDataContainer + { + public ViewDataDictionary ViewData { get; set; } + + public ViewDataContainer(ViewDataDictionary viewData) + { + ViewData = viewData; + } + } + + /// + /// Fake view implementation for HtmlHelper contexts that don't require actual view rendering + /// + internal class FakeView : IView + { + public void Render(ViewContext viewContext, TextWriter writer) + { + // No-op implementation since we're only using this for HtmlHelper context + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/DotNetNuke.Web.MvcUrlRewriter.csproj b/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/DotNetNuke.Web.MvcUrlRewriter.csproj deleted file mode 100644 index 210683dca86..00000000000 --- a/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/DotNetNuke.Web.MvcUrlRewriter.csproj +++ /dev/null @@ -1,42 +0,0 @@ - - - DotNetNuke.Web.MvcUrlRewriter - net48 - true - latest - bin - false - false - Sacha Trauwaen - Dnn - MvcUrlRewriter - 2025 - MvcUrlRewriter - 0.0.1.0 - 0.0.1.0 - DNN MVC-UrlRewriter project - en-US - - Library - - Portable - - - - - - - - - - - - - - - - - - - - diff --git a/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/Entities/Urls/MvcAdvancedUrlRewriter.cs b/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/Entities/Urls/MvcAdvancedUrlRewriter.cs deleted file mode 100644 index dd1e232dbf7..00000000000 --- a/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/Entities/Urls/MvcAdvancedUrlRewriter.cs +++ /dev/null @@ -1,3131 +0,0 @@ -namespace DotNetNuke.Web.MvcUrlRewriter.Entities.Urls -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Collections.Specialized; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Security.Principal; - using System.Text.RegularExpressions; - using System.Threading; - using System.Web; - using System.Web.Configuration; - using System.Web.Security; - - using DotNetNuke.Application; - using DotNetNuke.Common; - using DotNetNuke.Common.Internal; - using DotNetNuke.Common.Utilities; - using DotNetNuke.Entities.Controllers; - using DotNetNuke.Entities.Host; - using DotNetNuke.Entities.Portals; - using DotNetNuke.Entities.Tabs; - using DotNetNuke.Entities.Urls; - using DotNetNuke.Framework; - using DotNetNuke.Services.EventQueue; - - public class MvcAdvancedUrlRewriter : UrlRewriterBase - { - private const string ProductName = "AdvancedUrlRewriter"; - private static readonly Regex DefaultPageRegex = new Regex(@"(?.[^&]+)=$)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled); - private static readonly Regex UrlSlashesRegex = new Regex("[\\\\/]\\.\\.[\\\\/]", RegexOptions.Compiled); - private static readonly Regex AliasUrlRegex = new Regex(@"(?:^(?http[s]{0,1}://){0,1})(?:(?_ALIAS_)(?$|\?[\w]*|/[\w]*))", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled); - private FriendlyUrlSettings settings; - - public void ProcessTestRequestWithContext( - HttpContext context, - Uri requestUri, - bool useFriendlyUrls, - UrlAction result, - FriendlyUrlSettings settings) - { - Guid parentTraceId = Guid.Empty; - this.settings = settings; - this.ProcessRequest( - context, - requestUri, - useFriendlyUrls, - result, - settings, - false, - parentTraceId); - } - - internal static void RewriteAsChildAliasRoot( - HttpContext context, - UrlAction result, - string aliasQueryString, - FriendlyUrlSettings settings) - { - string culture = null; - - // look for specific alias to rewrite language parameter - var primaryAliases = PortalAliasController.Instance.GetPortalAliasesByPortalId(result.PortalId).ToList(); - if (result.PortalId > -1 && result.HttpAlias != null) - { - culture = primaryAliases.GetCultureByPortalIdAndAlias(result.PortalId, result.HttpAlias); - } - - if (string.IsNullOrEmpty(culture)) - { - // 732 : when no culture returned can be "" as well as null : no culture causes no rewrite, which results in redirect to parent alias - // set the default culture code here - // 735 : switch to custom method for getting portal - PortalInfo pi = CacheController.GetPortal(result.PortalId, false); - if (pi != null) - { - culture = pi.DefaultLanguage; - } - } - - if (!string.IsNullOrEmpty(culture)) - { - // a culture was identified for the alias root - if (RewriteController.AddLanguageCodeToRewritePath(ref aliasQueryString, culture)) - { - result.CultureCode = culture; - } - - result.DoRewrite = true; - result.RewritePath = "~/" + Globals.glbDefaultPage + aliasQueryString; - - // the expected /default.aspx path (defaultPageUrl) matches the requested Url (/default.aspx) - if (context != null) - { - // only do if not testing - RewriterUtils.RewriteUrl(context, result.RewritePath); - } - } - } - - internal static bool CheckForChildPortalRootUrl(string requestUrl, UrlAction result, out string aliasQueryString) - { - bool isChildPortalRootUrl = false; - - // what we are going to test for here is that if this is a child portal request, for the /default.aspx of the child portal - // then we are going to avoid the core 302 redirect to ?alias=portalALias by rewriting to the /default.aspx of the site root - // 684 : don't convert querystring items to lower case - // do the check by constructing what a child alias url would look like and compare it with the requested urls - // 912 : when requested without a valid portal alias, portalALias is null. Refuse and return false. - aliasQueryString = null; - if (result.PortalAlias != null && result.PortalAlias.HTTPAlias != null) - { - string defaultPageUrl = result.Scheme + result.PortalAlias.HTTPAlias + "/" + - Globals.glbDefaultPage.ToLowerInvariant(); // child alias Url with /default.aspx - - // 660 : look for a querystring on the site root for a child portal, and handle it if so - if (string.CompareOrdinal(requestUrl.ToLowerInvariant(), defaultPageUrl) == 0) - { - // exact match : that's the alias root - isChildPortalRootUrl = true; - aliasQueryString = string.Empty; - } - - if (!isChildPortalRootUrl && requestUrl.Contains("?")) - { - // is we didn't get an exact match but there is a querystring, then investigate - string[] requestUrlParts = requestUrl.Split('?'); - if (requestUrlParts.GetUpperBound(0) > 0) - { - string rootPart = requestUrlParts[0]; - string queryString = requestUrlParts[1]; - if (string.Compare(rootPart, defaultPageUrl, StringComparison.OrdinalIgnoreCase) == 0) - { - // rewrite, but put in the querystring on the rewrite path - isChildPortalRootUrl = true; - aliasQueryString = "?" + queryString; - - // 674: check for 301 if this value is a tabid/xx - otherwise the url will just evaluate as is - if (queryString.ToLowerInvariant().StartsWith("tabid=")) - { - result.Action = ActionType.CheckFor301; - } - } - } - } - } - - return isChildPortalRootUrl; - } - - /// Make sure any redirect to the site root doesn't append the nasty /default.aspx on the end. - /// - /// - /// without at the end. - internal static string CheckForSiteRootRedirect(string alias, string destUrl) - { - // 540 - don't append /default.aspx onto the end of a site root redirect. - if (destUrl.EndsWith(alias + "/" + Globals.glbDefaultPage, StringComparison.InvariantCultureIgnoreCase)) - { - // this is just the portal alias root + /defualt.aspx. - // we don't want that, just the portalAliasRoot + "/" - string aliasPlusSlash = alias + "/"; - - // get everything up to the end of the portal alias - destUrl = destUrl.Substring(0, destUrl.IndexOf(aliasPlusSlash, StringComparison.Ordinal) + aliasPlusSlash.Length); - } - - return destUrl; - } - - /// - internal override void RewriteUrl(object sender, EventArgs e) - { - Guid parentTraceId = Guid.Empty; - const bool debug = true; - bool failedInitialization = false; - bool ignoreForInstall = false; - var app = (HttpApplication)sender; - try - { - // 875 : completely ignore install/upgrade requests immediately - ignoreForInstall = IgnoreRequestForInstall(app.Request); - - if (ignoreForInstall == false) - { - this.settings = new FriendlyUrlSettings(-1); - - this.SecurityCheck(app); - } - } - catch (Exception ex) - { - // exception handling for advanced Url Rewriting requests - failedInitialization = true; - DotNetNuke.Services.Exceptions.Exceptions.LogException(ex); - if (app.Context != null) - { - ShowDebugData(app.Context, app.Request.Url.AbsoluteUri, null, ex); - var action = new UrlAction(app.Request) { Action = ActionType.Output404 }; - Handle404OrException(this.settings, app.Context, ex, action, false, debug); - } - else - { - throw; - } - } - - if (!failedInitialization && !ignoreForInstall) - { - // if made it through there and not installing, go to next call. Not in exception catch because it implements it's own top-level exception handling - var request = app.Context.Request; - - // 829 : change constructor to stop using physical path - var result = new UrlAction(request) - { - IsSecureConnection = request.IsSecureConnection, - IsSSLOffloaded = UrlUtils.IsSslOffloadEnabled(request), - RawUrl = request.RawUrl, - }; - this.ProcessRequest( - app.Context, - app.Context.Request.Url, - Host.UseFriendlyUrls, - result, - this.settings, - true, - parentTraceId); - } - } - - protected bool IsPortalAliasIncorrect( - HttpContext context, - HttpRequest request, - Uri requestUri, - UrlAction result, - NameValueCollection queryStringCol, - FriendlyUrlSettings settings, - Guid parentTraceId, - out string httpAlias) - { - // now check to make sure it's the primary portal alias for this portal/language/browser - bool incorrectAlias = false; - httpAlias = null; - - // if (result.RedirectAllowed && result.PortalId > -1) - if (result.PortalId > -1) - { - // portal has been identified - var portalAliases = PortalAliasController.Instance.GetPortalAliasesByPortalId(result.PortalId).ToList(); - - // if we're not on the primary alias, and portalaliasmapping is set to redirect, we might need to be redirected - var redirectToPrimary = !result.PortalAlias.IsPrimary && result.PortalAliasMapping == PortalSettings.PortalAliasMapping.Redirect; - - // forceAlias used in querystring? - var forceAliasInQueryString = queryStringCol != null && queryStringCol["forceAlias"] != null && queryStringCol["forceAlias"] != "true"; - if (redirectToPrimary || forceAliasInQueryString) - { - if (portalAliases.Count > 0) - { - string checkAlias = result.HttpAlias; - bool continueLoop = true; - bool triedWWW = false; - while (httpAlias == null && continueLoop) - { - if (portalAliases.ContainsAlias(result.PortalId, checkAlias)) - { - if (portalAliases.Count > 0) - { - // var cpa = portalAliases.GetAliasByPortalIdAndSettings(result); - string url = requestUri.ToString(); - RewriteController.CheckLanguageMatch(ref url, result); - var cpa = portalAliases - .Where(a => a.IsPrimary || result.PortalAliasMapping != PortalSettings.PortalAliasMapping.Redirect) - .GetAliasByPortalIdAndSettings(result.PortalId, result, result.CultureCode, result.BrowserType); - - if (cpa != null) - { - httpAlias = cpa.HTTPAlias; - continueLoop = false; - } - - if (string.IsNullOrEmpty(result.CultureCode) && cpa == null) - { - // if there is a specific culture for this portal alias, then check that - string culture = portalAliases.GetCultureByPortalIdAndAlias(result.PortalId, result.HttpAlias); - - // if this matches the alias of the request, then we know we have the correct alias because it is a specific culture - if (!string.IsNullOrEmpty(culture)) - { - continueLoop = false; - } - } - } - } - - // check whether to still go on or not - if (continueLoop) - { - // this alias doesn't exist in the list - // check if it has a www on it - if not, try adding, if it does, try removing - if (!triedWWW) - { - triedWWW = true; // now tried adding/removing www - if (checkAlias.StartsWith("www.", StringComparison.InvariantCultureIgnoreCase)) - { - checkAlias = checkAlias.Substring(4); - } - else - { - checkAlias = "www." + checkAlias; - } - } - else - { - // last thing to try, get the default language and see if there is a portal alias for that - // thus, any aliases not identified as belonging to a language are redirected back to the - // alias named for the default language - continueLoop = false; - - // 735 : switch to custom method for getting portal - PortalInfo pi = CacheController.GetPortal(result.PortalId, false); - if (pi != null) - { - string cultureCode = pi.DefaultLanguage; - if (!string.IsNullOrEmpty(cultureCode)) - { - var primaryPortalAlias = portalAliases.GetAliasByPortalIdAndSettings(result.PortalId, result, cultureCode, settings); - if (primaryPortalAlias != null) - { - httpAlias = primaryPortalAlias.HTTPAlias; - } - } - } - } - } - } - } - - // check to see if it is a custom tab alais - in that case, it is allowed to be requested for the tab - if (CheckIfAliasIsCustomTabAlias(ref result, httpAlias, settings)) - { - // change the primary alias to the custom tab alias that has been requested. - result.PrimaryAlias = result.PortalAlias; - } - else - if (httpAlias != null && string.Compare(httpAlias, result.HttpAlias, StringComparison.OrdinalIgnoreCase) != 0) - { - incorrectAlias = true; - } - } - } - - return incorrectAlias; - } - - private static void ShowDebugData(HttpContext context, string requestUri, UrlAction result, Exception ex) - { - if (context != null) - { - HttpResponse response = context.Response; - - // handle null responses wherever they might be found - this routine must be tolerant to all kinds of invalid inputs - if (requestUri == null) - { - requestUri = "null Uri"; - } - - string finalUrl = "null final Url"; - string rewritePath = "null rewrite path"; - string action = "null action"; - if (result != null) - { - finalUrl = result.FinalUrl; - action = result.Action.ToString(); - rewritePath = result.RewritePath; - } - - // format up the error message to show - const string debugMsg = "{0}, {1}, {2}, {3}, {4}, {5}, {6}"; - string productVer = DotNetNukeContext.Current.Application.Version.ToString(); - string portalSettings = string.Empty; - string browser = "Unknown"; - - // 949 : don't rely on 'result' being non-null - if (result != null) - { - browser = result.BrowserType.ToString(); - } - - if (context.Items.Contains("PortalSettings")) - { - var ps = (PortalSettings)context.Items["PortalSettings"]; - if (ps != null) - { - portalSettings = ps.PortalId.ToString(); - if (ps.PortalAlias != null) - { - portalSettings += ":" + ps.PortalAlias.HTTPAlias; - } - } - } - - response.AppendHeader( - "X-" + ProductName + "-Debug", - string.Format( - debugMsg, - requestUri, - finalUrl, - rewritePath, - action, - productVer, - portalSettings, - browser)); - int msgNum = 1; - if (result != null) - { - foreach (string msg in result.DebugMessages) - { - response.AppendHeader("X-" + ProductName + "-Debug-" + msgNum.ToString("00"), msg); - msgNum++; - } - } - - if (ex != null) - { - response.AppendHeader("X-" + ProductName + "-Ex", ex.Message); - } - } - } - - private static void Handle404OrException(FriendlyUrlSettings settings, HttpContext context, Exception ex, UrlAction result, bool transfer, bool showDebug) - { - // handle Auto-Add Alias - if (result.Action == ActionType.Output404 && CanAutoAddPortalAlias()) - { - // Need to determine if this is a real 404 or a possible new alias. - var portalId = Host.HostPortalID; - if (portalId > Null.NullInteger) - { - if (string.IsNullOrEmpty(result.DomainName)) - { - result.DomainName = Globals.GetDomainName(context.Request); // parse the domain name out of the request - } - - // Get all the existing aliases - var aliases = PortalAliasController.Instance.GetPortalAliasesByPortalId(portalId).ToList(); - - bool autoaddAlias; - bool isPrimary = false; - if (!aliases.Any()) - { - autoaddAlias = true; - isPrimary = true; - } - else - { - autoaddAlias = true; - foreach (var alias in aliases) - { - if (result.DomainName.ToLowerInvariant().IndexOf(alias.HTTPAlias, StringComparison.Ordinal) == 0 - && result.DomainName.Length >= alias.HTTPAlias.Length) - { - autoaddAlias = false; - break; - } - } - } - - if (autoaddAlias) - { - var portalAliasInfo = new PortalAliasInfo - { - PortalID = portalId, - HTTPAlias = result.DomainName, - IsPrimary = isPrimary, - }; - PortalAliasController.Instance.AddPortalAlias(portalAliasInfo); - - context.Response.Redirect(context.Request.Url.ToString(), true); - } - } - } - - if (context != null) - { - HttpRequest request = context.Request; - HttpResponse response = context.Response; - HttpServerUtility server = context.Server; - - const string errorPageHtmlHeader = @"{0}"; - const string errorPageHtmlFooter = @""; - var errorPageHtml = new StringWriter(); - CustomErrorsSection ceSection = null; - - // 876 : security catch for custom error reading - try - { - ceSection = (CustomErrorsSection)WebConfigurationManager.GetSection("system.web/customErrors"); - } - - // ReSharper disable once EmptyGeneralCatchClause - catch (Exception) - { - // on some medium trust environments, this will throw an exception for trying to read the custom Errors - // do nothing - } - - /* 454 new 404/500 error handling routine */ - bool useDNNTab = false; - int errTabId = -1; - string errUrl = null; - string status = string.Empty; - bool isPostback = false; - if (settings != null) - { - if (request.RequestType == "POST") - { - isPostback = true; - } - - if (result != null && ex != null) - { - result.DebugMessages.Add("Exception: " + ex.Message); - result.DebugMessages.Add("Stack Trace: " + ex.StackTrace); - if (ex.InnerException != null) - { - result.DebugMessages.Add("Inner Ex : " + ex.InnerException.Message); - result.DebugMessages.Add("Stack Trace: " + ex.InnerException.StackTrace); - } - else - { - result.DebugMessages.Add("Inner Ex : null"); - } - } - - string errRH; - string errRV; - int statusCode; - if (result != null && result.Action != ActionType.Output404) - { - // output everything but 404 (usually 500) - if (settings.TabId500 > -1) - { - // tabid specified for 500 error page, use that - useDNNTab = true; - errTabId = settings.TabId500; - } - - errUrl = settings.Url500; - errRH = "X-UrlRewriter-500"; - errRV = "500 Rewritten to {0} : {1}"; - statusCode = 500; - status = "500 Internal Server Error"; - } - else - { - // output 404 error - // if the tabid is specified for a 404 page, then use that - if (settings.TabId404 > -1) - { - useDNNTab = true; - errTabId = settings.TabId404; - } - - // with 404 errors, there's an option to catch certain urls and use an external url for extra processing. - if (!string.IsNullOrEmpty(settings.Regex404)) - { - try - { - // 944 : check the original Url in case the requested Url has been rewritten before discovering it's a 404 error - string requestedUrl = request.Url.ToString(); - if (result != null && !string.IsNullOrEmpty(result.OriginalPath)) - { - requestedUrl = result.OriginalPath; - } - - if (Regex.IsMatch(requestedUrl, settings.Regex404, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)) - { - useDNNTab = false; - - // if we have a match in the 404 regex value, then don't use the tabid - } - } - catch (Exception regexEx) - { - // .some type of exception : output in response header, and go back to using the tabid - response.AppendHeader("X-UrlRewriter-404Exception", regexEx.Message); - } - } - - errUrl = settings.Url404; - errRH = "X-UrlRewriter-404"; - errRV = "404 Rewritten to {0} : {1} : Reason {2}"; - status = "404 Not Found"; - statusCode = 404; - } - - // check for 404 logging - if (result == null || result.Action == ActionType.Output404) - { - // Log 404 errors to Event Log - UrlRewriterUtils.Log404(request, settings, result); - } - - // 912 : use unhandled 404 switch - string reason404 = null; - bool unhandled404 = true; - if (useDNNTab && errTabId > -1) - { - unhandled404 = false; // we're handling it here - TabInfo errTab = TabController.Instance.GetTab(errTabId, result.PortalId, true); - if (errTab != null) - { - bool redirect = false; - - // ok, valid tabid. what we're going to do is to load up this tab via a rewrite of the url, and then change the output status - string reason = "Not Found"; - if (result != null) - { - reason = result.Reason.ToString(); - } - - response.AppendHeader( - errRH, - string.Format( - errRV, - "DNN Tab", - errTab.TabName + "(Tabid:" + errTabId.ToString() + ")", - reason)); - - // show debug messages even if in debug mode - if (context != null && response != null && result != null && showDebug) - { - ShowDebugData(context, result.OriginalPath, result, null); - } - - if (!isPostback) - { - response.ClearContent(); - response.StatusCode = statusCode; - response.Status = status; - } - else - { - redirect = true; - - // redirect postbacks as you can't postback successfully to a server.transfer - } - - errUrl = Globals.glbDefaultPage + TabIndexController.CreateRewritePath(errTab.TabID, string.Empty); - - // have to update the portal settings with the new tabid - PortalSettings ps = null; - if (context != null && context.Items != null) - { - if (context.Items.Contains("PortalSettings")) - { - ps = (PortalSettings)context.Items["PortalSettings"]; - context.Items.Remove("PortalSettings"); // nix it from the context - } - } - - if (ps != null && ps.PortalAlias != null) - { - ps = new PortalSettings(errTabId, ps.PortalAlias); - } - else - { - if (result.HttpAlias != null && result.PortalId > -1) - { - PortalAliasInfo pa = PortalAliasController.Instance.GetPortalAlias(result.HttpAlias, result.PortalId); - ps = new PortalSettings(errTabId, pa); - } - else - { - // 912 : handle 404 when no valid portal can be identified - // results when iis is configured to handle portal alias, but - // DNN isn't. This always returns 404 because a multi-portal site - // can't just show the 404 page of the host site. - ArrayList portals = PortalController.Instance.GetPortals(); - if (portals != null && portals.Count == 1) - { - // single portal install, load up portal settings for this portal - var singlePortal = (PortalInfo)portals[0]; - - // list of aliases from database - var aliases = PortalAliasController.Instance.GetPortalAliasesByPortalId(singlePortal.PortalID).ToList(); - - // list of aliases from Advanced Url settings - List chosen = aliases.GetAliasesForPortalId(singlePortal.PortalID); - PortalAliasInfo useFor404 = null; - - // go through all aliases and either get the first valid one, or the first - // as chosen in the advanced url management settings - foreach (var pa in aliases) - { - if (useFor404 == null) - { - useFor404 = pa; // first one by default - } - - // matching? - if (chosen != null && chosen.Count > 0) - { - if (chosen.Contains(pa.HTTPAlias)) - { - useFor404 = pa; - } - } - else - { - break; // no further checking - } - } - - // now configure that as the portal settings - if (useFor404 != null) - { - // create portal settings context for identified portal alias in single portal install - ps = new PortalSettings(errTabId, useFor404); - } - } - else - { - reason404 = "Requested domain name is not configured as valid website"; - unhandled404 = true; - } - } - } - - if (ps != null) - { - // re-add the context items portal settings back in - context.Items.Add("PortalSettings", ps); - } - - if (redirect) - { - errUrl = TestableGlobals.Instance.NavigateURL(); - response.Redirect(errUrl, true); // redirect and end response. - - // It will mean the user will have to postback again, but it will work the second time - } - else - { - if (transfer) - { - // execute a server transfer to the default.aspx?tabid=xx url - // 767 : object not set error on extensionless 404 errors - if (context.User == null) - { - context.User = GetCurrentPrincipal(context); - } - - response.TrySkipIisCustomErrors = true; - - // 881 : spoof the basePage object so that the client dependency framework - // is satisfied it's working with a page-based handler - IHttpHandler spoofPage = new CDefault(); - context.Handler = spoofPage; - server.Transfer("~/" + errUrl, true); - } - else - { - context.RewritePath("~/Default.aspx", false); - response.TrySkipIisCustomErrors = true; - response.Status = "404 Not Found"; - response.StatusCode = 404; - } - } - } - } - - // 912 : change to new if statement to handle cases where the TabId404 couldn't be handled correctly - if (unhandled404) - { - // proces the error on the external Url by rewriting to the external url - if (!string.IsNullOrEmpty(errUrl)) - { - response.ClearContent(); - response.TrySkipIisCustomErrors = true; - string reason = "Not Found"; - if (result != null) - { - reason = result.Reason.ToString(); - } - - response.AppendHeader(errRH, string.Format(errRV, "Url", errUrl, reason)); - if (reason404 != null) - { - response.AppendHeader("X-Url-Master-404-Data", reason404); - } - - response.StatusCode = statusCode; - response.Status = status; - server.Transfer("~/" + errUrl, true); - } - else - { - errorPageHtml.Write(status + "
The requested Url does not return any valid content."); - if (reason404 != null) - { - errorPageHtml.Write(status + "
" + reason404); - } - - errorPageHtml.Write("
Administrators
"); - errorPageHtml.Write("
Change this message by configuring a specific 404 Error Page or Url for this website.
"); - - // output a reason for the 404 - string reason = string.Empty; - if (result != null) - { - reason = result.Reason.ToString(); - } - - if (!string.IsNullOrEmpty(errRH) && !string.IsNullOrEmpty(reason)) - { - response.AppendHeader(errRH, reason); - } - - response.StatusCode = statusCode; - response.Status = status; - } - } - } - else - { - // fallback output if not valid settings - if (result != null && result.Action == ActionType.Output404) - { - // don't restate the requested Url to prevent cross site scripting - errorPageHtml.Write("404 Not Found
The requested Url does not return any valid content."); - response.StatusCode = 404; - response.Status = "404 Not Found"; - } - else - { - // error, especially if invalid result object - errorPageHtml.Write("500 Server Error
An error occured during processing : if possible, check the event log of the server
"); - response.StatusCode = 500; - response.Status = "500 Internal Server Error"; - if (result != null) - { - result.Action = ActionType.Output500; - } - } - } - - if (ex != null) - { - if (context != null) - { - if (context.Items.Contains("UrlRewrite:Exception") == false) - { - context.Items.Add("UrlRewrite:Exception", ex.Message); - context.Items.Add("UrlRewrite:StackTrace", ex.StackTrace); - } - } - - if (ceSection != null && ceSection.Mode == CustomErrorsMode.Off) - { - errorPageHtml.Write(errorPageHtmlHeader); - errorPageHtml.Write("
Exception:
" + ex.Message + "
"); - errorPageHtml.Write("
Stack Trace:
" + ex.StackTrace + "
"); - errorPageHtml.Write("
Administrators
"); - errorPageHtml.Write("
You can see this exception because the customErrors attribute in the web.config is set to 'off'. Change this value to 'on' or 'RemoteOnly' to show Error Handling
"); - try - { - if (errUrl != null && errUrl.StartsWith("~")) - { - errUrl = VirtualPathUtility.ToAbsolute(errUrl); - } - } - finally - { - if (errUrl != null) - { - errorPageHtml.Write("
The error handling would have shown this page : " + errUrl + "
"); - } - else - { - errorPageHtml.Write("
The error handling could not determine the correct page to show.
"); - } - } - } - } - - string errorPageHtmlBody = errorPageHtml.ToString(); - if (errorPageHtmlBody.Length > 0) - { - response.Write(errorPageHtmlHeader); - response.Write(errorPageHtmlBody); - response.Write(errorPageHtmlFooter); - } - - if (ex != null) - { - UrlRewriterUtils.LogExceptionInRequest(ex, status, result); - } - } - } - - private static IPrincipal GetCurrentPrincipal(HttpContext context) - { - // Extract the forms authentication cookie - var authCookie = context.Request.Cookies[FormsAuthentication.FormsCookieName]; - var currentPrincipal = new GenericPrincipal(new GenericIdentity(string.Empty), new string[0]); - - try - { - if (authCookie != null) - { - var authTicket = FormsAuthentication.Decrypt(authCookie.Value); - if (authTicket != null && !authTicket.Expired) - { - var roles = authTicket.UserData.Split('|'); - var id = new FormsIdentity(authTicket); - currentPrincipal = new GenericPrincipal(id, roles); - } - } - } - catch (Exception) - { - // do nothing here. - } - - return currentPrincipal; - } - - private static bool CheckForDebug(HttpRequest request, NameValueCollection queryStringCol, bool debugEnabled) - { - string debugValue = string.Empty; - bool retVal = false; - - if (debugEnabled) - { - const string debugToken = "_aumdebug"; - if (queryStringCol != null && queryStringCol[debugToken] != null) - { - debugValue = queryStringCol[debugToken]; - } - else - { - if (request != null) - { - debugValue = request.Params.Get("HTTP_" + debugToken.ToUpper()); - } - - if (debugValue == null) - { - debugValue = "false"; - } - } - } - - switch (debugValue.ToLowerInvariant()) - { - case "true": - retVal = true; - break; - } - - return retVal; - } - - private static bool CheckForTabExternalForwardOrRedirect( - HttpContext context, - ref UrlAction result, - HttpResponse response, - FriendlyUrlSettings settings, - Guid parentTraceId) - { - bool finished = false; - HttpRequest request = null; - if (context != null) - { - request = context.Request; - } - - try - { - // check for external forwarding or a permanent redirect request - // 592 : check for permanent redirect (823 : moved location from 'checkForRedirects') - if (result.TabId > -1 && result.PortalId > -1 && - (settings.ForwardExternalUrlsType != DNNPageForwardType.NoForward || - result.Reason == RedirectReason.Tab_Permanent_Redirect)) - { - bool allowRedirect = !(result.RewritePath != null && result.RewritePath.ToLowerInvariant().Contains("&ctl=tab")); - - // 594 : do not redirect settings pages for external urls - if (allowRedirect) - { - TabInfo tab; - allowRedirect = CheckFor301RedirectExclusion(result.TabId, result.PortalId, false, out tab, settings); - if (allowRedirect) - { - // 772 : not redirecting file type Urls when requested. - bool permanentRedirect = false; - string redirectUrl = null; - string cleanPath = null; - bool doRedirect = false; - switch (tab.TabType) - { - case TabType.File: - // have to fudge in a portal settings object for this to work - shortcoming of LinkClick URl generation - var portalSettings = new PortalSettings(result.TabId, result.PortalAlias); - if (context != null) - { - context.Items.Add("PortalSettings", portalSettings); - result.Reason = RedirectReason.File_Url; - string fileUrl = Globals.LinkClick(tab.Url, tab.TabID, -1); - context.Items.Remove("PortalSettings"); - - // take back out again, because it will be done further downstream - // do a check to make sure we're not repeating the Url again, because the tabid is set but we don't want to touch - // a linkclick url - if (!result.OriginalPathNoAlias.EndsWith(HttpUtility.UrlDecode(fileUrl), true, CultureInfo.InvariantCulture)) - { - redirectUrl = fileUrl; - } - } - - if (redirectUrl != null) - { - doRedirect = true; - } - - break; - case TabType.Url: - result.Reason = RedirectReason.Tab_External_Url; - redirectUrl = tab.Url; - if (redirectUrl != null) - { - doRedirect = true; - if (tab.PermanentRedirect) - { - result.Action = ActionType.Redirect301; - } - else - { - result.Action = ActionType.Redirect302; - } - } - - break; - case TabType.Tab: - // get the redirect path of the specific tab, as long as we have a valid request to work from - if (request != null) - { - // get the rewrite or requested path in a clean format, suitable for input to the friendly url provider - cleanPath = RewriteController.GetRewriteOrRequestedPath(result, request.Url); - - // 727 prevent redirectLoop with do301 in querystring - if (result.Action == ActionType.Redirect301 || - result.Action == ActionType.Redirect302) - { - cleanPath = RedirectTokens.RemoveAnyRedirectTokens( - cleanPath, - request.QueryString); - } - - // get the redirect Url from the friendly url provider using the tab, path and settings - redirectUrl = RedirectController.GetTabRedirectUrl( - tab, - settings, - cleanPath, - result, - out permanentRedirect, - parentTraceId); - } - - // check to make sure there isn't a blank redirect Url - if (redirectUrl == null) - { - // problem : no redirect Url to redirect to - // solution : cancel the redirect - string message = "Permanent Redirect chosen for Tab " + - tab.TabPath.Replace("//", "/") + - " but forwarding Url was not valid"; - RedirectController.CancelRedirect(ref result, context, settings, message); - } - else - { - // if there was a redirect Url, set the redirect action and set the type of redirect going to use - doRedirect = true; - if (permanentRedirect) - { - result.Action = ActionType.Redirect301; - result.Reason = RedirectReason.Tab_Permanent_Redirect; - } - else - { - // not a permanent redirect, check if the page forwarding is set - result.Action = ActionType.Redirect302; - result.Reason = RedirectReason.Tab_Temporary_Redirect; - } - - // should be already set, anyway - result.RewritePath = cleanPath; - } - - break; - default: - // only concern here is if permanent redirect is requested, but there is no external url specified - if (result.Reason == RedirectReason.Tab_Permanent_Redirect) - { - bool permRedirect = tab.PermanentRedirect; - if (permRedirect) - { - // problem : permanent redirect marked, but no forwarding url supplied - // solution : cancel redirect - string message = "Permanent Redirect chosen for Tab " + - tab.TabPath.Replace("//", "/") + - " but no forwarding Url Supplied"; - RedirectController.CancelRedirect(ref result, context, settings, message); - } - } - - break; - } - - // do the redirect we have specified - if (doRedirect && - (result.Action == ActionType.Redirect301 || result.Action == ActionType.Redirect302)) - { - result.FinalUrl = redirectUrl; - if (result.Action == ActionType.Redirect301) - { - if (response != null) - { - // perform a 301 redirect to the external url of the tab - response.AppendHeader( - "X-Redirect-Reason", - result.Reason.ToString().Replace("_", " ") + " Requested"); - response.RedirectPermanent(result.FinalUrl); - } - } - else - { - if (result.Action == ActionType.Redirect302) - { - if (response != null) - { - // perform a 301 redirect to the external url of the tab - response.AppendHeader( - "X-Redirect-Reason", - result.Reason.ToString().Replace("_", " ") + " Requested"); - response.Redirect(result.FinalUrl); - } - } - } - - finished = true; - } - } - } - } - } - catch (ThreadAbortException) - { - // do nothing, a threadAbortException will have occured from using a server.transfer or response.redirect within the code block. This is the highest - // level try/catch block, so we handle it here. - } - - return finished; - } - - /// Redirects an alias if that is allowed by the settings. - /// - /// - /// - /// if the is a redirect, otherwise . - private static bool RedirectPortalAlias(string httpAlias, ref UrlAction result, FriendlyUrlSettings settings) - { - bool redirected = false; - - // redirect to primary alias - if (result.PortalAliasMapping == PortalSettings.PortalAliasMapping.Redirect && result.RedirectAllowed) - { - if (result.Reason == RedirectReason.Wrong_Portal_Alias_For_Browser_Type || result.Reason == RedirectReason.Wrong_Portal_Alias_For_Culture || - result.Reason == RedirectReason.Wrong_Portal_Alias_For_Culture_And_Browser) - { - redirected = ConfigurePortalAliasRedirect(ref result, result.HttpAlias, httpAlias, false, result.Reason, settings.InternalAliasList, settings); - } - else - { - redirected = ConfigurePortalAliasRedirect(ref result, result.HttpAlias, httpAlias, false, settings.InternalAliasList, settings); - } - } - - return redirected; - } - - private static bool ConfigurePortalAliasRedirect( - ref UrlAction result, - string wrongAlias, - string rightAlias, - bool ignoreCustomAliasTabs, - List internalAliases, - FriendlyUrlSettings settings) - { - return ConfigurePortalAliasRedirect( - ref result, - wrongAlias, - rightAlias, - ignoreCustomAliasTabs, - RedirectReason.Wrong_Portal_Alias, - internalAliases, - settings); - } - - /// Checks to see whether the specified alias is a customTabAlias. - /// - /// - /// - /// if the alias is a custom tab alias, otherwise . - private static bool CheckIfAliasIsCustomTabAlias(ref UrlAction result, string httpAlias, FriendlyUrlSettings settings) - { - List customAliasesForTabs = TabIndexController.GetCustomPortalAliases(settings); - bool isACustomTabAlias = false; - if (customAliasesForTabs != null && customAliasesForTabs.Count > 0) - { - // remove any customAliases that are also primary aliases. - foreach (var cpa in PortalAliasController.Instance.GetPortalAliasesByPortalId(result.PortalId)) - { - if (cpa.IsPrimary == true && customAliasesForTabs.Contains(cpa.HTTPAlias)) - { - customAliasesForTabs.Remove(cpa.HTTPAlias); - } - } - - isACustomTabAlias = customAliasesForTabs.Contains(httpAlias.ToLowerInvariant()); - } - - return isACustomTabAlias; - } - - /// Checks to see whether the specified alias is a customTabAlias for the TabId in result. - /// - /// - /// if the the current alias is a custom tab alias, otherwise . - private static bool CheckIfAliasIsCurrentTabCustomTabAlias(ref UrlAction result, FriendlyUrlSettings settings) - { - var customAliasesForTab = TabController.Instance.GetCustomAliases(result.TabId, result.PortalId); - bool isCurrentTabCustomTabAlias = false; - if (customAliasesForTab != null && customAliasesForTab.Count > 0) - { - // see if we have a customAlias for the current CultureCode - if (customAliasesForTab.ContainsKey(result.CultureCode)) - { - // if it is for the current culture, we need to know if it's a primary alias - var tabPortalAlias = PortalAliasController.Instance.GetPortalAlias(customAliasesForTab[result.CultureCode]); - if (tabPortalAlias != null && !tabPortalAlias.IsPrimary) - { - // it's not a primary alias, so must be a custom tab alias - isCurrentTabCustomTabAlias = true; - } - } - } - - // if it's not a custom alias for the current tab, we'll need to change the result - if (!isCurrentTabCustomTabAlias) - { - result.Action = ActionType.Redirect301; - result.Reason = RedirectReason.Wrong_Portal_Alias; - } - - return isCurrentTabCustomTabAlias; - } - - /// Configures the result object to set the correct Alias redirect parameters and destination URL. - /// - /// - /// - /// - /// - /// - /// - /// if the is a redirect, otherwise . - private static bool ConfigurePortalAliasRedirect( - ref UrlAction result, - string wrongAlias, - string rightAlias, - bool ignoreCustomAliasTabs, - RedirectReason redirectReason, - List internalAliases, - FriendlyUrlSettings settings) - { - // wrong alias for the portal - // check to see if the wrong portal alias could be a custom alias for a tab - bool doRedirect; - if (ignoreCustomAliasTabs == false) - { - // check out custom alias tabs collection - // if an alias is a custom tab alias for a specific tab, then don't redirect - // if we have the TabId, we'll need to check if the alias is valid for the current tab - if (result.TabId > 0 && CheckIfAliasIsCurrentTabCustomTabAlias(ref result, settings)) - { - doRedirect = false; - } - else if (result.TabId < 0 && CheckIfAliasIsCustomTabAlias(ref result, wrongAlias, settings)) - { - doRedirect = false; - } - else - { - doRedirect = true; - } - } - else - { - doRedirect = true; // do redirect, ignore custom alias entries for tabs - } - - // check to see if it is an internal alias. These are used to block redirects - // to allow for reverse proxy requests, which must change the rewritten alias - // while leaving the requested alias - bool internalAliasFound = false; - if (doRedirect && internalAliases != null && internalAliases.Count > 0) - { - if (internalAliases.Any(ia => string.Compare(ia.HttpAlias, wrongAlias, StringComparison.OrdinalIgnoreCase) == 0)) - { - internalAliasFound = true; - doRedirect = false; - } - } - - // if still need to do redirect, then set the settings that will cause the redirect (redirect not done here) - if (doRedirect) - { - result.Action = ActionType.Redirect301; - result.Reason = redirectReason; - var destUrl = result.OriginalPath; - if (result.OriginalPath.Contains(wrongAlias)) - { - destUrl = result.OriginalPath.Replace(wrongAlias, rightAlias); - } - else if (result.OriginalPath.ToLowerInvariant().Contains(wrongAlias)) - { - destUrl = result.OriginalPath.ToLowerInvariant().Replace(wrongAlias, rightAlias); - } - - if (redirectReason == RedirectReason.Wrong_Portal_Alias_For_Culture || - redirectReason == RedirectReason.Wrong_Portal_Alias_For_Culture_And_Browser) - { - destUrl = destUrl.Replace("/language/" + result.CultureCode, string.Empty); - } - - destUrl = CheckForSiteRootRedirect(rightAlias, destUrl); - result.FinalUrl = destUrl; - } - else - { - // 838 : don't overwrite the reason if already have checkfor301 - // and don't do a check on the basis that an internal alias was found - if (result.Action != ActionType.CheckFor301 && internalAliasFound == false) - { - // set status to 'check for redirect' - result.Action = ActionType.CheckFor301; - result.Reason = RedirectReason.Custom_Tab_Alias; - } - } - - return doRedirect; - } - - private static string MakeUrlWithAlias(Uri requestUri, string httpAlias) - { - return requestUri.AbsoluteUri.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase) - ? "https://" + httpAlias.Replace("*.", string.Empty) + "/" - : "http://" + httpAlias.Replace("*.", string.Empty) + "/"; - } - - private static string MakeUrlWithAlias(Uri requestUri, PortalAliasInfo alias) - { - return MakeUrlWithAlias(requestUri, alias.HTTPAlias); - } - - /// Determines if this is a request from an install / upgrade url. - /// - /// - /// - /// - /// if the request is for an install URL, otherwise . - /// - /// //875 : cater for the upgradewizard.aspx Url that is new to DNN 6.1. - /// - private static bool IgnoreRequestForInstall(string physicalPath, string refererPath, string requestedDomain, string refererDomain) - { - if (physicalPath.EndsWith("install.aspx", true, CultureInfo.InvariantCulture) - || physicalPath.EndsWith("installwizard.aspx", true, CultureInfo.InvariantCulture) - || physicalPath.EndsWith("upgradewizard.aspx", true, CultureInfo.InvariantCulture) - || Globals.Status == Globals.UpgradeStatus.Install - || Globals.Status == Globals.UpgradeStatus.Upgrade) - { - return true; - } - - // 954 : DNN 7.0 compatibility - // check for /default.aspx which is default Url launched from the Upgrade/Install wizard page - // 961 : check domain as well as path for the referer - if (physicalPath.EndsWith(Globals.glbDefaultPage, true, CultureInfo.InvariantCulture) == false - && refererPath != null - && string.Compare(requestedDomain, refererDomain, StringComparison.OrdinalIgnoreCase) == 0 - && (refererPath.EndsWith("install.aspx", true, CultureInfo.InvariantCulture) - || refererPath.EndsWith("installwizard.aspx", true, CultureInfo.InvariantCulture) - || refererPath.EndsWith("upgradewizard.aspx", true, CultureInfo.InvariantCulture))) - { - return true; - } - - return false; - } - - private static bool IgnoreRequestForWebServer(string requestedPath) - { - // Should standardize comparison methods - if (requestedPath.IndexOf("synchronizecache.aspx", StringComparison.OrdinalIgnoreCase) > 1 - || requestedPath.EndsWith("keepalive.aspx", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - // Get the root - var rootPath = requestedPath.Substring(0, requestedPath.LastIndexOf("/", StringComparison.Ordinal)); - rootPath = rootPath.Substring(rootPath.IndexOf("://", StringComparison.Ordinal) + 3); - - // Check if this is a WebServer and not a portalalias. - // if can auto add portal alias enabled, then return false, alias will add later. - var alias = PortalAliasController.Instance.GetPortalAlias(rootPath); - if (alias != null || CanAutoAddPortalAlias()) - { - return false; - } - - // Check if this is a WebServer - var server = ServerController.GetEnabledServers().SingleOrDefault(s => s.Url == rootPath); - if (server != null) - { - return true; - } - - return false; - } - - private static bool IgnoreRequestForInstall(HttpRequest request) - { - try - { - string physicalPath = request.PhysicalPath; - string requestedDomain = request.Url.Host; - string refererPath = null, refererDomain = null; - if (request.UrlReferrer != null) - { - refererDomain = request.UrlReferrer.Host; - refererPath = request.UrlReferrer.LocalPath; - } - - return IgnoreRequestForInstall(physicalPath, refererPath, requestedDomain, refererDomain); - } - catch (PathTooLongException) - { - // catch and handle this exception, caused by an excessively long file path based on the - // mapped virtual url - return false; - } - catch (ArgumentException) - { - // catch and handle this exception, caused by an invalid character in the file path based on the - // mapped virtual url - return false; - } - catch (UriFormatException) - { - // catch and handle this exception, caused by an invalid hostname in the referrer - return false; - } - } - - private static bool IgnoreRequest(UrlAction result, string requestedPath, string ignoreRegex, HttpRequest request) - { - bool retVal = false; - - // check if we are upgrading/installing - // 829 : use result physical path instead of requset physical path - // 875 : cater for the upgradewizard.aspx Url that is new to DNN 6.1 - if (request != null && (IgnoreRequestForInstall(request) || IgnoreRequestForWebServer(requestedPath))) - { - // ignore all install requests - retVal = true; - } - else if (request != null && request.Path.EndsWith("imagechallenge.captcha.aspx", StringComparison.InvariantCultureIgnoreCase)) - { - retVal = true; - } - else - { - try - { - if (ignoreRegex.Length > 0) - { - if (Regex.IsMatch(requestedPath, ignoreRegex, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)) - { - retVal = true; - } - } - } - catch (Exception ex) - { - UrlRewriterUtils.LogExceptionInRequest(ex, "Not Set", result); - result.Ex = ex; - } - } - - return retVal; - } - - private static void CheckForRewrite( - string fullUrl, - string querystring, - UrlAction result, - bool useFriendlyUrls, - NameValueCollection queryStringCol, - FriendlyUrlSettings settings, - out bool isPhysicalResource, - Guid parentTraceId) - { - bool checkForRewrites; - - // just check to make sure it isn't a physical resource on the server - RewriteController.IdentifyByPhysicalResource( - result.PhysicalPath, - fullUrl, - queryStringCol, - ref result, - useFriendlyUrls, - settings, - out isPhysicalResource, - out checkForRewrites, - parentTraceId); - - if (checkForRewrites && RewriteController.CanRewriteRequest(result, fullUrl, settings)) - { - bool doSiteUrlProcessing = false; - - // 728 new regex expression to pass values straight onto the siteurls.config file - if (!string.IsNullOrEmpty(settings.UseSiteUrlsRegex)) - { - doSiteUrlProcessing = Regex.IsMatch(fullUrl, settings.UseSiteUrlsRegex, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); - } - - // if a virtual request, and not starting with the siteUrls.config file, go on to find the rewritten path - if (!doSiteUrlProcessing) - { - // looks up the page index to find the correct Url - bool doRewrite = RewriteController.IdentifyByTabPathEx(fullUrl, querystring, result, queryStringCol, settings, parentTraceId); - if (!doRewrite) - { - doSiteUrlProcessing = true; - } - } - - if (doSiteUrlProcessing) - { - // 728 : compare requests against the siteurls.config file, either if no other match was found, or if we want to skip the rest of the processing - // the standard DNN way of rewriting, using expressions found in the siteurls.config file - RewriteController.IdentifyByRegEx(fullUrl, querystring, result.ApplicationPath, ref result, settings, parentTraceId); - } - } - } - - private static bool CheckForRedirects( - Uri requestUri, - string fullUrl, - NameValueCollection queryStringCol, - UrlAction result, - string requestType, - FriendlyUrlSettings settings, - int portalHomeTabId) - { - bool redirected = false; - if (queryStringCol["error"] == null && queryStringCol["message"] == null && requestType != "POST") - { - // if the / is missing from an extension-less request, then check for a 301 redirect - if (settings.PageExtensionUsageType == PageExtensionUsageType.Never) - { - // 575 check on absolutePath instead of absoluteUri : this ignores query strings and fragments like # - // 610 don't always end with '/' - reverses previous setting - // 687 don't double-check 301 redirects. 'CheckFor301' is less concise than 'Redirect301' - // DNN-21906: if the redirect is for splash page, then we should continue the 302 redirect. - if (requestUri.AbsolutePath.EndsWith("/") && result.Action != ActionType.Redirect301 && result.Reason != RedirectReason.Requested_SplashPage) - { - result.Action = ActionType.CheckFor301; - } - } - - if (settings.RedirectWrongCase && result.Action == ActionType.Continue) - { - result.Action = ActionType.CheckFor301; - } - - string scheme = requestUri.Scheme + Uri.SchemeDelimiter; - bool queryStringHas301Parm = queryStringCol["do301"] != null; - - // 727 : keep a bool value if there is a do301 request in the querystring - // check for a 301 request in the query string, or an explicit 301 or 302 request - // 2.0 - check for explicit do301=true instead of just do301 key - string do301Val = queryStringCol["do301"]; - if (result.TabId > -1 - && (result.Action == ActionType.Redirect301 - || (do301Val != null && do301Val == "true") - || result.Action == ActionType.Redirect302)) - { - // valid tab, specific 301 redirect, rewrite hint for specific 301 redirect, or specific 302 redirect - // we have ordered a 301 redirect earlier in the code - // get the url for redirection by re-submitting the path into the Friendly Url Provider - string pathOnly = RewriteController.GetRewriteOrRequestedPath(result, requestUri); - - // 727 prevent redirectLoop with do301 in querystring - if (result.Action == ActionType.Redirect301 || queryStringHas301Parm || result.Action == ActionType.Redirect302) - { - pathOnly = RedirectTokens.RemoveAnyRedirectTokens(pathOnly, queryStringCol); - } - - // check for exclusion by regex for this url - if (result.RedirectAllowed) - { - // get the tab so we know where to go - TabInfo tab; - bool checkRedirect = CheckFor301RedirectExclusion(result.TabId, result.PortalId, true, out tab, settings); - - if (checkRedirect) - { - if ((result.Reason == RedirectReason.Deleted_Page || result.Reason == RedirectReason.Disabled_Page) - && portalHomeTabId > 0 - && settings.DeletedTabHandlingType == DeletedTabHandlingType.Do301RedirectToPortalHome) - { - // redirecting to home page - TabInfo homeTab = TabController.Instance.GetTab(portalHomeTabId, result.PortalId, false); - if (homeTab != null) - { - string homePageUrl = AdvancedFriendlyUrlProvider.ImprovedFriendlyUrl( - homeTab, - pathOnly, - Globals.glbDefaultPage, - result.HttpAlias, - false, - settings, - Guid.Empty); - result.Action = ActionType.Redirect301; - result.FinalUrl = homePageUrl; - result.RewritePath = pathOnly; - redirected = true; - } - } - else - { - // get the rewrite or requested path in a clean format, suitable for input to the friendly url provider - string cleanPath = RewriteController.GetRewriteOrRequestedPath(result, requestUri); - - // 727 prevent redirectLoop with do301 in querystring - // also check for existing in path of do301 token - if (result.Action == ActionType.Redirect301 || do301Val != null || result.Action == ActionType.Redirect302) - { - cleanPath = RedirectTokens.RemoveAnyRedirectTokens(cleanPath, queryStringCol); - } - - // get best friendly url from friendly url provider - string bestFriendlyUrl = AdvancedFriendlyUrlProvider.ImprovedFriendlyUrl( - tab, - cleanPath, - Globals.glbDefaultPage, - result.HttpAlias, - false, - settings, - Guid.Empty); - - // get what the friendly Url for this tab should be and stick it in as the redirect - // 727 : using boolean because we wanted to get rid of the do301 before calculating the correct url - if (queryStringHas301Parm) - { - result.Action = ActionType.Redirect301; - if (result.Reason == RedirectReason.Not_Redirected) - { - result.Reason = RedirectReason.Unfriendly_Url_1; - } - } - - result.FinalUrl = bestFriendlyUrl; - result.RewritePath = pathOnly; - redirected = true; // mark as redirected - } - } - else - { - // redirect disallowed - // 618: dont' clear if 302 redirect selected - if (result.Action != ActionType.Redirect302Now || result.Action != ActionType.Redirect302) - { - RedirectController.CancelRedirect(ref result, null, settings, "Redirect requested but cancelled because disallowed"); - } - } - } - } - else if (result.TabId > -1 && result.RedirectAllowed && result.Action == ActionType.CheckFor301) - { - // 301 check was requested in earlier processing - // get the tab controller and retrieve the tab the request is for - // don't redirect unless allowed, the tab is valid, and it's not an admin or super tab - if (settings.RedirectUnfriendly) - { - TabInfo tab; - bool allowRedirect = CheckFor301RedirectExclusion(result.TabId, result.PortalId, true, out tab, settings); - if (allowRedirect && tab != null) - { - // remove the http alias from the url. Do this by putting the url back together from the request and removing the alias - string rewritePathOnly; - if (result.DoRewrite) - { - rewritePathOnly = result.RewritePath; - var pos = rewritePathOnly.IndexOf("default.aspx", StringComparison.OrdinalIgnoreCase); - if (pos > Null.NullInteger) - { - rewritePathOnly = rewritePathOnly.Substring(pos); - } - } - else - { - rewritePathOnly = requestUri.Host + requestUri.PathAndQuery; - } - - // remove the http alias from the path - var pathAliasEnd = rewritePathOnly.IndexOf(result.PortalAlias.HTTPAlias, StringComparison.InvariantCultureIgnoreCase); - var queryStringIndex = rewritePathOnly.IndexOf("?", StringComparison.InvariantCultureIgnoreCase); - if (pathAliasEnd > Null.NullInteger && (queryStringIndex == Null.NullInteger || pathAliasEnd < queryStringIndex)) - { - rewritePathOnly = rewritePathOnly.Substring(pathAliasEnd + result.PortalAlias.HTTPAlias.Length); - } - - // now check to see if need to remove /default.aspx from the end of the requested Url - string requestedUrl = fullUrl; - int requestedUrlAliasEnd = requestedUrl.IndexOf(result.PortalAlias.HTTPAlias, StringComparison.InvariantCultureIgnoreCase) - + (result.PortalAlias.HTTPAlias + "/").Length; - if (requestedUrlAliasEnd > Null.NullInteger) - { - // 818 : when a site root is used for a custom page Url, then check for max length within bounds - if ((requestedUrl.Length - requestedUrlAliasEnd) >= 12 && requestedUrl.Substring(requestedUrlAliasEnd).Equals("default.aspx", StringComparison.InvariantCultureIgnoreCase)) - { - requestedUrl = requestedUrl.Substring(0, requestedUrl.Length - 12); - - // 12 = default.aspx length - } - } - - // what happens here is that the request is reverse-engineered to see if it matches what the friendly Url shoudl have been - // get what the friendly Url for this tab should be - string bestFriendlyUrl; - - // 819 : leaving /do301/check in Url because not using cleanPath to remove from - string cleanPath = RedirectTokens.RemoveAnyRedirectTokensAndReasons(rewritePathOnly); - - // string cleanPath = rewritePathOnly.Replace("&do301=check","");//remove check parameter if it exists - // cleanPath = cleanPath.Replace("&do301=true", "");//don't pass through internal redirect check parameter - cleanPath = cleanPath.Replace("&_aumdebug=true", string.Empty); // remove debug parameter if it exists - - Match match = RewritePathRx.Match(rewritePathOnly ?? string.Empty); - if (match.Success) - { - // when the pathOnly value ends with '=' it means there is a query string pair with a key and no value - // make the assumption that this was passed in as a page name OTHER than default page - string pageName = match.Groups["parm"].Value; // get the last parameter in the list - - cleanPath = cleanPath.Replace(match.Value, string.Empty); - - // remove the last parameter from the path - - // generate teh friendly URl name with the last parameter as the page name, not a query string parameter - bestFriendlyUrl = AdvancedFriendlyUrlProvider.ImprovedFriendlyUrl( - tab, - cleanPath, - pageName + settings.PageExtension, - result.HttpAlias, - false, - settings, - Guid.Empty); - } - else - { - bestFriendlyUrl = AdvancedFriendlyUrlProvider.ImprovedFriendlyUrl( - tab, - cleanPath, - Globals.glbDefaultPage, - result.HttpAlias, - false, - settings, - Guid.Empty); - } - - // if the incoming request doesn't match the 'most friendly' url, a 301 Moved Permanently status is returned, along with the friendly url - // check the bestFriendlyUrl against either the url, or rawUrl (with and without host) - // in each case, the aumdebug parameter will be searched for and replaced - var urlDecode = HttpUtility.UrlDecode(requestedUrl); - if (urlDecode != null) - { - string rawUrlWithHost = StripDebugParameter(urlDecode.ToLowerInvariant()); - - // string rawUrlWithHost = StripDebugParameter(System.Web.HttpUtility.UrlDecode(scheme + requestUri.Host + requestUri.PathAndQuery).ToLowerInvariant()); - string rawUrlWithHostNoScheme = StripDebugParameter(rawUrlWithHost.Replace(scheme, string.Empty)); - string bestFriendlyNoScheme = StripDebugParameter(bestFriendlyUrl.ToLowerInvariant().Replace(scheme, string.Empty)); - string requestedPathNoScheme = StripDebugParameter(requestUri.AbsoluteUri.Replace(scheme, string.Empty).ToLowerInvariant()); - string rawUrlLowerCase = StripDebugParameter(requestUri.AbsoluteUri.ToLowerInvariant()); - - // check to see if just an alias redirect of an internal alias - var primaryAliases = PortalAliasController.Instance.GetPortalAliasesByPortalId(result.PortalId).ToList(); - - if (settings.InternalAliasList != null && settings.InternalAliasList.Count > 0 && primaryAliases.Count > 0) - { - var cpa = primaryAliases.GetAliasByPortalIdAndSettings(result); - if (cpa != null) - { - string chosenAlias = cpa.HTTPAlias.ToLowerInvariant(); - foreach (InternalAlias ia in settings.InternalAliasList) - { - string internalAlias = ia.HttpAlias.ToLowerInvariant(); - if (requestedPathNoScheme.Contains(internalAlias)) - { - // an internal alias has been used. - // replace this in the comparison charts to do a 'fair' comparison - requestedPathNoScheme = requestedPathNoScheme.Replace(internalAlias, chosenAlias); - rawUrlWithHost = rawUrlWithHost.Replace(scheme + internalAlias, scheme + chosenAlias); - rawUrlWithHostNoScheme = rawUrlWithHostNoScheme.Replace(internalAlias, chosenAlias); - rawUrlLowerCase = rawUrlLowerCase.Replace(internalAlias, chosenAlias); - break; - } - } - } - } - - // DNN-9158: prevent SSL Offloading infinite redirects - if (!result.IsSecureConnection && result.IsSSLOffloaded && bestFriendlyNoScheme.StartsWith("https")) - { - bestFriendlyNoScheme = $"http://{bestFriendlyNoScheme.Substring(8)}"; - } - - if (!(bestFriendlyNoScheme == requestedPathNoScheme - || bestFriendlyNoScheme == rawUrlWithHost - || HttpUtility.UrlDecode(bestFriendlyNoScheme) == rawUrlWithHost - || bestFriendlyNoScheme == rawUrlWithHostNoScheme - || bestFriendlyNoScheme == HttpUtility.UrlDecode(requestedPathNoScheme) - || HttpUtility.UrlDecode(bestFriendlyNoScheme) == HttpUtility.UrlDecode(requestedPathNoScheme) - || bestFriendlyNoScheme == rawUrlLowerCase)) - { - redirected = true; - result.Action = ActionType.Redirect301; - result.FinalUrl = bestFriendlyUrl; - if (result.Reason != RedirectReason.Custom_Tab_Alias && - result.Reason != RedirectReason.Deleted_Page && - result.Reason != RedirectReason.Disabled_Page) - { - result.Reason = RedirectReason.Unfriendly_Url_2; - } - - result.DebugMessages.Add("Compared :" + bestFriendlyNoScheme + " [generated] -> " + requestedPathNoScheme + " [requested with no scheme]"); - result.DebugMessages.Add("Compared :" + bestFriendlyNoScheme + " [generated] -> " + rawUrlWithHost + " [requested with host and scheme]"); - result.DebugMessages.Add("Compared :" + bestFriendlyNoScheme + " [generated] -> " + rawUrlWithHostNoScheme + " [requested with host, no scheme]"); - result.DebugMessages.Add("Compared :" + bestFriendlyNoScheme + " [generated] -> " + HttpUtility.UrlDecode(requestedPathNoScheme) + " [requested and decoded]"); - result.DebugMessages.Add("Compared :" + bestFriendlyNoScheme + " [generated] -> " + rawUrlLowerCase + " [requested raw Url]"); - } - } - } - } - } - - if (result.RedirectAllowed && settings.RedirectWrongCase) - { - // check for redirects where a redirectToSubDomain is specified, - // redirect for Wrong case is specified, and there is a valid tab and it's not already redirected somewhere else - bool doRedirect = false; - string redirectPath = redirected ? result.FinalUrl : requestUri.AbsoluteUri; - string redirectPathOnly = redirectPath; - if (redirectPathOnly.Contains("?")) - { - redirectPathOnly = redirectPathOnly.Substring(0, redirectPathOnly.IndexOf("?", StringComparison.Ordinal)); - } - - // Thanks Etienne for the fix for Diacritic Characters Terminal Loop! - // if the url contains url encoded characters, they appear here uppercase -> %C3%83%C2 - // decode the url to get back the original character and do proper casing comparison - string urlDecodedRedirectPath = HttpUtility.UrlDecode(redirectPathOnly); - - // check for wrong case redirection - if (urlDecodedRedirectPath != null && (settings.RedirectWrongCase && string.CompareOrdinal(urlDecodedRedirectPath, urlDecodedRedirectPath.ToLowerInvariant()) != 0)) - { - TabInfo tab; - bool allowRedirect = CheckFor301RedirectExclusion(result.TabId, result.PortalId, true, out tab, settings); - - if (allowRedirect && !string.IsNullOrEmpty(settings.ForceLowerCaseRegex)) - { - // don't allow redirect if excluded from redirecting in the force lower case regex pattern (606) - allowRedirect = !Regex.IsMatch(redirectPath, settings.ForceLowerCaseRegex, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); - } - - if (allowRedirect) - { - // special case : when IIS automatically places /default.aspx on the end of the string, - // then don't try and redirect to the lower case /default.aspx, just let it through. - // we don't know whether IIS appended /Default.aspx on the end, however, we can guess - // if the redirectDefault.aspx is turned on (511) - if (settings.RedirectDefaultPage == false && redirectPathOnly.EndsWith(Globals.glbDefaultPage, StringComparison.InvariantCultureIgnoreCase)) - { - // ignore this, because it's just a redirect of the /Default.aspx to /default.aspx - } - else - { - redirectPath = redirectPath.Replace(redirectPathOnly, redirectPathOnly.ToLowerInvariant()); - doRedirect = true; - result.Reason = RedirectReason.Not_Lower_Case; - } - } - } - - if (doRedirect) - { - result.Action = ActionType.Redirect301; - result.FinalUrl = CheckForSiteRootRedirect(result.PortalAlias.HTTPAlias, redirectPath); - redirected = true; - } - } - } - - return redirected; - } - - private static string StripDebugParameter(string url) - { - return AumDebugRegex.Replace(url, string.Empty); - } - - private static bool CheckFor301RedirectExclusion(int tabId, int portalId, bool checkBaseUrls, out TabInfo tab, FriendlyUrlSettings settings) - { - bool doRedirect = false; - tab = TabController.Instance.GetTab(tabId, portalId, false); - - // don't redirect unless allowed, the tab is valid, and it's not an admin or super tab - if (tab != null && tab.IsSuperTab == false && !tab.DoNotRedirect) - { - if (checkBaseUrls) - { - // no redirect for friendly url purposes if the tab is in the 'base friendly urls' section - doRedirect = !RewriteController.IsExcludedFromFriendlyUrls(tab, settings, true); - } - else - { - doRedirect = true; - } - } - - return doRedirect; - } - - private PortalAliasInfo GetPortalAlias(FriendlyUrlSettings settings, string requestUrl, out bool redirectAlias, out bool isPrimaryAlias, out string wrongAlias) - { - PortalAliasInfo aliasInfo = null; - redirectAlias = false; - wrongAlias = null; - isPrimaryAlias = false; - OrderedDictionary portalAliases = TabIndexController.GetPortalAliases(settings); - foreach (string alias in portalAliases.Keys) - { - var urlToMatch = requestUrl; - - // in fact, requested url should contain alias - // for better performance, need to check whether we want to proceed with a whole url matching or not - // if alias is not a part of url -> let's proceed to the next iteration - var aliasIndex = urlToMatch.IndexOf(alias, StringComparison.InvariantCultureIgnoreCase); - if (aliasIndex < 0) - { - continue; - } - else - { - // we do not accept URL if the first occurence of alias is presented somewhere in the query string - var queryIndex = urlToMatch.IndexOf("?", StringComparison.InvariantCultureIgnoreCase); - if (queryIndex >= 0 && queryIndex < aliasIndex) - { - // alias is in the query string, go to the next alias - continue; - } - - // we are fine here, lets prepare URL to be validated in regex - urlToMatch = urlToMatch.ReplaceIgnoreCase(alias, "_ALIAS_"); - } - - // check whether requested URL has the right URL format containing existing alias - // i.e. url is http://dnndev.me/site1/query?string=test, alias is dnndev.me/site1 - // in the below expression we will validate following value http://_ALIAS_/query?string=test - var aliasMatch = AliasUrlRegex.Match(urlToMatch); - if (aliasMatch.Success) - { - // check for mobile browser and matching - var aliasEx = (PortalAliasInfo)portalAliases[alias]; - redirectAlias = aliasEx.Redirect; - if (redirectAlias) - { - wrongAlias = alias; - } - - isPrimaryAlias = aliasEx.IsPrimary; - aliasInfo = aliasEx; - break; - } - } - - return aliasInfo; - } - - private void ProcessRequest( - HttpContext context, - Uri requestUri, - bool useFriendlyUrls, - UrlAction result, - FriendlyUrlSettings settings, - bool allowSettingsChange, - Guid parentTraceId) - { - bool finished = false; - bool showDebug = false; - bool postRequest = false; - - HttpRequest request = context.Request; - HttpResponse response = context.Response; - string requestType = request.RequestType; - NameValueCollection queryStringCol = request.QueryString; - - try - { - string fullUrl, querystring; - - // 699: get the full url based on the request and the quersytring, rather than the requestUri.ToString() - // there is a difference in encoding, which can corrupt results when an encoded value is in the querystring - RewriteController.GetUrlWithQuerystring(request, requestUri, out fullUrl, out querystring); - - showDebug = CheckForDebug(request, queryStringCol, settings.AllowDebugCode); - string ignoreRegex = settings.IgnoreRegex; - bool ignoreRequest = IgnoreRequest(result, fullUrl, ignoreRegex, request); - bool redirectAlias = false; - if (!ignoreRequest) - { - // set original path - context.Items["UrlRewrite:OriginalUrl"] = requestUri.AbsoluteUri; - - // set the path of the result object, and determine if a redirect is allowed on this request - result.SetOriginalPath(requestUri.ToString(), settings); - - // 737 : set the mobile browser - result.SetBrowserType(request, response, settings); - - // add to context - context.Items["UrlRewrite:BrowserType"] = result.BrowserType.ToString(); - - // 839 : split out this check - result.SetRedirectAllowed(result.OriginalPath, settings); - - // find the portal alias first - string wrongAlias; - bool isPrimaryAlias; - var requestedAlias = this.GetPortalAlias(settings, fullUrl, out redirectAlias, out isPrimaryAlias, out wrongAlias); - - if (requestedAlias != null) - { - // 827 : now get the correct settings for this portal (if not a test request) - // 839 : separate out redirect check as well and move above first redirect test (ConfigurePortalAliasRedirect) - if (allowSettingsChange) - { - settings = new FriendlyUrlSettings(requestedAlias.PortalID); - result.SetRedirectAllowed(result.OriginalPath, settings); - } - - result.PortalAlias = requestedAlias; - result.PrimaryAlias = requestedAlias; // this is the primary alias - result.PortalId = requestedAlias.PortalID; - result.CultureCode = requestedAlias.CultureCode; - - // get the portal alias mapping for this portal - result.PortalAliasMapping = PortalSettingsController.Instance().GetPortalAliasMappingMode(requestedAlias.PortalID); - - // if requested alias wasn't the primary, we have a replacement, redirects are allowed and the portal alias mapping mode is redirect - // then do a redirect based on the wrong portal - if ((redirectAlias && wrongAlias != null) && result.RedirectAllowed && result.PortalAliasMapping != PortalSettings.PortalAliasMapping.Redirect) - { - // this is the alias, we are going to enforce it as the primary alias - result.PortalAlias = requestedAlias; - result.PrimaryAlias = requestedAlias; - - // going to redirect this alias because it is incorrect - // or do we just want to mark as 'check for 301??' - redirectAlias = ConfigurePortalAliasRedirect( - ref result, - wrongAlias, - requestedAlias.HTTPAlias, - false, - settings.InternalAliasList, - settings); - } - else - { - // do not redirect the wrong alias, but set the primary alias value - if (wrongAlias != null) - { - // get the portal alias info for the requested alias (which is the wrong one) - // and set that as the alias, but also set the found alias as the primary - PortalAliasInfo wrongAliasInfo = PortalAliasController.Instance.GetPortalAlias(wrongAlias); - if (wrongAliasInfo != null) - { - result.PortalAlias = wrongAliasInfo; - result.PrimaryAlias = requestedAlias; - } - } - } - } - } - - ignoreRegex = settings.IgnoreRegex; - ignoreRequest = IgnoreRequest(result, fullUrl, ignoreRegex, request); - if (!ignoreRequest) - { - // check to see if a post request - if (request.RequestType == "POST") - { - postRequest = true; - } - - // check the portal alias again. This time, in more depth now that the portal Id is known - // this check handles browser types/language specific aliases & mobile aliases - string primaryHttpAlias; - if (!redirectAlias && this.IsPortalAliasIncorrect(context, request, requestUri, result, queryStringCol, settings, parentTraceId, out primaryHttpAlias)) - { - // it was an incorrect alias - PortalAliasInfo primaryAlias = PortalAliasController.Instance.GetPortalAlias(primaryHttpAlias); - if (primaryAlias != null) - { - result.PrimaryAlias = primaryAlias; - } - - // try and redirect the alias if the settings allow it - redirectAlias = RedirectPortalAlias(primaryHttpAlias, ref result, settings); - } - - if (redirectAlias) - { - // not correct alias for portal : will be redirected - // perform a 301 redirect if one has already been found - response.AppendHeader("X-Redirect-Reason", result.Reason.ToString().Replace("_", " ") + " Requested"); - response.RedirectPermanent(result.FinalUrl, false); - finished = true; - } - - if (!finished) - { - // Check to see if this to be rewritten into default.aspx?tabId=nn format - // this call is the main rewriting matching call. It makes the decision on whether it is a - // physical file, whether it is toe be rewritten or redirected by way of a stored rule - - // Check if we have a standard url - var uri = new Uri(fullUrl); - if (uri.PathAndQuery.StartsWith("/" + Globals.glbDefaultPage, StringComparison.InvariantCultureIgnoreCase)) - { - result.DoRewrite = true; - result.Action = ActionType.CheckFor301; - result.RewritePath = Globals.glbDefaultPage + uri.Query; - } - else - { - bool isPhysicalResource; - CheckForRewrite(fullUrl, querystring, result, useFriendlyUrls, queryStringCol, settings, out isPhysicalResource, parentTraceId); - } - - // return 404 if there is no portal alias for a rewritten request - if (result.DoRewrite && result.PortalAlias == null) - { - // 882 : move this logic in from where it was before to here - // so that non-rewritten requests don't trip over it - // no portal alias found for the request : that's a 404 error - result.Action = ActionType.Output404; - result.Reason = RedirectReason.No_Portal_Alias; - - Handle404OrException(settings, context, null, result, false, showDebug); - finished = true; // cannot fulfil request unless correct portal alias specified - } - } - - // now we may know the TabId. If the current alias is not the same as the primary alias, - // we should check if the current alias is indeed a valid custom alias for the current tab. - if (result.TabId > 0 && result.HttpAlias != result.PrimaryAlias.HTTPAlias && !CheckIfAliasIsCurrentTabCustomTabAlias(ref result, settings)) - { - // it was an incorrect alias - // try and redirect the alias if the settings allow it - if (RedirectPortalAlias(result.PrimaryAlias.HTTPAlias, ref result, settings)) - { - // not correct alias for tab : will be redirected - // perform a 301 redirect if one has already been found - response.AppendHeader("X-Redirect-Reason", result.Reason.ToString().Replace("_", " ") + " Requested"); - response.RedirectPermanent(result.FinalUrl, false); - finished = true; - } - } - - if (!finished && result.DoRewrite) - { - // check the identified portal alias details for any extra rewrite information required - // this includes the culture and the skin, which can be placed into the rewrite path - // This logic is here because it will catch any Urls which are excluded from rewriting - var primaryAliases = PortalAliasController.Instance.GetPortalAliasesByPortalId(result.PortalId).ToList(); - - if (result.PortalId > -1 && result.HttpAlias != null) - { - string culture; - string skin; - BrowserTypes browserType; - primaryAliases.GetSettingsByPortalIdAndAlias( - result.PortalId, - result.HttpAlias, - out culture, - out browserType, - out skin); - - // add language code to path if it exists (not null) and if it's not already there - string rewritePath = result.RewritePath; - if (RewriteController.AddLanguageCodeToRewritePath(ref rewritePath, culture)) - { - result.CultureCode = culture; - } - - // 852: add skinSrc to path if it exists and if it's not already there - string debugMessage; - RewriteController.AddSkinToRewritePath(result.TabId, result.PortalId, ref rewritePath, skin, out debugMessage); - result.RewritePath = rewritePath; // reset back from ref temp var - if (debugMessage != null) - { - result.DebugMessages.Add(debugMessage); - } - } - } - - if (!finished && result.DoRewrite) - { - // if so, do the rewrite - if (result.RewritePath.StartsWith(result.Scheme) || result.RewritePath.StartsWith(Globals.glbDefaultPage) == false) - { - if (result.RewritePath.Contains(Globals.glbDefaultPage) == false) - { - RewriterUtils.RewriteUrl(context, "~/" + result.RewritePath); - } - else - { - // if there is no TabId and we have the domain - if (!result.RewritePath.ToLowerInvariant().Contains("tabId=")) - { - RewriterUtils.RewriteUrl(context, "~/" + result.RewritePath); - } - else - { - RewriterUtils.RewriteUrl(context, result.RewritePath); - } - } - } - else - { - if (MvcUrlRewriterController.IsMvc(result, queryStringCol, context, result.TabId, result.PortalId)) - { - RewriterUtils.RewriteUrl(context, "~/" + result.RewritePath.Replace(Globals.glbDefaultPage, "DesktopModules/Default/Page/" + result.TabId + "/" + result.CultureCode)); - } - else - { - RewriterUtils.RewriteUrl(context, "~/" + result.RewritePath); - } - } - } - - // confirm which portal the request is for - if (!finished) - { - this.IdentifyPortalAlias(context, request, requestUri, result, queryStringCol, settings, parentTraceId); - if (result.Action == ActionType.Redirect302Now) - { - // performs a 302 redirect if requested - response.AppendHeader("X-Redirect-Reason", result.Reason.ToString().Replace("_", " ") + " Requested"); - response.Redirect(result.FinalUrl, false); - finished = true; - } - else - { - if (result.Action == ActionType.Redirect301 && !string.IsNullOrEmpty(result.FinalUrl)) - { - finished = true; - - // perform a 301 redirect if one has already been found - response.AppendHeader("X-Redirect-Reason", result.Reason.ToString().Replace("_", " ") + " Requested"); - response.RedirectPermanent(result.FinalUrl, false); - } - } - } - - if (!finished) - { - // check to see if this tab has an external url that should be forwared or not - finished = CheckForTabExternalForwardOrRedirect(context, ref result, response, settings, parentTraceId); - } - - // check for a parameter redirect (we had to do all the previous processing to know we are on the right portal and identify the tabid) - // if the CustomParmRewrite flag is set, it means we already rewrote these parameters, so they have to be correct, and aren't subject to - // redirection. The only reason to do a custom parm rewrite is to interpret already-friendly parameters - if (!finished - && !postRequest /* either request is null, or it's not a post - 551 */ - && result.HttpAlias != null /* must have a http alias */ - && !result.CustomParmRewrite && /* not custom rewritten parms */ - ((settings.EnableCustomProviders && - RedirectController.CheckForModuleProviderRedirect(requestUri, ref result, queryStringCol, settings, parentTraceId)) - - // 894 : allow disable of all custom providers - || - RedirectController.CheckForParameterRedirect(requestUri, ref result, queryStringCol, settings))) - { - // 301 redirect to new location based on parameter match - if (response != null) - { - switch (result.Action) - { - case ActionType.Redirect301: - response.AppendHeader("X-Redirect-Reason", result.Reason.ToString().Replace("_", " ") + " Requested"); - response.RedirectPermanent(result.FinalUrl); - break; - case ActionType.Redirect302: - response.AppendHeader("X-Redirect-Reason", result.Reason.ToString().Replace("_", " ") + " Requested"); - response.Redirect(result.FinalUrl); - break; - case ActionType.Output404: - response.AppendHeader("X-Result-Reason", result.Reason.ToString().Replace("_", " ")); - Handle404OrException(settings, context, null, result, true, showDebug); - break; - } - } - - finished = true; - } - - // shifted until after the 301 redirect code to allow redirects to be checked for pages which have no rewrite value - // look for a 404 result from the rewrite, because of a deleted page or rule - if (!finished && result.Action == ActionType.Output404) - { - if (result.OriginalPath.Equals(result.HttpAlias, StringComparison.InvariantCultureIgnoreCase) - && result.PortalAlias != null - && result.Reason != RedirectReason.Deleted_Page - && result.Reason != RedirectReason.Disabled_Page) - { - // Request for domain with no page identified (and no home page set in Site Settings) - result.Action = ActionType.Continue; - } - else - { - finished = true; - response.AppendHeader("X-Result-Reason", result.Reason.ToString().Replace("_", " ")); - - if (showDebug) - { - ShowDebugData(context, requestUri.AbsoluteUri, result, null); - } - - // show the 404 page if configured - result.Reason = RedirectReason.Requested_404; - Handle404OrException(settings, context, null, result, true, showDebug); - } - } - - if (!finished) - { - // add the portal settings to the app context if the portal alias has been found and is correct - if (result.PortalId != -1 && result.PortalAlias != null) - { - // for invalid tab id other than -1, show the 404 page - TabInfo tabInfo = TabController.Instance.GetTab(result.TabId, result.PortalId, false); - if (tabInfo == null && result.TabId > -1) - { - finished = true; - - if (showDebug) - { - ShowDebugData(context, requestUri.AbsoluteUri, result, null); - } - - // show the 404 page if configured - result.Action = ActionType.Output404; - result.Reason = RedirectReason.Requested_404; - response.AppendHeader("X-Result-Reason", result.Reason.ToString().Replace("_", " ")); - Handle404OrException(settings, context, null, result, true, showDebug); - } - else - { - Globals.SetApplicationName(result.PortalId); - - // load the PortalSettings into current context - var portalSettings = new PortalSettings(result.TabId, result.PortalAlias); - - // set the primary alias if one was specified - if (result.PrimaryAlias != null) - { - portalSettings.PrimaryAlias = result.PrimaryAlias; - } - - if (result.CultureCode != null && fullUrl.Contains(result.CultureCode) && - portalSettings.DefaultLanguage == result.CultureCode) - { - // when the request culture code is the same as the portal default, check for a 301 redirect, because we try and remove the language from the url where possible - result.Action = ActionType.CheckFor301; - } - - int portalHomeTabId = portalSettings.HomeTabId; - if (context != null && portalSettings != null && !context.Items.Contains("PortalSettings")) - { - context.Items.Add("PortalSettings", portalSettings); - - // load PortalSettings and HostSettings dictionaries into current context - // specifically for use in DotNetNuke.Web.Client, which can't reference DotNetNuke.dll to get settings the normal way - context.Items.Add("PortalSettingsDictionary", PortalController.Instance.GetPortalSettings(portalSettings.PortalId)); - context.Items.Add("HostSettingsDictionary", HostController.Instance.GetSettingsDictionary()); - } - - // check if a secure redirection is needed - // this would be done earlier in the piece, but need to know the portal settings, tabid etc before processing it - bool redirectSecure = this.CheckForSecureRedirect(portalSettings, requestUri, result, queryStringCol, settings); - if (redirectSecure) - { - if (response != null) - { - // 702 : don't check final url until checked for null reference first - if (result.FinalUrl != null) - { - if (result.FinalUrl.StartsWith("https://")) - { - if (showDebug) - { - /* - string debugMsg = "{0}, {1}, {2}, {3}, {4}"; - string productVer = System.Reflection.Assembly.GetExecutingAssembly().GetName(false).Version.ToString(); - response.AppendHeader("X-" + prodName + "-Debug", string.Format(debugMsg, requestUri.AbsoluteUri, result.FinalUrl, result.RewritePath, result.Action, productVer)); - */ - ShowDebugData(context, fullUrl, result, null); - } - - response.AppendHeader("X-Redirect-Reason", result.Reason.ToString().Replace("_", " ") + " Requested"); - response.RedirectPermanent(result.FinalUrl); - finished = true; - } - else - { - if (settings.SSLClientRedirect) - { - // redirect back to http version, use client redirect - response.Clear(); - - // add a refresh header to the response - response.AddHeader("Refresh", "0;URL=" + result.FinalUrl); - - // add the clientside javascript redirection script - var finalUrl = HttpUtility.HtmlEncode(result.FinalUrl); - response.Write(""); - response.Write(@""); - response.Write(""); - if (showDebug) - { - /* - string debugMsg = "{0}, {1}, {2}, {3}, {4}"; - string productVer = System.Reflection.Assembly.GetExecutingAssembly().GetName(false).Version.ToString(); - response.AppendHeader("X-" + prodName + "-Debug", string.Format(debugMsg, requestUri.AbsoluteUri, result.FinalUrl, result.RewritePath, result.Action, productVer)); - */ - ShowDebugData(context, fullUrl, result, null); - } - - // send the response - // 891 : reinstate the response.end to stop the entire page loading - response.End(); - finished = true; - } - else - { - response.AppendHeader("X-Redirect-Reason", result.Reason.ToString().Replace("_", " ") + " Requested"); - response.RedirectPermanent(result.FinalUrl); - finished = true; - } - } - } - } - } - else - { - // check for, and do a 301 redirect if required - if (CheckForRedirects(requestUri, fullUrl, queryStringCol, result, requestType, settings, portalHomeTabId)) - { - if (response != null) - { - if (result.Action == ActionType.Redirect301) - { - response.AppendHeader("X-Redirect-Reason", result.Reason.ToString().Replace("_", " ") + " Requested"); - response.RedirectPermanent(result.FinalUrl, false); - finished = true; - } - else if (result.Action == ActionType.Redirect302) - { - response.AppendHeader("X-Redirect-Reason", result.Reason.ToString().Replace("_", " ") + " Requested"); - response.Redirect(result.FinalUrl, false); - finished = true; - } - } - } - else - { - // 612 : Don't clear out a 302 redirect if set - if (result.Action != ActionType.Redirect302 && - result.Action != ActionType.Redirect302Now) - { - result.Reason = RedirectReason.Not_Redirected; - result.FinalUrl = null; - } - } - } - } - } - else - { - // alias does not exist in database - // and all attempts to find another have failed - // this should only happen if the HostPortal does not have any aliases - result.Action = ActionType.Output404; - if (response != null) - { - if (showDebug) - { - ShowDebugData(context, fullUrl, result, null); - } - - result.Reason = RedirectReason.Requested_404; - - // 912 : change 404 type to transfer to allow transfer to main portal in single-portal installs - Handle404OrException(settings, context, null, result, true, showDebug); - finished = true; - } - } - } - - // 404 page ?? - if (settings.TabId404 > 0 && settings.TabId404 == result.TabId) - { - string status = queryStringCol["status"]; - if (status == "404") - { - // respond with a 404 error - result.Action = ActionType.Output404; - result.Reason = RedirectReason.Requested_404_In_Url; - Handle404OrException(settings, context, null, result, true, showDebug); - } - } - else - { - if (result.DoRewrite == false && result.CanRewrite != StateBoolean.False && !finished && - result.Action == ActionType.Continue) - { - // 739 : catch no-extension 404 errors - string pathWithNoQs = result.OriginalPath; - if (pathWithNoQs.Contains("?")) - { - pathWithNoQs = pathWithNoQs.Substring(0, pathWithNoQs.IndexOf("?", StringComparison.Ordinal)); - } - - if (!pathWithNoQs.Substring(pathWithNoQs.Length - 5, 5).Contains(".")) - { - // no page extension, output a 404 if the Url is not found - // 766 : check for physical path before passing off as a 404 error - // 829 : change to use action physical path - // 893 : filter by regex pattern to exclude urls which are valid, but show up as extensionless - if ((request != null && Directory.Exists(result.PhysicalPath)) - || - Regex.IsMatch(pathWithNoQs, settings.ValidExtensionlessUrlsRegex, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)) - { - // do nothing : it's a request for a valid physical path, maybe including a default document - result.VirtualPath = StateBoolean.False; - } - else - { - if (!Globals.ServicesFrameworkRegex.IsMatch(context.Request.RawUrl)) - { - // no physical path, intercept the request and hand out a 404 error - result.Action = ActionType.Output404; - result.Reason = RedirectReason.Page_404; - result.VirtualPath = StateBoolean.True; - - // add in a message to explain this 404, becaue it can be cryptic - result.DebugMessages.Add("404 Reason : Not found and no extension"); - Handle404OrException(settings, context, null, result, true, showDebug); - } - } - } - } - } - - // show debug messages after extensionless-url special 404 handling - if (showDebug) - { - ShowDebugData(context, fullUrl, result, null); - } - } - } - catch (ThreadAbortException) - { - // do nothing, a threadAbortException will have occured from using a server.transfer or response.redirect within the code block. This is the highest - // level try/catch block, so we handle it here. - Thread.ResetAbort(); - } - catch (Exception ex) - { - if (showDebug) - { - Services.Exceptions.Exceptions.LogException(ex); - } - - if (response != null) - { - if (showDebug) - { - ShowDebugData(context, requestUri.AbsoluteUri, result, ex); - } - - if (result != null) - { - result.Ex = ex; - result.Reason = RedirectReason.Exception; - } - - Handle404OrException(settings, context, ex, result, false, showDebug); - } - else - { - if (result != null && result.DebugMessages != null) - { - result.DebugMessages.Add("Exception: " + ex.Message); - result.DebugMessages.Add("Stack Trace: " + ex.StackTrace); - } - - throw; - } - } - finally - { - // 809 : add in new code copied from urlRewrite class in standard Url Rewrite module - if (context != null && context.Items["FirstRequest"] != null) - { - context.Items.Remove("FirstRequest"); - - // process any messages in the eventQueue for the Application_Start_FIrstRequest event - EventQueueController.ProcessMessages("Application_Start_FirstRequest"); - } - } - } - - private bool CheckForSecureRedirect( - PortalSettings portalSettings, - Uri requestUri, - UrlAction result, - NameValueCollection queryStringCol, - FriendlyUrlSettings settings) - { - bool redirectSecure = false; - string url = requestUri.ToString(); - - // 889 : don't run secure redirect code for physical resources or requests that aren't a rewritten Url - if (result.IsPhysicalResource == false && result.TabId >= 0) - { - // no secure redirection for physical resources, only tab-specific requests can be redirected for ssl connections - if (portalSettings.ActiveTab != null) - { - result.DebugMessages.Add("ActiveTab: " + portalSettings.ActiveTab.TabID.ToString() + "/" + - portalSettings.ActiveTab.TabName + " IsSecure: " + - portalSettings.ActiveTab.IsSecure.ToString()); - - switch (portalSettings.SSLSetup) - { - case Abstractions.Security.SiteSslSetup.On: - if (!result.IsSecureConnection) - { - redirectSecure = true; - url = url.Replace("http://", "https://"); - } - - break; - case Abstractions.Security.SiteSslSetup.Advanced: - // 717 : check page is secure, connection is not secure - // 952 : support SSl Offloading in DNN 6.2+ - if (portalSettings.ActiveTab.IsSecure && !result.IsSecureConnection && !result.IsSSLOffloaded) - { - redirectSecure = true; - string stdUrl = portalSettings.STDURL; - string sslUrl = portalSettings.SSLURL; - if (string.IsNullOrEmpty(result.HttpAlias) == false) - { - stdUrl = result.HttpAlias; - } - - url = url.Replace("http://", "https://"); - url = this.ReplaceDomainName(url, stdUrl, sslUrl); - } - - if (portalSettings.SSLEnforced) - { - // Prevent browser's mixed-content error in case we open a secure PopUp or a secure iframe - // from an unsecure page - if (!portalSettings.ActiveTab.IsSecure && - result.IsSecureConnection && - !UrlUtils.IsPopUp(url)) - { - // has connection already been forced to secure? - if (queryStringCol["ssl"] == null) - { - // no? well this page shouldn't be secure - string stdUrl = portalSettings.STDURL; - string sslUrl = portalSettings.SSLURL; - url = url.Replace("https://", "http://"); - url = this.ReplaceDomainName(url, sslUrl, stdUrl); - redirectSecure = true; - } - } - } - - break; - } - } - - if (redirectSecure) - { - // now check to see if excluded. Why now? because less requests are made to redirect secure, - // so we don't have to check the exclusion as often. - bool exclude = false; - string doNotRedirectSecureRegex = settings.DoNotRedirectSecureRegex; - if (!string.IsNullOrEmpty(doNotRedirectSecureRegex)) - { - // match the raw url - exclude = Regex.IsMatch(result.RawUrl, doNotRedirectSecureRegex, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); - } - - if (!exclude) - { - result.Action = ActionType.Redirect302Now; - result.Reason = RedirectReason.Secure_Page_Requested; - - // 760 : get the culture specific home page tabid for a redirect comparison - int homePageTabId = portalSettings.HomeTabId; - homePageTabId = TabPathHelper.GetHomePageTabIdForCulture( - portalSettings.DefaultLanguage, - portalSettings.PortalId, - result.CultureCode, - homePageTabId); - if (result.TabId == homePageTabId) - { - // replace the /default.aspx in the Url if it was found - url = DefaultPageRegex.Replace(url, "/"); - } - - result.FinalUrl = url; - } - else - { - // 702 : change return value if exclusion has occured - redirectSecure = false; - } - } - } - - return redirectSecure; - } - - private string ReplaceDomainName(string url, string replaceDomain, string withDomain) - { - if (replaceDomain != string.Empty && withDomain != string.Empty) - { - // 951 : change find/replace routine to regex for more accurate replacement - // (previous method gives false positives if the SSL Url is contained within the STD url) - string find = @"(?<=https?://)" + Regex.Escape(withDomain); - if (Regex.IsMatch(url, find, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) == false) - { - string replaceFind = @"(?<=https?://)" + Regex.Escape(replaceDomain); - url = Regex.Replace(url, replaceFind, withDomain, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); - } - } - - return url; - } - - private void IdentifyPortalAlias( - HttpContext context, - HttpRequest request, - Uri requestUri, - UrlAction result, - NameValueCollection queryStringCol, - FriendlyUrlSettings settings, - Guid parentTraceId) - { - // get the domain name of the request, if it isn't already supplied - if (request != null && string.IsNullOrEmpty(result.DomainName)) - { - result.DomainName = Globals.GetDomainName(request); // parse the domain name out of the request - } - - // get tabId from querystring ( this is mandatory for maintaining portal context for child portals ) - if (queryStringCol["tabid"] != null) - { - string raw = queryStringCol["tabid"]; - int tabId; - if (int.TryParse(raw, out tabId)) - { - result.TabId = tabId; - } - else - { - // couldn't parse tab id - // split in two? - string[] tabids = raw.Split(','); - if (tabids.GetUpperBound(0) > 0) - { - // hmm more than one tabid - if (int.TryParse(tabids[0], out tabId)) - { - result.TabId = tabId; - - // but we want to warn against this! - var ex = - new Exception( - "Illegal request exception : Two TabId parameters provided in a single request: " + - requestUri); - UrlRewriterUtils.LogExceptionInRequest(ex, "Not Set", result); - - result.Ex = ex; - } - else - { - // yeah, nothing, divert to 404 - result.Action = ActionType.Output404; - var ex = - new Exception( - "Illegal request exception : TabId parameters in query string, but invalid TabId requested : " + - requestUri); - UrlRewriterUtils.LogExceptionInRequest(ex, "Not Set", result); - result.Ex = ex; - } - } - } - } - - // get PortalId from querystring ( this is used for host menu options as well as child portal navigation ) - if (queryStringCol["portalid"] != null) - { - string raw = queryStringCol["portalid"]; - int portalId; - if (int.TryParse(raw, out portalId)) - { - // 848 : if portal already found is different to portal id in querystring, then load up different alias - // this is so the portal settings will be loaded correctly. - if (result.PortalId != portalId) - { - // portal id different to what we expected - result.PortalId = portalId; - - // check the loaded portal alias, because it might be wrong - if (result.PortalAlias != null && result.PortalAlias.PortalID != portalId) - { - // yes, the identified portal alias is wrong. Find the correct alias for this portal - PortalAliasInfo pa = TabIndexController.GetPortalAliasByPortal(portalId, result.DomainName); - if (pa != null) - { - // note: sets portal id and portal alias - result.PortalAlias = pa; - } - } - } - } - } - else - { - // check for a portal alias if there's no portal Id in the query string - // check for absence of captcha value, because the captcha string re-uses the alias querystring value - if (queryStringCol["alias"] != null && queryStringCol["captcha"] == null) - { - string alias = queryStringCol["alias"]; - PortalAliasInfo portalAlias = PortalAliasController.Instance.GetPortalAlias(alias); - if (portalAlias != null) - { - // ok the portal alias was found by the alias name - // check if the alias contains the domain name - if (alias.Contains(result.DomainName) == false) - { - // replaced to the domain defined in the alias - if (request != null) - { - string redirectDomain = Globals.GetPortalDomainName(alias, request, true); - - // retVal.Url = redirectDomain; - result.FinalUrl = redirectDomain; - result.Action = ActionType.Redirect302Now; - result.Reason = RedirectReason.Alias_In_Url; - } - } - else - { - // the alias is the same as the current domain - result.HttpAlias = portalAlias.HTTPAlias; - result.PortalAlias = portalAlias; - result.PortalId = portalAlias.PortalID; - - // don't use this crap though - we don't want ?alias=portalAlias in our Url - if (result.RedirectAllowed) - { - string redirect = requestUri.Scheme + Uri.SchemeDelimiter + result.PortalAlias.HTTPAlias + - "/"; - result.Action = ActionType.Redirect301; - result.FinalUrl = redirect; - result.Reason = RedirectReason.Unfriendly_Url_Child_Portal; - } - } - } - } - } - - // first try and identify the portal using the tabId, but only if we identified this tab by looking up the tabid - // from the original url - // 668 : error in child portal redirects to child portal home page because of mismatch in tab/domain name - if (result.TabId != -1 && result.FriendlyRewrite == false) - { - // get the alias from the tabid, but only if it is for a tab in that domain - // 2.0 : change to compare retrieved alias to the already-set httpAlias - string httpAliasFromTab = PortalAliasController.GetPortalAliasByTab(result.TabId, result.DomainName); - if (httpAliasFromTab != null) - { - // 882 : account for situation when portalAlias is null. - if ((result.PortalAlias != null && string.Compare(result.PortalAlias.HTTPAlias, httpAliasFromTab, StringComparison.OrdinalIgnoreCase) != 0) - || result.PortalAlias == null) - { - // 691 : change logic to force change in portal alias context rather than force back. - // This is because the tabid in the query string should take precedence over the portal alias - // to handle parent.com/default.aspx?tabid=xx where xx lives in parent.com/child/ - var tab = TabController.Instance.GetTab(result.TabId, Null.NullInteger, false); - - // when result alias is null or result alias is different from tab-identified portalAlias - if (tab != null && (result.PortalAlias == null || tab.PortalID != result.PortalAlias.PortalID)) - { - // the tabid is different to the identified portalid from the original alias identified - // so get a new alias - PortalAliasInfo tabPortalAlias = PortalAliasController.Instance.GetPortalAlias(httpAliasFromTab, tab.PortalID); - if (tabPortalAlias != null) - { - result.PortalId = tabPortalAlias.PortalID; - result.PortalAlias = tabPortalAlias; - result.Action = ActionType.CheckFor301; - result.Reason = RedirectReason.Wrong_Portal; - } - } - } - } - } - - // if no alias, try and set by using the identified http alias or domain name - if (result.PortalAlias == null) - { - if (!string.IsNullOrEmpty(result.HttpAlias)) - { - result.PortalAlias = PortalAliasController.Instance.GetPortalAlias(result.HttpAlias); - } - else - { - result.PortalAlias = PortalAliasController.Instance.GetPortalAlias(result.DomainName); - if (result.PortalAlias == null && result.DomainName.EndsWith("/")) - { - result.DomainName = result.DomainName.TrimEnd('/'); - result.PortalAlias = PortalAliasController.Instance.GetPortalAlias(result.DomainName); - } - } - } - - if (result.PortalId == -1) - { - if (!requestUri.LocalPath.EndsWith(Globals.glbDefaultPage, StringComparison.InvariantCultureIgnoreCase)) - { - // allows requests for aspx pages in custom folder locations to be processed - return; - } - - // the domain name was not found so try using the host portal's first alias - if (Host.HostPortalID != -1) - { - result.PortalId = Host.HostPortalID; - - // use the host portal, but replaced to the host portal home page - var aliases = PortalAliasController.Instance.GetPortalAliasesByPortalId(result.PortalId).ToList(); - if (aliases.Count > 0) - { - string alias = null; - - // get the alias as the chosen portal alias for the host portal based on the result culture code - var cpa = aliases.GetAliasByPortalIdAndSettings(result.PortalId, result, result.CultureCode, settings); - if (cpa != null) - { - alias = cpa.HTTPAlias; - } - - if (alias != null) - { - result.Action = ActionType.Redirect301; - result.Reason = RedirectReason.Host_Portal_Used; - string destUrl = MakeUrlWithAlias(requestUri, alias); - destUrl = CheckForSiteRootRedirect(alias, destUrl); - result.FinalUrl = destUrl; - } - else - { - // Get the first Alias for the host portal - result.PortalAlias = aliases[result.PortalId]; - string url = MakeUrlWithAlias(requestUri, result.PortalAlias); - if (result.TabId != -1) - { - url += requestUri.Query; - } - - result.FinalUrl = url; - result.Reason = RedirectReason.Host_Portal_Used; - result.Action = ActionType.Redirect302Now; - } - } - } - } - - // double check to make sure we still have the correct alias now that all other information is known (ie tab, portal, culture) - // 770 : redirect alias based on tab id when custom alias used - if (result.TabId == -1 && result.Action == ActionType.CheckFor301 && - result.Reason == RedirectReason.Custom_Tab_Alias) - { - // here because the portal alias matched, but no tab was found, and because there are custom tab aliases used for this portal - // need to redirect back to the chosen portal alias and keep the current path. - string wrongAlias = result.HttpAlias; // it's a valid alias, but only for certain tabs - var primaryAliases = PortalAliasController.Instance.GetPortalAliasesByPortalId(result.PortalId).ToList(); - if (primaryAliases != null && result.PortalId > -1) - { - // going to look for the correct alias based on the culture of the request - string requestCultureCode = result.CultureCode; - - // if that didn't work use the default language of the portal - if (requestCultureCode == null) - { - // this might end up in a double redirect if the path of the Url is for a specific language as opposed - // to a path belonging to the default language domain - PortalInfo portal = PortalController.Instance.GetPortal(result.PortalId); - if (portal != null) - { - requestCultureCode = portal.DefaultLanguage; - } - } - - // now that the culture code is known, look up the correct portal alias for this portalid/culture code - var cpa = primaryAliases.GetAliasByPortalIdAndSettings(result.PortalId, result, requestCultureCode, settings); - if (cpa != null) - { - // if an alias was found that matches the request and the culture code, then run with that - string rightAlias = cpa.HTTPAlias; - - // will cause a redirect to the primary portal alias - we know now that there was no custom alias tab - // found, so it's just a plain wrong alias - ConfigurePortalAliasRedirect(ref result, wrongAlias, rightAlias, true, settings.InternalAliasList, settings); - } - } - } - else - { - // then check to make sure it's the chosen portal alias for this portal - // 627 : don't do it if we're redirecting to the host portal - if (result.RedirectAllowed && result.Reason != RedirectReason.Host_Portal_Used) - { - string primaryAlias; - - // checking again in case the rewriting operation changed the values for the valid portal alias - bool incorrectAlias = this.IsPortalAliasIncorrect(context, request, requestUri, result, queryStringCol, settings, parentTraceId, out primaryAlias); - if (incorrectAlias) - { - RedirectPortalAlias(primaryAlias, ref result, settings); - } - } - } - - // check to see if we have to avoid the core 302 redirect for the portal alias that is in the /defualt.aspx - // for child portals - // exception to that is when a custom alias is used but no rewrite has taken place - if (result.DoRewrite == false && (result.Action == ActionType.Continue - || - (result.Action == ActionType.CheckFor301 && - result.Reason == RedirectReason.Custom_Tab_Alias))) - { - string aliasQuerystring; - bool isChildAliasRootUrl = CheckForChildPortalRootUrl(requestUri.AbsoluteUri, result, out aliasQuerystring); - if (isChildAliasRootUrl) - { - RewriteAsChildAliasRoot(context, result, aliasQuerystring, settings); - } - } - } - - private void SecurityCheck(HttpApplication app) - { - HttpRequest request = app.Request; - HttpServerUtility server = app.Server; - - // 675 : unnecessarily strict url validation - // URL validation - // check for ".." escape characters commonly used by hackers to traverse the folder tree on the server - // the application should always use the exact relative location of the resource it is requesting - var strURL = request.Url.AbsolutePath; - var strDoubleDecodeURL = server.UrlDecode(server.UrlDecode(request.Url.AbsolutePath)) ?? string.Empty; - if (UrlSlashesRegex.Match(strURL).Success || UrlSlashesRegex.Match(strDoubleDecodeURL).Success) - { - throw new HttpException(404, "Not Found"); - } - } - } -} diff --git a/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/Entities/Urls/MvcUrlRewriterController.cs b/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/Entities/Urls/MvcUrlRewriterController.cs deleted file mode 100644 index 8203f6bb238..00000000000 --- a/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/Entities/Urls/MvcUrlRewriterController.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Collections.Specialized; -using DotNetNuke.Entities.Portals; -using DotNetNuke.Entities.Tabs; -using DotNetNuke.Entities.Urls; -using System.Web; - -namespace DotNetNuke.Web.MvcUrlRewriter.Entities.Urls -{ - internal class MvcUrlRewriterController - { - internal static bool IsMvc(UrlAction result, NameValueCollection queryStringCol, HttpContext context, int tabId, int portalId) - { - var mvcCtls = new[] { /*"Module",*/ "Terms", "Privacy" }; - bool mvcCtl = false; - /* - bool mvcSkin = false; - if (context.Items.Contains("PortalSettings")) - { - var ps = (PortalSettings)context.Items["PortalSettings"]; - if (ps != null) - { - mvcSkin = !string.IsNullOrEmpty(PortalSettings.Current.ActiveTab.SkinSrc) && - PortalSettings.Current.ActiveTab.SkinSrc.EndsWith("mvc"); - } - } - */ - - if (result.RewritePath.Contains("&ctl=")) - { - foreach (var item in mvcCtls) - { - mvcCtl = mvcCtl || result.RewritePath.Contains("&ctl=" + item); - } - /* - if (mvcCtl && result.RewritePath.Contains("&ctl=Module")) - { - TabInfo tab = null; - if (tabId > 0 && portalId > -1) - { - tab = TabController.Instance.GetTab(tabId, portalId, false); - if (tab != null) - { - mvcCtl = tab.GetTags().Contains("mvc"); - } - } - - // mvcCtl = queryStringCol["ReturnURL"] != null && queryStringCol["ReturnURL"].EndsWith("mvc"); - } - */ - } - else - { - TabInfo tab = null; - if (tabId > 0 && portalId > -1) - { - tab = TabController.Instance.GetTab(tabId, portalId, false); - if (tab != null) - { - mvcCtl = tab.GetTags().Contains("mvc"); - } - } - } - - mvcCtl = mvcCtl && !result.RewritePath.Contains("mvcpage=no") && queryStringCol["mvcpage"] != "no"; - mvcCtl = mvcCtl || result.RewritePath.Contains("mvcpage=yes") || queryStringCol["mvcpage"] == "yes"; - return mvcCtl; - } - } -} diff --git a/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/HttpModules/MvcUrlRewriteModule.cs b/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/HttpModules/MvcUrlRewriteModule.cs deleted file mode 100644 index 7b37b878ed2..00000000000 --- a/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/HttpModules/MvcUrlRewriteModule.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Web; -using DotNetNuke.Web.MvcUrlRewriter.Entities.Urls; - -namespace DotNetNuke.Web.MvcUrlRewriter.HttpModules -{ - public class MvcUrlRewriteModule : IHttpModule - { - public void Dispose() - { - } - - public void Init(HttpApplication context) - { - var mvcRewriter = new MvcAdvancedUrlRewriter(); - context.BeginRequest += mvcRewriter.RewriteUrl; - } - } -} diff --git a/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/dnn.json b/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/dnn.json deleted file mode 100644 index e299f8b8841..00000000000 --- a/DNN Platform/DotNetNuke.Web.MvcUrlRewriter/dnn.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "projectType": "library", - "name": "Dnn_DotNetNukeWebMvcUrlRewriter", - "friendlyName": "Dnn DotNetNukeWebMvcUrlRewriter", - "description": "Dnn DotNetNukeWebMvcUrlRewriter Library", - "packageName": "Dnn_DotNetNukeWebMvcUrlRewriter", - "folder": "Dnn/DotNetNukeWebMvcUrlRewriter", - "library": {}, - "pathsAndFiles": { - "pathToAssemblies": "./bin", - "pathToScripts": "./Server/SqlScripts", - "assemblies": ["DotNetNuke.Web.MvcUrlRewriter.dll"], - "releaseFiles": [], - "zipName": "Dnn.DotNetNukeWebMvcUrlRewriter" - } -} diff --git a/DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/DefaultController.cs b/DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/DefaultController.cs index c926b68eaf4..8f8a26629ba 100644 --- a/DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/DefaultController.cs +++ b/DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/DefaultController.cs @@ -23,17 +23,13 @@ namespace DotNetNuke.Web.MvcWebsite.Controllers using DotNetNuke.Web.Client.ClientResourceManagement; using DotNetNuke.Web.MvcPipeline.Controllers; using DotNetNuke.Web.MvcPipeline.Exceptions; - using DotNetNuke.Web.MvcPipeline.Framework; using DotNetNuke.Web.MvcPipeline.Framework.JavascriptLibraries; + using DotNetNuke.Web.MvcPipeline.ModelFactories; using DotNetNuke.Web.MvcPipeline.Models; using DotNetNuke.Web.MvcPipeline.UI.Utilities; public class DefaultController : DnnPageController { - private static readonly Regex HeaderTextRegex = new Regex( - "])+name=('|\")robots('|\")", - RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled); - private readonly INavigationManager navigationManager; private readonly IContentSecurityPolicy contentSecurityPolicy; private readonly IPageModelFactory pageModelFactory; @@ -158,13 +154,11 @@ private void InitializePage(PageModel page) } } - // this.Response.Redirect(this.NavigationManager.NavigateURL(tab.TabID, Null.NullString, parameters.ToArray()), true); throw new MvcPageException("redirect to a specific tab based on name", this.navigationManager.NavigateURL(tab.TabID, Null.NullString, parameters.ToArray())); } else { // 404 Error - Redirect to ErrorPage - // Exceptions.ProcessHttpException(this.Request); throw new NotFoundException("redirect to a specific tab based on name - tab not found"); } } diff --git a/DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/PrivacyViewController.cs b/DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/PrivacyViewController.cs deleted file mode 100644 index 0702fa530e2..00000000000 --- a/DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/PrivacyViewController.cs +++ /dev/null @@ -1,20 +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.MvcWebsite.Controllers -{ - using System.Web.Mvc; - - using DotNetNuke.Entities.Portals; - using DotNetNuke.Services.Localization; - - public class PrivacyViewController : Controller - { - public ActionResult Invoke() - { - var privacy = Localization.GetSystemMessage(PortalSettings.Current, "MESSAGE_PORTAL_PRIVACY"); - return this.View("Index", string.Empty, privacy); - } - } -} diff --git a/DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/TermsViewController.cs b/DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/TermsViewController.cs deleted file mode 100644 index 9aca37156c3..00000000000 --- a/DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/TermsViewController.cs +++ /dev/null @@ -1,20 +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.MvcWebsite.Controllers -{ - using System.Web.Mvc; - - using DotNetNuke.Entities.Portals; - using DotNetNuke.Services.Localization; - - public class TermsViewController : Controller - { - public ActionResult Invoke() - { - var terms = Localization.GetSystemMessage(PortalSettings.Current, "MESSAGE_PORTAL_TERMS"); - return this.View("Index", string.Empty, terms); - } - } -} diff --git a/DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/ModuleActionsViewController.cs b/DNN Platform/DotNetNuke.Web.MvcWebsite/Controls/ModuleActionsControl.cs similarity index 77% rename from DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/ModuleActionsViewController.cs rename to DNN Platform/DotNetNuke.Web.MvcWebsite/Controls/ModuleActionsControl.cs index 5c6ce7fdee4..4ce26569431 100644 --- a/DNN Platform/DotNetNuke.Web.MvcWebsite/Controllers/ModuleActionsViewController.cs +++ b/DNN Platform/DotNetNuke.Web.MvcWebsite/Controls/ModuleActionsControl.cs @@ -2,13 +2,12 @@ // 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.MvcWebsite.Controllers +namespace DotNetNuke.Web.MvcWebsite.Controls { using System; using System.Collections.Generic; using System.Web.Mvc; using System.Web.Script.Serialization; - using DotNetNuke.Common; using DotNetNuke.Common.Utilities; using DotNetNuke.Entities.Modules; @@ -17,26 +16,29 @@ namespace DotNetNuke.Web.MvcWebsite.Controllers using DotNetNuke.Entities.Tabs; using DotNetNuke.Framework; using DotNetNuke.Framework.JavaScriptLibraries; + using DotNetNuke.Instrumentation; using DotNetNuke.Security; using DotNetNuke.Security.Permissions; + using DotNetNuke.Services.Installer.Log; using DotNetNuke.Services.Localization; using DotNetNuke.Services.Personalization; - using DotNetNuke.UI; - using DotNetNuke.UI.Modules; + using DotNetNuke.Web.Client; - using DotNetNuke.Web.Client.ClientResourceManagement; - using DotNetNuke.Web.MvcPipeline.Framework.JavascriptLibraries; - using DotNetNuke.Web.MvcPipeline.Models; + using DotNetNuke.Web.MvcPipeline.ModuleControl; + using DotNetNuke.Web.MvcPipeline.ModuleControl.Razor; + using DotNetNuke.Web.MvcPipeline.ModuleControl.Resources; + using DotNetNuke.Web.MvcPipeline.Utils; using DotNetNuke.Web.MvcWebsite.Models; - public class ModuleActionsViewController : Controller + public class ModuleActionsControl : RazorModuleControlBase, IResourcable { + private ILog Logger = LoggerSource.Instance.GetLogger(typeof(ModuleActionsControl)); private readonly List validIDs = new List(); private ModuleAction actionRoot; private Dictionary actionScripts = new Dictionary(); - public ModuleInstanceContext ModuleContext { get; private set; } + //public ModuleInstanceContext ModuleContext { get; private set; } public bool EditMode { @@ -106,21 +108,9 @@ protected ModuleActionCollection Actions } } - [ChildActionOnly] - public ActionResult Invoke(ControlViewModel input) + public override IRazorModuleResult Invoke() { - var moduleInfo = ModuleController.Instance.GetModule(input.ModuleId, input.TabId, false); - if (moduleInfo.ModuleControlId != input.ModuleControlId) - { - moduleInfo = moduleInfo.Clone(); - moduleInfo.ContainerPath = input.ContainerPath; - moduleInfo.ContainerSrc = input.ContainerSrc; - moduleInfo.ModuleControlId = input.ModuleControlId; - moduleInfo.PaneName = input.PanaName; - moduleInfo.IconFile = input.IconFile; - } - - this.ModuleContext = new ModuleInstanceContext(/*new FakeModuleControl()*/) { Configuration = moduleInfo }; + var moduleInfo = ModuleConfiguration; this.OnInit(); this.OnLoad(moduleInfo); @@ -143,55 +133,82 @@ public ActionResult Invoke(ControlViewModel input) ActionScripts = this.actionScripts, }; - return this.View(viewModel); + return View("ModuleActions", viewModel); } protected string LocalizeString(string key) { return Localization.GetString(key, Localization.GlobalResourceFile); } + public ModuleResources ModuleResources + { + get + { + return new ModuleResources() + { + StyleSheets = new List() + { + new ModuleStyleSheet() + { + FilePath = "~/admin/menus/ModuleActions/ModuleActions.css", + Priority = FileOrder.Css.ModuleCss, + }, + new ModuleStyleSheet() + { + FilePath = "~/Resources/Shared/stylesheets/dnnicons/css/dnnicon.min.css", + Priority = FileOrder.Css.ModuleCss, + }, + }, + Scripts = new List() + { + new ModuleScript() + { + FilePath = "~/admin/menus/ModuleActions/ModuleActions.js", + }, + }, + Libraries = new List() + { + CommonJs.DnnPlugins, + }, + AjaxAntiForgery = true, + + }; + } + } protected void OnInit() { // base.OnInit(e); // this.ID = "ModuleActions"; // this.actionButton.Click += this.ActionButton_Click; - MvcJavaScript.RequestRegistration(CommonJs.DnnPlugins); + // MvcJavaScript.RequestRegistration(CommonJs.DnnPlugins); - MvcClientResourceManager.RegisterStyleSheet(this.ControllerContext, "~/admin/menus/ModuleActions/ModuleActions.css", FileOrder.Css.ModuleCss); - MvcClientResourceManager.RegisterStyleSheet(this.ControllerContext, "~/Resources/Shared/stylesheets/dnnicons/css/dnnicon.min.css", FileOrder.Css.ModuleCss); - MvcClientResourceManager.RegisterScript(this.ControllerContext, "~/admin/menus/ModuleActions/ModuleActions.js"); + // this.RegisterStyleSheet("~/admin/menus/ModuleActions/ModuleActions.css", FileOrder.Css.ModuleCss); + //this.RegisterStyleSheet("~/Resources/Shared/stylesheets/dnnicons/css/dnnicon.min.css", FileOrder.Css.ModuleCss); + //this.RegisterScript("~/admin/menus/ModuleActions/ModuleActions.js"); - ServicesFramework.Instance.RequestAjaxAntiForgerySupport(); + //ServicesFramework.Instance.RequestAjaxAntiForgerySupport(); } protected void OnLoad(ModuleInfo moduleInfo) { // base.OnLoad(e); - this.ModuleContext = new ModuleInstanceContext() { Configuration = moduleInfo }; + var moduleActions = new ModuleActionCollection(); - var desktopModule = DesktopModuleController.GetDesktopModule(moduleInfo.DesktopModuleID, moduleInfo.PortalID); - if (!string.IsNullOrEmpty(desktopModule.BusinessControllerClass)) + try { - var businessController = Reflection.CreateType(desktopModule.BusinessControllerClass); - var controlClass = businessController.Namespace + "." + System.IO.Path.GetFileNameWithoutExtension(moduleInfo.ModuleControl.ControlSrc) + "Control," + businessController.Assembly; - try - { - var controller = Reflection.CreateObject(controlClass, controlClass); + var moduleControl = MvcUtils.CreateModuleControl(moduleInfo); - var control = controller as IModuleControl; - control.ModuleContext.Configuration = moduleInfo; - - var actionable = controller as IActionable; - if (actionable != null) - { - moduleActions = actionable.ModuleActions; - } - } - catch (Exception) + var actionable = moduleControl as IActionable; + if (actionable != null) { + moduleActions = actionable.ModuleActions; } } + catch (Exception ex) + { + Logger.Error("Error loading module actions for module " + moduleInfo.ModuleID, ex); + } this.ActionRoot.Actions.AddRange(this.Actions); @@ -213,12 +230,12 @@ protected void OnLoad(ModuleInfo moduleInfo) } */ moduleSpecificActions.Actions.Add(action); - - if (!UIUtilities.IsLegacyUI(this.ModuleContext.ModuleId, action.ControlKey, this.ModuleContext.PortalId) && action.Url.Contains("ctl")) + + if (!DotNetNuke.UI.UIUtilities.IsLegacyUI(this.ModuleContext.ModuleId, action.ControlKey, this.ModuleContext.PortalId) && action.Url.Contains("ctl")) { action.ClientScript = UrlUtils.PopUpUrl(action.Url, null, this.PortalSettings, true, false); } - + } } @@ -290,7 +307,7 @@ protected void OnLoad(ModuleInfo moduleInfo) if (string.IsNullOrEmpty(action.ClientScript) && !string.IsNullOrEmpty(action.Url) && action.Url.StartsWith("javascript:")) { - if (!UIUtilities.IsLegacyUI(this.ModuleContext.ModuleId, action.ControlKey, this.ModuleContext.PortalId)) + if (!DotNetNuke.UI.UIUtilities.IsLegacyUI(this.ModuleContext.ModuleId, action.ControlKey, this.ModuleContext.PortalId)) { action.ClientScript = UrlUtils.PopUpUrl(action.Url, null, this.PortalSettings, true, false); } @@ -332,5 +349,6 @@ protected void OnLoad(ModuleInfo moduleInfo) throw exc; } } + } } diff --git a/DNN Platform/DotNetNuke.Web.MvcWebsite/Controls/PrivacyControl.cs b/DNN Platform/DotNetNuke.Web.MvcWebsite/Controls/PrivacyControl.cs new file mode 100644 index 00000000000..9ee24586ee0 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcWebsite/Controls/PrivacyControl.cs @@ -0,0 +1,22 @@ +// 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.MvcWebsite.Controls +{ + using DotNetNuke.Entities.Portals; + using DotNetNuke.Services.Localization; + using DotNetNuke.Web.MvcPipeline.ModuleControl; + using DotNetNuke.Web.MvcPipeline.ModuleControl.Razor; + + public class PrivacyControl : RazorModuleControlBase + { + public override string ControlName => "Privacy"; + + public override string ControlPath => "admin/Portal"; + public override IRazorModuleResult Invoke() + { + return View(Localization.GetSystemMessage(PortalSettings.Current, "MESSAGE_PORTAL_PRIVACY")); + } + } +} diff --git a/DNN Platform/DotNetNuke.Web.MvcWebsite/Controls/TermsControl.cs b/DNN Platform/DotNetNuke.Web.MvcWebsite/Controls/TermsControl.cs new file mode 100644 index 00000000000..fffb7e258f6 --- /dev/null +++ b/DNN Platform/DotNetNuke.Web.MvcWebsite/Controls/TermsControl.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 + +using DotNetNuke.Web.MvcPipeline.ModuleControl; + +namespace DotNetNuke.Web.MvcWebsite.Controls +{ + using DotNetNuke.Entities.Portals; + using DotNetNuke.Services.Localization; + using DotNetNuke.Web.MvcPipeline.ModuleControl.Razor; + + public class TermsControl : RazorModuleControlBase + { + public override string ControlName => "Terms"; + + public override string ControlPath => "admin/Portal"; + + public override IRazorModuleResult Invoke() + { + return View(Localization.GetSystemMessage(PortalSettings.Current, "MESSAGE_PORTAL_TERMS")); + } + + } +} diff --git a/DNN Platform/Library/Data/DataProvider.cs b/DNN Platform/Library/Data/DataProvider.cs index 0241e5547d9..577a8c5ebad 100644 --- a/DNN Platform/Library/Data/DataProvider.cs +++ b/DNN Platform/Library/Data/DataProvider.cs @@ -1309,7 +1309,7 @@ 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) + 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", @@ -1317,6 +1317,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), @@ -1336,7 +1337,7 @@ 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) + 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", @@ -1345,6 +1346,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 ffa39542aa8..2213368d51a 100644 --- a/DNN Platform/Library/Entities/Modules/ControlInfo.cs +++ b/DNN Platform/Library/Entities/Modules/ControlInfo.cs @@ -5,9 +5,9 @@ namespace DotNetNuke.Entities.Modules { using System; using System.Data; - using System.Xml; + using System.Xml; - using DotNetNuke.Common.Utilities; + using DotNetNuke.Common.Utilities; /// Project : DotNetNuke /// Namespace: DotNetNuke.Entities.Modules @@ -28,7 +28,11 @@ protected ControlInfo() /// Gets or sets the Control Source. /// A String. - public string ControlSrc { get; set; } + 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 gets and sets a flag that determines whether the control support the AJAX @@ -44,7 +48,8 @@ protected override void FillInternal(IDataReader dr) // Call EntityBaseInfo's implementation base.FillInternal(dr); this.ControlKey = Null.SetNullString(dr["ControlKey"]); - this.ControlSrc = Null.SetNullString(dr["ControlSrc"]); + this.ControlSrc = Null.SetNullString(dr["ControlSrc"]); + this.MvcControlClass = Null.SetNullString(dr["MvcControlClass"]); this.SupportsPartialRendering = Null.SetNullBoolean(dr["SupportsPartialRendering"]); } @@ -57,6 +62,9 @@ protected void ReadXmlInternal(XmlReader reader) break; case "controlSrc": this.ControlSrc = reader.ReadElementContentAsString(); + break; + case "mvcControlClass": + this.MvcControlClass = reader.ReadElementContentAsString(); break; case "supportsPartialRendering": string elementvalue = reader.ReadElementContentAsString(); @@ -73,7 +81,8 @@ protected void WriteXmlInternal(XmlWriter writer) { // write out properties writer.WriteElementString("controlKey", this.ControlKey); - writer.WriteElementString("controlSrc", this.ControlSrc); + 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 e77615c48b1..59a6d810657 100644 --- a/DNN Platform/Library/Entities/Modules/ModuleControlController.cs +++ b/DNN Platform/Library/Entities/Modules/ModuleControlController.cs @@ -80,6 +80,7 @@ public static int SaveModuleControl(ModuleControlInfo moduleControl, bool clearC moduleControl.ControlKey, moduleControl.ControlTitle, moduleControl.ControlSrc, + moduleControl.MvcControlClass, moduleControl.IconFile, Convert.ToInt32(moduleControl.ControlType), moduleControl.ViewOrder, @@ -97,6 +98,7 @@ public static int SaveModuleControl(ModuleControlInfo moduleControl, bool clearC moduleControl.ControlKey, moduleControl.ControlTitle, moduleControl.ControlSrc, + moduleControl.MvcControlClass, moduleControl.IconFile, Convert.ToInt32(moduleControl.ControlType), moduleControl.ViewOrder, diff --git a/DNN Platform/Library/Entities/Modules/ModuleInfo.cs b/DNN Platform/Library/Entities/Modules/ModuleInfo.cs index abafcd245ee..1c03fd2ac52 100644 --- a/DNN Platform/Library/Entities/Modules/ModuleInfo.cs +++ b/DNN Platform/Library/Entities/Modules/ModuleInfo.cs @@ -837,6 +837,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/IPortalSettingsController.cs b/DNN Platform/Library/Entities/Portals/IPortalSettingsController.cs index 177f3bda2dd..0f6245f813f 100644 --- a/DNN Platform/Library/Entities/Portals/IPortalSettingsController.cs +++ b/DNN Platform/Library/Entities/Portals/IPortalSettingsController.cs @@ -14,6 +14,8 @@ public interface IPortalSettingsController PortalSettings.PortalAliasMapping GetPortalAliasMappingMode(int portalId); + string GetPortalPagePipeline(int portalId); + /// The GetActiveTab method gets the active Tab for the current request. /// The current tab's id. /// The current PortalSettings. diff --git a/DNN Platform/Library/Entities/Portals/PortalSettings.cs b/DNN Platform/Library/Entities/Portals/PortalSettings.cs index 20c9688bf31..87688dde28c 100644 --- a/DNN Platform/Library/Entities/Portals/PortalSettings.cs +++ b/DNN Platform/Library/Entities/Portals/PortalSettings.cs @@ -587,6 +587,9 @@ public bool ShowQuickModuleAddMenu } } + /// + public string PagePipeline { get; internal set; } + /// public string GetProperty(string propertyName, string format, CultureInfo formatProvider, UserInfo accessingUser, Scope accessLevel, ref bool propertyNotFound) { diff --git a/DNN Platform/Library/Entities/Portals/PortalSettingsController.cs b/DNN Platform/Library/Entities/Portals/PortalSettingsController.cs index eadbc9f9e84..c36b0e51c78 100644 --- a/DNN Platform/Library/Entities/Portals/PortalSettingsController.cs +++ b/DNN Platform/Library/Entities/Portals/PortalSettingsController.cs @@ -10,6 +10,7 @@ namespace DotNetNuke.Entities.Portals using System.Linq; using DotNetNuke.Abstractions.Application; + using DotNetNuke.Abstractions.Portals; using DotNetNuke.Collections; using DotNetNuke.Common; using DotNetNuke.Common.Utilities; @@ -122,6 +123,17 @@ public virtual PortalSettings.PortalAliasMapping GetPortalAliasMappingMode(int p return aliasMapping; } + /// + public virtual string GetPortalPagePipeline(int portalId) + { + if (PortalController.Instance.GetPortalSettings(portalId).TryGetValue("PagePipeline", out var setting)) + { + return string.IsNullOrEmpty(setting) ? PagePipelineConstants.WebForms : setting; + } + + return PagePipelineConstants.WebForms; + } + /// public virtual IList GetTabModules(PortalSettings portalSettings) { @@ -275,6 +287,7 @@ public virtual void LoadPortalSettings(PortalSettings portalSettings) portalSettings.DataConsentDelayMeasurement = setting; setting = settings.GetValueOrDefault("AllowedExtensionsWhitelist", this.hostSettingsService.GetString("DefaultEndUserExtensionWhitelist")); portalSettings.AllowedExtensionsWhitelist = new FileExtensionWhitelist(setting); + portalSettings.PagePipeline = settings.GetValueOrDefault("PagePipeline", "webforms"); } protected List GetBreadcrumbs(int tabId, int portalId) diff --git a/DNN Platform/Library/Entities/Tabs/TabInfo.cs b/DNN Platform/Library/Entities/Tabs/TabInfo.cs index ff5be7180cd..538959f8922 100644 --- a/DNN Platform/Library/Entities/Tabs/TabInfo.cs +++ b/DNN Platform/Library/Entities/Tabs/TabInfo.cs @@ -698,6 +698,27 @@ public string SkinDoctype [JsonIgnore] public bool UseBaseFriendlyUrls { get; set; } + /// Gets a value indicating the pipeline type. + [XmlIgnore] + [JsonIgnore] + public string PagePipeline + { + get + { + string pagePipeline; + if (this.TabSettings.ContainsKey("PagePipeline") && !string.IsNullOrEmpty(this.TabSettings["PagePipeline"].ToString())) + { + pagePipeline = this.TabSettings["PagePipeline"].ToString(); + } + else + { + pagePipeline = string.Empty; + } + + return pagePipeline; + } + } + /// public string GetProperty(string propertyName, string format, CultureInfo formatProvider, UserInfo accessingUser, Scope currentScope, ref bool propertyNotFound) { @@ -853,6 +874,10 @@ public string GetProperty(string propertyName, string format, CultureInfo format propertyNotFound = false; result = PropertyAccess.FormatString(this.SiteMapPriority.ToString(), format); break; + case "pagepipeline": + propertyNotFound = false; + result = PropertyAccess.FormatString(this.PagePipeline, format); + break; } if (!isPublic && currentScope != Scope.Debug) diff --git a/DNN Platform/Library/Entities/Urls/AdvancedUrlRewriter.cs b/DNN Platform/Library/Entities/Urls/AdvancedUrlRewriter.cs index eb78a28e56f..8b82837c5f1 100644 --- a/DNN Platform/Library/Entities/Urls/AdvancedUrlRewriter.cs +++ b/DNN Platform/Library/Entities/Urls/AdvancedUrlRewriter.cs @@ -2294,7 +2294,14 @@ private void ProcessRequest( } else { - RewriterUtils.RewriteUrl(context, "~/" + result.RewritePath); + if (this.IsMvc(result, queryStringCol, context, result.TabId, result.PortalId)) + { + RewriterUtils.RewriteUrl(context, "~/" + result.RewritePath.Replace(Globals.glbDefaultPage, "DesktopModules/Default/Page/" + result.TabId + "/" + result.CultureCode)); + } + else + { + RewriterUtils.RewriteUrl(context, "~/" + result.RewritePath); + } } } @@ -3143,5 +3150,43 @@ private void SecurityCheck(HttpApplication app) throw new HttpException(404, "Not Found"); } } + + private bool IsMvc(UrlAction result, NameValueCollection queryStringCol, HttpContext context, int tabId, int portalId) + { + var mvcCtls = new[] { "Terms", "Privacy" }; + bool mvcCtl = false; + if (result.RewritePath.Contains("&ctl=")) + { + foreach (var item in mvcCtls) + { + mvcCtl = mvcCtl || result.RewritePath.Contains("&ctl=" + item); + } + } + else + { + TabInfo tab = null; + if (tabId > 0 && portalId > -1) + { + tab = TabController.Instance.GetTab(tabId, portalId, false); + if (tab != null) + { + var tabPipeline = tab.PagePipeline; + if (!string.IsNullOrEmpty(tabPipeline)) + { + mvcCtl = tabPipeline == PagePipelineConstants.Mvc; + } + else + { + var portalPipeline = PortalSettingsController.Instance().GetPortalPagePipeline(portalId); + mvcCtl = portalPipeline == PagePipelineConstants.Mvc; + } + } + } + } + + mvcCtl = mvcCtl && !result.RewritePath.Contains("mvcpage=no") && queryStringCol["mvcpage"] != "no"; + mvcCtl = mvcCtl || result.RewritePath.Contains("mvcpage=yes") || queryStringCol["mvcpage"] == "yes"; + return mvcCtl; + } } } diff --git a/DNN Platform/Library/Services/Installer/Writers/ModulePackageWriter.cs b/DNN Platform/Library/Services/Installer/Writers/ModulePackageWriter.cs index 61ec40ad88e..9306ab3c35e 100644 --- a/DNN Platform/Library/Services/Installer/Writers/ModulePackageWriter.cs +++ b/DNN Platform/Library/Services/Installer/Writers/ModulePackageWriter.cs @@ -137,7 +137,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 510af422a12..17cf58979d9 100644 --- a/DNN Platform/Library/Services/Upgrade/Upgrade.cs +++ b/DNN Platform/Library/Services/Upgrade/Upgrade.cs @@ -212,13 +212,14 @@ public static TabInfo AddHostPage(string tabName, string description, string tab /// The key for this control in the Definition. /// The title of this control. /// The source of ths control. + /// The mvc control class of ths 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 iconFile, SecurityAccessLevel controlType, int viewOrder) + 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. @@ -2109,12 +2110,12 @@ protected static bool IsLanguageEnabled(int portalid, string code) /// 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); @@ -2129,6 +2130,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/Modules/DDRMenu/Localisation/Localiser.cs b/DNN Platform/Modules/DDRMenu/Localisation/Localiser.cs index 70240a863b6..f34be3b93b2 100644 --- a/DNN Platform/Modules/DDRMenu/Localisation/Localiser.cs +++ b/DNN Platform/Modules/DDRMenu/Localisation/Localiser.cs @@ -108,7 +108,7 @@ public void LocaliseNode(MenuNode node, int portalId) node.TabId = -1; } - node.Children.ForEach(this.LocaliseNode); + node.Children.ForEach(n => this.LocaliseNode(n, portalId)); } private TabInfo LocaliseTab(TabInfo tab, int portalId) diff --git a/DNN Platform/Modules/HTML/Controllers/DNN_HTMLController.cs b/DNN Platform/Modules/HTML/Controllers/DNN_HTMLController.cs index e563b3b5270..c9ab6e076ab 100644 --- a/DNN Platform/Modules/HTML/Controllers/DNN_HTMLController.cs +++ b/DNN Platform/Modules/HTML/Controllers/DNN_HTMLController.cs @@ -19,18 +19,11 @@ namespace DotNetNuke.Modules.Html.Controllers using DotNetNuke.Entities.Content.Workflow; using DotNetNuke.Entities.Content.Workflow.Entities; using DotNetNuke.Entities.Modules; - using DotNetNuke.Entities.Modules.Settings; using DotNetNuke.Entities.Portals; - using DotNetNuke.Framework.JavaScriptLibraries; using DotNetNuke.Modules.Html; using DotNetNuke.Modules.Html.Components; using DotNetNuke.Modules.Html.Models; - using DotNetNuke.Security; - using DotNetNuke.Services.Exceptions; - using DotNetNuke.Services.Localization; - using DotNetNuke.Web.Client.ClientResourceManagement; - using DotNetNuke.Website.Controllers; - using Microsoft.Extensions.DependencyInjection; + using DotNetNuke.Web.MvcPipeline.Controllers; public class DNN_HTMLController : ModuleSettingsController { diff --git a/DNN Platform/Modules/HTML/Controllers/HtmlModuleViewController.cs b/DNN Platform/Modules/HTML/Controllers/HtmlModuleViewController.cs deleted file mode 100644 index a2d74e92d02..00000000000 --- a/DNN Platform/Modules/HTML/Controllers/HtmlModuleViewController.cs +++ /dev/null @@ -1,65 +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.Modules.Html.Controllers -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Net.NetworkInformation; - using System.Web; - using System.Web.Mvc; - - using DotNetNuke.Abstractions; - using DotNetNuke.Common; - using DotNetNuke.Entities.Content.Workflow.Entities; - using DotNetNuke.Entities.Modules; - using DotNetNuke.Entities.Modules.Actions; - using DotNetNuke.Entities.Modules.Settings; - using DotNetNuke.Framework.JavaScriptLibraries; - using DotNetNuke.Modules.Html; - using DotNetNuke.Modules.Html.Components; - using DotNetNuke.Modules.Html.Models; - using DotNetNuke.Services.Exceptions; - using DotNetNuke.Services.Localization; - using DotNetNuke.Services.Personalization; - using DotNetNuke.UI.Modules; - using DotNetNuke.Web.Client.ClientResourceManagement; - using DotNetNuke.Web.MvcPipeline.Controllers; - using Microsoft.Extensions.DependencyInjection; - - public class HtmlModuleViewController : ModuleViewControllerBase - { - private readonly INavigationManager navigationManager; - private readonly HtmlTextController htmlTextController; - - public HtmlModuleViewController() - { - this.navigationManager = Globals.DependencyProvider.GetRequiredService(); - this.htmlTextController = new HtmlTextController(this.navigationManager); - } - - public override string ControlPath => "~/DesktopModules/Html/"; - - public override string ID => "HtmlModule"; - - protected override object ViewModel() - { - int workflowID = this.htmlTextController.GetWorkflow(this.ModuleId, this.TabId, this.PortalId).Value; - HtmlTextInfo content = this.htmlTextController.GetTopHtmlText(this.ModuleId, true, workflowID); - - var html = string.Empty; - if (content != null) - { - html = System.Web.HttpUtility.HtmlDecode(content.Content); - } - - return new HtmlModuleModel() - { - Html = html, - }; - } - } -} diff --git a/DNN Platform/Modules/HTML/Controllers/MyWorkViewController.cs b/DNN Platform/Modules/HTML/Controllers/MyWorkViewController.cs deleted file mode 100644 index 2f6440eeaab..00000000000 --- a/DNN Platform/Modules/HTML/Controllers/MyWorkViewController.cs +++ /dev/null @@ -1,67 +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.Modules.Html.Controllers -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Net.NetworkInformation; - using System.Web; - using System.Web.Mvc; - - using DotNetNuke.Abstractions; - using DotNetNuke.Common; - using DotNetNuke.Entities.Content.Workflow.Entities; - using DotNetNuke.Entities.Modules; - using DotNetNuke.Entities.Modules.Settings; - using DotNetNuke.Framework.JavaScriptLibraries; - using DotNetNuke.Modules.Html; - using DotNetNuke.Modules.Html.Components; - using DotNetNuke.Modules.Html.Models; - using DotNetNuke.Services.Exceptions; - using DotNetNuke.Services.Localization; - using DotNetNuke.Web.Client.ClientResourceManagement; - using DotNetNuke.Web.Mvc; - using DotNetNuke.Web.MvcPipeline.Controllers; - using DotNetNuke.Website.Controllers; - using Microsoft.Extensions.DependencyInjection; - - public class MyWorkViewController : ModuleViewControllerBase - { - private readonly INavigationManager navigationManager; - - public MyWorkViewController() - { - this.navigationManager = Globals.DependencyProvider.GetRequiredService(); - } - - public override string ControlPath => "~/DesktopModules/Html/"; - - public override string ID => "MyWork"; - - protected override object ViewModel() - { - var objHtmlTextUsers = new HtmlTextUserController(); - var lst = objHtmlTextUsers.GetHtmlTextUser(this.UserInfo.UserID).Cast(); - MvcClientResourceManager.RegisterStyleSheet(this.ControllerContext, "~/DesktopModules/HTML/edit.css"); - MvcClientResourceManager.RegisterStyleSheet(this.ControllerContext, "~/Portals/_default/Skins/_default/WebControlSkin/Default/GridView.default.css"); - return new MyWorkModel() - { - LocalResourceFile = this.LocalResourceFile, - ModuleId = this.ModuleId, - TabId = this.TabId, - RedirectUrl = this.navigationManager.NavigateURL(), - HtmlTextUsers = lst.Select(u => new HtmlTextUserModel() - { - Url = this.navigationManager.NavigateURL(u.TabID), - ModuleID = u.ModuleID, - ModuleTitle = u.ModuleTitle, - StateName = u.StateName, - }).ToList(), - }; - } - } -} diff --git a/DNN Platform/Modules/HTML/Controllers/EditHTMLViewController.cs b/DNN Platform/Modules/HTML/Controls/EditHTMLControl.cs similarity index 72% rename from DNN Platform/Modules/HTML/Controllers/EditHTMLViewController.cs rename to DNN Platform/Modules/HTML/Controls/EditHTMLControl.cs index 35ac5d1a56b..809ec5ac33d 100644 --- a/DNN Platform/Modules/HTML/Controllers/EditHTMLViewController.cs +++ b/DNN Platform/Modules/HTML/Controls/EditHTMLControl.cs @@ -2,7 +2,7 @@ // 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.Modules.Html.Controllers +namespace DotNetNuke.Modules.Html.Controls { using System; using System.Collections.Generic; @@ -29,47 +29,52 @@ namespace DotNetNuke.Modules.Html.Controllers using DotNetNuke.Services.Localization; using DotNetNuke.Web.Client.ClientResourceManagement; using DotNetNuke.Web.MvcPipeline.Controllers; + using DotNetNuke.Web.MvcPipeline.ModuleControl; + using DotNetNuke.Web.MvcPipeline.ModuleControl.Razor; + using DotNetNuke.Web.MvcPipeline.ModuleControl.Resources; using Microsoft.Extensions.DependencyInjection; - /// - /// Controller for the HTML module's edit view in DotNetNuke (DNN). - /// - /// This controller is responsible for preparing and managing the data and resources required for the HTML module's editing interface. - /// It interacts with the workflow system, content security policy, and navigation manager to provide a secure and user-friendly editing experience. - /// - /// - /// Key responsibilities: - /// - /// Initializes dependencies such as navigation, HTML content controller, workflow manager, and content security policy. - /// Builds the for the edit view, including workflow state, content, permissions, and UI options. - /// Handles content retrieval, workflow state management, and sets up client-side resources (JavaScript and CSS). - /// Configures content security policy for inline styles, scripts, and image sources. - /// - /// - /// - /// Usage: - /// This controller is used by the DNN MVC pipeline to render and manage the HTML module's edit interface. - /// - /// - public partial class EditHTMLViewController : ModuleViewControllerBase + public class EditHTMLControl : RazorModuleControlBase, IResourcable { private readonly INavigationManager navigationManager; private readonly HtmlTextController htmlTextController; private readonly IWorkflowManager workflowManager = WorkflowManager.Instance; private readonly IContentSecurityPolicy contentSecurityPolicy; - public EditHTMLViewController(IContentSecurityPolicy csp) + public EditHTMLControl(IContentSecurityPolicy csp) { this.navigationManager = Globals.DependencyProvider.GetRequiredService(); this.htmlTextController = new HtmlTextController(this.navigationManager); this.contentSecurityPolicy = csp; } - public override string ControlPath => "~/DesktopModules/Html/"; - - public override string ID => "EditHTML"; + public ModuleResources ModuleResources => new ModuleResources() + { + StyleSheets = new List() + { + new ModuleStyleSheet() + { + FilePath = "~/DesktopModules/HTML/edit.css", + }, + new ModuleStyleSheet() + { + FilePath = "~/Portals/_default/Skins/_default/WebControlSkin/Default/GridView.default.css", + }, + }, + Scripts = new List() + { + new ModuleScript() + { + FilePath = "~/Resources/Shared/scripts/jquery/jquery.form.min.js", + }, + new ModuleScript() + { + FilePath = "~/DesktopModules/HTML/js/edit.js", + }, + }, + }; - protected override object ViewModel() + public override IRazorModuleResult Invoke() { var model = new EditHtmlViewModel(); try @@ -133,15 +138,10 @@ protected override object ViewModel() throw new Exception("EditHTML", exc); } - MvcClientResourceManager.RegisterScript(this.ControllerContext, "~/Resources/Shared/scripts/jquery/jquery.form.min.js"); - MvcClientResourceManager.RegisterStyleSheet(this.ControllerContext, "~/Portals/_default/Skins/_default/WebControlSkin/Default/GridView.default.css"); - MvcClientResourceManager.RegisterStyleSheet(this.ControllerContext, "~/DesktopModules/HTML/edit.css"); - MvcClientResourceManager.RegisterScript(this.ControllerContext, "~/DesktopModules/HTML/js/edit.js"); - this.contentSecurityPolicy.StyleSource.AddInline(); this.contentSecurityPolicy.ScriptSource.AddSelf().AddInline(); this.contentSecurityPolicy.ImgSource.AddScheme("data:"); - return model; + return this.View(model); } private void PopulateModelWithContent(EditHtmlViewModel model, HtmlTextInfo htmlContent) diff --git a/DNN Platform/Modules/HTML/Mvc/HtmlModuleControl.cs b/DNN Platform/Modules/HTML/Controls/HtmlModuleControl.cs similarity index 69% rename from DNN Platform/Modules/HTML/Mvc/HtmlModuleControl.cs rename to DNN Platform/Modules/HTML/Controls/HtmlModuleControl.cs index 980defaabf3..2461e877504 100644 --- a/DNN Platform/Modules/HTML/Mvc/HtmlModuleControl.cs +++ b/DNN Platform/Modules/HTML/Controls/HtmlModuleControl.cs @@ -1,31 +1,34 @@ // 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.Modules.Html +namespace DotNetNuke.Modules.Html.Controls { using System; using System.Collections.Generic; using System.Linq; using System.Web; + using System.Web.Mvc; using DotNetNuke.Abstractions; using DotNetNuke.Entities.Modules; using DotNetNuke.Entities.Modules.Actions; + using DotNetNuke.Modules.Html.Models; using DotNetNuke.Security; using DotNetNuke.Security.Permissions; using DotNetNuke.Services.Localization; using DotNetNuke.Web.MvcPipeline.ModuleControl; + using DotNetNuke.Web.MvcPipeline.ModuleControl.Razor; using Microsoft.Extensions.DependencyInjection; - public class HtmlModuleControl : ModuleControlBase, IActionable + public class HtmlModuleControl : RazorModuleControlBase, IActionable { private readonly INavigationManager navigationManager; + private readonly HtmlTextController htmlTextController; public HtmlModuleControl() { this.navigationManager = this.DependencyProvider.GetRequiredService(); - this.ControlPath = "DesktopModules/HTML"; - this.ID = "HtmlModule.ascx"; + this.htmlTextController = new HtmlTextController(this.navigationManager); } /// Gets moduleActions is an interface property that returns the module actions collection for the module. @@ -63,5 +66,22 @@ public ModuleActionCollection ModuleActions return actions; } } + + public override IRazorModuleResult Invoke() + { + int workflowID = this.htmlTextController.GetWorkflow(this.ModuleId, this.TabId, this.PortalId).Value; + HtmlTextInfo content = this.htmlTextController.GetTopHtmlText(this.ModuleId, true, workflowID); + + var html = string.Empty; + if (content != null) + { + html = System.Web.HttpUtility.HtmlDecode(content.Content); + } + + return this.View(new HtmlModuleModel() + { + Html = html, + }); + } } } diff --git a/DNN Platform/Modules/HTML/Controls/MyWorkControl.cs b/DNN Platform/Modules/HTML/Controls/MyWorkControl.cs new file mode 100644 index 00000000000..1866c634f96 --- /dev/null +++ b/DNN Platform/Modules/HTML/Controls/MyWorkControl.cs @@ -0,0 +1,95 @@ +// 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.Modules.Html.Controls +{ + using System.Collections.Generic; + using System.Linq; + + using DotNetNuke.Abstractions; + using DotNetNuke.Common; + using DotNetNuke.Entities.Modules; + using DotNetNuke.Entities.Modules.Actions; + using DotNetNuke.Modules.Html; + using DotNetNuke.Modules.Html.Models; + using DotNetNuke.Security; + using DotNetNuke.Services.Localization; + using DotNetNuke.Web.MvcPipeline.ModuleControl; + using DotNetNuke.Web.MvcPipeline.ModuleControl.Razor; + using DotNetNuke.Web.MvcPipeline.ModuleControl.Resources; + using Microsoft.Extensions.DependencyInjection; + + public class MyWorkControl : RazorModuleControlBase, IActionable, IResourcable + { + private readonly INavigationManager navigationManager; + + public MyWorkControl() + { + this.navigationManager = Globals.DependencyProvider.GetRequiredService(); + } + + public ModuleActionCollection ModuleActions + { + get + { + var actions = new ModuleActionCollection(); + actions.Add( + this.GetNextActionID(), + Localization.GetString(ModuleActionType.AddContent, this.LocalResourceFile), + ModuleActionType.AddContent, + string.Empty, + string.Empty, + this.EditUrl(), + false, + SecurityAccessLevel.Edit, + true, + false); + + return actions; + } + } + + public ModuleResources ModuleResources + { + get + { + return new ModuleResources() + { + StyleSheets = new List() + { + new ModuleStyleSheet() + { + FilePath = "~/DesktopModules/HTML/edit.css", + }, + new ModuleStyleSheet() + { + FilePath = "~/Portals/_default/Skins/_default/WebControlSkin/Default/GridView.default.css", + }, + }, + }; + } + } + + public override IRazorModuleResult Invoke() + { + var objHtmlTextUsers = new HtmlTextUserController(); + var lst = objHtmlTextUsers.GetHtmlTextUser(this.UserInfo.UserID).Cast(); + + return this.View(new MyWorkModel() + { + LocalResourceFile = this.LocalResourceFile, + ModuleId = this.ModuleId, + TabId = this.TabId, + RedirectUrl = this.navigationManager.NavigateURL(), + HtmlTextUsers = lst.Select(u => new HtmlTextUserModel() + { + Url = this.navigationManager.NavigateURL(u.TabID), + ModuleID = u.ModuleID, + ModuleTitle = u.ModuleTitle, + StateName = u.StateName, + }).ToList(), + }); + } + } +} diff --git a/DNN Platform/Modules/HTML/DotNetNuke.Modules.Html.csproj b/DNN Platform/Modules/HTML/DotNetNuke.Modules.Html.csproj index 7d35aa71413..984ddfe301d 100644 --- a/DNN Platform/Modules/HTML/DotNetNuke.Modules.Html.csproj +++ b/DNN Platform/Modules/HTML/DotNetNuke.Modules.Html.csproj @@ -159,9 +159,6 @@ - - - @@ -184,7 +181,9 @@ - + + + MyWork.ascx diff --git a/DNN Platform/Modules/HTML/HtmlModule.ascx.cs b/DNN Platform/Modules/HTML/HtmlModule.ascx.cs index 3e87d745e81..b1e1275d647 100644 --- a/DNN Platform/Modules/HTML/HtmlModule.ascx.cs +++ b/DNN Platform/Modules/HTML/HtmlModule.ascx.cs @@ -22,6 +22,8 @@ namespace DotNetNuke.Modules.Html using DotNetNuke.Services.Localization; using DotNetNuke.Services.Personalization; using DotNetNuke.UI.WebControls; + using DotNetNuke.Web.MvcPipeline.ModuleControl; + using DotNetNuke.Web.MvcPipeline.Utils; using Microsoft.Extensions.DependencyInjection; /// The HtmlModule Class provides the UI for displaying the Html. diff --git a/DNN Platform/Modules/HTML/Models/EditHtmlViewModel.cs b/DNN Platform/Modules/HTML/Models/EditHtmlViewModel.cs index 798807a7c7a..ad12008d525 100644 --- a/DNN Platform/Modules/HTML/Models/EditHtmlViewModel.cs +++ b/DNN Platform/Modules/HTML/Models/EditHtmlViewModel.cs @@ -7,7 +7,6 @@ namespace DotNetNuke.Modules.Html.Models using System.Collections.Generic; using System.Web.Mvc; - using DotNetNuke.Web.Mvc.Page; using DotNetNuke.Web.MvcPipeline.Models; public class EditHtmlViewModel : ModuleModelBase diff --git a/DNN Platform/Modules/HTML/Models/MyWorkModel.cs b/DNN Platform/Modules/HTML/Models/MyWorkModel.cs index cfcde9e9cb0..1fc2cdc1ca6 100644 --- a/DNN Platform/Modules/HTML/Models/MyWorkModel.cs +++ b/DNN Platform/Modules/HTML/Models/MyWorkModel.cs @@ -4,10 +4,7 @@ namespace DotNetNuke.Modules.Html.Models { using System.Collections.Generic; - using System.Web.Mvc; - using DotNetNuke.Modules.Html; - using DotNetNuke.Web.Mvc.Page; using DotNetNuke.Web.MvcPipeline.Models; public class MyWorkModel : ModuleModelBase diff --git a/DNN Platform/Modules/HTML/MyWork.ascx.designer.cs b/DNN Platform/Modules/HTML/MyWork.ascx.designer.cs index d1c20a4491d..0310486ee53 100644 --- a/DNN Platform/Modules/HTML/MyWork.ascx.designer.cs +++ b/DNN Platform/Modules/HTML/MyWork.ascx.designer.cs @@ -1,36 +1,43 @@ -// -// 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. +//------------------------------------------------------------------------------ +// +// Ce code a été généré par un outil. // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// +// Les changements apportés à ce fichier peuvent provoquer un comportement incorrect et seront perdues si +// le code est régénéré. +// //------------------------------------------------------------------------------ -namespace DotNetNuke.Modules.Html { - - - public partial class MyWork { - /// customJS control. +namespace DotNetNuke.Modules.Html +{ + + + public partial class MyWork + { + + /// + /// Contrôle customJS. + /// /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. + /// Champ généré automatiquement. + /// Pour modifier, déplacez la déclaration de champ du fichier de concepteur dans le fichier code-behind. /// protected global::DotNetNuke.Web.Client.ClientResourceManagement.DnnCssInclude customJS; - /// dgTabs control. + + /// + /// Contrôle dgTabs. + /// /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. + /// Champ généré automatiquement. + /// Pour modifier, déplacez la déclaration de champ du fichier de concepteur dans le fichier code-behind. /// protected global::DotNetNuke.Web.UI.WebControls.Internal.DnnGrid dgTabs; - /// hlCancel control. + + /// + /// Contrôle hlCancel. + /// /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. + /// Champ généré automatiquement. + /// Pour modifier, déplacez la déclaration de champ du fichier de concepteur dans le fichier code-behind. /// protected global::System.Web.UI.WebControls.HyperLink hlCancel; } diff --git a/DNN Platform/Modules/HTML/dnn_HTML.dnn b/DNN Platform/Modules/HTML/dnn_HTML.dnn index 9c505e1be5a..6c4ae00ed47 100644 --- a/DNN Platform/Modules/HTML/dnn_HTML.dnn +++ b/DNN Platform/Modules/HTML/dnn_HTML.dnn @@ -104,7 +104,7 @@ Providers\DataProviders\SqlDataProvider 10.00.02.SqlDataProvider 10.00.02 - +