diff --git a/src/Umbraco.Core/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs index 89ae269469..aa9d6174dd 100644 --- a/src/Umbraco.Core/IO/PhysicalFileSystem.cs +++ b/src/Umbraco.Core/IO/PhysicalFileSystem.cs @@ -13,7 +13,6 @@ namespace Umbraco.Core.IO public class PhysicalFileSystem : IFileSystem { private readonly IIOHelper _ioHelper; - private readonly IHostingEnvironment _hostingEnvironment; private readonly ILogger _logger; // the rooted, filesystem path, using directory separator chars, NOT ending with a separator @@ -34,19 +33,19 @@ namespace Umbraco.Core.IO public PhysicalFileSystem(IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, ILogger logger, string virtualRoot) { _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); - _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); + if (hostingEnvironment == null) throw new ArgumentNullException(nameof(hostingEnvironment)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - if (virtualRoot == null) throw new ArgumentNullException("virtualRoot"); + if (virtualRoot == null) throw new ArgumentNullException(nameof(virtualRoot)); if (virtualRoot.StartsWith("~/") == false) throw new ArgumentException("The virtualRoot argument must be a virtual path and start with '~/'"); - _rootPath = EnsureDirectorySeparatorChar(_hostingEnvironment.MapPathContentRoot(virtualRoot)).TrimEnd(Path.DirectorySeparatorChar); + _rootPath = EnsureDirectorySeparatorChar(hostingEnvironment.MapPathContentRoot(virtualRoot)).TrimEnd(Path.DirectorySeparatorChar); _rootPathFwd = EnsureUrlSeparatorChar(_rootPath); - _rootUrl = EnsureUrlSeparatorChar(_ioHelper.ResolveUrl(virtualRoot)).TrimEnd('/'); + _rootUrl = EnsureUrlSeparatorChar(hostingEnvironment.ToAbsolute(virtualRoot)).TrimEnd('/'); } - public PhysicalFileSystem(IIOHelper ioHelper, ILogger logger, string rootPath, string rootUrl) + public PhysicalFileSystem(IIOHelper ioHelper,IHostingEnvironment hostingEnvironment, ILogger logger, string rootPath, string rootUrl) { _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -62,8 +61,7 @@ namespace Umbraco.Core.IO if (Path.IsPathRooted(rootPath) == false) { // but the test suite App.config cannot really "root" anything so we have to do it here - // TODO: This will map to the web content root (www) not the web app root, is that what we want? Else we need to use IHostingEnvironment.ApplicationPhysicalPath - var localRoot = _ioHelper.MapPath("~"); + var localRoot = hostingEnvironment.MapPathContentRoot("~"); rootPath = Path.Combine(localRoot, rootPath); } diff --git a/src/Umbraco.Core/Net/IUmbracoComponentRenderer.cs b/src/Umbraco.Core/Templates/IUmbracoComponentRenderer.cs similarity index 98% rename from src/Umbraco.Core/Net/IUmbracoComponentRenderer.cs rename to src/Umbraco.Core/Templates/IUmbracoComponentRenderer.cs index 0391a01183..cc75ff706c 100644 --- a/src/Umbraco.Core/Net/IUmbracoComponentRenderer.cs +++ b/src/Umbraco.Core/Templates/IUmbracoComponentRenderer.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Umbraco.Core.Strings; -namespace Umbraco.Core.Net +namespace Umbraco.Core.Templates { /// /// Methods used to render umbraco components as HTML in templates diff --git a/src/Umbraco.Core/Net/UmbracoComponentRenderer.cs b/src/Umbraco.Core/Templates/UmbracoComponentRenderer.cs similarity index 98% rename from src/Umbraco.Core/Net/UmbracoComponentRenderer.cs rename to src/Umbraco.Core/Templates/UmbracoComponentRenderer.cs index 22703a111e..27c298d54f 100644 --- a/src/Umbraco.Core/Net/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Core/Templates/UmbracoComponentRenderer.cs @@ -2,7 +2,6 @@ using System.Linq; using System.IO; using System.Web; -using Umbraco.Core; using Umbraco.Web.Templates; using System.Collections.Generic; using Umbraco.Core.Models.PublishedContent; @@ -10,7 +9,7 @@ using Umbraco.Core.Strings; using Umbraco.Web; using Umbraco.Web.Macros; -namespace Umbraco.Core.Net +namespace Umbraco.Core.Templates { /// @@ -19,7 +18,6 @@ namespace Umbraco.Core.Net /// /// Used by UmbracoHelper /// - // Migrated to .NET Core public class UmbracoComponentRenderer : IUmbracoComponentRenderer { private readonly IUmbracoContextAccessor _umbracoContextAccessor; diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs index 1c37968a6a..2843236252 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs @@ -18,7 +18,7 @@ using Umbraco.Core.Migrations; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Migrations.PostMigrations; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Net; +using Umbraco.Core.Templates; using Umbraco.Core.Persistence; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; @@ -40,7 +40,6 @@ using Umbraco.Web.Features; using Umbraco.Web.HealthCheck; using Umbraco.Web.HealthCheck.NotificationMethods; using Umbraco.Web.Install; -using Umbraco.Web.Macros; using Umbraco.Web.Media.EmbedProviders; using Umbraco.Web.Migrations.PostMigrations; using Umbraco.Web.Models.PublishedContent; diff --git a/src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs b/src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs index c25085f6b0..f1e705c9cb 100644 --- a/src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs @@ -16,7 +16,7 @@ namespace Umbraco.Tests.IO public class PhysicalFileSystemTests : AbstractFileSystemTests { public PhysicalFileSystemTests() - : base(new PhysicalFileSystem(TestHelper.IOHelper, Mock.Of(), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "FileSysTests"), "/Media/")) + : base(new PhysicalFileSystem(TestHelper.IOHelper, TestHelper.GetHostingEnvironment(), Mock.Of(), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "FileSysTests"), "/Media/")) { } [SetUp] diff --git a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs index b8517d8263..07a04479a4 100644 --- a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs @@ -54,14 +54,15 @@ namespace Umbraco.Tests.IO { var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); Directory.CreateDirectory(path + "/ShadowTests/d1"); @@ -91,14 +92,15 @@ namespace Umbraco.Tests.IO { var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); Directory.CreateDirectory(path + "/ShadowTests/sub"); @@ -143,14 +145,15 @@ namespace Umbraco.Tests.IO { var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); File.WriteAllText(path + "/ShadowTests/f1.txt", "foo"); @@ -185,6 +188,7 @@ namespace Umbraco.Tests.IO { var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); @@ -192,8 +196,8 @@ namespace Umbraco.Tests.IO Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); Directory.CreateDirectory(path + "/ShadowTests/sub"); @@ -244,14 +248,15 @@ namespace Umbraco.Tests.IO { var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); Assert.Throws(() => @@ -266,14 +271,15 @@ namespace Umbraco.Tests.IO { var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper,hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); File.WriteAllText(path + "/ShadowTests/f2.txt", "foo"); @@ -308,14 +314,15 @@ namespace Umbraco.Tests.IO { var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) @@ -351,14 +358,15 @@ namespace Umbraco.Tests.IO { var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) @@ -376,14 +384,15 @@ namespace Umbraco.Tests.IO { var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); Directory.CreateDirectory(path + "/ShadowTests/sub/sub"); @@ -415,6 +424,7 @@ namespace Umbraco.Tests.IO { var logger = Mock.Of(); var ioHelper = TestHelper.IOHelper; + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); var shadowfs = ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"); @@ -423,7 +433,7 @@ namespace Umbraco.Tests.IO var scopedFileSystems = false; - var phy = new PhysicalFileSystem(ioHelper, logger, path, "ignore"); + var phy = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path, "ignore"); var container = Mock.Of(); var fileSystems = new FileSystems(container, logger, ioHelper, SettingsForTests.GenerateMockGlobalSettings(), TestHelper.GetHostingEnvironment()) { IsScoped = () => scopedFileSystems }; @@ -511,6 +521,7 @@ namespace Umbraco.Tests.IO { var logger = Mock.Of(); var ioHelper = TestHelper.IOHelper; + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); var shadowfs = ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"); @@ -518,7 +529,7 @@ namespace Umbraco.Tests.IO var scopedFileSystems = false; - var phy = new PhysicalFileSystem(ioHelper, logger, path, "ignore"); + var phy = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path, "ignore"); var container = Mock.Of(); var fileSystems = new FileSystems(container, logger, ioHelper, SettingsForTests.GenerateMockGlobalSettings(), TestHelper.GetHostingEnvironment()) { IsScoped = () => scopedFileSystems }; @@ -565,6 +576,7 @@ namespace Umbraco.Tests.IO { var logger = Mock.Of(); var ioHelper = TestHelper.IOHelper; + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); var shadowfs = ioHelper.MapPath(Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"); @@ -572,7 +584,7 @@ namespace Umbraco.Tests.IO var scopedFileSystems = false; - var phy = new PhysicalFileSystem(ioHelper, logger, path, "ignore"); + var phy = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path, "ignore"); var container = Mock.Of(); var fileSystems = new FileSystems(container, logger, ioHelper, SettingsForTests.GenerateMockGlobalSettings(), TestHelper.GetHostingEnvironment()) { IsScoped = () => scopedFileSystems }; @@ -683,14 +695,15 @@ namespace Umbraco.Tests.IO // Arrange var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); // Act @@ -718,14 +731,15 @@ namespace Umbraco.Tests.IO // Arrange var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); // Act @@ -756,14 +770,15 @@ namespace Umbraco.Tests.IO // Arrange var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); // Act @@ -791,14 +806,15 @@ namespace Umbraco.Tests.IO // Arrange var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); // Act @@ -829,14 +845,15 @@ namespace Umbraco.Tests.IO // Arrange var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); // Act @@ -879,14 +896,15 @@ namespace Umbraco.Tests.IO // Arrange var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); // Act @@ -916,14 +934,15 @@ namespace Umbraco.Tests.IO // Arrange var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "ignore"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "ignore"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "ignore"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "ignore"); var ss = new ShadowFileSystem(fs, sfs); // Act @@ -958,14 +977,15 @@ namespace Umbraco.Tests.IO // Arrange var ioHelper = TestHelper.IOHelper; var logger = Mock.Of(); + var hostingEnvironment = TestHelper.GetHostingEnvironment(); var path = ioHelper.MapPath("FileSysTests"); Directory.CreateDirectory(path); Directory.CreateDirectory(path + "/ShadowTests"); Directory.CreateDirectory(path + "/ShadowSystem"); - var fs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowTests/", "rootUrl"); - var sfs = new PhysicalFileSystem(ioHelper, logger, path + "/ShadowSystem/", "rootUrl"); + var fs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowTests/", "rootUrl"); + var sfs = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, path + "/ShadowSystem/", "rootUrl"); var ss = new ShadowFileSystem(fs, sfs); // Act diff --git a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs index 6b16f49d7d..2e2ebf392c 100644 --- a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs @@ -55,7 +55,7 @@ namespace Umbraco.Tests.Scoping [TestCase(false)] public void CreateMediaTest(bool complete) { - var physMediaFileSystem = new PhysicalFileSystem(IOHelper, Mock.Of(), IOHelper.MapPath("media"), "ignore"); + var physMediaFileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, Mock.Of(), IOHelper.MapPath("media"), "ignore"); var mediaFileSystem = Current.MediaFileSystem; Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); @@ -88,7 +88,7 @@ namespace Umbraco.Tests.Scoping [Test] public void MultiThread() { - var physMediaFileSystem = new PhysicalFileSystem(IOHelper, Mock.Of(),IOHelper.MapPath("media"), "ignore"); + var physMediaFileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, Mock.Of(),IOHelper.MapPath("media"), "ignore"); var mediaFileSystem = Current.MediaFileSystem; var scopeProvider = ScopeProvider; diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 3f848325cc..76b23cf5e9 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -14,7 +14,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Net; +using Umbraco.Core.Templates; using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Tests.Common; diff --git a/src/Umbraco.Web.BackOffice/Controllers/MacroRenderingController.cs b/src/Umbraco.Web.BackOffice/Controllers/MacroRenderingController.cs index 58c30aa0ee..c3c4c9d9b7 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MacroRenderingController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MacroRenderingController.cs @@ -12,7 +12,7 @@ using Umbraco.Core; using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Net; +using Umbraco.Core.Templates; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Common.Attributes; diff --git a/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs b/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs index 5b61b91c66..720b01acd9 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs @@ -237,7 +237,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// The . /// - public List GetPartialViews() + public IEnumerable GetPartialViews() { var views = new List(); diff --git a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs index 832d92f4f0..6105383a3d 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs @@ -23,10 +23,6 @@ namespace Umbraco.Extensions { services.AddAntiforgery(); - //We need to have runtime compilation of views when using backoffice. We could consider having only this when a specific config is set. - //But as far as I can see, there are still precompiled views, even when this is activated, so maybe it is okay. - services.AddControllersWithViews().AddRazorRuntimeCompilation(); - services .AddAuthentication(Constants.Security.BackOfficeAuthenticationType) .AddCookie(Constants.Security.BackOfficeAuthenticationType); diff --git a/src/Umbraco.Web.Common/Routing/PublishedRouter.cs b/src/Umbraco.Web.Common/Routing/PublishedRouter.cs deleted file mode 100644 index 6618d6edf5..0000000000 --- a/src/Umbraco.Web.Common/Routing/PublishedRouter.cs +++ /dev/null @@ -1,766 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Globalization; -using System.IO; -using Umbraco.Core; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Services; -using Umbraco.Web.Routing; -using Umbraco.Web.Security; -namespace Umbraco.Web.Common.Routing -{ - /// - /// Provides the default implementation. - /// - public class PublishedRouter : IPublishedRouter - { - private readonly IWebRoutingSettings _webRoutingSettings; - private readonly ContentFinderCollection _contentFinders; - private readonly IContentLastChanceFinder _contentLastChanceFinder; - private readonly IProfilingLogger _profilingLogger; - private readonly IVariationContextAccessor _variationContextAccessor; - private readonly ILogger _logger; - private readonly IPublishedUrlProvider _publishedUrlProvider; - private readonly IRequestAccessor _requestAccessor; - private readonly IPublishedValueFallback _publishedValueFallback; - private readonly IPublicAccessChecker _publicAccessChecker; - private readonly IFileService _fileService; - private readonly IContentTypeService _contentTypeService; - private readonly IPublicAccessService _publicAccessService; - - /// - /// Initializes a new instance of the class. - /// - public PublishedRouter( - IWebRoutingSettings webRoutingSettings, - ContentFinderCollection contentFinders, - IContentLastChanceFinder contentLastChanceFinder, - IVariationContextAccessor variationContextAccessor, - IProfilingLogger proflog, - IPublishedUrlProvider publishedUrlProvider, - IRequestAccessor requestAccessor, - IPublishedValueFallback publishedValueFallback, - IPublicAccessChecker publicAccessChecker, - IFileService fileService, - IContentTypeService contentTypeService, - IPublicAccessService publicAccessService) - { - _webRoutingSettings = webRoutingSettings ?? throw new ArgumentNullException(nameof(webRoutingSettings)); - _contentFinders = contentFinders ?? throw new ArgumentNullException(nameof(contentFinders)); - _contentLastChanceFinder = contentLastChanceFinder ?? throw new ArgumentNullException(nameof(contentLastChanceFinder)); - _profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog)); - _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); - _logger = proflog; - _publishedUrlProvider = publishedUrlProvider; - _requestAccessor = requestAccessor; - _publishedValueFallback = publishedValueFallback; - _publicAccessChecker = publicAccessChecker; - _fileService = fileService; - _contentTypeService = contentTypeService; - _publicAccessService = publicAccessService; - } - - /// - public IPublishedRequest CreateRequest(IUmbracoContext umbracoContext, Uri uri = null) - { - return new PublishedRequest(this, umbracoContext, _webRoutingSettings, uri ?? umbracoContext.CleanedUmbracoUrl); - } - - #region Request - - /// - public bool TryRouteRequest(IPublishedRequest request) - { - // disabled - is it going to change the routing? - //_pcr.OnPreparing(); - - FindDomain(request); - if (request.IsRedirect) return false; - if (request.HasPublishedContent) return true; - FindPublishedContent(request); - if (request.IsRedirect) return false; - - // don't handle anything - we just want to ensure that we find the content - //HandlePublishedContent(); - //FindTemplate(); - //FollowExternalRedirect(); - //HandleWildcardDomains(); - - // disabled - we just want to ensure that we find the content - //_pcr.OnPrepared(); - - return request.HasPublishedContent; - } - - private void SetVariationContext(string culture) - { - var variationContext = _variationContextAccessor.VariationContext; - if (variationContext != null && variationContext.Culture == culture) return; - _variationContextAccessor.VariationContext = new VariationContext(culture); - } - - /// - public bool PrepareRequest(IPublishedRequest request) - { - // note - at that point the original legacy module did something do handle IIS custom 404 errors - // ie pages looking like /anything.aspx?404;/path/to/document - I guess the reason was to support - // "directory urls" without having to do wildcard mapping to ASP.NET on old IIS. This is a pain - // to maintain and probably not used anymore - removed as of 06/2012. @zpqrtbnk. - // - // to trigger Umbraco's not-found, one should configure IIS and/or ASP.NET custom 404 errors - // so that they point to a non-existing page eg /redirect-404.aspx - // TODO: SD: We need more information on this for when we release 4.10.0 as I'm not sure what this means. - - // trigger the Preparing event - at that point anything can still be changed - // the idea is that it is possible to change the uri - // - request.OnPreparing(); - - //find domain - FindDomain(request); - - // if request has been flagged to redirect then return - // whoever called us is in charge of actually redirecting - if (request.IsRedirect) - { - return false; - } - - // set the culture on the thread - once, so it's set when running document lookups - Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = request.Culture; - SetVariationContext(request.Culture.Name); - - //find the published content if it's not assigned. This could be manually assigned with a custom route handler, or - // with something like EnsurePublishedContentRequestAttribute or UmbracoVirtualNodeRouteHandler. Those in turn call this method - // to setup the rest of the pipeline but we don't want to run the finders since there's one assigned. - if (request.PublishedContent == null) - { - // find the document & template - FindPublishedContentAndTemplate(request); - } - - // handle wildcard domains - HandleWildcardDomains(request); - - // set the culture on the thread -- again, 'cos it might have changed due to a finder or wildcard domain - Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = request.Culture; - SetVariationContext(request.Culture.Name); - - // trigger the Prepared event - at that point it is still possible to change about anything - // even though the request might be flagged for redirection - we'll redirect _after_ the event - // - // also, OnPrepared() will make the PublishedRequest readonly, so nothing can change - // - request.OnPrepared(); - - // we don't take care of anything so if the content has changed, it's up to the user - // to find out the appropriate template - - //complete the PCR and assign the remaining values - return ConfigureRequest(request); - } - - /// - /// Called by PrepareRequest once everything has been discovered, resolved and assigned to the PCR. This method - /// finalizes the PCR with the values assigned. - /// - /// - /// Returns false if the request was not successfully configured - /// - /// - /// This method logic has been put into it's own method in case developers have created a custom PCR or are assigning their own values - /// but need to finalize it themselves. - /// - public bool ConfigureRequest(IPublishedRequest frequest) - { - if (frequest.HasPublishedContent == false) - { - return false; - } - - // set the culture on the thread -- again, 'cos it might have changed in the event handler - Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = frequest.Culture; - SetVariationContext(frequest.Culture.Name); - - // if request has been flagged to redirect, or has no content to display, - // then return - whoever called us is in charge of actually redirecting - if (frequest.IsRedirect || frequest.HasPublishedContent == false) - { - return false; - } - - // we may be 404 _and_ have a content - - // can't go beyond that point without a PublishedContent to render - // it's ok not to have a template, in order to give MVC a chance to hijack routes - - return true; - } - - /// - public void UpdateRequestToNotFound(IPublishedRequest request) - { - // clear content - var content = request.PublishedContent; - request.PublishedContent = null; - - HandlePublishedContent(request); // will go 404 - FindTemplate(request); - - // if request has been flagged to redirect then return - // whoever called us is in charge of redirecting - if (request.IsRedirect) - return; - - if (request.HasPublishedContent == false) - { - // means the engine could not find a proper document to handle 404 - // restore the saved content so we know it exists - request.PublishedContent = content; - return; - } - - if (request.HasTemplate == false) - { - // means we may have a document, but we have no template - // at that point there isn't much we can do and there is no point returning - // to Mvc since Mvc can't do much either - return; - } - } - - #endregion - - #region Domain - - /// - /// Finds the site root (if any) matching the http request, and updates the PublishedRequest accordingly. - /// - /// A value indicating whether a domain was found. - internal bool FindDomain(IPublishedRequest request) - { - const string tracePrefix = "FindDomain: "; - - // note - we are not handling schemes nor ports here. - - _logger.Debug("{TracePrefix}Uri={RequestUri}", tracePrefix, request.Uri); - - var domainsCache = request.UmbracoContext.PublishedSnapshot.Domains; - var domains = domainsCache.GetAll(includeWildcards: false).ToList(); - - // determines whether a domain corresponds to a published document, since some - // domains may exist but on a document that has been unpublished - as a whole - or - // that is not published for the domain's culture - in which case the domain does - // not apply - bool IsPublishedContentDomain(Domain domain) - { - // just get it from content cache - optimize there, not here - var domainDocument = request.UmbracoContext.PublishedSnapshot.Content.GetById(domain.ContentId); - - // not published - at all - if (domainDocument == null) - return false; - - // invariant - always published - if (!domainDocument.ContentType.VariesByCulture()) - return true; - - // variant, ensure that the culture corresponding to the domain's language is published - return domainDocument.Cultures.ContainsKey(domain.Culture.Name); - } - - domains = domains.Where(IsPublishedContentDomain).ToList(); - - var defaultCulture = domainsCache.DefaultCulture; - - // try to find a domain matching the current request - var domainAndUri = DomainUtilities.SelectDomain(domains, request.Uri, defaultCulture: defaultCulture); - - // handle domain - always has a contentId and a culture - if (domainAndUri != null) - { - // matching an existing domain - _logger.Debug("{TracePrefix}Matches domain={Domain}, rootId={RootContentId}, culture={Culture}", tracePrefix, domainAndUri.Name, domainAndUri.ContentId, domainAndUri.Culture); - - request.Domain = domainAndUri; - request.Culture = domainAndUri.Culture; - - // canonical? not implemented at the moment - // if (...) - // { - // _pcr.RedirectUrl = "..."; - // return true; - // } - } - else - { - // not matching any existing domain - _logger.Debug("{TracePrefix}Matches no domain", tracePrefix); - - request.Culture = defaultCulture == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultCulture); - } - - _logger.Debug("{TracePrefix}Culture={CultureName}", tracePrefix, request.Culture.Name); - - return request.Domain != null; - } - - /// - /// Looks for wildcard domains in the path and updates Culture accordingly. - /// - internal void HandleWildcardDomains(IPublishedRequest request) - { - const string tracePrefix = "HandleWildcardDomains: "; - - if (request.HasPublishedContent == false) - return; - - var nodePath = request.PublishedContent.Path; - _logger.Debug("{TracePrefix}Path={NodePath}", tracePrefix, nodePath); - var rootNodeId = request.HasDomain ? request.Domain.ContentId : (int?)null; - var domain = DomainUtilities.FindWildcardDomainInPath(request.UmbracoContext.PublishedSnapshot.Domains.GetAll(true), nodePath, rootNodeId); - - // always has a contentId and a culture - if (domain != null) - { - request.Culture = domain.Culture; - _logger.Debug("{TracePrefix}Got domain on node {DomainContentId}, set culture to {CultureName}", tracePrefix, domain.ContentId, request.Culture.Name); - } - else - { - _logger.Debug("{TracePrefix}No match.", tracePrefix); - } - } - - #endregion - - #region Rendering engine - - internal bool FindTemplateRenderingEngineInDirectory(DirectoryInfo directory, string alias, string[] extensions) - { - if (directory == null || directory.Exists == false) - return false; - - var pos = alias.IndexOf('/'); - if (pos > 0) - { - // recurse - var subdir = directory.GetDirectories(alias.Substring(0, pos)).FirstOrDefault(); - alias = alias.Substring(pos + 1); - return subdir != null && FindTemplateRenderingEngineInDirectory(subdir, alias, extensions); - } - - // look here - return directory.GetFiles().Any(f => extensions.Any(e => f.Name.InvariantEquals(alias + e))); - } - - #endregion - - #region Document and template - - /// - public ITemplate GetTemplate(string alias) - { - return _fileService.GetTemplate(alias); - } - - /// - /// Finds the Umbraco document (if any) matching the request, and updates the PublishedRequest accordingly. - /// - /// A value indicating whether a document and template were found. - private void FindPublishedContentAndTemplate(IPublishedRequest request) - { - _logger.Debug("FindPublishedContentAndTemplate: Path={UriAbsolutePath}", request.Uri.AbsolutePath); - - // run the document finders - FindPublishedContent(request); - - // if request has been flagged to redirect then return - // whoever called us is in charge of actually redirecting - // -- do not process anything any further -- - if (request.IsRedirect) - return; - - // not handling umbracoRedirect here but after LookupDocument2 - // so internal redirect, 404, etc has precedence over redirect - - // handle not-found, redirects, access... - HandlePublishedContent(request); - - // find a template - FindTemplate(request); - - // handle umbracoRedirect - FollowExternalRedirect(request); - } - - /// - /// Tries to find the document matching the request, by running the IPublishedContentFinder instances. - /// - /// There is no finder collection. - internal void FindPublishedContent(IPublishedRequest request) - { - const string tracePrefix = "FindPublishedContent: "; - - // look for the document - // the first successful finder, if any, will set this.PublishedContent, and may also set this.Template - // some finders may implement caching - - using (_profilingLogger.DebugDuration( - $"{tracePrefix}Begin finders", - $"{tracePrefix}End finders, {(request.HasPublishedContent ? "a document was found" : "no document was found")}")) - { - //iterate but return on first one that finds it - var found = _contentFinders.Any(finder => - { - _logger.Debug("Finder {ContentFinderType}", finder.GetType().FullName); - return finder.TryFindContent(request); - }); - } - - // indicate that the published content (if any) we have at the moment is the - // one that was found by the standard finders before anything else took place. - request.SetIsInitialPublishedContent(); - } - - /// - /// Handles the published content (if any). - /// - /// - /// Handles "not found", internal redirects, access validation... - /// things that must be handled in one place because they can create loops - /// - private void HandlePublishedContent(IPublishedRequest request) - { - // because these might loop, we have to have some sort of infinite loop detection - int i = 0, j = 0; - const int maxLoop = 8; - do - { - _logger.Debug("HandlePublishedContent: Loop {LoopCounter}", i); - - // handle not found - if (request.HasPublishedContent == false) - { - request.Is404 = true; - _logger.Debug("HandlePublishedContent: No document, try last chance lookup"); - - // if it fails then give up, there isn't much more that we can do - if (_contentLastChanceFinder.TryFindContent(request) == false) - { - _logger.Debug("HandlePublishedContent: Failed to find a document, give up"); - break; - } - - _logger.Debug("HandlePublishedContent: Found a document"); - } - - // follow internal redirects as long as it's not running out of control ie infinite loop of some sort - j = 0; - while (FollowInternalRedirects(request) && j++ < maxLoop) - { } - if (j == maxLoop) // we're running out of control - break; - - // ensure access - if (request.HasPublishedContent) - EnsurePublishedContentAccess(request); - - // loop while we don't have page, ie the redirect or access - // got us to nowhere and now we need to run the notFoundLookup again - // as long as it's not running out of control ie infinite loop of some sort - - } while (request.HasPublishedContent == false && i++ < maxLoop); - - if (i == maxLoop || j == maxLoop) - { - _logger.Debug("HandlePublishedContent: Looks like we are running into an infinite loop, abort"); - request.PublishedContent = null; - } - - _logger.Debug("HandlePublishedContent: End"); - } - - /// - /// Follows internal redirections through the umbracoInternalRedirectId document property. - /// - /// A value indicating whether redirection took place and led to a new published document. - /// - /// Redirecting to a different site root and/or culture will not pick the new site root nor the new culture. - /// As per legacy, if the redirect does not work, we just ignore it. - /// - private bool FollowInternalRedirects(IPublishedRequest request) - { - if (request.PublishedContent == null) - throw new InvalidOperationException("There is no PublishedContent."); - - // don't try to find a redirect if the property doesn't exist - if (request.PublishedContent.HasProperty(Core.Constants.Conventions.Content.InternalRedirectId) == false) - return false; - - var redirect = false; - var valid = false; - IPublishedContent internalRedirectNode = null; - var internalRedirectId = request.PublishedContent.Value(_publishedValueFallback, Core.Constants.Conventions.Content.InternalRedirectId, defaultValue: -1); - - if (internalRedirectId > 0) - { - // try and get the redirect node from a legacy integer ID - valid = true; - internalRedirectNode = request.UmbracoContext.Content.GetById(internalRedirectId); - } - else - { - var udiInternalRedirectId = request.PublishedContent.Value(_publishedValueFallback, Core.Constants.Conventions.Content.InternalRedirectId); - if (udiInternalRedirectId != null) - { - // try and get the redirect node from a UDI Guid - valid = true; - internalRedirectNode = request.UmbracoContext.Content.GetById(udiInternalRedirectId.Guid); - } - } - - if (valid == false) - { - // bad redirect - log and display the current page (legacy behavior) - _logger.Debug("FollowInternalRedirects: Failed to redirect to id={InternalRedirectId}: value is not an int nor a GuidUdi.", - request.PublishedContent.GetProperty(Core.Constants.Conventions.Content.InternalRedirectId).GetSourceValue()); - } - - if (internalRedirectNode == null) - { - _logger.Debug("FollowInternalRedirects: Failed to redirect to id={InternalRedirectId}: no such published document.", - request.PublishedContent.GetProperty(Core.Constants.Conventions.Content.InternalRedirectId).GetSourceValue()); - } - else if (internalRedirectId == request.PublishedContent.Id) - { - // redirect to self - _logger.Debug("FollowInternalRedirects: Redirecting to self, ignore"); - } - else - { - request.SetInternalRedirectPublishedContent(internalRedirectNode); // don't use .PublishedContent here - redirect = true; - _logger.Debug("FollowInternalRedirects: Redirecting to id={InternalRedirectId}", internalRedirectId); - } - - return redirect; - } - - /// - /// Ensures that access to current node is permitted. - /// - /// Redirecting to a different site root and/or culture will not pick the new site root nor the new culture. - private void EnsurePublishedContentAccess(IPublishedRequest request) - { - if (request.PublishedContent == null) - throw new InvalidOperationException("There is no PublishedContent."); - - var path = request.PublishedContent.Path; - - var publicAccessAttempt = _publicAccessService.IsProtected(path); - - if (publicAccessAttempt) - { - _logger.Debug("EnsurePublishedContentAccess: Page is protected, check for access"); - - var status = _publicAccessChecker.HasMemberAccessToContent(request.PublishedContent.Id); - switch (status) - { - case PublicAccessStatus.NotLoggedIn: - _logger.Debug("EnsurePublishedContentAccess: Not logged in, redirect to login page"); - SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.LoginNodeId); - break; - case PublicAccessStatus.AccessDenied: - _logger.Debug("EnsurePublishedContentAccess: Current member has not access, redirect to error page"); - SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId); - break; - case PublicAccessStatus.LockedOut: - _logger.Debug("Current member is locked out, redirect to error page"); - SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId); - break; - case PublicAccessStatus.NotApproved: - _logger.Debug("Current member is unapproved, redirect to error page"); - SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId); - break; - case PublicAccessStatus.AccessAccepted: - _logger.Debug("Current member has access"); - break; - } - } - else - { - _logger.Debug("EnsurePublishedContentAccess: Page is not protected"); - } - } - - private static void SetPublishedContentAsOtherPage(IPublishedRequest request, int errorPageId) - { - if (errorPageId != request.PublishedContent.Id) - request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(errorPageId); - } - - /// - /// Finds a template for the current node, if any. - /// - private void FindTemplate(IPublishedRequest request) - { - // NOTE: at the moment there is only 1 way to find a template, and then ppl must - // use the Prepared event to change the template if they wish. Should we also - // implement an ITemplateFinder logic? - - if (request.PublishedContent == null) - { - request.TemplateModel = null; - return; - } - - // read the alternate template alias, from querystring, form, cookie or server vars, - // only if the published content is the initial once, else the alternate template - // does not apply - // + optionally, apply the alternate template on internal redirects - var useAltTemplate = request.IsInitialPublishedContent - || (_webRoutingSettings.InternalRedirectPreservesTemplate && request.IsInternalRedirectPublishedContent); - var altTemplate = useAltTemplate - ? _requestAccessor.GetRequestValue(Core.Constants.Conventions.Url.AltTemplate) - : null; - - if (string.IsNullOrWhiteSpace(altTemplate)) - { - // we don't have an alternate template specified. use the current one if there's one already, - // which can happen if a content lookup also set the template (LookupByNiceUrlAndTemplate...), - // else lookup the template id on the document then lookup the template with that id. - - if (request.HasTemplate) - { - _logger.Debug("FindTemplate: Has a template already, and no alternate template."); - return; - } - - // TODO: When we remove the need for a database for templates, then this id should be irrelevant, - // not sure how were going to do this nicely. - - // TODO: We need to limit altTemplate to only allow templates that are assigned to the current document type! - // if the template isn't assigned to the document type we should log a warning and return 404 - - var templateId = request.PublishedContent.TemplateId; - request.TemplateModel = GetTemplateModel(templateId); - } - else - { - // we have an alternate template specified. lookup the template with that alias - // this means the we override any template that a content lookup might have set - // so /path/to/page/template1?altTemplate=template2 will use template2 - - // ignore if the alias does not match - just trace - - if (request.HasTemplate) - _logger.Debug("FindTemplate: Has a template already, but also an alternative template."); - _logger.Debug("FindTemplate: Look for alternative template alias={AltTemplate}", altTemplate); - - // IsAllowedTemplate deals both with DisableAlternativeTemplates and ValidateAlternativeTemplates settings - if (request.PublishedContent.IsAllowedTemplate( - _fileService, - _contentTypeService, - _webRoutingSettings.DisableAlternativeTemplates, - _webRoutingSettings.ValidateAlternativeTemplates, - altTemplate)) - { - // allowed, use - var template = _fileService.GetTemplate(altTemplate); - - if (template != null) - { - request.TemplateModel = template; - _logger.Debug("FindTemplate: Got alternative template id={TemplateId} alias={TemplateAlias}", template.Id, template.Alias); - } - else - { - _logger.Debug("FindTemplate: The alternative template with alias={AltTemplate} does not exist, ignoring.", altTemplate); - } - } - else - { - _logger.Warn("FindTemplate: Alternative template {TemplateAlias} is not allowed on node {NodeId}, ignoring.", altTemplate, request.PublishedContent.Id); - - // no allowed, back to default - var templateId = request.PublishedContent.TemplateId; - request.TemplateModel = GetTemplateModel(templateId); - } - } - - if (request.HasTemplate == false) - { - _logger.Debug("FindTemplate: No template was found."); - - // initial idea was: if we're not already 404 and UmbracoSettings.HandleMissingTemplateAs404 is true - // then reset _pcr.Document to null to force a 404. - // - // but: because we want to let MVC hijack routes even though no template is defined, we decide that - // a missing template is OK but the request will then be forwarded to MVC, which will need to take - // care of everything. - // - // so, don't set _pcr.Document to null here - } - else - { - _logger.Debug("FindTemplate: Running with template id={TemplateId} alias={TemplateAlias}", request.TemplateModel.Id, request.TemplateModel.Alias); - } - } - - private ITemplate GetTemplateModel(int? templateId) - { - if (templateId.HasValue == false || templateId.Value == default) - { - _logger.Debug("GetTemplateModel: No template."); - return null; - } - - _logger.Debug("GetTemplateModel: Get template id={TemplateId}", templateId); - - if (templateId == null) - throw new InvalidOperationException("The template is not set, the page cannot render."); - - var template = _fileService.GetTemplate(templateId.Value); - if (template == null) - throw new InvalidOperationException("The template with Id " + templateId + " does not exist, the page cannot render."); - _logger.Debug("GetTemplateModel: Got template id={TemplateId} alias={TemplateAlias}", template.Id, template.Alias); - return template; - } - - /// - /// Follows external redirection through umbracoRedirect document property. - /// - /// As per legacy, if the redirect does not work, we just ignore it. - private void FollowExternalRedirect(IPublishedRequest request) - { - if (request.HasPublishedContent == false) return; - - // don't try to find a redirect if the property doesn't exist - if (request.PublishedContent.HasProperty(Core.Constants.Conventions.Content.Redirect) == false) - return; - - var redirectId = request.PublishedContent.Value(_publishedValueFallback, Core.Constants.Conventions.Content.Redirect, defaultValue: -1); - var redirectUrl = "#"; - if (redirectId > 0) - { - redirectUrl = _publishedUrlProvider.GetUrl(redirectId); - } - else - { - // might be a UDI instead of an int Id - var redirectUdi = request.PublishedContent.Value(_publishedValueFallback, Core.Constants.Conventions.Content.Redirect); - if (redirectUdi != null) - redirectUrl = _publishedUrlProvider.GetUrl(redirectUdi.Guid); - } - if (redirectUrl != "#") - request.SetRedirect(redirectUrl); - } - - #endregion - } -} diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index 3ff9d0cff2..53fb1e6a14 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -52,6 +52,11 @@ namespace Umbraco.Web.UI.BackOffice options.ShouldProfile = request => false; // WebProfiler determine and start profiling. We should not use the MiniProfilerMiddleware to also profile }); + //We need to have runtime compilation of views when using umbraco. We could consider having only this when a specific config is set. + //But as far as I can see, there are still precompiled views, even when this is activated, so maybe it is okay. + services.AddControllersWithViews().AddRazorRuntimeCompilation(); + + // If using Kestrel: https://stackoverflow.com/a/55196057 services.Configure(options => { diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index 7ce8890018..68120947df 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -11,7 +11,7 @@ using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; using Umbraco.Core.Mapping; -using Umbraco.Core.Net; +using Umbraco.Core.Templates; using Umbraco.Net; using Umbraco.Core.PackageActions; using Umbraco.Core.Packaging; diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 99a0c12339..e2f6334a11 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -4,7 +4,7 @@ using Microsoft.AspNet.SignalR; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Dictionary; -using Umbraco.Core.Net; +using Umbraco.Core.Templates; using Umbraco.Core.Runtime; using Umbraco.Core.Security; using Umbraco.Core.Services; @@ -41,8 +41,6 @@ namespace Umbraco.Web.Runtime composition.RegisterUnique(); composition.RegisterUnique(); - composition.RegisterUnique(); - // register the umbraco helper - this is Transient! very important! // also, if not level.Run, we cannot really use the helper (during upgrade...) diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs deleted file mode 100644 index 4a1a6c97ec..0000000000 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Web.Mvc; -using System.Web.Routing; -using Umbraco.Core; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Services; -using Umbraco.Core.Strings; -using Umbraco.Web.Models; -using Umbraco.Web.Mvc; -using Umbraco.Web.Routing; - -namespace Umbraco.Web.Templates -{ - /// - /// This is used purely for the RenderTemplate functionality in Umbraco - /// - /// - /// This allows you to render an MVC template based purely off of a node id and an optional alttemplate id as string output. - /// - internal class TemplateRenderer : ITemplateRenderer - { - private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly IPublishedRouter _publishedRouter; - private readonly IFileService _fileService; - private readonly ILocalizationService _languageService; - private readonly IWebRoutingSettings _webRoutingSettings; - private readonly IShortStringHelper _shortStringHelper; - private readonly IHttpContextAccessor _httpContextAccessor; - - public TemplateRenderer(IUmbracoContextAccessor umbracoContextAccessor, IPublishedRouter publishedRouter, IFileService fileService, ILocalizationService textService, IWebRoutingSettings webRoutingSettings, IShortStringHelper shortStringHelper, IHttpContextAccessor httpContextAccessor) - { - _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); - _publishedRouter = publishedRouter ?? throw new ArgumentNullException(nameof(publishedRouter)); - _fileService = fileService ?? throw new ArgumentNullException(nameof(fileService)); - _languageService = textService ?? throw new ArgumentNullException(nameof(textService)); - _webRoutingSettings = webRoutingSettings ?? throw new ArgumentNullException(nameof(webRoutingSettings)); - _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); - _httpContextAccessor = httpContextAccessor; - } - - public void Render(int pageId, int? altTemplateId, StringWriter writer) - { - if (writer == null) throw new ArgumentNullException(nameof(writer)); - - var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - // instantiate a request and process - // important to use CleanedUmbracoUrl - lowercase path-only version of the current url, though this isn't going to matter - // terribly much for this implementation since we are just creating a doc content request to modify it's properties manually. - var contentRequest = _publishedRouter.CreateRequest(umbracoContext); - - var doc = contentRequest.UmbracoContext.Content.GetById(pageId); - - if (doc == null) - { - writer.Write("", pageId); - return; - } - - //in some cases the UmbracoContext will not have a PublishedRequest assigned to it if we are not in the - //execution of a front-end rendered page. In this case set the culture to the default. - //set the culture to the same as is currently rendering - if (umbracoContext.PublishedRequest == null) - { - var defaultLanguage = _languageService.GetAllLanguages().FirstOrDefault(); - contentRequest.Culture = defaultLanguage == null - ? CultureInfo.CurrentUICulture - : defaultLanguage.CultureInfo; - } - else - { - contentRequest.Culture = umbracoContext.PublishedRequest.Culture; - } - - //set the doc that was found by id - contentRequest.PublishedContent = doc; - //set the template, either based on the AltTemplate found or the standard template of the doc - var templateId = _webRoutingSettings.DisableAlternativeTemplates || !altTemplateId.HasValue - ? doc.TemplateId - : altTemplateId.Value; - if (templateId.HasValue) - contentRequest.TemplateModel = _fileService.GetTemplate(templateId.Value); - - //if there is not template then exit - if (contentRequest.HasTemplate == false) - { - if (altTemplateId.HasValue == false) - { - writer.Write("", doc.TemplateId); - } - else - { - writer.Write("", altTemplateId); - } - return; - } - - //First, save all of the items locally that we know are used in the chain of execution, we'll need to restore these - //after this page has rendered. - SaveExistingItems(out var oldPublishedRequest); - - try - { - //set the new items on context objects for this templates execution - SetNewItemsOnContextObjects(contentRequest); - - //Render the template - ExecuteTemplateRendering(writer, contentRequest); - } - finally - { - //restore items on context objects to continuing rendering the parent template - RestoreItems(oldPublishedRequest); - } - - } - - private void ExecuteTemplateRendering(TextWriter sw, IPublishedRequest request) - { - //NOTE: Before we used to build up the query strings here but this is not necessary because when we do a - // Server.Execute in the TemplateRenderer, we pass in a 'true' to 'preserveForm' which automatically preserves all current - // query strings so there's no need for this. HOWEVER, once we get MVC involved, we might have to do some fun things, - // though this will happen in the TemplateRenderer. - - //var queryString = _umbracoContext.HttpContext.Request.QueryString.AllKeys - // .ToDictionary(key => key, key => context.Request.QueryString[key]); - - var requestContext = new RequestContext(_httpContextAccessor.GetRequiredHttpContext(), new RouteData() - { - Route = RouteTable.Routes["Umbraco_default"] - }); - var routeHandler = new RenderRouteHandler(_umbracoContextAccessor, ControllerBuilder.Current.GetControllerFactory(), _shortStringHelper); - var routeDef = routeHandler.GetUmbracoRouteDefinition(requestContext, request); - var renderModel = new ContentModel(request.PublishedContent); - //manually add the action/controller, this is required by mvc - requestContext.RouteData.Values.Add("action", routeDef.ActionName); - requestContext.RouteData.Values.Add("controller", routeDef.ControllerName); - //add the rest of the required route data - routeHandler.SetupRouteDataForRequest(renderModel, requestContext, request); - - var stringOutput = RenderUmbracoRequestToString(requestContext); - - sw.Write(stringOutput); - } - - /// - /// This will execute the UmbracoMvcHandler for the request specified and get the string output. - /// - /// - /// Assumes the RequestContext is setup specifically to render an Umbraco view. - /// - /// - /// - /// To achieve this we temporarily change the output text writer of the current HttpResponse, then - /// execute the controller via the handler which inevitably writes the result to the text writer - /// that has been assigned to the response. Then we change the response textwriter back to the original - /// before continuing . - /// - private string RenderUmbracoRequestToString(RequestContext requestContext) - { - var currentWriter = requestContext.HttpContext.Response.Output; - var newWriter = new StringWriter(); - requestContext.HttpContext.Response.Output = newWriter; - - var handler = new UmbracoMvcHandler(requestContext); - handler.ExecuteUmbracoRequest(); - - //reset it - requestContext.HttpContext.Response.Output = currentWriter; - return newWriter.ToString(); - } - - private void SetNewItemsOnContextObjects(IPublishedRequest request) - { - //now, set the new ones for this page execution - _umbracoContextAccessor.UmbracoContext.PublishedRequest = request; - } - - /// - /// Save all items that we know are used for rendering execution to variables so we can restore after rendering - /// - private void SaveExistingItems(out IPublishedRequest oldPublishedRequest) - { - //Many objects require that these legacy items are in the http context items... before we render this template we need to first - //save the values in them so that we can re-set them after we render so the rest of the execution works as per normal - oldPublishedRequest = _umbracoContextAccessor.UmbracoContext.PublishedRequest; - } - - /// - /// Restores all items back to their context's to continue normal page rendering execution - /// - private void RestoreItems(IPublishedRequest oldPublishedRequest) - { - _umbracoContextAccessor.UmbracoContext.PublishedRequest = oldPublishedRequest; - } - } -} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index fb93ca5435..cced16fc93 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -425,6 +425,8 @@ + + diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 469837c5f2..5b72e9cda0 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -7,7 +7,7 @@ using Umbraco.Composing; using Umbraco.Core; using Umbraco.Core.Dictionary; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Net; +using Umbraco.Core.Templates; using Umbraco.Core.Strings; using Umbraco.Core.Xml; using Umbraco.Web.Mvc;