Merge remote-tracking branch 'origin/netcore/netcore' into netcore/feature/migrate-remaining-trees

This commit is contained in:
Bjarke Berg
2020-06-11 12:40:17 +02:00
18 changed files with 82 additions and 1031 deletions

View File

@@ -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);
}

View File

@@ -1,7 +1,7 @@
using System.Collections.Generic;
using Umbraco.Core.Strings;
namespace Umbraco.Core.Net
namespace Umbraco.Core.Templates
{
/// <summary>
/// Methods used to render umbraco components as HTML in templates

View File

@@ -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
{
/// <summary>
@@ -19,7 +18,6 @@ namespace Umbraco.Core.Net
/// <remarks>
/// Used by UmbracoHelper
/// </remarks>
// Migrated to .NET Core
public class UmbracoComponentRenderer : IUmbracoComponentRenderer
{
private readonly IUmbracoContextAccessor _umbracoContextAccessor;

View File

@@ -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;

View File

@@ -16,7 +16,7 @@ namespace Umbraco.Tests.IO
public class PhysicalFileSystemTests : AbstractFileSystemTests
{
public PhysicalFileSystemTests()
: base(new PhysicalFileSystem(TestHelper.IOHelper, Mock.Of<ILogger>(), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "FileSysTests"), "/Media/"))
: base(new PhysicalFileSystem(TestHelper.IOHelper, TestHelper.GetHostingEnvironment(), Mock.Of<ILogger>(), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "FileSysTests"), "/Media/"))
{ }
[SetUp]

View File

@@ -54,14 +54,15 @@ namespace Umbraco.Tests.IO
{
var ioHelper = TestHelper.IOHelper;
var logger = Mock.Of<ILogger>();
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<ILogger>();
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<ILogger>();
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<ILogger>();
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<ILogger>();
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<UnauthorizedAccessException>(() =>
@@ -266,14 +271,15 @@ namespace Umbraco.Tests.IO
{
var ioHelper = TestHelper.IOHelper;
var logger = Mock.Of<ILogger>();
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<ILogger>();
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<ILogger>();
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<ILogger>();
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<ILogger>();
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<IFactory>();
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<ILogger>();
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<IFactory>();
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<ILogger>();
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<IFactory>();
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<ILogger>();
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<ILogger>();
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<ILogger>();
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<ILogger>();
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<ILogger>();
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<ILogger>();
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<ILogger>();
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<ILogger>();
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

View File

@@ -55,7 +55,7 @@ namespace Umbraco.Tests.Scoping
[TestCase(false)]
public void CreateMediaTest(bool complete)
{
var physMediaFileSystem = new PhysicalFileSystem(IOHelper, Mock.Of<ILogger>(), IOHelper.MapPath("media"), "ignore");
var physMediaFileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, Mock.Of<ILogger>(), 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<ILogger>(),IOHelper.MapPath("media"), "ignore");
var physMediaFileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, Mock.Of<ILogger>(),IOHelper.MapPath("media"), "ignore");
var mediaFileSystem = Current.MediaFileSystem;
var scopeProvider = ScopeProvider;

View File

@@ -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;

View File

@@ -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;

View File

@@ -237,7 +237,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <returns>
/// The <see cref="HttpResponseMessage"/>.
/// </returns>
public List<string> GetPartialViews()
public IEnumerable<string> GetPartialViews()
{
var views = new List<string>();

View File

@@ -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);

View File

@@ -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
{
/// <summary>
/// Provides the default <see cref="IPublishedRouter"/> implementation.
/// </summary>
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;
/// <summary>
/// Initializes a new instance of the <see cref="PublishedRouter"/> class.
/// </summary>
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;
}
/// <inheritdoc />
public IPublishedRequest CreateRequest(IUmbracoContext umbracoContext, Uri uri = null)
{
return new PublishedRequest(this, umbracoContext, _webRoutingSettings, uri ?? umbracoContext.CleanedUmbracoUrl);
}
#region Request
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <summary>
/// Called by PrepareRequest once everything has been discovered, resolved and assigned to the PCR. This method
/// finalizes the PCR with the values assigned.
/// </summary>
/// <returns>
/// Returns false if the request was not successfully configured
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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;
}
/// <inheritdoc />
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
/// <summary>
/// Finds the site root (if any) matching the http request, and updates the PublishedRequest accordingly.
/// </summary>
/// <returns>A value indicating whether a domain was found.</returns>
internal bool FindDomain(IPublishedRequest request)
{
const string tracePrefix = "FindDomain: ";
// note - we are not handling schemes nor ports here.
_logger.Debug<PublishedRouter>("{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<PublishedRouter>("{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<PublishedRouter>("{TracePrefix}Matches no domain", tracePrefix);
request.Culture = defaultCulture == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultCulture);
}
_logger.Debug<PublishedRouter>("{TracePrefix}Culture={CultureName}", tracePrefix, request.Culture.Name);
return request.Domain != null;
}
/// <summary>
/// Looks for wildcard domains in the path and updates <c>Culture</c> accordingly.
/// </summary>
internal void HandleWildcardDomains(IPublishedRequest request)
{
const string tracePrefix = "HandleWildcardDomains: ";
if (request.HasPublishedContent == false)
return;
var nodePath = request.PublishedContent.Path;
_logger.Debug<PublishedRouter>("{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<PublishedRouter>("{TracePrefix}Got domain on node {DomainContentId}, set culture to {CultureName}", tracePrefix, domain.ContentId, request.Culture.Name);
}
else
{
_logger.Debug<PublishedRouter>("{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
/// <inheritdoc />
public ITemplate GetTemplate(string alias)
{
return _fileService.GetTemplate(alias);
}
/// <summary>
/// Finds the Umbraco document (if any) matching the request, and updates the PublishedRequest accordingly.
/// </summary>
/// <returns>A value indicating whether a document and template were found.</returns>
private void FindPublishedContentAndTemplate(IPublishedRequest request)
{
_logger.Debug<PublishedRouter>("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);
}
/// <summary>
/// Tries to find the document matching the request, by running the IPublishedContentFinder instances.
/// </summary>
/// <exception cref="InvalidOperationException">There is no finder collection.</exception>
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<PublishedRouter>(
$"{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<PublishedRouter>("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();
}
/// <summary>
/// Handles the published content (if any).
/// </summary>
/// <remarks>
/// Handles "not found", internal redirects, access validation...
/// things that must be handled in one place because they can create loops
/// </remarks>
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<PublishedRouter>("HandlePublishedContent: Loop {LoopCounter}", i);
// handle not found
if (request.HasPublishedContent == false)
{
request.Is404 = true;
_logger.Debug<PublishedRouter>("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<PublishedRouter>("HandlePublishedContent: Failed to find a document, give up");
break;
}
_logger.Debug<PublishedRouter>("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<PublishedRouter>("HandlePublishedContent: Looks like we are running into an infinite loop, abort");
request.PublishedContent = null;
}
_logger.Debug<PublishedRouter>("HandlePublishedContent: End");
}
/// <summary>
/// Follows internal redirections through the <c>umbracoInternalRedirectId</c> document property.
/// </summary>
/// <returns>A value indicating whether redirection took place and led to a new published document.</returns>
/// <remarks>
/// <para>Redirecting to a different site root and/or culture will not pick the new site root nor the new culture.</para>
/// <para>As per legacy, if the redirect does not work, we just ignore it.</para>
/// </remarks>
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<GuidUdi>(_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<PublishedRouter>("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<PublishedRouter>("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<PublishedRouter>("FollowInternalRedirects: Redirecting to self, ignore");
}
else
{
request.SetInternalRedirectPublishedContent(internalRedirectNode); // don't use .PublishedContent here
redirect = true;
_logger.Debug<PublishedRouter>("FollowInternalRedirects: Redirecting to id={InternalRedirectId}", internalRedirectId);
}
return redirect;
}
/// <summary>
/// Ensures that access to current node is permitted.
/// </summary>
/// <remarks>Redirecting to a different site root and/or culture will not pick the new site root nor the new culture.</remarks>
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<PublishedRouter>("EnsurePublishedContentAccess: Page is protected, check for access");
var status = _publicAccessChecker.HasMemberAccessToContent(request.PublishedContent.Id);
switch (status)
{
case PublicAccessStatus.NotLoggedIn:
_logger.Debug<PublishedRouter>("EnsurePublishedContentAccess: Not logged in, redirect to login page");
SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.LoginNodeId);
break;
case PublicAccessStatus.AccessDenied:
_logger.Debug<PublishedRouter>("EnsurePublishedContentAccess: Current member has not access, redirect to error page");
SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId);
break;
case PublicAccessStatus.LockedOut:
_logger.Debug<PublishedRouter>("Current member is locked out, redirect to error page");
SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId);
break;
case PublicAccessStatus.NotApproved:
_logger.Debug<PublishedRouter>("Current member is unapproved, redirect to error page");
SetPublishedContentAsOtherPage(request, publicAccessAttempt.Result.NoAccessNodeId);
break;
case PublicAccessStatus.AccessAccepted:
_logger.Debug<PublishedRouter>("Current member has access");
break;
}
}
else
{
_logger.Debug<PublishedRouter>("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);
}
/// <summary>
/// Finds a template for the current node, if any.
/// </summary>
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<IPublishedRequest>("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<PublishedRouter>("FindTemplate: Has a template already, but also an alternative template.");
_logger.Debug<PublishedRouter>("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<PublishedRouter>("FindTemplate: Got alternative template id={TemplateId} alias={TemplateAlias}", template.Id, template.Alias);
}
else
{
_logger.Debug<PublishedRouter>("FindTemplate: The alternative template with alias={AltTemplate} does not exist, ignoring.", altTemplate);
}
}
else
{
_logger.Warn<PublishedRouter>("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<PublishedRouter>("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<PublishedRouter>("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<PublishedRouter>("GetTemplateModel: No template.");
return null;
}
_logger.Debug<PublishedRouter>("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<PublishedRouter>("GetTemplateModel: Got template id={TemplateId} alias={TemplateAlias}", template.Id, template.Alias);
return template;
}
/// <summary>
/// Follows external redirection through <c>umbracoRedirect</c> document property.
/// </summary>
/// <remarks>As per legacy, if the redirect does not work, we just ignore it.</remarks>
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<GuidUdi>(_publishedValueFallback, Core.Constants.Conventions.Content.Redirect);
if (redirectUdi != null)
redirectUrl = _publishedUrlProvider.GetUrl(redirectUdi.Guid);
}
if (redirectUrl != "#")
request.SetRedirect(redirectUrl);
}
#endregion
}
}

View File

@@ -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<KestrelServerOptions>(options =>
{

View File

@@ -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;

View File

@@ -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<IMemberUserKeyProvider, MemberUserKeyProvider>();
composition.RegisterUnique<IPublicAccessChecker, PublicAccessChecker>();
composition.RegisterUnique<ITemplateRenderer, TemplateRenderer>();
// register the umbraco helper - this is Transient! very important!
// also, if not level.Run, we cannot really use the helper (during upgrade...)

View File

@@ -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
{
/// <summary>
/// This is used purely for the RenderTemplate functionality in Umbraco
/// </summary>
/// <remarks>
/// This allows you to render an MVC template based purely off of a node id and an optional alttemplate id as string output.
/// </remarks>
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("<!-- Could not render template for Id {0}, the document was not found -->", 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("<!-- Could not render template for Id {0}, the document's template was not found with id {0}-->", doc.TemplateId);
}
else
{
writer.Write("<!-- Could not render template for Id {0}, the altTemplate was not found with id {0}-->", 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);
}
/// <summary>
/// This will execute the UmbracoMvcHandler for the request specified and get the string output.
/// </summary>
/// <param name="requestContext">
/// Assumes the RequestContext is setup specifically to render an Umbraco view.
/// </param>
/// <returns></returns>
/// <remarks>
/// 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 .
/// </remarks>
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;
}
/// <summary>
/// Save all items that we know are used for rendering execution to variables so we can restore after rendering
/// </summary>
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;
}
/// <summary>
/// Restores all items back to their context's to continue normal page rendering execution
/// </summary>
private void RestoreItems(IPublishedRequest oldPublishedRequest)
{
_umbracoContextAccessor.UmbracoContext.PublishedRequest = oldPublishedRequest;
}
}
}

View File

@@ -425,6 +425,8 @@
<Compile Include="Mvc\UmbracoPageResult.cs" />
<Compile Include="RouteCollectionExtensions.cs" />
<Compile Include="Templates\TemplateRenderer.cs" />
<Compile Include="Trees\PartialViewMacrosTreeController.cs" />
<Compile Include="Trees\PartialViewsTreeController.cs" />
<Compile Include="UmbracoHelper.cs" />
<Compile Include="Mvc\ViewContextExtensions.cs" />
<Compile Include="Mvc\ViewDataContainerExtensions.cs" />

View File

@@ -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;