diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactoryTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactoryTests.cs index 2bd482b8eb..1e7d19e18f 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactoryTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactoryTests.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Features; @@ -29,7 +30,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Website.Routing { private UmbracoRouteValuesFactory GetFactory( out Mock publishedRouter, - out UmbracoRenderingDefaults renderingDefaults, + out IOptions renderingDefaults, out IPublishedRequest request) { var builder = new PublishedRequestBuilder(new Uri("https://example.com"), Mock.Of()); @@ -41,7 +42,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Website.Routing .Returns((IPublishedRequest r, IPublishedContent c) => Task.FromResult(builtRequest)) .Verifiable(); - renderingDefaults = new UmbracoRenderingDefaults(); + renderingDefaults = Mock.Of>(x => x.Value.DefaultControllerType == typeof(RenderController)); // add the default one var actionDescriptors = new List @@ -82,12 +83,12 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Website.Routing [Test] public async Task Adds_Result_To_Route_Value_Dictionary() { - UmbracoRouteValuesFactory factory = GetFactory(out _, out UmbracoRenderingDefaults renderingDefaults, out IPublishedRequest request); + UmbracoRouteValuesFactory factory = GetFactory(out _, out IOptions renderingDefaults, out IPublishedRequest request); UmbracoRouteValues result = await factory.CreateAsync(new DefaultHttpContext(), request); Assert.IsNotNull(result); - Assert.AreEqual(renderingDefaults.DefaultControllerType, result.ControllerType); + Assert.AreEqual(renderingDefaults.Value.DefaultControllerType, result.ControllerType); Assert.AreEqual(UmbracoRouteValues.DefaultActionName, result.ActionName); Assert.IsNull(result.TemplateName); } diff --git a/src/Umbraco.Web.Common/Extensions/FriendlyUrlHelperExtensions.cs b/src/Umbraco.Web.Common/Extensions/FriendlyUrlHelperExtensions.cs new file mode 100644 index 0000000000..0340ce7a50 --- /dev/null +++ b/src/Umbraco.Web.Common/Extensions/FriendlyUrlHelperExtensions.cs @@ -0,0 +1,50 @@ +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Web.Common.DependencyInjection; + +namespace Umbraco.Extensions +{ + public static class FriendlyUrlHelperExtensions + { + + private static IUmbracoContext UmbracoContext { get; } = + StaticServiceProvider.Instance.GetRequiredService().GetRequiredUmbracoContext(); + + private static IDataProtectionProvider DataProtectionProvider { get; } = + StaticServiceProvider.Instance.GetRequiredService(); + /// + /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + /// + /// + /// + /// + /// + public static string SurfaceAction(this IUrlHelper url, string action, string controllerName) + => UrlHelperExtensions.SurfaceAction(url, UmbracoContext, DataProtectionProvider, action, controllerName); + + /// + /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + /// + /// + /// + /// + /// + /// + public static string SurfaceAction(this IUrlHelper url, string action, string controllerName, object additionalRouteVals) + => UrlHelperExtensions.SurfaceAction(url, UmbracoContext, DataProtectionProvider, action, controllerName, additionalRouteVals); + + /// + /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + /// + /// + /// + /// + /// + /// + /// + public static string SurfaceAction(this IUrlHelper url, string action, string controllerName, string area, object additionalRouteVals) + => UrlHelperExtensions.SurfaceAction(url, UmbracoContext, DataProtectionProvider, action, controllerName, area, additionalRouteVals); + } +} diff --git a/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs b/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs index d3fd3bddba..fb2a272585 100644 --- a/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Web; +using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Html; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; @@ -12,8 +14,10 @@ using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PropertyEditors.ValueConverters; +using Umbraco.Cms.Core.Web; using Umbraco.Cms.Core.WebAssets; using Umbraco.Cms.Web.Common.Controllers; +using Umbraco.Cms.Web.Common.Security; namespace Umbraco.Extensions { @@ -280,5 +284,51 @@ namespace Umbraco.Extensions return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); } + /// + /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + /// + /// + /// + /// + /// + public static string SurfaceAction(this IUrlHelper url, IUmbracoContext umbracoContext, IDataProtectionProvider dataProtectionProvider,string action, string controllerName) + { + return url.SurfaceAction(umbracoContext, dataProtectionProvider, action, controllerName, null); + } + + /// + /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + /// + /// + /// + /// + /// + /// + public static string SurfaceAction(this IUrlHelper url, IUmbracoContext umbracoContext, IDataProtectionProvider dataProtectionProvider,string action, string controllerName, object additionalRouteVals) + { + return url.SurfaceAction(umbracoContext, dataProtectionProvider, action, controllerName, "", additionalRouteVals); + } + + /// + /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + /// + /// + /// + /// + /// + /// + /// + public static string SurfaceAction(this IUrlHelper url, IUmbracoContext umbracoContext, IDataProtectionProvider dataProtectionProvider, string action, string controllerName, string area, object additionalRouteVals) + { + if (action == null) throw new ArgumentNullException(nameof(action)); + if (string.IsNullOrEmpty(action)) throw new ArgumentException("Value can't be empty.", nameof(action)); + if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); + if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); + + var encryptedRoute = EncryptionHelper.CreateEncryptedRouteString(dataProtectionProvider, controllerName, action, area, additionalRouteVals); + + var result = umbracoContext.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; + return result; + } } } diff --git a/src/Umbraco.Web.Website/Controllers/UmbracoRenderingDefaults.cs b/src/Umbraco.Web.Website/Controllers/UmbracoRenderingDefaults.cs deleted file mode 100644 index 095f57b631..0000000000 --- a/src/Umbraco.Web.Website/Controllers/UmbracoRenderingDefaults.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using Umbraco.Cms.Web.Common.Controllers; - -namespace Umbraco.Cms.Web.Website.Controllers -{ - /// - /// The defaults used for rendering Umbraco front-end pages - /// - public class UmbracoRenderingDefaults : IUmbracoRenderingDefaults - { - /// - public Type DefaultControllerType => typeof(RenderController); - } -} diff --git a/src/Umbraco.Web.Website/Controllers/IUmbracoRenderingDefaults.cs b/src/Umbraco.Web.Website/Controllers/UmbracoRenderingDefaultsOptions.cs similarity index 61% rename from src/Umbraco.Web.Website/Controllers/IUmbracoRenderingDefaults.cs rename to src/Umbraco.Web.Website/Controllers/UmbracoRenderingDefaultsOptions.cs index 6f4fdb0cb2..fb9f02d385 100644 --- a/src/Umbraco.Web.Website/Controllers/IUmbracoRenderingDefaults.cs +++ b/src/Umbraco.Web.Website/Controllers/UmbracoRenderingDefaultsOptions.cs @@ -1,15 +1,16 @@ using System; +using Umbraco.Cms.Web.Common.Controllers; namespace Umbraco.Cms.Web.Website.Controllers { /// /// The defaults used for rendering Umbraco front-end pages /// - public interface IUmbracoRenderingDefaults + public class UmbracoRenderingDefaultsOptions { /// /// Gets the default umbraco render controller type /// - Type DefaultControllerType { get; } + public Type DefaultControllerType { get; set; } = typeof(RenderController); } } diff --git a/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs index 69618004c7..59905e5626 100644 --- a/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs @@ -40,7 +40,6 @@ namespace Umbraco.Extensions builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/src/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactory.cs b/src/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactory.cs index e1b85919f4..5cba399fba 100644 --- a/src/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactory.cs +++ b/src/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactory.cs @@ -2,6 +2,7 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Features; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Routing; @@ -18,7 +19,6 @@ namespace Umbraco.Cms.Web.Website.Routing /// public class UmbracoRouteValuesFactory : IUmbracoRouteValuesFactory { - private readonly IUmbracoRenderingDefaults _renderingDefaults; private readonly IShortStringHelper _shortStringHelper; private readonly UmbracoFeatures _umbracoFeatures; private readonly IControllerActionSearcher _controllerActionSearcher; @@ -30,18 +30,17 @@ namespace Umbraco.Cms.Web.Website.Routing /// Initializes a new instance of the class. /// public UmbracoRouteValuesFactory( - IUmbracoRenderingDefaults renderingDefaults, + IOptions renderingDefaults, IShortStringHelper shortStringHelper, UmbracoFeatures umbracoFeatures, IControllerActionSearcher controllerActionSearcher, IPublishedRouter publishedRouter) { - _renderingDefaults = renderingDefaults; _shortStringHelper = shortStringHelper; _umbracoFeatures = umbracoFeatures; _controllerActionSearcher = controllerActionSearcher; _publishedRouter = publishedRouter; - _defaultControllerName = new Lazy(() => ControllerExtensions.GetControllerName(_renderingDefaults.DefaultControllerType)); + _defaultControllerName = new Lazy(() => ControllerExtensions.GetControllerName(renderingDefaults.Value.DefaultControllerType)); _defaultControllerDescriptor = new Lazy(() => { ControllerActionDescriptor descriptor = _controllerActionSearcher.Find( diff --git a/src/Umbraco.Web/UrlHelperRenderExtensions.cs b/src/Umbraco.Web/UrlHelperRenderExtensions.cs index d1eb46a2b8..70a414ff29 100644 --- a/src/Umbraco.Web/UrlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/UrlHelperRenderExtensions.cs @@ -261,52 +261,52 @@ namespace Umbraco.Web // // #endregion - /// - /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - /// - /// - /// - /// - /// - public static string SurfaceAction(this UrlHelper url, string action, string controllerName) - { - return url.SurfaceAction(action, controllerName, null); - } - - /// - /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - /// - /// - /// - /// - /// - /// - public static string SurfaceAction(this UrlHelper url, string action, string controllerName, object additionalRouteVals) - { - return url.SurfaceAction(action, controllerName, "", additionalRouteVals); - } - - /// - /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - /// - /// - /// - /// - /// - /// - /// - public static string SurfaceAction(this UrlHelper url, string action, string controllerName, string area, object additionalRouteVals) - { - if (action == null) throw new ArgumentNullException(nameof(action)); - if (string.IsNullOrEmpty(action)) throw new ArgumentException("Value can't be empty.", nameof(action)); - if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); - if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); - - var encryptedRoute = CreateEncryptedRouteString(controllerName, action, area, additionalRouteVals); - - var result = Current.UmbracoContext.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; - return result; - } + // /// + // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + // /// + // /// + // /// + // /// + // /// + // public static string SurfaceAction(this UrlHelper url, string action, string controllerName) + // { + // return url.SurfaceAction(action, controllerName, null); + // } + // + // /// + // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + // /// + // /// + // /// + // /// + // /// + // /// + // public static string SurfaceAction(this UrlHelper url, string action, string controllerName, object additionalRouteVals) + // { + // return url.SurfaceAction(action, controllerName, "", additionalRouteVals); + // } + // + // /// + // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + // /// + // /// + // /// + // /// + // /// + // /// + // /// + // public static string SurfaceAction(this UrlHelper url, string action, string controllerName, string area, object additionalRouteVals) + // { + // if (action == null) throw new ArgumentNullException(nameof(action)); + // if (string.IsNullOrEmpty(action)) throw new ArgumentException("Value can't be empty.", nameof(action)); + // if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); + // if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); + // + // var encryptedRoute = CreateEncryptedRouteString(controllerName, action, area, additionalRouteVals); + // + // var result = Current.UmbracoContext.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; + // return result; + // } //TODO Move to LinkGenerator // /// @@ -384,42 +384,42 @@ namespace Umbraco.Web // return url.SurfaceAction(action, typeof (T), additionalRouteVals); // } - /// - /// This is used in methods like BeginUmbracoForm and SurfaceAction to generate an encrypted string which gets submitted in a request for which - /// Umbraco can decrypt during the routing process in order to delegate the request to a specific MVC Controller. - /// - /// - /// - /// - /// - /// - internal static string CreateEncryptedRouteString(string controllerName, string controllerAction, string area, object additionalRouteVals = null) - { - if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); - if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); - if (controllerAction == null) throw new ArgumentNullException(nameof(controllerAction)); - if (string.IsNullOrEmpty(controllerAction)) throw new ArgumentException("Value can't be empty.", nameof(controllerAction)); - if (area == null) throw new ArgumentNullException(nameof(area)); - - //need to create a params string as Base64 to put into our hidden field to use during the routes - var surfaceRouteParams = $"c={HttpUtility.UrlEncode(controllerName)}&a={HttpUtility.UrlEncode(controllerAction)}&ar={area}"; - - //checking if the additional route values is already a dictionary and convert to querystring - string additionalRouteValsAsQuery; - if (additionalRouteVals != null) - { - if (additionalRouteVals is Dictionary additionalRouteValsAsDictionary) - additionalRouteValsAsQuery = additionalRouteValsAsDictionary.ToQueryString(); - else - additionalRouteValsAsQuery = additionalRouteVals.ToDictionary().ToQueryString(); - } - else - additionalRouteValsAsQuery = null; - - if (additionalRouteValsAsQuery.IsNullOrWhiteSpace() == false) - surfaceRouteParams += "&" + additionalRouteValsAsQuery; - - return surfaceRouteParams.EncryptWithMachineKey(); - } + // /// + // /// This is used in methods like BeginUmbracoForm and SurfaceAction to generate an encrypted string which gets submitted in a request for which + // /// Umbraco can decrypt during the routing process in order to delegate the request to a specific MVC Controller. + // /// + // /// + // /// + // /// + // /// + // /// + // internal static string CreateEncryptedRouteString(string controllerName, string controllerAction, string area, object additionalRouteVals = null) + // { + // if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); + // if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); + // if (controllerAction == null) throw new ArgumentNullException(nameof(controllerAction)); + // if (string.IsNullOrEmpty(controllerAction)) throw new ArgumentException("Value can't be empty.", nameof(controllerAction)); + // if (area == null) throw new ArgumentNullException(nameof(area)); + // + // //need to create a params string as Base64 to put into our hidden field to use during the routes + // var surfaceRouteParams = $"c={HttpUtility.UrlEncode(controllerName)}&a={HttpUtility.UrlEncode(controllerAction)}&ar={area}"; + // + // //checking if the additional route values is already a dictionary and convert to querystring + // string additionalRouteValsAsQuery; + // if (additionalRouteVals != null) + // { + // if (additionalRouteVals is Dictionary additionalRouteValsAsDictionary) + // additionalRouteValsAsQuery = additionalRouteValsAsDictionary.ToQueryString(); + // else + // additionalRouteValsAsQuery = additionalRouteVals.ToDictionary().ToQueryString(); + // } + // else + // additionalRouteValsAsQuery = null; + // + // if (additionalRouteValsAsQuery.IsNullOrWhiteSpace() == false) + // surfaceRouteParams += "&" + additionalRouteValsAsQuery; + // + // return surfaceRouteParams.EncryptWithMachineKey(); + // } } }