From 652da168dbe6b1bc1a9c05b8f6c11d5f3695280a Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Tue, 28 Apr 2020 16:00:29 +0200 Subject: [PATCH 01/22] Migrating ActionResults and a dependency class --- .../ActionResults/JsonNetResult.cs | 55 ++++++ .../RedirectToUmbracoPageResult.cs | 174 ++++++++++++++++++ .../RedirectToUmbracoUrlResult.cs | 43 +++++ .../ActionResults/UmbracoPageResult.cs | 168 +++++++++++++++++ src/Umbraco.Web.Website/RouteDefinition.cs | 29 +++ 5 files changed, 469 insertions(+) create mode 100644 src/Umbraco.Web.BackOffice/ActionResults/JsonNetResult.cs create mode 100644 src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs create mode 100644 src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoUrlResult.cs create mode 100644 src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs create mode 100644 src/Umbraco.Web.Website/RouteDefinition.cs diff --git a/src/Umbraco.Web.BackOffice/ActionResults/JsonNetResult.cs b/src/Umbraco.Web.BackOffice/ActionResults/JsonNetResult.cs new file mode 100644 index 0000000000..24f4ec51b8 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/ActionResults/JsonNetResult.cs @@ -0,0 +1,55 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; + +namespace Umbraco.Web.BackOffice.ActionResults +{ + /// + /// Custom json result using newtonsoft json.net + /// + public class JsonNetResult : IActionResult + { + public Encoding ContentEncoding { get; set; } + public string ContentType { get; set; } + public object Data { get; set; } + + public JsonSerializerSettings SerializerSettings { get; set; } + public Formatting Formatting { get; set; } + + public JsonNetResult() + { + SerializerSettings = new JsonSerializerSettings(); + } + + public Task ExecuteResultAsync(ActionContext context) + { + if (context is null) + throw new ArgumentNullException(nameof(context)); + + var response = context.HttpContext.Response; + + response.ContentType = string.IsNullOrEmpty(ContentType) == false + ? ContentType + : System.Net.Mime.MediaTypeNames.Application.Json; + + if (!(ContentEncoding is null)) + response.Headers.Add(Microsoft.Net.Http.Headers.HeaderNames.ContentEncoding, ContentEncoding.ToString()); + + if (!(Data is null)) + { + using var bodyWriter = new StreamWriter(response.Body); + using var writer = new JsonTextWriter(bodyWriter) { Formatting = Formatting }; + + var serializer = JsonSerializer.Create(SerializerSettings); + serializer.Serialize(writer, Data); + + writer.Flush(); + } + + return Task.CompletedTask; + } + } +} diff --git a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs new file mode 100644 index 0000000000..dfc28f1412 --- /dev/null +++ b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Composing; +using Umbraco.Core.IO; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.Routing; + +namespace Umbraco.Web.Website.ActionResults +{ + public class RedirectToUmbracoPageResult : IActionResult + { + private IPublishedContent _publishedContent; + private readonly int _pageId; + private readonly NameValueCollection _queryStringValues; + private readonly IPublishedUrlProvider _publishedUrlProvider; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private string _url; + + private string Url + { + get + { + if (!string.IsNullOrWhiteSpace(_url)) return _url; + + if (PublishedContent is null) + throw new InvalidOperationException($"Cannot redirect, no entity was found for id {PageId}"); + + var result = _publishedUrlProvider.GetUrl(PublishedContent.Id); + + if (result == "#") + throw new InvalidOperationException( + $"Could not route to entity with id {PageId}, the NiceUrlProvider could not generate a URL"); + + _url = result; + + return _url; + } + } + + private int PageId => _pageId; + + private IPublishedContent PublishedContent + { + get + { + if (!(_publishedContent is null)) return _publishedContent; + + //need to get the URL for the page + _publishedContent = _umbracoContextAccessor.UmbracoContext.Content.GetById(_pageId); + + return _publishedContent; + } + } + + /// + /// Creates a new RedirectToUmbracoResult + /// + /// + /// + public RedirectToUmbracoPageResult(int pageId, IPublishedUrlProvider publishedUrlProvider) + { + _pageId = pageId; + _publishedUrlProvider = publishedUrlProvider; + } + + /// + /// Creates a new RedirectToUmbracoResult + /// + /// + /// + /// + public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringValues, IPublishedUrlProvider publishedUrlProvider) + { + _pageId = pageId; + _queryStringValues = queryStringValues; + _publishedUrlProvider = publishedUrlProvider; + } + + /// + /// Creates a new RedirectToUmbracoResult + /// + /// + /// + /// + public RedirectToUmbracoPageResult(int pageId, string queryString, IPublishedUrlProvider publishedUrlProvider) + { + _pageId = pageId; + _queryStringValues = ParseQueryString(queryString); + _publishedUrlProvider = publishedUrlProvider; + } + + /// + /// Creates a new RedirectToUmbracoResult + /// + /// + /// + /// + public RedirectToUmbracoPageResult(IPublishedContent publishedContent, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) + { + _publishedContent = publishedContent; + _pageId = publishedContent.Id; + _publishedUrlProvider = publishedUrlProvider; + _umbracoContextAccessor = umbracoContextAccessor; + } + + /// + /// Creates a new RedirectToUmbracoResult + /// + /// + /// + /// + /// + public RedirectToUmbracoPageResult(IPublishedContent publishedContent, NameValueCollection queryStringValues, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) + { + _publishedContent = publishedContent; + _pageId = publishedContent.Id; + _queryStringValues = queryStringValues; + _publishedUrlProvider = publishedUrlProvider; + _umbracoContextAccessor = umbracoContextAccessor; + } + + /// + /// Creates a new RedirectToUmbracoResult + /// + /// + /// + /// + /// + public RedirectToUmbracoPageResult(IPublishedContent publishedContent, string queryString, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) + { + _publishedContent = publishedContent; + _pageId = publishedContent.Id; + _queryStringValues = ParseQueryString(queryString); + _publishedUrlProvider = publishedUrlProvider; + _umbracoContextAccessor = umbracoContextAccessor; + } + + public Task ExecuteResultAsync(ActionContext context) + { + if (context is null) throw new ArgumentNullException(nameof(context)); + + var httpContext = context.HttpContext; + var ioHelper = httpContext.RequestServices.GetRequiredService(); + var destinationUrl = ioHelper.ResolveUrl(Url); + + if (!(_queryStringValues is null) && _queryStringValues.Count > 0) + { + destinationUrl += "?" + string.Join("&", + _queryStringValues.AllKeys.Select(x => x + "=" + HttpUtility.UrlEncode(_queryStringValues[x]))); + } + + var tempData = httpContext.RequestServices.GetRequiredService(); + tempData?.Keep(); + + httpContext.Response.Redirect(destinationUrl); + + return Task.CompletedTask; + } + + private NameValueCollection ParseQueryString(string queryString) + { + return !string.IsNullOrEmpty(queryString) ? HttpUtility.ParseQueryString(queryString) : null; + } + } +} diff --git a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoUrlResult.cs b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoUrlResult.cs new file mode 100644 index 0000000000..929cdb85e6 --- /dev/null +++ b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoUrlResult.cs @@ -0,0 +1,43 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.Extensions.DependencyInjection; + +namespace Umbraco.Web.Website.ActionResults +{ + /// + /// Redirects to the current URL rendering an Umbraco page including it's query strings + /// + /// + /// This is useful if you need to redirect + /// to the current page but the current page is actually a rewritten URL normally done with something like + /// Server.Transfer. It is also handy if you want to persist the query strings. + /// + public class RedirectToUmbracoUrlResult : IActionResult + { + private readonly IUmbracoContext _umbracoContext; + + /// + /// Creates a new RedirectToUmbracoResult + /// + /// + public RedirectToUmbracoUrlResult(IUmbracoContext umbracoContext) + { + _umbracoContext = umbracoContext; + } + + public Task ExecuteResultAsync(ActionContext context) + { + if (context is null) throw new ArgumentNullException(nameof(context)); + + var destinationUrl = _umbracoContext.OriginalRequestUrl.PathAndQuery; + var tempData = context.HttpContext.RequestServices.GetRequiredService(); + tempData?.Keep(); + + context.HttpContext.Response.Redirect(destinationUrl); + + return Task.CompletedTask; + } + } +} diff --git a/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs b/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs new file mode 100644 index 0000000000..5019dbd7c2 --- /dev/null +++ b/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewEngines; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Core; +using Umbraco.Core.Logging; + +namespace Umbraco.Web.Website.ActionResults +{ + /// + /// Used by posted forms to proxy the result to the page in which the current URL matches on + /// + public class UmbracoPageResult : IActionResult + { + private readonly IProfilingLogger _profilingLogger; + + public UmbracoPageResult(IProfilingLogger profilingLogger) + { + _profilingLogger = profilingLogger; + } + + public Task ExecuteResultAsync(ActionContext context) + { + var routeData = context.RouteData; + + ResetRouteData(routeData); + ValidateRouteData(routeData); + + var routeDef = (RouteDefinition)routeData.DataTokens[Constants.Web.UmbracoRouteDefinitionDataToken]; + routeData.Values["action"] = routeDef.ActionName; + + var factory = context.HttpContext.RequestServices.GetRequiredService(); + Controller controller = null; + + if (!(context is ControllerContext controllerContext)) + return Task.FromCanceled(new System.Threading.CancellationToken()); + + try + { + controller = CreateController(controllerContext, factory, routeDef); + + CopyControllerData(controllerContext, controller); + + ExecuteControllerAction(controllerContext, controller); + } + finally + { + CleanupController(controllerContext, controller, factory); + } + + return Task.CompletedTask; + } + + /// + /// Executes the controller action + /// + private void ExecuteControllerAction(ControllerContext context, Controller controller) + { + using (_profilingLogger.TraceDuration("Executing Umbraco RouteDefinition controller", "Finished")) + { + var aec = new ActionExecutingContext(context, new List(), new Dictionary(), controller); + var actionExecutedDelegate = CreateActionExecutedDelegate(aec); + + controller.OnActionExecutionAsync(aec, actionExecutedDelegate); + } + } + + /// + /// Creates action execution delegate from ActionExecutingContext + /// + private static ActionExecutionDelegate CreateActionExecutedDelegate(ActionExecutingContext context) + { + var actionExecutedContext = new ActionExecutedContext(context, context.Filters, context.Controller) + { + Result = context.Result, + }; + return () => Task.FromResult(actionExecutedContext); + } + + /// + /// Since we could be returning the current page from a surface controller posted values in which the routing values are changed, we + /// need to revert these values back to nothing in order for the normal page to render again. + /// + private static void ResetRouteData(RouteData routeData) + { + routeData.DataTokens["area"] = null; + routeData.DataTokens["Namespaces"] = null; + } + + /// + /// Validate that the current page execution is not being handled by the normal umbraco routing system + /// + private static void ValidateRouteData(RouteData routeData) + { + if (routeData.DataTokens.ContainsKey(Constants.Web.UmbracoRouteDefinitionDataToken) == false) + { + throw new InvalidOperationException("Can only use " + typeof(UmbracoPageResult).Name + + " in the context of an Http POST when using a SurfaceController form"); + } + } + + /// + /// Ensure ModelState, ViewData and TempData is copied across + /// + private static void CopyControllerData(ControllerContext context, Controller controller) + { + controller.ViewData.ModelState.Merge(context.ModelState); + + foreach (var d in controller.ViewData) + controller.ViewData[d.Key] = d.Value; + + // We cannot simply merge the temp data because during controller execution it will attempt to 'load' temp data + // but since it has not been saved, there will be nothing to load and it will revert to nothing, so the trick is + // to Save the state of the temp data first then it will automatically be picked up. + // http://issues.umbraco.org/issue/U4-1339 + + var targetController = controller; + var tempData = context.HttpContext.RequestServices.GetRequiredService(); + + targetController.TempData = tempData; + targetController.TempData.Save(); + } + + /// + /// Creates a controller using the controller factory + /// + private static Controller CreateController(ControllerContext context, IControllerFactory factory, RouteDefinition routeDef) + { + if (!(factory.CreateController(context) is Controller controller)) + throw new InvalidOperationException("Could not create controller with name " + routeDef.ControllerName + "."); + + return controller; + } + + /// + /// Cleans up the controller by releasing it using the controller factory, and by disposing it. + /// + private static void CleanupController(ControllerContext context, Controller controller, IControllerFactory factory) + { + if (!(controller is null)) + factory.ReleaseController(context, controller); + + controller?.DisposeIfDisposable(); + } + + private class DummyView : IView + { + public DummyView(string path) + { + Path = path; + } + + public Task RenderAsync(ViewContext context) + { + return Task.CompletedTask; + } + + public string Path { get; } + } + } +} diff --git a/src/Umbraco.Web.Website/RouteDefinition.cs b/src/Umbraco.Web.Website/RouteDefinition.cs new file mode 100644 index 0000000000..02eab6ae77 --- /dev/null +++ b/src/Umbraco.Web.Website/RouteDefinition.cs @@ -0,0 +1,29 @@ +using System; +using Umbraco.Web.Routing; + +namespace Umbraco.Web.Website +{ + /// + /// Represents the data required to route to a specific controller/action during an Umbraco request + /// + public class RouteDefinition + { + public string ControllerName { get; set; } + public string ActionName { get; set; } + + /// + /// The Controller type found for routing to + /// + public Type ControllerType { get; set; } + + /// + /// Everything related to the current content request including the requested content + /// + public IPublishedRequest PublishedRequest { get; set; } + + /// + /// Gets/sets whether the current request has a hijacked route/user controller routed for it + /// + public bool HasHijackedRoute { get; set; } + } +} From 450226a2c35c8de18d3ad9526f411c8541a2e402 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Tue, 28 Apr 2020 16:01:11 +0200 Subject: [PATCH 02/22] Marking classes as "Migrated" --- src/Umbraco.Web/Mvc/JsonNetResult.cs | 1 + src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs | 1 + src/Umbraco.Web/Mvc/RedirectToUmbracoUrlResult.cs | 1 + src/Umbraco.Web/Mvc/RouteDefinition.cs | 1 + src/Umbraco.Web/Mvc/UmbracoPageResult.cs | 1 + 5 files changed, 5 insertions(+) diff --git a/src/Umbraco.Web/Mvc/JsonNetResult.cs b/src/Umbraco.Web/Mvc/JsonNetResult.cs index da6780451e..c086b5f375 100644 --- a/src/Umbraco.Web/Mvc/JsonNetResult.cs +++ b/src/Umbraco.Web/Mvc/JsonNetResult.cs @@ -13,6 +13,7 @@ namespace Umbraco.Web.Mvc /// /// Custom json result using newtonsoft json.net /// + /// Migrated already to .Net Core public class JsonNetResult : ActionResult { public Encoding ContentEncoding { get; set; } diff --git a/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs b/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs index 46d6019860..e379f569e0 100644 --- a/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs +++ b/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs @@ -14,6 +14,7 @@ namespace Umbraco.Web.Mvc /// /// Redirects to an Umbraco page by Id or Entity /// + /// Migrated already to .Net Core public class RedirectToUmbracoPageResult : ActionResult { private IPublishedContent _publishedContent; diff --git a/src/Umbraco.Web/Mvc/RedirectToUmbracoUrlResult.cs b/src/Umbraco.Web/Mvc/RedirectToUmbracoUrlResult.cs index 3690e98790..6f97bff534 100644 --- a/src/Umbraco.Web/Mvc/RedirectToUmbracoUrlResult.cs +++ b/src/Umbraco.Web/Mvc/RedirectToUmbracoUrlResult.cs @@ -11,6 +11,7 @@ namespace Umbraco.Web.Mvc /// to the current page but the current page is actually a rewritten URL normally done with something like /// Server.Transfer. It is also handy if you want to persist the query strings. /// + /// Migrated already to .Net Core public class RedirectToUmbracoUrlResult : ActionResult { private readonly IUmbracoContext _umbracoContext; diff --git a/src/Umbraco.Web/Mvc/RouteDefinition.cs b/src/Umbraco.Web/Mvc/RouteDefinition.cs index 94f97e7e11..45e759fd66 100644 --- a/src/Umbraco.Web/Mvc/RouteDefinition.cs +++ b/src/Umbraco.Web/Mvc/RouteDefinition.cs @@ -7,6 +7,7 @@ namespace Umbraco.Web.Mvc /// /// Represents the data required to route to a specific controller/action during an Umbraco request /// + /// Migrated already to .Net Core public class RouteDefinition { public string ControllerName { get; set; } diff --git a/src/Umbraco.Web/Mvc/UmbracoPageResult.cs b/src/Umbraco.Web/Mvc/UmbracoPageResult.cs index 75804b47bb..30c990a981 100644 --- a/src/Umbraco.Web/Mvc/UmbracoPageResult.cs +++ b/src/Umbraco.Web/Mvc/UmbracoPageResult.cs @@ -10,6 +10,7 @@ namespace Umbraco.Web.Mvc /// /// Used by posted forms to proxy the result to the page in which the current URL matches on /// + /// Migrated already to .Net Core public class UmbracoPageResult : ActionResult { private readonly IProfilingLogger _profilingLogger; From d33aa6cdf5053613d95de17ff1d628637103054a Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Mon, 11 May 2020 16:23:33 +0200 Subject: [PATCH 03/22] Updating the way we get UmbracoContext in RedirectToUmbracoPageResult.cs; removing the flushing in JsonNetResult.cs and adding necessary configuration for allowing sync operations needed in JsonNetResult.cs --- .../ActionResults/JsonNetResult.cs | 3 --- src/Umbraco.Web.UI.NetCore/Startup.cs | 11 +++++++++-- .../ActionResults/RedirectToUmbracoPageResult.cs | 10 +++++----- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/ActionResults/JsonNetResult.cs b/src/Umbraco.Web.BackOffice/ActionResults/JsonNetResult.cs index 24f4ec51b8..db5c7a8510 100644 --- a/src/Umbraco.Web.BackOffice/ActionResults/JsonNetResult.cs +++ b/src/Umbraco.Web.BackOffice/ActionResults/JsonNetResult.cs @@ -42,11 +42,8 @@ namespace Umbraco.Web.BackOffice.ActionResults { using var bodyWriter = new StreamWriter(response.Body); using var writer = new JsonTextWriter(bodyWriter) { Formatting = Formatting }; - var serializer = JsonSerializer.Create(SerializerSettings); serializer.Serialize(writer, Data); - - writer.Flush(); } return Task.CompletedTask; diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index 37440006aa..fb491e6d2c 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -2,6 +2,7 @@ using System; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -53,10 +54,16 @@ namespace Umbraco.Web.UI.BackOffice options.ShouldProfile = request => false; // WebProfiler determine and start profiling. We should not use the MiniProfilerMiddleware to also profile }); + // If using Kestrel: https://stackoverflow.com/a/55196057 + services.Configure(options => + { + options.AllowSynchronousIO = true; + }); + //Finally initialize Current // TODO: This should be moved to the UmbracoServiceProviderFactory when the container is cross-wired and then don't use the overload above to `out var factory` Current.Initialize( - factory.GetInstance (), + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), @@ -70,7 +77,7 @@ namespace Umbraco.Web.UI.BackOffice public void Configure(IApplicationBuilder app) { - // app.UseMiniProfiler(); + // app.UseMiniProfiler(); app.UseUmbracoRequest(); if (_env.IsDevelopment()) { diff --git a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs index dfc28f1412..3d06ca101e 100644 --- a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs +++ b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs @@ -1,21 +1,21 @@ using System; -using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; -using System.Text; using System.Threading.Tasks; using System.Web; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.Extensions.DependencyInjection; -using Umbraco.Composing; +using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.Routing; namespace Umbraco.Web.Website.ActionResults { + /// + /// Redirects to an Umbraco page by Id or Entity + /// public class RedirectToUmbracoPageResult : IActionResult { private IPublishedContent _publishedContent; @@ -55,7 +55,7 @@ namespace Umbraco.Web.Website.ActionResults if (!(_publishedContent is null)) return _publishedContent; //need to get the URL for the page - _publishedContent = _umbracoContextAccessor.UmbracoContext.Content.GetById(_pageId); + _publishedContent = _umbracoContextAccessor.GetRequiredUmbracoContext().Content.GetById(_pageId); return _publishedContent; } From 0e47718d0cca8c139a954fc94dc9f390362b2a52 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Mon, 11 May 2020 16:44:08 +0200 Subject: [PATCH 04/22] Migrating 2 actions in relation to testing JsonNetResult action result --- .../Controllers/BackOfficeController.cs | 67 ++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index 79612fec45..3c334e14ee 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -1,10 +1,18 @@ -using System.Threading.Tasks; +using System; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.Grid; using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Runtime; +using Umbraco.Core.Services; using Umbraco.Core.WebAssets; +using Umbraco.Web.BackOffice.ActionResults; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.ActionResults; using Umbraco.Web.WebAssets; @@ -16,12 +24,18 @@ namespace Umbraco.Web.BackOffice.Controllers private readonly IRuntimeMinifier _runtimeMinifier; private readonly IGlobalSettings _globalSettings; private readonly IHostingEnvironment _hostingEnvironment; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly ILocalizedTextService _textService; + private readonly IGridConfig _gridConfig; - public BackOfficeController(IRuntimeMinifier runtimeMinifier, IGlobalSettings globalSettings, IHostingEnvironment hostingEnvironment) + public BackOfficeController(IRuntimeMinifier runtimeMinifier, IGlobalSettings globalSettings, IHostingEnvironment hostingEnvironment, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IGridConfig gridConfig) { _runtimeMinifier = runtimeMinifier; _globalSettings = globalSettings; _hostingEnvironment = hostingEnvironment; + _umbracoContextAccessor = umbracoContextAccessor; + _textService = textService; + _gridConfig = gridConfig ?? throw new ArgumentNullException(nameof(gridConfig)); } // GET @@ -41,5 +55,54 @@ namespace Umbraco.Web.BackOffice.Controllers return new JavaScriptResult(result); } + + /// + /// Get the json localized text for a given culture or the culture for the current user + /// + /// + /// + [HttpGet] + public JsonNetResult LocalizedText(string culture = null) + { + //var securityHelper = _umbracoContextAccessor.GetRequiredUmbracoContext().Security; + //securityHelper.IsAuthenticated() + var isAuth = false; + + var cultureInfo = string.IsNullOrWhiteSpace(culture) + //if the user is logged in, get their culture, otherwise default to 'en' + ? isAuth + //current culture is set at the very beginning of each request + ? Thread.CurrentThread.CurrentCulture + : CultureInfo.GetCultureInfo(_globalSettings.DefaultUILanguage) + : CultureInfo.GetCultureInfo(culture); + + var allValues = _textService.GetAllStoredValues(cultureInfo); + var pathedValues = allValues.Select(kv => + { + var slashIndex = kv.Key.IndexOf('/'); + var areaAlias = kv.Key.Substring(0, slashIndex); + var valueAlias = kv.Key.Substring(slashIndex + 1); + return new + { + areaAlias, + valueAlias, + value = kv.Value + }; + }); + + var nestedDictionary = pathedValues + .GroupBy(pv => pv.areaAlias) + .ToDictionary(pv => pv.Key, pv => + pv.ToDictionary(pve => pve.valueAlias, pve => pve.value)); + + return new JsonNetResult { Data = nestedDictionary, Formatting = Formatting.None }; + } + + //[UmbracoAuthorize(Order = 0)] TODO: Re-implement UmbracoAuthorizeAttribute + [HttpGet] + public JsonNetResult GetGridConfig() + { + return new JsonNetResult { Data = _gridConfig.EditorsConfig.Editors, Formatting = Formatting.None }; + } } } From 87ce202a1ec2cda7b7aa3459017f238a7e09a9cd Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Mon, 11 May 2020 17:05:57 +0200 Subject: [PATCH 05/22] Marking actions in old BackOfficeController as migrated. Updating the netcore BackOfficeController after merge --- .../Controllers/BackOfficeController.cs | 67 ++++++++++++++++++- .../Editors/BackOfficeController.cs | 4 +- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index 429b2e625a..d776749e6b 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -1,9 +1,18 @@ -using System.Threading.Tasks; +using System; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.Grid; using Umbraco.Core.Hosting; +using Umbraco.Core.Services; using Umbraco.Core.WebAssets; using Umbraco.Net; +using Umbraco.Web.BackOffice.ActionResults; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.ActionResults; using Umbraco.Web.WebAssets; @@ -16,13 +25,19 @@ namespace Umbraco.Web.BackOffice.Controllers private readonly IGlobalSettings _globalSettings; private readonly IHostingEnvironment _hostingEnvironment; private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly ILocalizedTextService _textService; + private readonly IGridConfig _gridConfig; - public BackOfficeController(IRuntimeMinifier runtimeMinifier, IGlobalSettings globalSettings, IHostingEnvironment hostingEnvironment, IUmbracoApplicationLifetime umbracoApplicationLifetime) + public BackOfficeController(IRuntimeMinifier runtimeMinifier, IGlobalSettings globalSettings, IHostingEnvironment hostingEnvironment, IUmbracoApplicationLifetime umbracoApplicationLifetime, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IGridConfig gridConfig) { _runtimeMinifier = runtimeMinifier; _globalSettings = globalSettings; _hostingEnvironment = hostingEnvironment; _umbracoApplicationLifetime = umbracoApplicationLifetime; + _umbracoContextAccessor = umbracoContextAccessor; + _textService = textService; + _gridConfig = gridConfig ?? throw new ArgumentNullException(nameof(gridConfig)); } // GET @@ -42,5 +57,53 @@ namespace Umbraco.Web.BackOffice.Controllers return new JavaScriptResult(result); } + + /// + /// Get the json localized text for a given culture or the culture for the current user + /// + /// + /// + [HttpGet] + public JsonNetResult LocalizedText(string culture = null) + { + var securityHelper = _umbracoContextAccessor.GetRequiredUmbracoContext().Security; + var isAuthenticated = securityHelper.IsAuthenticated(); + + var cultureInfo = string.IsNullOrWhiteSpace(culture) + //if the user is logged in, get their culture, otherwise default to 'en' + ? isAuthenticated + //current culture is set at the very beginning of each request + ? Thread.CurrentThread.CurrentCulture + : CultureInfo.GetCultureInfo(_globalSettings.DefaultUILanguage) + : CultureInfo.GetCultureInfo(culture); + + var allValues = _textService.GetAllStoredValues(cultureInfo); + var pathedValues = allValues.Select(kv => + { + var slashIndex = kv.Key.IndexOf('/'); + var areaAlias = kv.Key.Substring(0, slashIndex); + var valueAlias = kv.Key.Substring(slashIndex + 1); + return new + { + areaAlias, + valueAlias, + value = kv.Value + }; + }); + + var nestedDictionary = pathedValues + .GroupBy(pv => pv.areaAlias) + .ToDictionary(pv => pv.Key, pv => + pv.ToDictionary(pve => pve.valueAlias, pve => pve.value)); + + return new JsonNetResult { Data = nestedDictionary, Formatting = Formatting.None }; + } + + //[UmbracoAuthorize(Order = 0)] TODO: Re-implement UmbracoAuthorizeAttribute + [HttpGet] + public JsonNetResult GetGridConfig() + { + return new JsonNetResult { Data = _gridConfig.EditorsConfig.Editors, Formatting = Formatting.None }; + } } } diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 7e3638d96a..825b67767a 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -194,6 +194,7 @@ namespace Umbraco.Web.Editors /// /// /// + /// Migrated already to .Net Core [HttpGet] public JsonNetResult LocalizedText(string culture = null) { @@ -239,7 +240,8 @@ namespace Umbraco.Web.Editors return JavaScript(result); } - + + /// Migrated already to .Net Core [UmbracoAuthorize(Order = 0)] [HttpGet] public JsonNetResult GetGridConfig() From 0eefbfd449e4b7bee2b79b75fc6fd893dae426be Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Mon, 11 May 2020 20:29:38 +0200 Subject: [PATCH 06/22] Migrating SurfaceController --- .../Controllers/SurfaceController.cs | 178 ++++++++++++++++++ src/Umbraco.Web/Mvc/SurfaceController.cs | 2 + 2 files changed, 180 insertions(+) create mode 100644 src/Umbraco.Web.Website/Controllers/SurfaceController.cs diff --git a/src/Umbraco.Web.Website/Controllers/SurfaceController.cs b/src/Umbraco.Web.Website/Controllers/SurfaceController.cs new file mode 100644 index 0000000000..22b50250af --- /dev/null +++ b/src/Umbraco.Web.Website/Controllers/SurfaceController.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Specialized; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; +using Umbraco.Web.Common.Controllers; +using Umbraco.Web.Routing; +using Umbraco.Web.Website.ActionResults; + +namespace Umbraco.Web.Website.Controllers +{ + /// + /// Provides a base class for front-end add-in controllers. + /// + public abstract class SurfaceController : PluginController + { + private readonly IPublishedUrlProvider _publishedUrlProvider; + + /// + /// Gets the current page. + /// + protected virtual IPublishedContent CurrentPage + { + get + { + var routeDefAttempt = TryGetRouteDefinitionFromAncestorViewContexts(); + if (routeDefAttempt.Success == false) + throw routeDefAttempt.Exception; + + var routeDef = routeDefAttempt.Result; + return routeDef.PublishedRequest.PublishedContent; + } + } + + protected SurfaceController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, IPublishedUrlProvider publishedUrlProvider) + : base(umbracoContextAccessor, databaseFactory, services, appCaches, logger, profilingLogger) + { + _publishedUrlProvider = publishedUrlProvider; + } + + /// + /// Redirects to the Umbraco page with the given id + /// + /// + /// + protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId) + { + return new RedirectToUmbracoPageResult(pageId, _publishedUrlProvider); + } + + /// + /// Redirects to the Umbraco page with the given id and passes provided querystring + /// + /// + /// + /// + protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, NameValueCollection queryStringValues) + { + return new RedirectToUmbracoPageResult(pageId, queryStringValues, _publishedUrlProvider); + } + + /// + /// Redirects to the Umbraco page with the given id and passes provided querystring + /// + /// + /// + /// + protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, string queryString) + { + return new RedirectToUmbracoPageResult(pageId, queryString, _publishedUrlProvider); + } + + /// + /// Redirects to the Umbraco page with the given published content + /// + /// + /// + protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent) + { + return new RedirectToUmbracoPageResult(publishedContent, _publishedUrlProvider, UmbracoContextAccessor); + } + + /// + /// Redirects to the Umbraco page with the given published content and passes provided querystring + /// + /// + /// + /// + protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent, NameValueCollection queryStringValues) + { + return new RedirectToUmbracoPageResult(publishedContent, queryStringValues, _publishedUrlProvider, UmbracoContextAccessor); + } + + /// + /// Redirects to the Umbraco page with the given published content and passes provided querystring + /// + /// + /// + /// + protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent, string queryString) + { + return new RedirectToUmbracoPageResult(publishedContent, queryString, _publishedUrlProvider, UmbracoContextAccessor); + } + + /// + /// Redirects to the currently rendered Umbraco page + /// + /// + protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage() + { + return new RedirectToUmbracoPageResult(CurrentPage, _publishedUrlProvider, UmbracoContextAccessor); + } + + /// + /// Redirects to the currently rendered Umbraco page and passes provided querystring + /// + /// + /// + protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage(NameValueCollection queryStringValues) + { + return new RedirectToUmbracoPageResult(CurrentPage, queryStringValues, _publishedUrlProvider, UmbracoContextAccessor); + } + + /// + /// Redirects to the currently rendered Umbraco page and passes provided querystring + /// + /// + /// + protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage(string queryString) + { + return new RedirectToUmbracoPageResult(CurrentPage, queryString, _publishedUrlProvider, UmbracoContextAccessor); + } + + /// + /// Redirects to the currently rendered Umbraco URL + /// + /// + /// + /// This is useful if you need to redirect + /// to the current page but the current page is actually a rewritten URL normally done with something like + /// Server.Transfer.* + /// + protected RedirectToUmbracoUrlResult RedirectToCurrentUmbracoUrl() + { + return new RedirectToUmbracoUrlResult(UmbracoContext); + } + + /// + /// Returns the currently rendered Umbraco page + /// + /// + protected UmbracoPageResult CurrentUmbracoPage() + { + return new UmbracoPageResult(ProfilingLogger); + } + + /// + /// we need to recursively find the route definition based on the parent view context + /// + /// + private Attempt TryGetRouteDefinitionFromAncestorViewContexts() + { + var currentContext = ControllerContext; + while (!(currentContext is null)) + { + var currentRouteData = currentContext.RouteData; + if (currentRouteData.DataTokens.ContainsKey(Core.Constants.Web.UmbracoRouteDefinitionDataToken)) + return Attempt.Succeed((RouteDefinition)currentRouteData.DataTokens[Core.Constants.Web.UmbracoRouteDefinitionDataToken]); + } + + return Attempt.Fail( + new InvalidOperationException("Cannot find the Umbraco route definition in the route values, the request must be made in the context of an Umbraco request")); + } + } +} diff --git a/src/Umbraco.Web/Mvc/SurfaceController.cs b/src/Umbraco.Web/Mvc/SurfaceController.cs index 877a3c31d8..b9c857a8b3 100644 --- a/src/Umbraco.Web/Mvc/SurfaceController.cs +++ b/src/Umbraco.Web/Mvc/SurfaceController.cs @@ -13,6 +13,8 @@ namespace Umbraco.Web.Mvc /// /// Provides a base class for front-end add-in controllers. /// + /// Migrated already to .Net Core without MergeModelStateToChildAction and MergeParentContextViewData action filters + /// TODO: Migrate MergeModelStateToChildAction and MergeParentContextViewData action filters [MergeModelStateToChildAction] [MergeParentContextViewData] public abstract class SurfaceController : PluginController From 8b047b55a08f19e478b89f7ccd0519cc9c6d8c38 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Date: Mon, 11 May 2020 21:24:21 +0200 Subject: [PATCH 07/22] Uncommenting testing implementation --- .../Controllers/BackOfficeController.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index 3c334e14ee..a76120875b 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -64,13 +64,12 @@ namespace Umbraco.Web.BackOffice.Controllers [HttpGet] public JsonNetResult LocalizedText(string culture = null) { - //var securityHelper = _umbracoContextAccessor.GetRequiredUmbracoContext().Security; - //securityHelper.IsAuthenticated() - var isAuth = false; + var securityHelper = _umbracoContextAccessor.GetRequiredUmbracoContext().Security; + var isAuthenticated = securityHelper.IsAuthenticated(); var cultureInfo = string.IsNullOrWhiteSpace(culture) //if the user is logged in, get their culture, otherwise default to 'en' - ? isAuth + ? isAuthenticated //current culture is set at the very beginning of each request ? Thread.CurrentThread.CurrentCulture : CultureInfo.GetCultureInfo(_globalSettings.DefaultUILanguage) From 33768e380653ae6083c13c588af0f7a5675c7d7c Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Tue, 12 May 2020 07:26:03 +0200 Subject: [PATCH 08/22] Adding missing import --- src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index a76120875b..ee5d03ce7f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; +using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Grid; using Umbraco.Core.Hosting; From 3672e6110405ff194f1687e8a13fbaa3e54cc1a2 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Tue, 12 May 2020 08:10:32 +0200 Subject: [PATCH 09/22] Resolving service dependency --- .../ActionResults/RedirectToUmbracoPageResult.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs index 3d06ca101e..88ad704938 100644 --- a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs +++ b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs @@ -149,7 +149,7 @@ namespace Umbraco.Web.Website.ActionResults if (context is null) throw new ArgumentNullException(nameof(context)); var httpContext = context.HttpContext; - var ioHelper = httpContext.RequestServices.GetRequiredService(); + var ioHelper = httpContext.RequestServices.GetRequiredService(); var destinationUrl = ioHelper.ResolveUrl(Url); if (!(_queryStringValues is null) && _queryStringValues.Count > 0) From 86cef945660c69d89ec49c8e2db980eda2246a1b Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Tue, 12 May 2020 16:11:11 +0200 Subject: [PATCH 10/22] Re-implementing SurfaceControllerTests --- .../SurfaceControllerTests.cs | 234 ++++++++++++++++++ .../Umbraco.Tests.Integration.csproj | 1 + 2 files changed, 235 insertions(+) create mode 100644 src/Umbraco.Tests.Integration/SurfaceControllerTests.cs diff --git a/src/Umbraco.Tests.Integration/SurfaceControllerTests.cs b/src/Umbraco.Tests.Integration/SurfaceControllerTests.cs new file mode 100644 index 0000000000..bb9379c234 --- /dev/null +++ b/src/Umbraco.Tests.Integration/SurfaceControllerTests.cs @@ -0,0 +1,234 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Cache; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; +using Umbraco.Tests.Common; +using Umbraco.Tests.Integration.Implementations; +using Umbraco.Tests.Integration.Testing; +using Umbraco.Tests.Testing; +using Umbraco.Web; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Web.Website.Controllers; + +namespace Umbraco.Tests.Integration +{ + [TestFixture] + [UmbracoTest(WithApplication = true)] + public class SurfaceControllerTests : UmbracoIntegrationTest + { + private IUmbracoContextAccessor _umbracoContextAccessor; + + [SetUp] + public void SetUp() + { + _umbracoContextAccessor = new TestUmbracoContextAccessor(); + } + + [Test] + public void Can_Construct_And_Get_Result() + { + var testHelper = new TestHelper(); + var httpContextAccessor = testHelper.GetHttpContextAccessor(); + var hostingEnvironment = testHelper.GetHostingEnvironment(); + + var umbracoContextFactory = new UmbracoContextFactory( + _umbracoContextAccessor, + Mock.Of(), + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), + GlobalSettings, + Mock.Of(), + hostingEnvironment, + new UriUtility(hostingEnvironment), + httpContextAccessor, + Mock.Of(), + Mock.Of()); + + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); + var umbracoContext = umbracoContextReference.UmbracoContext; + + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + + var ctrl = new TestSurfaceController(umbracoContextAccessor, Mock.Of(), Mock.Of()); + + var result = ctrl.Index(); + + Assert.IsNotNull(result); + } + + [Test] + public void Umbraco_Context_Not_Null() + { + var testHelper = new TestHelper(); + var httpContextAccessor = testHelper.GetHttpContextAccessor(); + var hostingEnvironment = testHelper.GetHostingEnvironment(); + + var umbracoContextFactory = new UmbracoContextFactory( + _umbracoContextAccessor, + Mock.Of(), + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), + GlobalSettings, + Mock.Of(), + hostingEnvironment, + new UriUtility(hostingEnvironment), + httpContextAccessor, + Mock.Of(), + Mock.Of()); + + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); + var umbCtx = umbracoContextReference.UmbracoContext; + + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbCtx); + + var ctrl = new TestSurfaceController(umbracoContextAccessor, Mock.Of(), Mock.Of()); + + Assert.IsNotNull(ctrl.UmbracoContext); + } + + [Test] + public void Can_Lookup_Content() + { + var publishedSnapshot = new Mock(); + publishedSnapshot.Setup(x => x.Members).Returns(Mock.Of()); + var content = new Mock(); + content.Setup(x => x.Id).Returns(2); + + var publishedSnapshotService = new Mock(); + var testHelper = new TestHelper(); + var httpContextAccessor = testHelper.GetHttpContextAccessor(); + var hostingEnvironment = testHelper.GetHostingEnvironment(); + + var umbracoContextFactory = new UmbracoContextFactory( + _umbracoContextAccessor, + publishedSnapshotService.Object, + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), + GlobalSettings, + Mock.Of(), + hostingEnvironment, + new UriUtility(hostingEnvironment), + httpContextAccessor, + Mock.Of(), + Mock.Of()); + + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); + var umbracoContext = umbracoContextReference.UmbracoContext; + + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + + var publishedContentQuery = Mock.Of(query => query.Content(2) == content.Object); + + var ctrl = new TestSurfaceController(umbracoContextAccessor, publishedContentQuery, Mock.Of()); + var result = ctrl.GetContent(2) as PublishedContentResult; + + Assert.IsNotNull(result); + Assert.IsNotNull(result.Content); + Assert.AreEqual(2, result.Content.Id); + } + + /* + [Test] + public void Mock_Current_Page() + { + var testHelper = new TestHelper(); + var httpContextAccessor = testHelper.GetHttpContextAccessor(); + var hostingEnvironment = testHelper.GetHostingEnvironment(); + + var umbracoContextFactory = new UmbracoContextFactory( + _umbracoContextAccessor, + Mock.Of(), + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), + GlobalSettings, + Mock.Of(), + hostingEnvironment, + new UriUtility(hostingEnvironment), + httpContextAccessor, + Mock.Of(), + Mock.Of()); + + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(); + var umbracoContext = umbracoContextReference.UmbracoContext; + + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + + var content = Mock.Of(publishedContent => publishedContent.Id == 12345); + + // TODO: Figure out how to create published router + var publishedRouter = BaseWebTest.CreatePublishedRouter(TestHelpers.SettingsForTests.GenerateMockWebRoutingSettings()); + var frequest = publishedRouter.CreateRequest(umbracoContext, new Uri("http://localhost/test")); + frequest.PublishedContent = content; + + var routeDefinition = new RouteDefinition + { + PublishedRequest = frequest + }; + + var routeData = new RouteData(); + routeData.DataTokens.Add(Core.Constants.Web.UmbracoRouteDefinitionDataToken, routeDefinition); + + var ctrl = new TestSurfaceController(umbracoContextAccessor, Mock.Of(), Mock.Of()); + ctrl.ControllerContext = new ControllerContext() + { + HttpContext = Mock.Of(), + RouteData = routeData + }; + + var result = ctrl.GetContentFromCurrentPage() as PublishedContentResult; + + Assert.AreEqual(12345, result.Content.Id); + } + */ + + public class TestSurfaceController : SurfaceController + { + private readonly IPublishedContentQuery _publishedContentQuery; + + public TestSurfaceController(IUmbracoContextAccessor umbracoContextAccessor, IPublishedContentQuery publishedContentQuery, IPublishedUrlProvider publishedUrlProvider) + : base(umbracoContextAccessor, null, ServiceContext.CreatePartial(), AppCaches.Disabled, null, null, publishedUrlProvider) + { + _publishedContentQuery = publishedContentQuery; + } + + public IActionResult Index() + { + // ReSharper disable once Mvc.ViewNotResolved + return View(); + } + + public IActionResult GetContent(int id) + { + var content = _publishedContentQuery.Content(id); + + return new PublishedContentResult(content); + } + + public IActionResult GetContentFromCurrentPage() + { + var content = CurrentPage; + + return new PublishedContentResult(content); + } + } + + public class PublishedContentResult : IActionResult + { + public IPublishedContent Content { get; set; } + + public PublishedContentResult(IPublishedContent content) + { + Content = content; + } + + public Task ExecuteResultAsync(ActionContext context) + { + return Task.CompletedTask; + } + } + } +} diff --git a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index e203d378ef..1b49f4d7bc 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -30,6 +30,7 @@ + From d3b15c4a41dba0f00b039b7ee1fc4a5022bf93f9 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 12 May 2020 19:12:57 +0200 Subject: [PATCH 11/22] AB6455 Fix missing umbracoContextAccessor in ctors --- .../ActionResults/RedirectToUmbracoPageResult.cs | 9 ++++++--- .../ActionResults/RedirectToUmbracoUrlResult.cs | 3 ++- src/Umbraco.Web.Website/Controllers/SurfaceController.cs | 8 ++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs index 88ad704938..54c220cd55 100644 --- a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs +++ b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs @@ -66,10 +66,11 @@ namespace Umbraco.Web.Website.ActionResults /// /// /// - public RedirectToUmbracoPageResult(int pageId, IPublishedUrlProvider publishedUrlProvider) + public RedirectToUmbracoPageResult(int pageId, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) { _pageId = pageId; _publishedUrlProvider = publishedUrlProvider; + _umbracoContextAccessor = umbracoContextAccessor; } /// @@ -78,11 +79,12 @@ namespace Umbraco.Web.Website.ActionResults /// /// /// - public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringValues, IPublishedUrlProvider publishedUrlProvider) + public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringValues, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) { _pageId = pageId; _queryStringValues = queryStringValues; _publishedUrlProvider = publishedUrlProvider; + _umbracoContextAccessor = umbracoContextAccessor; } /// @@ -91,11 +93,12 @@ namespace Umbraco.Web.Website.ActionResults /// /// /// - public RedirectToUmbracoPageResult(int pageId, string queryString, IPublishedUrlProvider publishedUrlProvider) + public RedirectToUmbracoPageResult(int pageId, string queryString, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) { _pageId = pageId; _queryStringValues = ParseQueryString(queryString); _publishedUrlProvider = publishedUrlProvider; + _umbracoContextAccessor = umbracoContextAccessor; } /// diff --git a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoUrlResult.cs b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoUrlResult.cs index 929cdb85e6..6a7b4d678d 100644 --- a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoUrlResult.cs +++ b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoUrlResult.cs @@ -32,7 +32,8 @@ namespace Umbraco.Web.Website.ActionResults if (context is null) throw new ArgumentNullException(nameof(context)); var destinationUrl = _umbracoContext.OriginalRequestUrl.PathAndQuery; - var tempData = context.HttpContext.RequestServices.GetRequiredService(); + var tempDataDictionaryFactory = context.HttpContext.RequestServices.GetRequiredService(); + var tempData = tempDataDictionaryFactory.GetTempData(context.HttpContext); tempData?.Keep(); context.HttpContext.Response.Redirect(destinationUrl); diff --git a/src/Umbraco.Web.Website/Controllers/SurfaceController.cs b/src/Umbraco.Web.Website/Controllers/SurfaceController.cs index 22b50250af..2f2984cf21 100644 --- a/src/Umbraco.Web.Website/Controllers/SurfaceController.cs +++ b/src/Umbraco.Web.Website/Controllers/SurfaceController.cs @@ -48,18 +48,18 @@ namespace Umbraco.Web.Website.Controllers /// protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId) { - return new RedirectToUmbracoPageResult(pageId, _publishedUrlProvider); + return new RedirectToUmbracoPageResult(pageId, _publishedUrlProvider, UmbracoContextAccessor); } /// - /// Redirects to the Umbraco page with the given id and passes provided querystring + /// Redirects to the Umbraco page with the given id and passes provided querystring /// /// /// /// protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, NameValueCollection queryStringValues) { - return new RedirectToUmbracoPageResult(pageId, queryStringValues, _publishedUrlProvider); + return new RedirectToUmbracoPageResult(pageId, queryStringValues, _publishedUrlProvider, UmbracoContextAccessor); } /// @@ -70,7 +70,7 @@ namespace Umbraco.Web.Website.Controllers /// protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, string queryString) { - return new RedirectToUmbracoPageResult(pageId, queryString, _publishedUrlProvider); + return new RedirectToUmbracoPageResult(pageId, queryString, _publishedUrlProvider, UmbracoContextAccessor); } /// From f586883da1bf1fee4d632107fffca7c8a77a6a9f Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 12 May 2020 19:58:51 +0200 Subject: [PATCH 12/22] AB6455 Fix issue with ITempDataDictionary --- src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs b/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs index 5019dbd7c2..c6f0a95206 100644 --- a/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs +++ b/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs @@ -122,7 +122,8 @@ namespace Umbraco.Web.Website.ActionResults // http://issues.umbraco.org/issue/U4-1339 var targetController = controller; - var tempData = context.HttpContext.RequestServices.GetRequiredService(); + var tempDataDictionaryFactory = context.HttpContext.RequestServices.GetRequiredService(); + var tempData = tempDataDictionaryFactory.GetTempData(context.HttpContext); targetController.TempData = tempData; targetController.TempData.Save(); From cd48adc7afcea68efad39b8808a5d745cec234f9 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 12 May 2020 20:28:40 +0200 Subject: [PATCH 13/22] AB6455 - Stub'ed WebSecurity, cleanup and fix issue with content root vs web root --- .../Configuration/Grid/GridConfig.cs | 8 +++---- .../Configuration/Grid/GridEditorsConfig.cs | 22 +++++++++---------- .../Security/WebSecurity.cs | 19 ++++++++-------- .../Config}/grid.editors.config.js | 0 .../Umbraco.Web.UI.NetCore.csproj | 1 + src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 1 - .../Controllers/SurfaceController.cs | 3 +++ 7 files changed, 28 insertions(+), 26 deletions(-) rename src/{Umbraco.Web.UI/config => Umbraco.Web.UI.NetCore/Config}/grid.editors.config.js (100%) diff --git a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs index 72c720e3d6..363dc7b048 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs @@ -1,7 +1,5 @@ -using System.IO; -using Umbraco.Core.Cache; +using Umbraco.Core.Cache; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; using Umbraco.Core.Serialization; @@ -10,9 +8,9 @@ namespace Umbraco.Core.Configuration.Grid { public class GridConfig : IGridConfig { - public GridConfig(AppCaches appCaches, IIOHelper ioHelper, IManifestParser manifestParser, IJsonSerializer jsonSerializer, IHostingEnvironment hostingEnvironment) + public GridConfig(AppCaches appCaches, IManifestParser manifestParser, IJsonSerializer jsonSerializer, IHostingEnvironment hostingEnvironment, ILogger logger) { - EditorsConfig = new GridEditorsConfig(appCaches, ioHelper, manifestParser, jsonSerializer, hostingEnvironment.IsDebugMode); + EditorsConfig = new GridEditorsConfig(appCaches, hostingEnvironment, manifestParser, jsonSerializer, logger); } public IGridEditorsConfig EditorsConfig { get; } diff --git a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs index 410c83ff1a..6cf985fecb 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; using System.IO; -using Umbraco.Composing; using Umbraco.Core.Cache; -using Umbraco.Core.IO; +using Umbraco.Core.Hosting; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; using Umbraco.Core.PropertyEditors; @@ -14,18 +13,19 @@ namespace Umbraco.Core.Configuration.Grid internal class GridEditorsConfig : IGridEditorsConfig { private readonly AppCaches _appCaches; - private readonly IIOHelper _ioHelper; + private readonly IHostingEnvironment _hostingEnvironment; private readonly IManifestParser _manifestParser; - private readonly bool _isDebug; - private readonly IJsonSerializer _jsonSerializer; - public GridEditorsConfig(AppCaches appCaches, IIOHelper ioHelper, IManifestParser manifestParser,IJsonSerializer jsonSerializer, bool isDebug) + private readonly IJsonSerializer _jsonSerializer; + private readonly ILogger _logger; + + public GridEditorsConfig(AppCaches appCaches, IHostingEnvironment hostingEnvironment, IManifestParser manifestParser,IJsonSerializer jsonSerializer, ILogger logger) { _appCaches = appCaches; - _ioHelper = ioHelper; + _hostingEnvironment = hostingEnvironment; _manifestParser = manifestParser; _jsonSerializer = jsonSerializer; - _isDebug = isDebug; + _logger = logger; } public IEnumerable Editors @@ -34,7 +34,7 @@ namespace Umbraco.Core.Configuration.Grid { List GetResult() { - var configFolder = new DirectoryInfo(_ioHelper.MapPath(Constants.SystemDirectories.Config)); + var configFolder = new DirectoryInfo(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Config)); var editors = new List(); var gridConfig = Path.Combine(configFolder.FullName, "grid.editors.config.js"); if (File.Exists(gridConfig)) @@ -47,7 +47,7 @@ namespace Umbraco.Core.Configuration.Grid } catch (Exception ex) { - Current.Logger.Error(ex, "Could not parse the contents of grid.editors.config.js into a JSON array '{Json}", sourceString); + _logger.Error(ex, "Could not parse the contents of grid.editors.config.js into a JSON array '{Json}", sourceString); } } @@ -61,7 +61,7 @@ namespace Umbraco.Core.Configuration.Grid } //cache the result if debugging is disabled - var result = _isDebug + var result = _hostingEnvironment.IsDebugMode ? GetResult() : _appCaches.RuntimeCache.GetCacheItem>(typeof(GridEditorsConfig) + ".Editors",GetResult, TimeSpan.FromMinutes(10)); diff --git a/src/Umbraco.Web.Common/Security/WebSecurity.cs b/src/Umbraco.Web.Common/Security/WebSecurity.cs index 5f54d2e9ee..e87720dc86 100644 --- a/src/Umbraco.Web.Common/Security/WebSecurity.cs +++ b/src/Umbraco.Web.Common/Security/WebSecurity.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using Umbraco.Composing; using Umbraco.Core; using Umbraco.Core.Models.Membership; using Umbraco.Web.Security; @@ -11,46 +12,46 @@ namespace Umbraco.Web.Common.Security public class WebSecurity : IWebSecurity { - public IUser CurrentUser => throw new NotImplementedException(); + public IUser CurrentUser => new User(Current.Configs.Global()); public ValidateRequestAttempt AuthorizeRequest(bool throwExceptions = false) { - throw new NotImplementedException(); + return ValidateRequestAttempt.Success; } public void ClearCurrentLogin() { - throw new NotImplementedException(); + //throw new NotImplementedException(); } public Attempt GetUserId() { - throw new NotImplementedException(); + return Attempt.Succeed(-1); } public bool IsAuthenticated() { - throw new NotImplementedException(); + return true; } public double PerformLogin(int userId) { - throw new NotImplementedException(); + return 100; } public bool UserHasSectionAccess(string section, IUser user) { - throw new NotImplementedException(); + return true; } public bool ValidateCurrentUser() { - throw new NotImplementedException(); + return true; } public ValidateRequestAttempt ValidateCurrentUser(bool throwExceptions, bool requiresApproval = true) { - throw new NotImplementedException(); + return ValidateRequestAttempt.Success; } } } diff --git a/src/Umbraco.Web.UI/config/grid.editors.config.js b/src/Umbraco.Web.UI.NetCore/Config/grid.editors.config.js similarity index 100% rename from src/Umbraco.Web.UI/config/grid.editors.config.js rename to src/Umbraco.Web.UI.NetCore/Config/grid.editors.config.js diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj index 966cb9c86e..8e5df46c55 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -63,6 +63,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 5e66765379..8c0b98787c 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -142,7 +142,6 @@ True Settings.settings - diff --git a/src/Umbraco.Web.Website/Controllers/SurfaceController.cs b/src/Umbraco.Web.Website/Controllers/SurfaceController.cs index 2f2984cf21..161fe1750f 100644 --- a/src/Umbraco.Web.Website/Controllers/SurfaceController.cs +++ b/src/Umbraco.Web.Website/Controllers/SurfaceController.cs @@ -15,6 +15,9 @@ namespace Umbraco.Web.Website.Controllers /// /// Provides a base class for front-end add-in controllers. /// + // TODO: Migrate MergeModelStateToChildAction and MergeParentContextViewData action filters + // [MergeModelStateToChildAction] + // [MergeParentContextViewData] public abstract class SurfaceController : PluginController { private readonly IPublishedUrlProvider _publishedUrlProvider; From 317edabf02683bc5b2ba04ebd63bb3fa4fe2be75 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 12 May 2020 21:04:55 +0200 Subject: [PATCH 14/22] AB6455 - Fix issue with ITempDataDictionary --- .../ActionResults/RedirectToUmbracoPageResult.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs index 54c220cd55..d271e63e2f 100644 --- a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs +++ b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs @@ -161,7 +161,8 @@ namespace Umbraco.Web.Website.ActionResults _queryStringValues.AllKeys.Select(x => x + "=" + HttpUtility.UrlEncode(_queryStringValues[x]))); } - var tempData = httpContext.RequestServices.GetRequiredService(); + var tempDataDictionaryFactory = context.HttpContext.RequestServices.GetRequiredService(); + var tempData = tempDataDictionaryFactory.GetTempData(context.HttpContext); tempData?.Keep(); httpContext.Response.Redirect(destinationUrl); From c7a9f562956d9d0ce04bb693bfe7ecc0e6b968c6 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 15 May 2020 10:05:29 +0200 Subject: [PATCH 15/22] AB6455 - Moved SurfaceControllerTests to unit tests --- src/Umbraco.Tests.Common/TestHelperBase.cs | 1 - .../Umbraco.Tests.UnitTests.csproj | 1 + .../Controllers}/SurfaceControllerTests.cs | 58 ++++++++++--------- 3 files changed, 31 insertions(+), 29 deletions(-) rename src/{Umbraco.Tests.Integration => Umbraco.Tests.UnitTests/Umbraco.Web.Website/Controllers}/SurfaceControllerTests.cs (82%) diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index ce20e723df..b79fb67fe7 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -16,7 +16,6 @@ using Umbraco.Net; using Umbraco.Core.Persistence; using Umbraco.Core.Serialization; using Umbraco.Core.Strings; -using Umbraco.Core.Sync; using Umbraco.Web; using Umbraco.Web.Routing; diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj b/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj index c5732870f3..f2cb3489cc 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj @@ -21,6 +21,7 @@ + diff --git a/src/Umbraco.Tests.Integration/SurfaceControllerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Controllers/SurfaceControllerTests.cs similarity index 82% rename from src/Umbraco.Tests.Integration/SurfaceControllerTests.cs rename to src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Controllers/SurfaceControllerTests.cs index bb9379c234..1b4630c2da 100644 --- a/src/Umbraco.Tests.Integration/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Controllers/SurfaceControllerTests.cs @@ -1,24 +1,28 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; using Moq; using NUnit.Framework; using Umbraco.Core.Cache; +using Umbraco.Core.Hosting; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Tests.Common; -using Umbraco.Tests.Integration.Implementations; -using Umbraco.Tests.Integration.Testing; +using Umbraco.Tests.Common.Builders; using Umbraco.Tests.Testing; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; +using Umbraco.Web.Website; using Umbraco.Web.Website.Controllers; namespace Umbraco.Tests.Integration { [TestFixture] [UmbracoTest(WithApplication = true)] - public class SurfaceControllerTests : UmbracoIntegrationTest + public class SurfaceControllerTests { private IUmbracoContextAccessor _umbracoContextAccessor; @@ -31,16 +35,16 @@ namespace Umbraco.Tests.Integration [Test] public void Can_Construct_And_Get_Result() { - var testHelper = new TestHelper(); - var httpContextAccessor = testHelper.GetHttpContextAccessor(); - var hostingEnvironment = testHelper.GetHostingEnvironment(); + var httpContextAccessor = Mock.Of(); + var hostingEnvironment = Mock.Of(); + var globalSettings = new GlobalSettingsBuilder().Build(); var umbracoContextFactory = new UmbracoContextFactory( _umbracoContextAccessor, Mock.Of(), new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - GlobalSettings, + globalSettings, Mock.Of(), hostingEnvironment, new UriUtility(hostingEnvironment), @@ -63,16 +67,16 @@ namespace Umbraco.Tests.Integration [Test] public void Umbraco_Context_Not_Null() { - var testHelper = new TestHelper(); - var httpContextAccessor = testHelper.GetHttpContextAccessor(); - var hostingEnvironment = testHelper.GetHostingEnvironment(); + var globalSettings = new GlobalSettingsBuilder().Build(); + var httpContextAccessor = Mock.Of(); + var hostingEnvironment = Mock.Of(); var umbracoContextFactory = new UmbracoContextFactory( _umbracoContextAccessor, Mock.Of(), new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - GlobalSettings, + globalSettings, Mock.Of(), hostingEnvironment, new UriUtility(hostingEnvironment), @@ -99,16 +103,16 @@ namespace Umbraco.Tests.Integration content.Setup(x => x.Id).Returns(2); var publishedSnapshotService = new Mock(); - var testHelper = new TestHelper(); - var httpContextAccessor = testHelper.GetHttpContextAccessor(); - var hostingEnvironment = testHelper.GetHostingEnvironment(); + var httpContextAccessor = Mock.Of(); + var hostingEnvironment = Mock.Of(); + var globalSettings = new GlobalSettingsBuilder().Build(); var umbracoContextFactory = new UmbracoContextFactory( _umbracoContextAccessor, publishedSnapshotService.Object, new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - GlobalSettings, + globalSettings, Mock.Of(), hostingEnvironment, new UriUtility(hostingEnvironment), @@ -131,20 +135,20 @@ namespace Umbraco.Tests.Integration Assert.AreEqual(2, result.Content.Id); } - /* + [Test] public void Mock_Current_Page() { - var testHelper = new TestHelper(); - var httpContextAccessor = testHelper.GetHttpContextAccessor(); - var hostingEnvironment = testHelper.GetHostingEnvironment(); + var globalSettings = new GlobalSettingsBuilder().Build(); + var httpContextAccessor = Mock.Of(); + var hostingEnvironment = Mock.Of(); var umbracoContextFactory = new UmbracoContextFactory( _umbracoContextAccessor, Mock.Of(), new TestVariationContextAccessor(), new TestDefaultCultureAccessor(), - GlobalSettings, + globalSettings, Mock.Of(), hostingEnvironment, new UriUtility(hostingEnvironment), @@ -159,14 +163,12 @@ namespace Umbraco.Tests.Integration var content = Mock.Of(publishedContent => publishedContent.Id == 12345); - // TODO: Figure out how to create published router - var publishedRouter = BaseWebTest.CreatePublishedRouter(TestHelpers.SettingsForTests.GenerateMockWebRoutingSettings()); - var frequest = publishedRouter.CreateRequest(umbracoContext, new Uri("http://localhost/test")); - frequest.PublishedContent = content; + var publishedRequestMock = new Mock(); + publishedRequestMock.Setup(x => x.PublishedContent).Returns(content); - var routeDefinition = new RouteDefinition + var routeDefinition = new RouteDefinition { - PublishedRequest = frequest + PublishedRequest = publishedRequestMock.Object }; var routeData = new RouteData(); @@ -183,7 +185,7 @@ namespace Umbraco.Tests.Integration Assert.AreEqual(12345, result.Content.Id); } - */ + public class TestSurfaceController : SurfaceController { From 87a26f7b6fb6a4be464915be33ddb27eae060f23 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 15 May 2020 12:24:37 +0200 Subject: [PATCH 16/22] AB6455 - Cleanup and added TODO to ensure we get this tested --- .../ActionResults/UmbracoPageResult.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs b/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs index c6f0a95206..62942541e9 100644 --- a/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs +++ b/src/Umbraco.Web.Website/ActionResults/UmbracoPageResult.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Rendering; @@ -11,6 +12,7 @@ using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Umbraco.Core; using Umbraco.Core.Logging; +using Umbraco.Extensions; namespace Umbraco.Web.Website.ActionResults { @@ -33,9 +35,6 @@ namespace Umbraco.Web.Website.ActionResults ResetRouteData(routeData); ValidateRouteData(routeData); - var routeDef = (RouteDefinition)routeData.DataTokens[Constants.Web.UmbracoRouteDefinitionDataToken]; - routeData.Values["action"] = routeDef.ActionName; - var factory = context.HttpContext.RequestServices.GetRequiredService(); Controller controller = null; @@ -44,7 +43,7 @@ namespace Umbraco.Web.Website.ActionResults try { - controller = CreateController(controllerContext, factory, routeDef); + controller = CreateController(controllerContext, factory); CopyControllerData(controllerContext, controller); @@ -65,6 +64,7 @@ namespace Umbraco.Web.Website.ActionResults { using (_profilingLogger.TraceDuration("Executing Umbraco RouteDefinition controller", "Finished")) { + //TODO I do not think this will work, We need to test this, when we can, in the .NET Core executable. var aec = new ActionExecutingContext(context, new List(), new Dictionary(), controller); var actionExecutedDelegate = CreateActionExecutedDelegate(aec); @@ -132,10 +132,10 @@ namespace Umbraco.Web.Website.ActionResults /// /// Creates a controller using the controller factory /// - private static Controller CreateController(ControllerContext context, IControllerFactory factory, RouteDefinition routeDef) + private static Controller CreateController(ControllerContext context, IControllerFactory factory) { if (!(factory.CreateController(context) is Controller controller)) - throw new InvalidOperationException("Could not create controller with name " + routeDef.ControllerName + "."); + throw new InvalidOperationException("Could not create controller with name " + context.ActionDescriptor.ControllerName + "."); return controller; } From dd986d4f73260033718a562930f94aeae440c0e4 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 15 May 2020 14:22:10 +0200 Subject: [PATCH 17/22] Post merge + fix in views --- .../Umbraco/Views/AuthorizeUpgrade.cshtml | 4 ++-- src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml | 2 +- src/Umbraco.Web/Editors/BackOfficeController.cs | 11 ++--------- src/Umbraco.Web/Editors/BackOfficeServerVariables.cs | 2 -- src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs | 4 ++-- 5 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml index 7984bf14f7..6cd3d18be1 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml @@ -19,7 +19,7 @@ Umbraco - + @Html.Raw(runtimeMinifier.RenderCssHere(BackOfficeWebAssets.UmbracoUpgradeCssBundleName)); @*Because we're lazy loading angular js, the embedded cloak style will not be loaded initially, but we need it*@ @@ -45,7 +45,7 @@ }); } -@Html.BareMinimumServerVariablesScript(Url, Model.Features, Model.GlobalSettings, Model.UmbracoVersion, Model.ContentSettings, Model.TreeCollection, Model.HttpContextAccessor, Model.HostingEnvironment, Model.RuntimeSettings, Model.SecuritySettings, runtimeMinifier) +@Html.BareMinimumServerVariablesScript(Url, Model.Features, Model.GlobalSettings, Model.UmbracoVersion, Model.ContentSettings, Model.TreeCollection, Model.HostingEnvironment, Model.RuntimeSettings, Model.SecuritySettings, runtimeMinifier)