From bb1b04be1569f8c560b01179ef586bf66a7d0a69 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 12 Nov 2020 11:01:19 +0100 Subject: [PATCH 1/2] Netcore: Cleanup and migrate few classes from Umbraco.Web (#9375) * Migrated a few classes, and cleaned up by removing some old classs too in the Umbraco.Web Project Signed-off-by: Bjarke Berg * Migrated a few classes, and cleaned up by removing some old classs too in the Umbraco.Web Project Signed-off-by: Bjarke Berg * Moved SurfaceControllerTypeCollection + Builder Signed-off-by: Bjarke Berg * Moved Extension methods Signed-off-by: Bjarke Berg * Removed Wrap methods.. People should use their own TagBuilder Signed-off-by: Bjarke Berg --- .../HtmlHelperExtensionMethodsTests.cs | 118 +++++ ...lidateUmbracoFormRouteStringFilterTests.cs | 41 ++ .../Mvc/HtmlStringUtilitiesTests.cs | 3 +- .../Security/EncryptionHelperTests.cs} | 23 +- .../Publishing/PublishingStrategyTests.cs | 59 --- .../Routing/RenderRouteHandlerTests.cs | 6 +- .../Runtimes/CoreRuntimeTests.cs | 173 ------- src/Umbraco.Tests/Umbraco.Tests.csproj | 7 - .../BackOfficeControllerUnitTests.cs | 26 - .../Mvc/HtmlHelperExtensionMethodsTests.cs | 131 ------ ...ateUmbracoFormRouteStringAttributeTests.cs | 37 -- .../Controllers/AuthenticationController.cs | 6 +- .../Controllers/BackOfficeController.cs | 5 +- .../Controllers/ContentController.cs | 2 +- .../Controllers/CurrentUserController.cs | 4 +- .../Controllers/DashboardController.cs | 2 +- .../Controllers/PreviewController.cs | 4 +- .../UmbracoAuthorizedApiController.cs | 2 +- .../AspNetCore/UmbracoViewPage.cs | 16 +- .../Constants/ViewConstants.cs | 7 + .../HttpUmbracoFormRouteStringException.cs | 44 ++ .../BlockListTemplateExtensions.cs | 18 +- .../Extensions}/CacheHelperExtensions.cs | 14 +- .../Extensions/RazorPageExtensions.cs | 19 + .../Extensions/ViewContextExtensions.cs | 75 +++ ...=> UmbracoBackOfficeAuthorizeAttribute.cs} | 8 +- ...cs => UmbracoBackOfficeAuthorizeFilter.cs} | 8 +- .../UmbracoMemberAuthorizeAttribute.cs | 20 + .../Filters/UmbracoMemberAuthorizeFilter.cs | 69 +++ ...ValidateUmbracoFormRouteStringAttribute.cs | 74 +++ .../Mvc}/HtmlStringUtilities.cs | 13 +- .../Security/EncryptionHelper.cs | 104 ++++ .../RedirectToUmbracoPageResult.cs | 24 +- .../SurfaceControllerTypeCollection.cs | 2 +- .../SurfaceControllerTypeCollectionBuilder.cs | 4 +- .../Controllers/SurfaceController.cs | 12 +- .../UmbracoAuthorizedController.cs | 2 +- .../Extensions/HtmlContentExtensions.cs | 17 + .../Extensions}/HtmlHelperRenderExtensions.cs | 357 +++++++------- .../Extensions/TypeLoaderExtensions.cs | 28 ++ ...racoWebstiteServiceCollectionExtensions.cs | 2 + .../Runtime/WebsiteComposer.cs | 5 + src/Umbraco.Web.Website/UmbracoHelper.cs | 445 ++++++++++++++++++ .../AreaRegistrationContextExtensions.cs | 28 -- src/Umbraco.Web/Composing/Current.cs | 3 - .../Editors/BackOfficeController.cs | 2 +- .../Mvc/ActionExecutedEventArgs.cs | 18 - .../Mvc/AdminTokenAuthorizeAttribute.cs | 127 ----- src/Umbraco.Web/Mvc/BackOfficeArea.cs | 54 --- src/Umbraco.Web/Mvc/Constants.cs | 13 - .../Mvc/DisableBrowserCacheAttribute.cs | 43 -- src/Umbraco.Web/Mvc/HtmlTagWrapper.cs | 212 --------- src/Umbraco.Web/Mvc/HtmlTagWrapperTextNode.cs | 18 - src/Umbraco.Web/Mvc/IHtmlTagWrapper.cs | 9 - .../Mvc/UmbracoViewPageOfTModel.cs | 97 ---- ...ValidateUmbracoFormRouteStringAttribute.cs | 4 +- src/Umbraco.Web/Mvc/ViewContextExtensions.cs | 53 --- .../Runtime/WebInitialComponent.cs | 15 +- src/Umbraco.Web/Runtime/WebInitialComposer.cs | 4 +- src/Umbraco.Web/TypeLoaderExtensions.cs | 1 + src/Umbraco.Web/Umbraco.Web.csproj | 19 +- src/Umbraco.Web/UmbracoHelper.cs | 2 + src/Umbraco.Web/UrlHelperRenderExtensions.cs | 139 +++--- src/Umbraco.Web/WebViewPageExtensions.cs | 30 -- 64 files changed, 1434 insertions(+), 1493 deletions(-) create mode 100644 src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/HtmlHelperExtensionMethodsTests.cs create mode 100644 src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringFilterTests.cs rename src/{Umbraco.Tests/Web => Umbraco.Tests.UnitTests/Umbraco.Web.Common}/Mvc/HtmlStringUtilitiesTests.cs (94%) rename src/{Umbraco.Tests/Web/UrlHelperExtensionTests.cs => Umbraco.Tests.UnitTests/Umbraco.Web.Common/Security/EncryptionHelperTests.cs} (59%) delete mode 100644 src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs delete mode 100644 src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs delete mode 100644 src/Umbraco.Tests/Web/Controllers/BackOfficeControllerUnitTests.cs delete mode 100644 src/Umbraco.Tests/Web/Mvc/HtmlHelperExtensionMethodsTests.cs delete mode 100644 src/Umbraco.Tests/Web/Mvc/ValidateUmbracoFormRouteStringAttributeTests.cs create mode 100644 src/Umbraco.Web.Common/Exceptions/HttpUmbracoFormRouteStringException.cs rename src/{Umbraco.Web => Umbraco.Web.Common/Extensions}/BlockListTemplateExtensions.cs (51%) rename src/{Umbraco.Web => Umbraco.Web.Common/Extensions}/CacheHelperExtensions.cs (83%) create mode 100644 src/Umbraco.Web.Common/Extensions/RazorPageExtensions.cs create mode 100644 src/Umbraco.Web.Common/Extensions/ViewContextExtensions.cs rename src/Umbraco.Web.Common/Filters/{UmbracoAuthorizeAttribute.cs => UmbracoBackOfficeAuthorizeAttribute.cs} (66%) rename src/Umbraco.Web.Common/Filters/{UmbracoAuthorizeFilter.cs => UmbracoBackOfficeAuthorizeFilter.cs} (94%) create mode 100644 src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeAttribute.cs create mode 100644 src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs create mode 100644 src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs rename src/{Umbraco.Web => Umbraco.Web.Common/Mvc}/HtmlStringUtilities.cs (97%) create mode 100644 src/Umbraco.Web.Common/Security/EncryptionHelper.cs rename src/{Umbraco.Web/Mvc => Umbraco.Web.Website/Collections}/SurfaceControllerTypeCollection.cs (87%) rename src/{Umbraco.Web/Mvc => Umbraco.Web.Website/Collections}/SurfaceControllerTypeCollectionBuilder.cs (79%) create mode 100644 src/Umbraco.Web.Website/Extensions/HtmlContentExtensions.cs rename src/{Umbraco.Web => Umbraco.Web.Website/Extensions}/HtmlHelperRenderExtensions.cs (79%) create mode 100644 src/Umbraco.Web.Website/Extensions/TypeLoaderExtensions.cs create mode 100644 src/Umbraco.Web.Website/UmbracoHelper.cs delete mode 100644 src/Umbraco.Web/AreaRegistrationContextExtensions.cs delete mode 100644 src/Umbraco.Web/Mvc/ActionExecutedEventArgs.cs delete mode 100644 src/Umbraco.Web/Mvc/AdminTokenAuthorizeAttribute.cs delete mode 100644 src/Umbraco.Web/Mvc/BackOfficeArea.cs delete mode 100644 src/Umbraco.Web/Mvc/Constants.cs delete mode 100644 src/Umbraco.Web/Mvc/DisableBrowserCacheAttribute.cs delete mode 100644 src/Umbraco.Web/Mvc/HtmlTagWrapper.cs delete mode 100644 src/Umbraco.Web/Mvc/HtmlTagWrapperTextNode.cs delete mode 100644 src/Umbraco.Web/Mvc/IHtmlTagWrapper.cs delete mode 100644 src/Umbraco.Web/Mvc/ViewContextExtensions.cs delete mode 100644 src/Umbraco.Web/WebViewPageExtensions.cs diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/HtmlHelperExtensionMethodsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/HtmlHelperExtensionMethodsTests.cs new file mode 100644 index 0000000000..abb44a5dfb --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/HtmlHelperExtensionMethodsTests.cs @@ -0,0 +1,118 @@ +using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ViewEngines; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers; +using Microsoft.Extensions.WebEncoders.Testing; +using Moq; +using NUnit.Framework; +using Umbraco.Extensions; + +namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Filters +{ + + [TestFixture] + public class HtmlHelperExtensionMethodsTests + { + private const string SampleWithAnchorElement = "Hello world, this is some text with a link"; + private const string SampleWithBoldAndAnchorElements = "Hello world, this is some text with a link"; + + [SetUp] + public virtual void Initialize() + { + //create an empty htmlHelper + _htmlHelper = new HtmlHelper(Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + new HtmlTestEncoder(), + UrlEncoder.Default); + } + + private HtmlHelper _htmlHelper; + + [Test] + public void Truncate_Simple() + { + var result = _htmlHelper.Truncate(SampleWithAnchorElement, 25).ToString(); + + Assert.AreEqual("Hello world, this is some…", result); + } + + [Test] + public void When_Truncating_A_String_Ends_With_A_Space_We_Should_Trim_The_Space_Before_Appending_The_Ellipsis() + { + var result = _htmlHelper.Truncate(SampleWithAnchorElement, 26).ToString(); + + Assert.AreEqual("Hello world, this is some…", result); + } + + [Test] + public void Truncate_Inside_Word() + { + var result = _htmlHelper.Truncate(SampleWithAnchorElement, 24).ToString(); + + Assert.AreEqual("Hello world, this is som…", result); + } + + [Test] + public void Truncate_With_Tag() + { + var result = _htmlHelper.Truncate(SampleWithAnchorElement, 35).ToString(); + + Assert.AreEqual("Hello world, this is some text with…", result); + } + + [Test] + public void Truncate_By_Words() + { + var result = _htmlHelper.TruncateByWords(SampleWithAnchorElement, 4).ToString(); + + Assert.AreEqual("Hello world, this is…", result); + } + + [Test] + public void Truncate_By_Words_With_Tag() + { + var result = _htmlHelper.TruncateByWords(SampleWithBoldAndAnchorElements, 4).ToString(); + + Assert.AreEqual("Hello world, this is…", result); + } + + [Test] + public void Truncate_By_Words_Mid_Tag() + { + var result = _htmlHelper.TruncateByWords(SampleWithAnchorElement, 7).ToString(); + + Assert.AreEqual("Hello world, this is some text with…", result); + } + + [Test] + public void Strip_All_Html() + { + var result = _htmlHelper.StripHtml(SampleWithBoldAndAnchorElements, null).ToString(); + + Assert.AreEqual("Hello world, this is some text with a link", result); + } + + [Test] + public void Strip_Specific_Html() + { + string[] tags = { "b" }; + + var result = _htmlHelper.StripHtml(SampleWithBoldAndAnchorElements, tags).ToString(); + + Assert.AreEqual(SampleWithAnchorElement, result); + } + + [Test] + public void Strip_Invalid_Html() + { + const string text = "Hello world, is some text with a link"; + + var result = _htmlHelper.StripHtml(text).ToString(); + + Assert.AreEqual("Hello world, is some text with a link", result); + } + } +} diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringFilterTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringFilterTests.cs new file mode 100644 index 0000000000..3ed4a28b06 --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringFilterTests.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.DataProtection; +using NUnit.Framework; +using Umbraco.Web.Common.Exceptions; +using Umbraco.Web.Common.Filters; +using Umbraco.Web.Common.Security; + +namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Filters +{ + [TestFixture] + public class ValidateUmbracoFormRouteStringFilterTests + { + private IDataProtectionProvider DataProtectionProvider { get; } = new EphemeralDataProtectionProvider(); + + [Test] + public void Validate_Route_String() + { + var filter = new ValidateUmbracoFormRouteStringAttribute.ValidateUmbracoFormRouteStringFilter(DataProtectionProvider); + + Assert.Throws(() => filter.ValidateRouteString(null, null, null, null)); + + const string ControllerName = "Test"; + const string ControllerAction = "Index"; + const string Area = "MyArea"; + var validUfprt = EncryptionHelper.CreateEncryptedRouteString(DataProtectionProvider, ControllerName, ControllerAction, Area); + + var invalidUfprt = validUfprt + "z"; + Assert.Throws(() => filter.ValidateRouteString(invalidUfprt, null, null, null)); + + Assert.Throws(() => filter.ValidateRouteString(validUfprt, ControllerName, ControllerAction, "doesntMatch")); + Assert.Throws(() => filter.ValidateRouteString(validUfprt, ControllerName, ControllerAction, null)); + Assert.Throws(() => filter.ValidateRouteString(validUfprt, ControllerName, "doesntMatch", Area)); + Assert.Throws(() => filter.ValidateRouteString(validUfprt, ControllerName, null, Area)); + Assert.Throws(() => filter.ValidateRouteString(validUfprt, "doesntMatch", ControllerAction, Area)); + Assert.Throws(() => filter.ValidateRouteString(validUfprt, null, ControllerAction, Area)); + + Assert.DoesNotThrow(() => filter.ValidateRouteString(validUfprt, ControllerName, ControllerAction, Area)); + Assert.DoesNotThrow(() => filter.ValidateRouteString(validUfprt, ControllerName.ToLowerInvariant(), ControllerAction.ToLowerInvariant(), Area.ToLowerInvariant())); + } + + } +} diff --git a/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Mvc/HtmlStringUtilitiesTests.cs similarity index 94% rename from src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs rename to src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Mvc/HtmlStringUtilitiesTests.cs index 36ddecc676..4b1675b7f0 100644 --- a/src/Umbraco.Tests/Web/Mvc/HtmlStringUtilitiesTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Mvc/HtmlStringUtilitiesTests.cs @@ -1,7 +1,8 @@ using NUnit.Framework; using Umbraco.Web; +using Umbraco.Web.Common.Mvc; -namespace Umbraco.Tests.Web.Mvc +namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Mvc { [TestFixture] public class HtmlStringUtilitiesTests diff --git a/src/Umbraco.Tests/Web/UrlHelperExtensionTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Security/EncryptionHelperTests.cs similarity index 59% rename from src/Umbraco.Tests/Web/UrlHelperExtensionTests.cs rename to src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Security/EncryptionHelperTests.cs index a4b96ab4ff..db80e2cd74 100644 --- a/src/Umbraco.Tests/Web/UrlHelperExtensionTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Security/EncryptionHelperTests.cs @@ -1,15 +1,20 @@ using System.Collections.Generic; +using Microsoft.AspNetCore.DataProtection; using NUnit.Framework; using Umbraco.Core; using Umbraco.Web; +using Umbraco.Web.Common.Security; -namespace Umbraco.Tests.Web +namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Security { [TestFixture] - public class UrlHelperExtensionTests + public class EncryptionHelperTests { + + private IDataProtectionProvider DataProtectionProvider { get; } = new EphemeralDataProtectionProvider(); + [Test] - public static void Create_Encrypted_RouteString_From_Anonymous_Object() + public void Create_Encrypted_RouteString_From_Anonymous_Object() { var additionalRouteValues = new { @@ -19,14 +24,15 @@ namespace Umbraco.Tests.Web keY4 = "valuE4" }; - var encryptedRouteString = UrlHelperRenderExtensions.CreateEncryptedRouteString( + var encryptedRouteString = EncryptionHelper.CreateEncryptedRouteString( + DataProtectionProvider, "FormController", "FormAction", "", additionalRouteValues ); - var result = encryptedRouteString.DecryptWithMachineKey(); + var result = EncryptionHelper.Decrypt(encryptedRouteString, DataProtectionProvider); const string expectedResult = "c=FormController&a=FormAction&ar=&key1=value1&key2=value2&Key3=Value3&keY4=valuE4"; @@ -34,7 +40,7 @@ namespace Umbraco.Tests.Web } [Test] - public static void Create_Encrypted_RouteString_From_Dictionary() + public void Create_Encrypted_RouteString_From_Dictionary() { var additionalRouteValues = new Dictionary() { @@ -44,14 +50,15 @@ namespace Umbraco.Tests.Web {"keY4", "valuE4"} }; - var encryptedRouteString = UrlHelperRenderExtensions.CreateEncryptedRouteString( + var encryptedRouteString = EncryptionHelper.CreateEncryptedRouteString( + DataProtectionProvider, "FormController", "FormAction", "", additionalRouteValues ); - var result = encryptedRouteString.DecryptWithMachineKey(); + var result = EncryptionHelper.Decrypt(encryptedRouteString, DataProtectionProvider); const string expectedResult = "c=FormController&a=FormAction&ar=&key1=value1&key2=value2&Key3=Value3&keY4=valuE4"; diff --git a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs deleted file mode 100644 index 342aa99b47..0000000000 --- a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Collections.Generic; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Events; -using Umbraco.Core.Models; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; -using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using Umbraco.Core.Services; -using Umbraco.Tests.Testing; - -namespace Umbraco.Tests.Publishing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class PublishingStrategyTests : TestWithDatabaseBase - { - private IContent _homePage; - - [NUnit.Framework.Ignore("fixme - ignored test")] - [Test] - public void Can_Publish_And_Update_Xml_Cache() - { - // TODO: Create new test - } - - public IEnumerable CreateTestData() - { - //NOTE Maybe not the best way to create/save test data as we are using the services, which are being tested. - - //Create and Save ContentType "umbTextpage" -> 1045 - ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); - ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! - ServiceContext.ContentTypeService.Save(contentType); - var mandatoryType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true); - //ServiceContext.FileService.SaveTemplate(mandatoryType.DefaultTemplate); // else, FK violation on contentType! - ServiceContext.ContentTypeService.Save(mandatoryType); - - //Create and Save Content "Homepage" based on "umbTextpage" -> 1046 - _homePage = MockedContent.CreateSimpleContent(contentType); - ServiceContext.ContentService.Save(_homePage, 0); - - //Create and Save Content "Text Page 1" based on "umbTextpage" -> 1047 - Content subpage = MockedContent.CreateSimpleContent(contentType, "Text Page 1", _homePage.Id); - ServiceContext.ContentService.Save(subpage, 0); - - //Create and Save Content "Text Page 2" based on "umbTextpage" -> 1048 - Content subpage2 = MockedContent.CreateSimpleContent(contentType, "Text Page 2", _homePage.Id); - ServiceContext.ContentService.Save(subpage2, 0); - - //Create and Save Content "Text Page 3" based on "umbTextpage" -> 1048 - Content subpage3 = MockedContent.CreateSimpleContent(contentType, "Text Page 3", subpage2.Id); - ServiceContext.ContentService.Save(subpage3, 0); - - return new[] {_homePage, subpage, subpage2, subpage3}; - } - } -} diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index de20211774..b3663738dd 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -48,7 +48,7 @@ namespace Umbraco.Tests.Routing new TestUmbracoContextAccessor(), TestObjects.GetGlobalSettings(), ShortStringHelper, - new SurfaceControllerTypeCollection(Enumerable.Empty()), + // new SurfaceControllerTypeCollection(Enumerable.Empty()), new UmbracoApiControllerTypeCollection(Enumerable.Empty()), HostingEnvironment); } @@ -69,8 +69,8 @@ namespace Umbraco.Tests.Routing // set the default RenderMvcController Current.DefaultRenderMvcControllerType = typeof(RenderMvcController); // FIXME: Wrong! - var surfaceControllerTypes = new SurfaceControllerTypeCollection(Composition.TypeLoader.GetSurfaceControllers()); - Composition.Services.AddUnique(surfaceControllerTypes); + // var surfaceControllerTypes = new SurfaceControllerTypeCollection(Composition.TypeLoader.GetSurfaceControllers()); + // Composition.Services.AddUnique(surfaceControllerTypes); var umbracoApiControllerTypes = new UmbracoApiControllerTypeCollection(Composition.TypeLoader.GetUmbracoApiControllers()); Composition.Services.AddUnique(umbracoApiControllerTypes); diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs deleted file mode 100644 index 70beb3643a..0000000000 --- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Collections.Generic; -using Examine; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Moq; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.Models; -using Umbraco.Core.Exceptions; -using Umbraco.Core.Hosting; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; -using Umbraco.Core.Runtime; -using Umbraco.Net; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Stubs; -using Umbraco.Web; -using Umbraco.Web.Hosting; -using Umbraco.Web.Runtime; -using ConnectionStrings = Umbraco.Core.Configuration.Models.ConnectionStrings; -using Current = Umbraco.Web.Composing.Current; - -namespace Umbraco.Tests.Runtimes -{ - [TestFixture] - public class CoreRuntimeTests - { - [SetUp] - public void SetUp() - { - TestComponent.Reset(); - } - - public void TearDown() - { - TestComponent.Reset(); - } - - - // test application - public class TestUmbracoApplication : UmbracoApplicationBase - { - public TestUmbracoApplication() : base(new NullLogger(), - NullLoggerFactory.Instance, - new SecuritySettings(), - new GlobalSettings(), - new ConnectionStrings(), - _ioHelper, _profiler, new AspNetHostingEnvironment(Options.Create(new HostingSettings())), new AspNetBackOfficeInfo(_globalSettings, _ioHelper, new NullLogger(), Options.Create(new WebRoutingSettings()))) - { - } - - private static readonly IIOHelper _ioHelper = TestHelper.IOHelper; - private static readonly IProfiler _profiler = new TestProfiler(); - private static readonly GlobalSettings _globalSettings = new GlobalSettings(); - - - - protected override CoreRuntimeBootstrapper GetRuntime(GlobalSettings globalSettings, ConnectionStrings connectionStrings, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, ILoggerFactory loggerFactory, IProfiler profiler, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) - { - return new TestRuntimeBootstrapper(globalSettings, connectionStrings, umbracoVersion, ioHelper, logger, loggerFactory, profiler, hostingEnvironment, backOfficeInfo); - } - } - - // test runtime - public class TestRuntimeBootstrapper : CoreRuntimeBootstrapper - { - public TestRuntimeBootstrapper(GlobalSettings globalSettings, ConnectionStrings connectionStrings, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, ILoggerFactory loggerFactory, IProfiler profiler, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) - :base(globalSettings, connectionStrings,umbracoVersion, ioHelper, loggerFactory, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, TestHelper.GetTypeFinder(), AppCaches.NoCache) - { - - } - - // must override the database factory - // else BootFailedException because U cannot connect to the configured db - protected internal override IUmbracoDatabaseFactory CreateBootstrapDatabaseFactory() - { - var mock = new Mock(); - mock.Setup(x => x.Configured).Returns(true); - mock.Setup(x => x.CanConnect).Returns(true); - return mock.Object; - } - - public override void Configure(IServiceCollection services) - { - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); - - - base.Configure(services); - } - - // runs with only one single component - // UmbracoCoreComponent will be force-added too - // and that's it - protected override IEnumerable GetComposerTypes(TypeLoader typeLoader) - { - return new[] { typeof(TestComposer) }; - } - } - - - public class TestComposer : IComposer - { - // test flags - public static bool Ctored; - public static bool Composed; - - public static void Reset() - { - Ctored = Composed = false; - } - - public TestComposer() - { - Ctored = true; - } - - public void Compose(Composition composition) - { - composition.Services.AddUnique(); - composition.Components().Append(); - - Composed = true; - } - } - - public class TestComponent : IComponent, IDisposable - { - // test flags - public static bool Ctored; - public static bool Initialized; - public static bool Terminated; - public static IProfilingLogger ProfilingLogger; - - public bool Disposed; - - public static void Reset() - { - Ctored = Initialized = Terminated = false; - ProfilingLogger = null; - } - - public TestComponent(IProfilingLogger proflog) - { - Ctored = true; - ProfilingLogger = proflog; - } - - public void Initialize() - { - Initialized = true; - } - - public void Terminate() - { - Terminated = true; - } - - public void Dispose() - { - Disposed = true; - } - } - } -} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 5d6d4222e4..ec62d7f1f5 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -141,7 +141,6 @@ - @@ -202,20 +201,16 @@ - - - - @@ -242,7 +237,6 @@ - @@ -283,7 +277,6 @@ - diff --git a/src/Umbraco.Tests/Web/Controllers/BackOfficeControllerUnitTests.cs b/src/Umbraco.Tests/Web/Controllers/BackOfficeControllerUnitTests.cs deleted file mode 100644 index fa9335bc3f..0000000000 --- a/src/Umbraco.Tests/Web/Controllers/BackOfficeControllerUnitTests.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Linq; -using NUnit.Framework; -using Umbraco.Web.Editors; - -namespace Umbraco.Tests.Web.Controllers -{ - [TestFixture] - public class BackOfficeControllerUnitTests - { - public static object[] TestLegacyJsActionPaths = new object[] { - new string[] - { - "alert('hello');", - "function test() { window.location = 'http://www.google.com'; }", - "function openCourierSecurity(userid){ UmbClientMgr.contentFrame('page?userid=123); }", - @"function openRepository(repo, folder){ UmbClientMgr.contentFrame('page?repo=repo&folder=folder); } - function openTransfer(revision, repo, folder){ UmbClientMgr.contentFrame('page?revision=revision&repo=repo&folder=folder); }", - "umbraco/js/test.js", - "/umbraco/js/test.js", - "~/umbraco/js/test.js" - } - }; - - - } -} diff --git a/src/Umbraco.Tests/Web/Mvc/HtmlHelperExtensionMethodsTests.cs b/src/Umbraco.Tests/Web/Mvc/HtmlHelperExtensionMethodsTests.cs deleted file mode 100644 index 1a4220c83b..0000000000 --- a/src/Umbraco.Tests/Web/Mvc/HtmlHelperExtensionMethodsTests.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System.Web.Mvc; -using NUnit.Framework; -using Umbraco.Web; - -namespace Umbraco.Tests.Web.Mvc -{ - - [TestFixture] - public class HtmlHelperExtensionMethodsTests - { - private const string SampleWithAnchorElement = "Hello world, this is some text with a link"; - private const string SampleWithBoldAndAnchorElements = "Hello world, this is some text with a link"; - - [SetUp] - public virtual void Initialize() - { - //create an empty htmlHelper - _htmlHelper = new HtmlHelper(new ViewContext(), new ViewPage()); - } - - private HtmlHelper _htmlHelper; - - [Test] - public void Wrap_Simple() - { - var output = _htmlHelper.Wrap("div", "hello world"); - Assert.AreEqual("
hello world
", output.ToHtmlString()); - } - - [Test] - public void Wrap_Object_Attributes() - { - var output = _htmlHelper.Wrap("div", "hello world", new {style = "color:red;", onclick = "void();"}); - Assert.AreEqual("
hello world
", output.ToHtmlString()); - } - - [Test] - public static void Truncate_Simple() - { - var helper = new HtmlHelper(new ViewContext(), new ViewPage()); - var result = helper.Truncate(SampleWithAnchorElement, 25).ToString(); - - Assert.AreEqual("Hello world, this is some…", result); - } - - [Test] - public static void When_Truncating_A_String_Ends_With_A_Space_We_Should_Trim_The_Space_Before_Appending_The_Ellipsis() - { - var helper = new HtmlHelper(new ViewContext(), new ViewPage()); - var result = helper.Truncate(SampleWithAnchorElement, 26).ToString(); - - Assert.AreEqual("Hello world, this is some…", result); - } - - [Test] - public static void Truncate_Inside_Word() - { - var helper = new HtmlHelper(new ViewContext(), new ViewPage()); - var result = helper.Truncate(SampleWithAnchorElement, 24).ToString(); - - Assert.AreEqual("Hello world, this is som…", result); - } - - [Test] - public static void Truncate_With_Tag() - { - var helper = new HtmlHelper(new ViewContext(), new ViewPage()); - var result = helper.Truncate(SampleWithAnchorElement, 35).ToString(); - - Assert.AreEqual("Hello world, this is some text with…", result); - } - - [Test] - public static void Truncate_By_Words() - { - var helper = new HtmlHelper(new ViewContext(), new ViewPage()); - var result = helper.TruncateByWords(SampleWithAnchorElement, 4).ToString(); - - Assert.AreEqual("Hello world, this is…", result); - } - - [Test] - public static void Truncate_By_Words_With_Tag() - { - var helper = new HtmlHelper(new ViewContext(), new ViewPage()); - var result = helper.TruncateByWords(SampleWithBoldAndAnchorElements, 4).ToString(); - - Assert.AreEqual("Hello world, this is…", result); - } - - [Test] - public static void Truncate_By_Words_Mid_Tag() - { - var helper = new HtmlHelper(new ViewContext(), new ViewPage()); - var result = helper.TruncateByWords(SampleWithAnchorElement, 7).ToString(); - - Assert.AreEqual("Hello world, this is some text with…", result); - } - - [Test] - public static void Strip_All_Html() - { - var helper = new HtmlHelper(new ViewContext(), new ViewPage()); - var result = helper.StripHtml(SampleWithBoldAndAnchorElements, null).ToString(); - - Assert.AreEqual("Hello world, this is some text with a link", result); - } - - [Test] - public static void Strip_Specific_Html() - { - string[] tags = { "b" }; - - var helper = new HtmlHelper(new ViewContext(), new ViewPage()); - var result = helper.StripHtml(SampleWithBoldAndAnchorElements, tags).ToString(); - - Assert.AreEqual(SampleWithAnchorElement, result); - } - - [Test] - public static void Strip_Invalid_Html() - { - const string text = "Hello world, is some text with a link"; - - var helper = new HtmlHelper(new ViewContext(), new ViewPage()); - var result = helper.StripHtml(text).ToString(); - - Assert.AreEqual("Hello world, is some text with a link", result); - } - } -} diff --git a/src/Umbraco.Tests/Web/Mvc/ValidateUmbracoFormRouteStringAttributeTests.cs b/src/Umbraco.Tests/Web/Mvc/ValidateUmbracoFormRouteStringAttributeTests.cs deleted file mode 100644 index d4c3b7c887..0000000000 --- a/src/Umbraco.Tests/Web/Mvc/ValidateUmbracoFormRouteStringAttributeTests.cs +++ /dev/null @@ -1,37 +0,0 @@ -using NUnit.Framework; -using Umbraco.Web; -using Umbraco.Web.Mvc; - -namespace Umbraco.Tests.Web.Mvc -{ - [TestFixture] - public class ValidateUmbracoFormRouteStringAttributeTests - { - [Test] - public void Validate_Route_String() - { - var attribute = new ValidateUmbracoFormRouteStringAttribute(); - - Assert.Throws(() => attribute.ValidateRouteString(null, null, null, null)); - - const string ControllerName = "Test"; - const string ControllerAction = "Index"; - const string Area = "MyArea"; - var validUfprt = UrlHelperRenderExtensions.CreateEncryptedRouteString(ControllerName, ControllerAction, Area); - - var invalidUfprt = validUfprt + "z"; - Assert.Throws(() => attribute.ValidateRouteString(invalidUfprt, null, null, null)); - - Assert.Throws(() => attribute.ValidateRouteString(validUfprt, ControllerName, ControllerAction, "doesntMatch")); - Assert.Throws(() => attribute.ValidateRouteString(validUfprt, ControllerName, ControllerAction, null)); - Assert.Throws(() => attribute.ValidateRouteString(validUfprt, ControllerName, "doesntMatch", Area)); - Assert.Throws(() => attribute.ValidateRouteString(validUfprt, ControllerName, null, Area)); - Assert.Throws(() => attribute.ValidateRouteString(validUfprt, "doesntMatch", ControllerAction, Area)); - Assert.Throws(() => attribute.ValidateRouteString(validUfprt, null, ControllerAction, Area)); - - Assert.DoesNotThrow(() => attribute.ValidateRouteString(validUfprt, ControllerName, ControllerAction, Area)); - Assert.DoesNotThrow(() => attribute.ValidateRouteString(validUfprt, ControllerName.ToLowerInvariant(), ControllerAction.ToLowerInvariant(), Area.ToLowerInvariant())); - } - - } -} diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index 511254e96b..928c0dcd3d 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -95,7 +95,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Returns the configuration for the backoffice user membership provider - used to configure the change password dialog /// /// - [UmbracoAuthorize] + [UmbracoBackOfficeAuthorize] public IDictionary GetPasswordConfig(int userId) { return _passwordConfiguration.GetConfiguration(userId != _backofficeSecurityAccessor.BackofficeSecurity.CurrentUser.Id); @@ -183,7 +183,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// is valid before the login screen is displayed. The Auth cookie can be persisted for up to a day but the csrf cookies are only session /// cookies which means that the auth cookie could be valid but the csrf cookies are no longer there, in that case we need to re-set the csrf cookies. /// - [UmbracoAuthorize] + [UmbracoBackOfficeAuthorize] [SetAngularAntiForgeryTokens] //[CheckIfUserTicketDataIsStale] // TODO: Migrate this, though it will need to be done differently at the cookie auth level public UserDetail GetCurrentUser() @@ -205,7 +205,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// We cannot user GetCurrentUser since that requires they are approved, this is the same as GetCurrentUser but doesn't require them to be approved /// - [UmbracoAuthorize(redirectToUmbracoLogin: false, requireApproval: false)] + [UmbracoBackOfficeAuthorize(redirectToUmbracoLogin: false, requireApproval: false)] [SetAngularAntiForgeryTokens] public ActionResult GetCurrentInvitedUser() { diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index 99eae2c459..06ac53441b 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Microsoft.Extensions.Logging; @@ -220,7 +221,7 @@ namespace Umbraco.Web.BackOffice.Controllers return nestedDictionary; } - [UmbracoAuthorize(Order = 0)] + [UmbracoBackOfficeAuthorize(Order = 0)] [HttpGet] public IEnumerable GetGridConfig() { @@ -231,7 +232,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// Returns the JavaScript object representing the static server variables javascript object /// /// - [UmbracoAuthorize(Order = 0)] + [UmbracoBackOfficeAuthorize(Order = 0)] [MinifyJavaScriptResult(Order = 1)] public async Task ServerVariables() { diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index 6dd7164520..709be76acc 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -131,7 +131,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [HttpGet] - [UmbracoAuthorize, OverrideAuthorization] + [UmbracoBackOfficeAuthorize, OverrideAuthorization] public bool AllowsCultureVariation() { var contentTypes = _contentTypeService.GetAll(); diff --git a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs index 0254021b40..3605e65563 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs @@ -170,7 +170,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// This only works when the user is logged in (partially) /// - [UmbracoAuthorize(redirectToUmbracoLogin: false, requireApproval : true)] + [UmbracoBackOfficeAuthorize(redirectToUmbracoLogin: false, requireApproval : true)] public async Task PostSetInvitedUserPassword([FromBody]string newPassword) { var user = await _backOfficeUserManager.FindByIdAsync(_backofficeSecurityAccessor.BackofficeSecurity.GetUserId().ResultOr(0).ToString()); @@ -235,7 +235,7 @@ namespace Umbraco.Web.BackOffice.Controllers throw HttpResponseException.CreateValidationErrorResponse(ModelState); } - [UmbracoAuthorize] + [UmbracoBackOfficeAuthorize] [ValidateAngularAntiForgeryToken] public async Task> GetCurrentUserLinkedLogins() { diff --git a/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs b/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs index 0d341e9a04..4d12f8db0c 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web.BackOffice.Controllers [ValidationFilter] [AngularJsonOnlyConfiguration] // TODO: This could be applied with our Application Model conventions [IsBackOffice] - [UmbracoAuthorize] + [UmbracoBackOfficeAuthorize] public class DashboardController : UmbracoApiController { private readonly IUmbracoContextAccessor _umbracoContextAccessor; diff --git a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs index 07e3cb13da..cd320d49ff 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs @@ -64,7 +64,7 @@ namespace Umbraco.Web.BackOffice.Controllers _viewEngines = viewEngines; } - [UmbracoAuthorize(redirectToUmbracoLogin: true, requireApproval : false)] + [UmbracoBackOfficeAuthorize(redirectToUmbracoLogin: true, requireApproval : false)] [DisableBrowserCache] public ActionResult Index() { @@ -107,7 +107,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// The endpoint that is loaded within the preview iframe /// /// - [UmbracoAuthorize] + [UmbracoBackOfficeAuthorize] public ActionResult Frame(int id, string culture) { var user = _backofficeSecurityAccessor.BackofficeSecurity.CurrentUser; diff --git a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs index c232401b78..e3d779d61d 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs @@ -16,7 +16,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// [IsBackOffice] [UmbracoUserTimeoutFilter] - [UmbracoAuthorize] + [UmbracoBackOfficeAuthorize] [DisableBrowserCache] [UmbracoWebApiRequireHttps] [CheckIfUserTicketDataIsStale] diff --git a/src/Umbraco.Web.Common/AspNetCore/UmbracoViewPage.cs b/src/Umbraco.Web.Common/AspNetCore/UmbracoViewPage.cs index cf41670d8e..4b8f730e45 100644 --- a/src/Umbraco.Web.Common/AspNetCore/UmbracoViewPage.cs +++ b/src/Umbraco.Web.Common/AspNetCore/UmbracoViewPage.cs @@ -1,6 +1,7 @@ using System; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNetCore.Html; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor; @@ -8,12 +9,12 @@ using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Core; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Models; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Strings; +using Umbraco.Extensions; using Umbraco.Web.Common.ModelBinders; namespace Umbraco.Web.Common.AspNetCore @@ -110,6 +111,8 @@ namespace Umbraco.Web.Common.AspNetCore ViewData = (ViewDataDictionary) viewData; } + + // viewData is the ViewDataDictionary (maybe ) that we have // modelType is the type of the model that we need to bind to // @@ -143,5 +146,16 @@ namespace Umbraco.Web.Common.AspNetCore var nViewData = (ViewDataDictionary)Activator.CreateInstance(nViewDataType, tViewData); return nViewData; } + + public HtmlString RenderSection(string name, HtmlString defaultContents) + { + return RazorPageExtensions.RenderSection(this, name, defaultContents); + } + + public HtmlString RenderSection(string name, string defaultContents) + { + return RazorPageExtensions.RenderSection(this, name, defaultContents); + } + } } diff --git a/src/Umbraco.Web.Common/Constants/ViewConstants.cs b/src/Umbraco.Web.Common/Constants/ViewConstants.cs index 5da1a74f55..4c87509069 100644 --- a/src/Umbraco.Web.Common/Constants/ViewConstants.cs +++ b/src/Umbraco.Web.Common/Constants/ViewConstants.cs @@ -8,5 +8,12 @@ internal const string ViewLocation = "~/Views"; internal const string DataTokenCurrentViewContext = "umbraco-current-view-context"; + + internal static class ReservedAdditionalKeys + { + internal const string Controller = "c"; + internal const string Action = "a"; + internal const string Area = "ar"; + } } } diff --git a/src/Umbraco.Web.Common/Exceptions/HttpUmbracoFormRouteStringException.cs b/src/Umbraco.Web.Common/Exceptions/HttpUmbracoFormRouteStringException.cs new file mode 100644 index 0000000000..8ba326a926 --- /dev/null +++ b/src/Umbraco.Web.Common/Exceptions/HttpUmbracoFormRouteStringException.cs @@ -0,0 +1,44 @@ +using System; +using System.Runtime.Serialization; + +namespace Umbraco.Web.Common.Exceptions +{ + /// + /// Exception that occurs when an Umbraco form route string is invalid + /// + [Serializable] + public sealed class HttpUmbracoFormRouteStringException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public HttpUmbracoFormRouteStringException() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that holds the contextual information about the source or destination. + private HttpUmbracoFormRouteStringException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } + + /// + /// Initializes a new instance of the class. + /// + /// The error message displayed to the client when the exception is thrown. + public HttpUmbracoFormRouteStringException(string message) + : base(message) + { } + + /// + /// Initializes a new instance of the class. + /// + /// The error message displayed to the client when the exception is thrown. + /// The , if any, that threw the current exception. + public HttpUmbracoFormRouteStringException(string message, Exception innerException) + : base(message, innerException) + { } + } +} diff --git a/src/Umbraco.Web/BlockListTemplateExtensions.cs b/src/Umbraco.Web.Common/Extensions/BlockListTemplateExtensions.cs similarity index 51% rename from src/Umbraco.Web/BlockListTemplateExtensions.cs rename to src/Umbraco.Web.Common/Extensions/BlockListTemplateExtensions.cs index 6e105a24d6..06c3efaf02 100644 --- a/src/Umbraco.Web/BlockListTemplateExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/BlockListTemplateExtensions.cs @@ -1,30 +1,30 @@ using System; -using System.Web.Mvc; -using System.Web.Mvc.Html; using Umbraco.Core.Models.Blocks; using Umbraco.Core.Models.PublishedContent; -using System.Web; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; -namespace Umbraco.Web +namespace Umbraco.Extensions { public static class BlockListTemplateExtensions { public const string DefaultFolder = "BlockList/"; public const string DefaultTemplate = "Default"; - public static IHtmlString GetBlockListHtml(this HtmlHelper html, BlockListModel model, string template = DefaultTemplate) + public static IHtmlContent GetBlockListHtml(this HtmlHelper html, BlockListModel model, string template = DefaultTemplate) { - if (model?.Count == 0) return new MvcHtmlString(string.Empty); + if (model?.Count == 0) return new HtmlString(string.Empty); var view = DefaultFolder + template; return html.Partial(view, model); } - public static IHtmlString GetBlockListHtml(this HtmlHelper html, IPublishedProperty property, string template = DefaultTemplate) => GetBlockListHtml(html, property?.GetValue() as BlockListModel, template); + public static IHtmlContent GetBlockListHtml(this HtmlHelper html, IPublishedProperty property, string template = DefaultTemplate) => GetBlockListHtml(html, property?.GetValue() as BlockListModel, template); - public static IHtmlString GetBlockListHtml(this HtmlHelper html, IPublishedContent contentItem, string propertyAlias) => GetBlockListHtml(html, contentItem, propertyAlias, DefaultTemplate); + public static IHtmlContent GetBlockListHtml(this HtmlHelper html, IPublishedContent contentItem, string propertyAlias) => GetBlockListHtml(html, contentItem, propertyAlias, DefaultTemplate); - public static IHtmlString GetBlockListHtml(this HtmlHelper html, IPublishedContent contentItem, string propertyAlias, string template) + public static IHtmlContent GetBlockListHtml(this HtmlHelper html, IPublishedContent contentItem, string propertyAlias, string template) { if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias)); if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyAlias)); diff --git a/src/Umbraco.Web/CacheHelperExtensions.cs b/src/Umbraco.Web.Common/Extensions/CacheHelperExtensions.cs similarity index 83% rename from src/Umbraco.Web/CacheHelperExtensions.cs rename to src/Umbraco.Web.Common/Extensions/CacheHelperExtensions.cs index e27b0db1fc..e8309262e6 100644 --- a/src/Umbraco.Web/CacheHelperExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/CacheHelperExtensions.cs @@ -1,11 +1,11 @@ using System; -using System.Web; -using System.Web.Mvc; -using System.Web.Mvc.Html; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; using Umbraco.Core.Cache; using Umbraco.Core.Hosting; -namespace Umbraco.Web +namespace Umbraco.Extensions { /// @@ -25,10 +25,10 @@ namespace Umbraco.Web /// used to cache the partial view, this key could change if it is cached by page or by member /// /// - public static IHtmlString CachedPartialView( + public static IHtmlContent CachedPartialView( this AppCaches appCaches, IHostingEnvironment hostingEnvironment, - HtmlHelper htmlHelper, + IHtmlHelper htmlHelper, string partialViewName, object model, int cachedSeconds, @@ -42,7 +42,7 @@ namespace Umbraco.Web return htmlHelper.Partial(partialViewName, model, viewData); } - return appCaches.RuntimeCache.GetCacheItem( + return appCaches.RuntimeCache.GetCacheItem( Core.CacheHelperExtensions.PartialViewCacheKey + cacheKey, () => htmlHelper.Partial(partialViewName, model, viewData), timeout: new TimeSpan(0, 0, 0, cachedSeconds)); diff --git a/src/Umbraco.Web.Common/Extensions/RazorPageExtensions.cs b/src/Umbraco.Web.Common/Extensions/RazorPageExtensions.cs new file mode 100644 index 0000000000..884e2bbdbc --- /dev/null +++ b/src/Umbraco.Web.Common/Extensions/RazorPageExtensions.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc.Razor; + +namespace Umbraco.Extensions +{ + public static class RazorPageExtensions + { + public static HtmlString RenderSection(this RazorPage webPage, string name, HtmlString defaultContents) + { + return webPage.IsSectionDefined(name) ? webPage.RenderSection(name) : defaultContents; + } + + public static HtmlString RenderSection(this RazorPage webPage, string name, string defaultContents) + { + return webPage.IsSectionDefined(name) ? webPage.RenderSection(name) : new HtmlString(defaultContents); + } + + } +} diff --git a/src/Umbraco.Web.Common/Extensions/ViewContextExtensions.cs b/src/Umbraco.Web.Common/Extensions/ViewContextExtensions.cs new file mode 100644 index 0000000000..17a9d048fc --- /dev/null +++ b/src/Umbraco.Web.Common/Extensions/ViewContextExtensions.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; + +namespace Umbraco.Extensions +{ + public static class ViewContextExtensions + { + /// + /// Creates a new ViewContext from an existing one but specifies a new Model for the ViewData + /// + /// + /// + /// + public static ViewContext CopyWithModel(this ViewContext vc, object model) + { + return new ViewContext + { + View = vc.View, + Writer = vc.Writer, + ActionDescriptor = vc.ActionDescriptor, + FormContext = vc.FormContext, + HttpContext = vc.HttpContext, + RouteData = vc.RouteData, + TempData = vc.TempData, + ViewData = new ViewDataDictionary(vc.ViewData) + { + Model = model + }, + ClientValidationEnabled = vc.ClientValidationEnabled, + ExecutingFilePath = vc.ExecutingFilePath, + ValidationMessageElement = vc.ValidationMessageElement, + Html5DateRenderingMode = vc.Html5DateRenderingMode, + ValidationSummaryMessageElement = vc.ValidationSummaryMessageElement + }; + } + + public static ViewContext Clone(this ViewContext vc) + { + return new ViewContext + { + View = vc.View, + Writer = vc.Writer, + ActionDescriptor = vc.ActionDescriptor, + FormContext = vc.FormContext, + HttpContext = vc.HttpContext, + RouteData = vc.RouteData, + TempData = vc.TempData, + ViewData = new ViewDataDictionary(vc.ViewData), + ClientValidationEnabled = vc.ClientValidationEnabled, + ExecutingFilePath = vc.ExecutingFilePath, + ValidationMessageElement = vc.ValidationMessageElement, + Html5DateRenderingMode = vc.Html5DateRenderingMode, + ValidationSummaryMessageElement = vc.ValidationSummaryMessageElement + }; + } + + //public static ViewContext CloneWithWriter(this ViewContext vc, TextWriter writer) + //{ + // return new ViewContext + // { + // Controller = vc.Controller, + // HttpContext = vc.HttpContext, + // RequestContext = vc.RequestContext, + // RouteData = vc.RouteData, + // TempData = vc.TempData, + // View = vc.View, + // ViewData = vc.ViewData, + // FormContext = vc.FormContext, + // ClientValidationEnabled = vc.ClientValidationEnabled, + // UnobtrusiveJavaScriptEnabled = vc.UnobtrusiveJavaScriptEnabled, + // Writer = writer + // }; + //} + } +} diff --git a/src/Umbraco.Web.Common/Filters/UmbracoAuthorizeAttribute.cs b/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeAttribute.cs similarity index 66% rename from src/Umbraco.Web.Common/Filters/UmbracoAuthorizeAttribute.cs rename to src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeAttribute.cs index 8a7c7b04d5..1f4abbaa25 100644 --- a/src/Umbraco.Web.Common/Filters/UmbracoAuthorizeAttribute.cs +++ b/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeAttribute.cs @@ -5,12 +5,12 @@ namespace Umbraco.Web.Common.Filters /// /// Ensures authorization is successful for a back office user. /// - public class UmbracoAuthorizeAttribute : TypeFilterAttribute + public class UmbracoBackOfficeAuthorizeAttribute : TypeFilterAttribute { /// /// Default constructor /// - public UmbracoAuthorizeAttribute() : this(false, false) + public UmbracoBackOfficeAuthorizeAttribute() : this(false, false) { } @@ -20,7 +20,7 @@ namespace Umbraco.Web.Common.Filters /// /// - public UmbracoAuthorizeAttribute(bool redirectToUmbracoLogin, bool requireApproval) : base(typeof(UmbracoAuthorizeFilter)) + public UmbracoBackOfficeAuthorizeAttribute(bool redirectToUmbracoLogin, bool requireApproval) : base(typeof(UmbracoBackOfficeAuthorizeFilter)) { Arguments = new object[] { redirectToUmbracoLogin, requireApproval }; } @@ -29,7 +29,7 @@ namespace Umbraco.Web.Common.Filters /// Constructor with redirect url behavior /// /// - public UmbracoAuthorizeAttribute(string redirectUrl) : base(typeof(UmbracoAuthorizeFilter)) + public UmbracoBackOfficeAuthorizeAttribute(string redirectUrl) : base(typeof(UmbracoBackOfficeAuthorizeFilter)) { Arguments = new object[] { redirectUrl }; } diff --git a/src/Umbraco.Web.Common/Filters/UmbracoAuthorizeFilter.cs b/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeFilter.cs similarity index 94% rename from src/Umbraco.Web.Common/Filters/UmbracoAuthorizeFilter.cs rename to src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeFilter.cs index 66b1462ae9..e7889cd1a8 100644 --- a/src/Umbraco.Web.Common/Filters/UmbracoAuthorizeFilter.cs +++ b/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeFilter.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.Common.Filters /// /// Ensures authorization is successful for a back office user. /// - public class UmbracoAuthorizeFilter : IAuthorizationFilter + public class UmbracoBackOfficeAuthorizeFilter : IAuthorizationFilter { private readonly bool _requireApproval; @@ -28,7 +28,7 @@ namespace Umbraco.Web.Common.Filters private readonly bool _redirectToUmbracoLogin; private string _redirectUrl; - private UmbracoAuthorizeFilter( + private UmbracoBackOfficeAuthorizeFilter( IHostingEnvironment hostingEnvironment, IUmbracoContextAccessor umbracoContext, IRuntimeState runtimeState, LinkGenerator linkGenerator, @@ -51,7 +51,7 @@ namespace Umbraco.Web.Common.Filters /// /// /// - public UmbracoAuthorizeFilter( + public UmbracoBackOfficeAuthorizeFilter( IHostingEnvironment hostingEnvironment, IUmbracoContextAccessor umbracoContext, IRuntimeState runtimeState, LinkGenerator linkGenerator, @@ -59,7 +59,7 @@ namespace Umbraco.Web.Common.Filters { } - public UmbracoAuthorizeFilter( + public UmbracoBackOfficeAuthorizeFilter( IHostingEnvironment hostingEnvironment, IUmbracoContextAccessor umbracoContext, IRuntimeState runtimeState, LinkGenerator linkGenerator, diff --git a/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeAttribute.cs b/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeAttribute.cs new file mode 100644 index 0000000000..cc6058121b --- /dev/null +++ b/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeAttribute.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Umbraco.Web.Common.Filters +{ + /// + /// Ensures authorization is successful for a website user (member). + /// + public class UmbracoMemberAuthorizeAttribute : TypeFilterAttribute + { + public UmbracoMemberAuthorizeAttribute() : this(string.Empty, string.Empty, string.Empty) + { + } + + public UmbracoMemberAuthorizeAttribute(string allowType, string allowGroup, string allowMembers) : base(typeof(UmbracoMemberAuthorizeFilter)) + { + Arguments = new object[] { allowType, allowGroup, allowMembers}; + } + + } +} diff --git a/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs b/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs new file mode 100644 index 0000000000..27c2922637 --- /dev/null +++ b/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs @@ -0,0 +1,69 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Extensions; + +namespace Umbraco.Web.Common.Filters +{ + + /// + /// Ensures authorization is successful for a back office user. + /// + public class UmbracoMemberAuthorizeFilter : IAuthorizationFilter + { + /// + /// Comma delimited list of allowed member types + /// + public string AllowType { get; private set;} + + /// + /// Comma delimited list of allowed member groups + /// + public string AllowGroup { get; private set;} + + /// + /// Comma delimited list of allowed members + /// + public string AllowMembers { get; private set; } + + + private UmbracoMemberAuthorizeFilter( + string allowType, string allowGroup, string allowMembers) + { + AllowType = allowType; + AllowGroup = allowGroup; + AllowMembers = allowMembers; + } + + public void OnAuthorization(AuthorizationFilterContext context) + { + if (!IsAuthorized()) + { + context.HttpContext.SetReasonPhrase("Resource restricted: either member is not logged on or is not of a permitted type or group."); + context.Result = new ForbidResult(); + } + } + + private bool IsAuthorized() + { + if (AllowMembers.IsNullOrWhiteSpace()) + AllowMembers = ""; + if (AllowGroup.IsNullOrWhiteSpace()) + AllowGroup = ""; + if (AllowType.IsNullOrWhiteSpace()) + AllowType = ""; + + var members = new List(); + foreach (var s in AllowMembers.Split(',')) + { + if (int.TryParse(s, out var id)) + { + members.Add(id); + } + } + + return false;// TODO reintroduce when members are implemented: _memberHelper.IsMemberAuthorized(AllowType.Split(','), AllowGroup.Split(','), members); + } + } +} diff --git a/src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs b/src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs new file mode 100644 index 0000000000..45806b9d18 --- /dev/null +++ b/src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs @@ -0,0 +1,74 @@ +using System; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Umbraco.Core; +using Umbraco.Web.Common.Constants; +using Umbraco.Web.Common.Exceptions; +using Umbraco.Web.Common.Security; + +namespace Umbraco.Web.Common.Filters +{ + + /// + /// Attribute used to check that the request contains a valid Umbraco form request string. + /// + /// + /// Applying this attribute/filter to a or SurfaceController Action will ensure that the Action can only be executed + /// when it is routed to from within Umbraco, typically when rendering a form with BeginUmbracoForm. It will mean that the natural MVC route for this Action + /// will fail with a . + /// + public class ValidateUmbracoFormRouteStringAttribute : TypeFilterAttribute + { + + public ValidateUmbracoFormRouteStringAttribute() : base(typeof(ValidateUmbracoFormRouteStringFilter)) + { + Arguments = new object[] { }; + } + + internal class ValidateUmbracoFormRouteStringFilter: IAuthorizationFilter + { + private readonly IDataProtectionProvider _dataProtectionProvider; + + public ValidateUmbracoFormRouteStringFilter(IDataProtectionProvider dataProtectionProvider) + { + _dataProtectionProvider = dataProtectionProvider; + } + + public void OnAuthorization(AuthorizationFilterContext context) + { + if (context == null) throw new ArgumentNullException(nameof(context)); + + var ufprt = context.HttpContext.Request.Form["ufprt"]; + + if (context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor) + { + ValidateRouteString(ufprt, controllerActionDescriptor.ControllerName, controllerActionDescriptor.ActionName, context.RouteData?.DataTokens["area"]?.ToString()); + } + + } + + public void ValidateRouteString(string ufprt, string currentController, string currentAction, string currentArea) + { + if (ufprt.IsNullOrWhiteSpace()) + { + throw new HttpUmbracoFormRouteStringException("The required request field \"ufprt\" is not present."); + } + + if (!EncryptionHelper.DecryptAndValidateEncryptedRouteString(_dataProtectionProvider, ufprt, out var additionalDataParts)) + { + throw new HttpUmbracoFormRouteStringException("The Umbraco form request route string could not be decrypted."); + } + + if (!additionalDataParts[ViewConstants.ReservedAdditionalKeys.Controller].InvariantEquals(currentController) || + !additionalDataParts[ViewConstants.ReservedAdditionalKeys.Action].InvariantEquals(currentAction) || + (!additionalDataParts[ViewConstants.ReservedAdditionalKeys.Area].IsNullOrWhiteSpace() && !additionalDataParts[ViewConstants.ReservedAdditionalKeys.Area].InvariantEquals(currentArea))) + { + throw new HttpUmbracoFormRouteStringException("The provided Umbraco form request route string was meant for a different controller and action."); + } + + } + } + } +} diff --git a/src/Umbraco.Web/HtmlStringUtilities.cs b/src/Umbraco.Web.Common/Mvc/HtmlStringUtilities.cs similarity index 97% rename from src/Umbraco.Web/HtmlStringUtilities.cs rename to src/Umbraco.Web.Common/Mvc/HtmlStringUtilities.cs index cbeff72acf..204bb61425 100644 --- a/src/Umbraco.Web/HtmlStringUtilities.cs +++ b/src/Umbraco.Web.Common/Mvc/HtmlStringUtilities.cs @@ -5,8 +5,9 @@ using System.Linq; using System.Text; using System.Web; using HtmlAgilityPack; +using Microsoft.AspNetCore.Html; -namespace Umbraco.Web +namespace Umbraco.Web.Common.Mvc { /// /// Provides utility methods for UmbracoHelper for working with strings and HTML in views. @@ -20,7 +21,7 @@ namespace Umbraco.Web /// /// The HTML encoded text with text line breaks replaced with HTML line breaks (<br />). /// - public IHtmlString ReplaceLineBreaks(string text) + public IHtmlContent ReplaceLineBreaks(string text) { var value = HttpUtility.HtmlEncode(text)? .Replace("\r\n", "
") @@ -63,7 +64,7 @@ namespace Umbraco.Web return new HtmlString(doc.DocumentNode.FirstChild.InnerHtml.Replace(" ", " ")); } - internal string Join(string separator, params object[] args) + public string Join(string separator, params object[] args) { var results = args .Where(x => x != null) @@ -72,7 +73,7 @@ namespace Umbraco.Web return string.Join(separator, results); } - internal string Concatenate(params object[] args) + public string Concatenate(params object[] args) { var sb = new StringBuilder(); foreach (var arg in args @@ -85,7 +86,7 @@ namespace Umbraco.Web return sb.ToString(); } - internal string Coalesce(params object[] args) + public string Coalesce(params object[] args) { var arg = args .Where(x => x != null) @@ -95,7 +96,7 @@ namespace Umbraco.Web return arg ?? string.Empty; } - public IHtmlString Truncate(string html, int length, bool addElipsis, bool treatTagsAsContent) + public IHtmlContent Truncate(string html, int length, bool addElipsis, bool treatTagsAsContent) { const string hellip = "…"; diff --git a/src/Umbraco.Web.Common/Security/EncryptionHelper.cs b/src/Umbraco.Web.Common/Security/EncryptionHelper.cs new file mode 100644 index 0000000000..300afd530d --- /dev/null +++ b/src/Umbraco.Web.Common/Security/EncryptionHelper.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Web; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Logging; +using Umbraco.Core; +using Umbraco.Web.Common.Constants; + +namespace Umbraco.Web.Common.Security +{ + public class EncryptionHelper + { + private static IDataProtector CreateDataProtector(IDataProtectionProvider dataProtectionProvider) + { + return dataProtectionProvider.CreateProtector(nameof(EncryptionHelper)); + } + + public static string Decrypt(string encryptedString, IDataProtectionProvider dataProtectionProvider) + { + return CreateDataProtector(dataProtectionProvider).Unprotect(encryptedString); + } + + public static string Encrypt(string plainString, IDataProtectionProvider dataProtectionProvider) + { + return CreateDataProtector(dataProtectionProvider).Protect(plainString); + } + + /// + /// This is used in methods like BeginUmbracoForm and SurfaceAction to generate an encrypted string which gets submitted in a request for which + /// Umbraco can decrypt during the routing process in order to delegate the request to a specific MVC Controller. + /// + /// + /// + /// + /// + /// + /// + public static string CreateEncryptedRouteString(IDataProtectionProvider dataProtectionProvider, string controllerName, string controllerAction, string area, object additionalRouteVals = null) + { + if (dataProtectionProvider == null) throw new ArgumentNullException(nameof(dataProtectionProvider)); + if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); + if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); + if (controllerAction == null) throw new ArgumentNullException(nameof(controllerAction)); + if (string.IsNullOrEmpty(controllerAction)) throw new ArgumentException("Value can't be empty.", nameof(controllerAction)); + if (area == null) throw new ArgumentNullException(nameof(area)); + + //need to create a params string as Base64 to put into our hidden field to use during the routes + var surfaceRouteParams = $"{ViewConstants.ReservedAdditionalKeys.Controller}={WebUtility.UrlEncode(controllerName)}&{ViewConstants.ReservedAdditionalKeys.Action}={WebUtility.UrlEncode(controllerAction)}&{ViewConstants.ReservedAdditionalKeys.Area}={area}"; + + //checking if the additional route values is already a dictionary and convert to querystring + string additionalRouteValsAsQuery; + if (additionalRouteVals != null) + { + if (additionalRouteVals is Dictionary additionalRouteValsAsDictionary) + additionalRouteValsAsQuery = additionalRouteValsAsDictionary.ToQueryString(); + else + additionalRouteValsAsQuery = additionalRouteVals.ToDictionary().ToQueryString(); + } + else + additionalRouteValsAsQuery = null; + + if (additionalRouteValsAsQuery.IsNullOrWhiteSpace() == false) + surfaceRouteParams += "&" + additionalRouteValsAsQuery; + + return Encrypt(surfaceRouteParams, dataProtectionProvider); + } + + public static bool DecryptAndValidateEncryptedRouteString(IDataProtectionProvider dataProtectionProvider, string encryptedString, out IDictionary parts) + { + if (dataProtectionProvider == null) throw new ArgumentNullException(nameof(dataProtectionProvider)); + string decryptedString; + try + { + decryptedString = Decrypt(encryptedString, dataProtectionProvider); + } + catch (Exception) + { + StaticApplicationLogging.Logger.LogWarning("A value was detected in the ufprt parameter but Umbraco could not decrypt the string"); + parts = null; + return false; + } + var parsedQueryString = HttpUtility.ParseQueryString(decryptedString); + parts = new Dictionary(); + foreach (var key in parsedQueryString.AllKeys) + { + parts[key] = parsedQueryString[key]; + } + //validate all required keys exist + //the controller + if (parts.All(x => x.Key != ViewConstants.ReservedAdditionalKeys.Controller)) + return false; + //the action + if (parts.All(x => x.Key != ViewConstants.ReservedAdditionalKeys.Action)) + return false; + //the area + if (parts.All(x => x.Key != ViewConstants.ReservedAdditionalKeys.Area)) + return false; + + return true; + } + } +} diff --git a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs index 4b400b35dd..93b3a5bb0d 100644 --- a/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs +++ b/src/Umbraco.Web.Website/ActionResults/RedirectToUmbracoPageResult.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.Website.ActionResults public class RedirectToUmbracoPageResult : IActionResult { private IPublishedContent _publishedContent; - private readonly int _pageId; + private readonly Guid _key; private readonly QueryString _queryString; private readonly IPublishedUrlProvider _publishedUrlProvider; private readonly IUmbracoContextAccessor _umbracoContextAccessor; @@ -31,22 +31,20 @@ namespace Umbraco.Web.Website.ActionResults if (!string.IsNullOrWhiteSpace(_url)) return _url; if (PublishedContent is null) - throw new InvalidOperationException($"Cannot redirect, no entity was found for id {PageId}"); + throw new InvalidOperationException($"Cannot redirect, no entity was found for key {Key}"); var result = _publishedUrlProvider.GetUrl(PublishedContent.Id); if (result == "#") throw new InvalidOperationException( - $"Could not route to entity with id {PageId}, the NiceUrlProvider could not generate a URL"); + $"Could not route to entity with key {Key}, the NiceUrlProvider could not generate a URL"); _url = result; return _url; } } - - private int PageId => _pageId; - + public Guid Key => _key; private IPublishedContent PublishedContent { get @@ -54,7 +52,7 @@ namespace Umbraco.Web.Website.ActionResults if (!(_publishedContent is null)) return _publishedContent; //need to get the URL for the page - _publishedContent = _umbracoContextAccessor.GetRequiredUmbracoContext().Content.GetById(_pageId); + _publishedContent = _umbracoContextAccessor.GetRequiredUmbracoContext().Content.GetById(_key); return _publishedContent; } @@ -65,9 +63,9 @@ namespace Umbraco.Web.Website.ActionResults /// /// /// - public RedirectToUmbracoPageResult(int pageId, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) + public RedirectToUmbracoPageResult(Guid key, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) { - _pageId = pageId; + _key = key; _publishedUrlProvider = publishedUrlProvider; _umbracoContextAccessor = umbracoContextAccessor; } @@ -78,9 +76,9 @@ namespace Umbraco.Web.Website.ActionResults /// /// /// - public RedirectToUmbracoPageResult(int pageId, QueryString queryString, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) + public RedirectToUmbracoPageResult(Guid key, QueryString queryString, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) { - _pageId = pageId; + _key = key; _queryString = queryString; _publishedUrlProvider = publishedUrlProvider; _umbracoContextAccessor = umbracoContextAccessor; @@ -95,7 +93,7 @@ namespace Umbraco.Web.Website.ActionResults public RedirectToUmbracoPageResult(IPublishedContent publishedContent, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) { _publishedContent = publishedContent; - _pageId = publishedContent.Id; + _key = publishedContent.Key; _publishedUrlProvider = publishedUrlProvider; _umbracoContextAccessor = umbracoContextAccessor; } @@ -110,7 +108,7 @@ namespace Umbraco.Web.Website.ActionResults public RedirectToUmbracoPageResult(IPublishedContent publishedContent, QueryString queryString, IPublishedUrlProvider publishedUrlProvider, IUmbracoContextAccessor umbracoContextAccessor) { _publishedContent = publishedContent; - _pageId = publishedContent.Id; + _key = publishedContent.Key; _queryString = queryString; _publishedUrlProvider = publishedUrlProvider; _umbracoContextAccessor = umbracoContextAccessor; diff --git a/src/Umbraco.Web/Mvc/SurfaceControllerTypeCollection.cs b/src/Umbraco.Web.Website/Collections/SurfaceControllerTypeCollection.cs similarity index 87% rename from src/Umbraco.Web/Mvc/SurfaceControllerTypeCollection.cs rename to src/Umbraco.Web.Website/Collections/SurfaceControllerTypeCollection.cs index 59b8adc950..fa888dfe88 100644 --- a/src/Umbraco.Web/Mvc/SurfaceControllerTypeCollection.cs +++ b/src/Umbraco.Web.Website/Collections/SurfaceControllerTypeCollection.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Core.Composing; -namespace Umbraco.Web.Mvc +namespace Umbraco.Web.Website.Collections { public class SurfaceControllerTypeCollection : BuilderCollectionBase { diff --git a/src/Umbraco.Web/Mvc/SurfaceControllerTypeCollectionBuilder.cs b/src/Umbraco.Web.Website/Collections/SurfaceControllerTypeCollectionBuilder.cs similarity index 79% rename from src/Umbraco.Web/Mvc/SurfaceControllerTypeCollectionBuilder.cs rename to src/Umbraco.Web.Website/Collections/SurfaceControllerTypeCollectionBuilder.cs index 03de95c798..892184632d 100644 --- a/src/Umbraco.Web/Mvc/SurfaceControllerTypeCollectionBuilder.cs +++ b/src/Umbraco.Web.Website/Collections/SurfaceControllerTypeCollectionBuilder.cs @@ -1,7 +1,7 @@ using Umbraco.Core.Composing; -using Umbraco.Web.WebApi; +using Umbraco.Web.Website.Controllers; -namespace Umbraco.Web.Mvc +namespace Umbraco.Web.Website.Collections { public class SurfaceControllerTypeCollectionBuilder : TypeCollectionBuilderBase { diff --git a/src/Umbraco.Web.Website/Controllers/SurfaceController.cs b/src/Umbraco.Web.Website/Controllers/SurfaceController.cs index 726ce4ab82..8125923ee4 100644 --- a/src/Umbraco.Web.Website/Controllers/SurfaceController.cs +++ b/src/Umbraco.Web.Website/Controllers/SurfaceController.cs @@ -48,22 +48,22 @@ namespace Umbraco.Web.Website.Controllers /// /// Redirects to the Umbraco page with the given id /// - /// + /// /// - protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId) + protected RedirectToUmbracoPageResult RedirectToUmbracoPage(Guid contentKey) { - return new RedirectToUmbracoPageResult(pageId, _publishedUrlProvider, UmbracoContextAccessor); + return new RedirectToUmbracoPageResult(contentKey, _publishedUrlProvider, UmbracoContextAccessor); } /// /// Redirects to the Umbraco page with the given id and passes provided querystring /// - /// + /// /// /// - protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, QueryString queryString) + protected RedirectToUmbracoPageResult RedirectToUmbracoPage(Guid contentKey, QueryString queryString) { - return new RedirectToUmbracoPageResult(pageId, queryString, _publishedUrlProvider, UmbracoContextAccessor); + return new RedirectToUmbracoPageResult(contentKey, queryString, _publishedUrlProvider, UmbracoContextAccessor); } /// diff --git a/src/Umbraco.Web.Website/Controllers/UmbracoAuthorizedController.cs b/src/Umbraco.Web.Website/Controllers/UmbracoAuthorizedController.cs index 2b5d7a61da..b2611848df 100644 --- a/src/Umbraco.Web.Website/Controllers/UmbracoAuthorizedController.cs +++ b/src/Umbraco.Web.Website/Controllers/UmbracoAuthorizedController.cs @@ -9,7 +9,7 @@ namespace Umbraco.Web.Mvc /// This controller essentially just uses a global UmbracoAuthorizeAttribute, inheritors that require more granular control over the /// authorization of each method can use this attribute instead of inheriting from this controller. /// - [UmbracoAuthorize] + [UmbracoBackOfficeAuthorize] [DisableBrowserCache] public abstract class UmbracoAuthorizedController : UmbracoController { diff --git a/src/Umbraco.Web.Website/Extensions/HtmlContentExtensions.cs b/src/Umbraco.Web.Website/Extensions/HtmlContentExtensions.cs new file mode 100644 index 0000000000..3b74c39475 --- /dev/null +++ b/src/Umbraco.Web.Website/Extensions/HtmlContentExtensions.cs @@ -0,0 +1,17 @@ +using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Html; + +namespace Umbraco.Extensions +{ + public static class HtmlContentExtensions + { + public static string ToHtmlString(this IHtmlContent content) + { + using (var writer = new System.IO.StringWriter()) + { + content.WriteTo(writer, HtmlEncoder.Default); + return writer.ToString(); + } + } + } +} diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs similarity index 79% rename from src/Umbraco.Web/HtmlHelperRenderExtensions.cs rename to src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs index eccbe073cb..a48ed435bf 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs @@ -3,35 +3,54 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; +using System.Text.Encodings.Web; using System.Web; -using System.Web.Helpers; -using System.Web.Mvc; -using System.Web.Mvc.Html; -using System.Web.Routing; +using Microsoft.AspNetCore.Antiforgery; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; using Umbraco.Core; -using Umbraco.Core.Configuration; +using Umbraco.Core.Cache; using Umbraco.Core.Configuration.Models; -using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Hosting; using Umbraco.Core.IO; -using Umbraco.Web.Mvc; -using Umbraco.Web.Security; -using Current = Umbraco.Web.Composing.Current; +using Umbraco.Core.Logging; +using Umbraco.Web; +using Umbraco.Web.Common.Controllers; +using Umbraco.Web.Common.Mvc; +using Umbraco.Web.Common.Security; +using Umbraco.Web.Website.Collections; +using Umbraco.Web.Website.Controllers; -namespace Umbraco.Web +namespace Umbraco.Extensions { /// /// HtmlHelper extensions for use in templates /// public static class HtmlHelperRenderExtensions { + private static T GetRequiredService(IHtmlHelper htmlHelper) + { + return GetRequiredService(htmlHelper.ViewContext); + } + + private static T GetRequiredService(ViewContext viewContext) + { + return viewContext.HttpContext.RequestServices.GetRequiredService(); + } + /// /// Renders the markup for the profiler /// /// /// - public static IHtmlString RenderProfiler(this HtmlHelper helper) + public static IHtmlContent RenderProfiler(this IHtmlHelper helper) { - return new HtmlString(Current.ProfilerHtml.Render()); + return new HtmlString(GetRequiredService(helper).Render()); } /// @@ -43,7 +62,7 @@ namespace Umbraco.Web /// /// /// - public static MvcHtmlString AreaPartial(this HtmlHelper helper, string partial, string area, object model = null, ViewDataDictionary viewData = null) + public static IHtmlContent AreaPartial(this IHtmlHelper helper, string partial, string area, object model = null, ViewDataDictionary viewData = null) { var originalArea = helper.ViewContext.RouteData.DataTokens["area"]; helper.ViewContext.RouteData.DataTokens["area"] = area; @@ -61,23 +80,24 @@ namespace Umbraco.Web /// /// See: http://issues.umbraco.org/issue/U4-1614 /// - public static MvcHtmlString PreviewBadge(this HtmlHelper helper, IHttpContextAccessor httpContextAccessor, GlobalSettings globalSettings, IIOHelper ioHelper, ContentSettings contentSettings) + public static IHtmlContent PreviewBadge(this IHtmlHelper helper, IUmbracoContextAccessor umbracoContextAccessor, IHttpContextAccessor httpContextAccessor, GlobalSettings globalSettings, IIOHelper ioHelper, ContentSettings contentSettings) { - if (Current.UmbracoContext.InPreviewMode) + var umbrcoContext = umbracoContextAccessor.UmbracoContext; + if (umbrcoContext.InPreviewMode) { var htmlBadge = String.Format(contentSettings.PreviewBadge, ioHelper.ResolveUrl(globalSettings.UmbracoPath), WebUtility.UrlEncode(httpContextAccessor.GetRequiredHttpContext().Request.Path), - Current.UmbracoContext.PublishedRequest.PublishedContent.Id); - return new MvcHtmlString(htmlBadge); + umbrcoContext.PublishedRequest.PublishedContent.Id); + return new HtmlString(htmlBadge); } - return new MvcHtmlString(""); + return new HtmlString(""); } - public static IHtmlString CachedPartial( - this HtmlHelper htmlHelper, + public static IHtmlContent CachedPartial( + this IHtmlHelper htmlHelper, string partialViewName, object model, int cachedSeconds, @@ -95,36 +115,50 @@ namespace Umbraco.Web } if (cacheByPage) { - if (Current.UmbracoContext == null) + var umbracoContextAccessor = GetRequiredService(htmlHelper); + var umbracoContext = umbracoContextAccessor.UmbracoContext; + if (umbracoContext == null) { throw new InvalidOperationException("Cannot cache by page if the UmbracoContext has not been initialized, this parameter can only be used in the context of an Umbraco request"); } - cacheKey.AppendFormat("{0}-", Current.UmbracoContext.PublishedRequest?.PublishedContent?.Id ?? 0); + cacheKey.AppendFormat("{0}-", umbracoContext.PublishedRequest?.PublishedContent?.Id ?? 0); } if (cacheByMember) { - var helper = Current.MembershipHelper; - var currentMember = helper.GetCurrentMember(); - cacheKey.AppendFormat("m{0}-", currentMember?.Id ?? 0); + //TODO reintroduce when members are migrated + throw new NotImplementedException("Reintroduce when members are migrated"); + // var helper = Current.MembershipHelper; + // var currentMember = helper.GetCurrentMember(); + // cacheKey.AppendFormat("m{0}-", currentMember?.Id ?? 0); } if (contextualKeyBuilder != null) { var contextualKey = contextualKeyBuilder(model, viewData); cacheKey.AppendFormat("c{0}-", contextualKey); } - return Current.AppCaches.CachedPartialView(Current.HostingEnvironment, htmlHelper, partialViewName, model, cachedSeconds, cacheKey.ToString(), viewData); + + var appCaches = GetRequiredService(htmlHelper); + var hostingEnvironment = GetRequiredService(htmlHelper); + + return appCaches.CachedPartialView(hostingEnvironment, htmlHelper, partialViewName, model, cachedSeconds, cacheKey.ToString(), viewData); } - public static MvcHtmlString EditorFor(this HtmlHelper htmlHelper, string templateName = "", string htmlFieldName = "", object additionalViewData = null) - where T : new() - { - var model = new T(); - var typedHelper = new HtmlHelper( - htmlHelper.ViewContext.CopyWithModel(model), - htmlHelper.ViewDataContainer.CopyWithModel(model)); - - return typedHelper.EditorFor(x => model, templateName, htmlFieldName, additionalViewData); - } + // public static IHtmlContent EditorFor(this IHtmlHelper htmlHelper, string templateName = "", string htmlFieldName = "", object additionalViewData = null) + // where T : new() + // { + // var model = new T(); + // htmlHelper.Contextualize(htmlHelper.ViewContext.CopyWithModel(model)); + // + // // + // // var typedHelper = new HtmlHelper(htmlHelper. + // // htmlHelper. + // // , + // // htmlHelper.ViewDataContainer.CopyWithModel(model)); + // + // + // + // return htmlHelper.EditorForModel(x => model, templateName, htmlFieldName, additionalViewData); + // } /// /// A validation summary that lets you pass in a prefix so that the summary only displays for elements @@ -136,72 +170,95 @@ namespace Umbraco.Web /// /// /// - public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, - string prefix = "", - bool excludePropertyErrors = false, - string message = "", - IDictionary htmlAttributes = null) + public static IHtmlContent ValidationSummary(this IHtmlHelper htmlHelper, + string prefix = "", + bool excludePropertyErrors = false, + string message = "", + object htmlAttributes = null) { if (prefix.IsNullOrWhiteSpace()) { return htmlHelper.ValidationSummary(excludePropertyErrors, message, htmlAttributes); } - //if there's a prefix applied, we need to create a new HTML helper with a filtered ModelState collection so that it only looks for - //specific model state with the prefix. - var filteredHtmlHelper = new HtmlHelper(htmlHelper.ViewContext, htmlHelper.ViewDataContainer.FilterContainer(prefix)); - return filteredHtmlHelper.ValidationSummary(excludePropertyErrors, message, htmlAttributes); - } - /// - /// Returns the result of a child action of a strongly typed SurfaceController - /// - /// - /// - /// - /// - public static IHtmlString Action(this HtmlHelper htmlHelper, string actionName) - where T : SurfaceController - { - return htmlHelper.Action(actionName, typeof(T)); - } - /// - /// Returns the result of a child action of a SurfaceController - /// - /// - /// - /// - /// - /// - public static IHtmlString Action(this HtmlHelper htmlHelper, string actionName, Type surfaceType) - { - if (actionName == null) throw new ArgumentNullException(nameof(actionName)); - if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(actionName)); - if (surfaceType == null) throw new ArgumentNullException(nameof(surfaceType)); - var routeVals = new RouteValueDictionary(new {area = ""}); + var htmlGenerator = GetRequiredService(htmlHelper); - var surfaceController = Current.SurfaceControllerTypes.SingleOrDefault(x => x == surfaceType); - if (surfaceController == null) - throw new InvalidOperationException("Could not find the surface controller of type " + surfaceType.FullName); - var metaData = PluginController.GetMetadata(surfaceController); - if (!metaData.AreaName.IsNullOrWhiteSpace()) + var viewContext = htmlHelper.ViewContext.Clone(); + foreach (var key in viewContext.ViewData.Keys.ToArray()){ + if(!key.StartsWith(prefix)){ + viewContext.ViewData.Remove(key); + } + } + var tagBuilder = htmlGenerator.GenerateValidationSummary( + viewContext, + excludePropertyErrors, + message, + headerTag: null, + htmlAttributes: htmlAttributes); + if (tagBuilder == null) { - //set the area to the plugin area - if (routeVals.ContainsKey("area")) - { - routeVals["area"] = metaData.AreaName; - } - else - { - routeVals.Add("area", metaData.AreaName); - } + return HtmlString.Empty; } - return htmlHelper.Action(actionName, metaData.ControllerName, routeVals); + return tagBuilder; } +// TODO what to do here? This will be view components right? + // /// + // /// Returns the result of a child action of a strongly typed SurfaceController + // /// + // /// + // /// + // /// + // /// + // public static IHtmlContent Action(this HtmlHelper htmlHelper, string actionName) + // where T : SurfaceController + // { + // return htmlHelper.Action(actionName, typeof(T)); + // } + // + +// TODO what to do here? This will be view components right? + // /// + // /// Returns the result of a child action of a SurfaceController + // /// + // /// + // /// + // /// + // /// + // /// + // public static IHtmlContent Action(this IHtmlHelper htmlHelper, string actionName, Type surfaceType) + // { + // if (actionName == null) throw new ArgumentNullException(nameof(actionName)); + // if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(actionName)); + // if (surfaceType == null) throw new ArgumentNullException(nameof(surfaceType)); + // + // var routeVals = new RouteValueDictionary(new {area = ""}); + // + // var surfaceControllerTypeCollection = GetRequiredService(htmlHelper); + // var surfaceController = surfaceControllerTypeCollection.SingleOrDefault(x => x == surfaceType); + // if (surfaceController == null) + // throw new InvalidOperationException("Could not find the surface controller of type " + surfaceType.FullName); + // var metaData = PluginController.GetMetadata(surfaceController); + // if (!metaData.AreaName.IsNullOrWhiteSpace()) + // { + // //set the area to the plugin area + // if (routeVals.ContainsKey("area")) + // { + // routeVals["area"] = metaData.AreaName; + // } + // else + // { + // routeVals.Add("area", metaData.AreaName); + // } + // } + // + // return htmlHelper.Action(actionName, metaData.ControllerName, routeVals); + // } + #region BeginUmbracoForm /// @@ -213,6 +270,7 @@ namespace Umbraco.Web /// Creates an UmbracoForm /// /// + /// /// /// /// @@ -220,17 +278,18 @@ namespace Umbraco.Web /// public UmbracoForm( ViewContext viewContext, + HtmlEncoder htmlEncoder, string controllerName, string controllerAction, string area, FormMethod method, object additionalRouteVals = null) - : base(viewContext) + : base(viewContext, htmlEncoder) { _viewContext = viewContext; _method = method; _controllerName = controllerName; - _encryptedString = UrlHelperRenderExtensions.CreateEncryptedRouteString(controllerName, controllerAction, area, additionalRouteVals); + _encryptedString = EncryptionHelper.CreateEncryptedRouteString(GetRequiredService(viewContext), controllerName, controllerAction, area, additionalRouteVals); } @@ -240,7 +299,7 @@ namespace Umbraco.Web private readonly string _encryptedString; private readonly string _controllerName; - protected override void Dispose(bool disposing) + protected new void Dispose() { if (this._disposed) return; @@ -252,13 +311,14 @@ namespace Umbraco.Web || _controllerName == "UmbLoginStatus" || _controllerName == "UmbLogin") { - _viewContext.Writer.Write(AntiForgery.GetHtml().ToString()); + var antiforgery = _viewContext.HttpContext.RequestServices.GetRequiredService(); + _viewContext.Writer.Write(antiforgery.GetHtml(_viewContext.HttpContext).ToString()); } //write out the hidden surface form routes _viewContext.Writer.Write(""); - base.Dispose(disposing); + base.Dispose(); } } @@ -594,7 +654,8 @@ namespace Umbraco.Web var area = ""; - var surfaceController = Current.SurfaceControllerTypes.SingleOrDefault(x => x == surfaceType); + var surfaceControllerTypeCollection = GetRequiredService(html); + var surfaceController = surfaceControllerTypeCollection.SingleOrDefault(x => x == surfaceType); if (surfaceController == null) throw new InvalidOperationException("Could not find the surface controller of type " + surfaceType.FullName); var metaData = PluginController.GetMetadata(surfaceController); @@ -706,7 +767,8 @@ namespace Umbraco.Web if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(controllerName)); - var formAction = Current.UmbracoContext.OriginalRequestUrl.PathAndQuery; + var umbracoContextAccessor = GetRequiredService(html); + var formAction = umbracoContextAccessor.UmbracoContext.OriginalRequestUrl.PathAndQuery; return html.RenderForm(formAction, method, htmlAttributes, controllerName, action, area, additionalRouteVals); } @@ -764,89 +826,28 @@ namespace Umbraco.Web tagBuilder.MergeAttribute("action", formAction); // method is an explicit parameter, so it takes precedence over the htmlAttributes. tagBuilder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true); - var traditionalJavascriptEnabled = htmlHelper.ViewContext.ClientValidationEnabled && htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled == false; + var traditionalJavascriptEnabled = htmlHelper.ViewContext.ClientValidationEnabled; if (traditionalJavascriptEnabled) { // forms must have an ID for client validation - tagBuilder.GenerateId("form" + Guid.NewGuid().ToString("N")); + tagBuilder.GenerateId("form" + Guid.NewGuid().ToString("N"), string.Empty); } - htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag)); + htmlHelper.ViewContext.Writer.Write(tagBuilder.RenderStartTag()); + + var htmlEncoder = GetRequiredService(htmlHelper); //new UmbracoForm: - var theForm = new UmbracoForm(htmlHelper.ViewContext, surfaceController, surfaceAction, area, method, additionalRouteVals); + var theForm = new UmbracoForm(htmlHelper.ViewContext, htmlEncoder, surfaceController, surfaceAction, area, method, additionalRouteVals); if (traditionalJavascriptEnabled) { - htmlHelper.ViewContext.FormContext.FormId = tagBuilder.Attributes["id"]; + htmlHelper.ViewContext.FormContext.FormData["FormId"] = tagBuilder.Attributes["id"]; } return theForm; } #endregion - #region Wrap - - public static HtmlTagWrapper Wrap(this HtmlHelper html, string tag, string innerText, params IHtmlTagWrapper[] children) - { - var item = html.Wrap(tag, innerText, (object)null); - foreach (var child in children) - { - item.AddChild(child); - } - return item; - } - - public static HtmlTagWrapper Wrap(this HtmlHelper html, string tag, object inner, object anonymousAttributes, params IHtmlTagWrapper[] children) - { - string innerText = null; - if (inner != null) - { - innerText = string.Format("{0}", inner); - } - var item = html.Wrap(tag, innerText, anonymousAttributes); - foreach (var child in children) - { - item.AddChild(child); - } - return item; - } - public static HtmlTagWrapper Wrap(this HtmlHelper html, string tag, object inner) - { - string innerText = null; - if (inner != null) - { - innerText = string.Format("{0}", inner); - } - return html.Wrap(tag, innerText, (object)null); - } - - public static HtmlTagWrapper Wrap(this HtmlHelper html, string tag, string innerText, object anonymousAttributes, params IHtmlTagWrapper[] children) - { - var wrap = new HtmlTagWrapper(tag); - if (anonymousAttributes != null) - { - wrap.ReflectAttributesFromAnonymousType(anonymousAttributes); - } - if (!string.IsNullOrWhiteSpace(innerText)) - { - wrap.AddChild(new HtmlTagWrapperTextNode(innerText)); - } - foreach (var child in children) - { - wrap.AddChild(child); - } - return wrap; - } - - public static HtmlTagWrapper Wrap(this HtmlHelper html, bool visible, string tag, string innerText, object anonymousAttributes, params IHtmlTagWrapper[] children) - { - var item = html.Wrap(tag, innerText, anonymousAttributes, children); - item.Visible = visible; - return item; - } - - #endregion - #region If /// @@ -858,7 +859,7 @@ namespace Umbraco.Web /// /// The HTML encoded value. /// - public static IHtmlString If(this HtmlHelper html, bool test, string valueIfTrue) + public static IHtmlContent If(this HtmlHelper html, bool test, string valueIfTrue) { return If(html, test, valueIfTrue, string.Empty); } @@ -873,7 +874,7 @@ namespace Umbraco.Web /// /// The HTML encoded value. /// - public static IHtmlString If(this HtmlHelper html, bool test, string valueIfTrue, string valueIfFalse) + public static IHtmlContent If(this HtmlHelper html, bool test, string valueIfTrue, string valueIfFalse) { return new HtmlString(HttpUtility.HtmlEncode(test ? valueIfTrue : valueIfFalse)); } @@ -892,7 +893,7 @@ namespace Umbraco.Web /// /// The HTML encoded text with text line breaks replaced with HTML line breaks (<br />). /// - public static IHtmlString ReplaceLineBreaks(this HtmlHelper helper, string text) + public static IHtmlContent ReplaceLineBreaks(this HtmlHelper helper, string text) { return StringUtilities.ReplaceLineBreaks(text); } @@ -912,15 +913,17 @@ namespace Umbraco.Web /// /// Strips all HTML tags from a given string, all contents of the tags will remain. /// - public static IHtmlString StripHtml(this HtmlHelper helper, IHtmlString html, params string[] tags) + public static IHtmlContent StripHtml(this HtmlHelper helper, IHtmlContent html, params string[] tags) { return helper.StripHtml(html.ToHtmlString(), tags); } + + /// /// Strips all HTML tags from a given string, all contents of the tags will remain. /// - public static IHtmlString StripHtml(this HtmlHelper helper, string html, params string[] tags) + public static IHtmlContent StripHtml(this HtmlHelper helper, string html, params string[] tags) { return StringUtilities.StripHtmlTags(html, tags); } @@ -952,7 +955,7 @@ namespace Umbraco.Web /// /// Truncates a string to a given length, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them /// - public static IHtmlString Truncate(this HtmlHelper helper, IHtmlString html, int length) + public static IHtmlContent Truncate(this HtmlHelper helper, IHtmlContent html, int length) { return helper.Truncate(html.ToHtmlString(), length, true, false); } @@ -960,7 +963,7 @@ namespace Umbraco.Web /// /// Truncates a string to a given length, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them /// - public static IHtmlString Truncate(this HtmlHelper helper, IHtmlString html, int length, bool addElipsis) + public static IHtmlContent Truncate(this HtmlHelper helper, IHtmlContent html, int length, bool addElipsis) { return helper.Truncate(html.ToHtmlString(), length, addElipsis, false); } @@ -968,7 +971,7 @@ namespace Umbraco.Web /// /// Truncates a string to a given length, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them /// - public static IHtmlString Truncate(this HtmlHelper helper, IHtmlString html, int length, bool addElipsis, bool treatTagsAsContent) + public static IHtmlContent Truncate(this HtmlHelper helper, IHtmlContent html, int length, bool addElipsis, bool treatTagsAsContent) { return helper.Truncate(html.ToHtmlString(), length, addElipsis, treatTagsAsContent); } @@ -976,7 +979,7 @@ namespace Umbraco.Web /// /// Truncates a string to a given length, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them /// - public static IHtmlString Truncate(this HtmlHelper helper, string html, int length) + public static IHtmlContent Truncate(this HtmlHelper helper, string html, int length) { return helper.Truncate(html, length, true, false); } @@ -984,7 +987,7 @@ namespace Umbraco.Web /// /// Truncates a string to a given length, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them /// - public static IHtmlString Truncate(this HtmlHelper helper, string html, int length, bool addElipsis) + public static IHtmlContent Truncate(this HtmlHelper helper, string html, int length, bool addElipsis) { return helper.Truncate(html, length, addElipsis, false); } @@ -992,7 +995,7 @@ namespace Umbraco.Web /// /// Truncates a string to a given length, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them /// - public static IHtmlString Truncate(this HtmlHelper helper, string html, int length, bool addElipsis, bool treatTagsAsContent) + public static IHtmlContent Truncate(this HtmlHelper helper, string html, int length, bool addElipsis, bool treatTagsAsContent) { return StringUtilities.Truncate(html, length, addElipsis, treatTagsAsContent); } @@ -1002,7 +1005,7 @@ namespace Umbraco.Web /// /// Truncates a string to a given amount of words, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them /// - public static IHtmlString TruncateByWords(this HtmlHelper helper, string html, int words) + public static IHtmlContent TruncateByWords(this HtmlHelper helper, string html, int words) { int length = StringUtilities.WordsToLength(html, words); @@ -1012,7 +1015,7 @@ namespace Umbraco.Web /// /// Truncates a string to a given amount of words, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them /// - public static IHtmlString TruncateByWords(this HtmlHelper helper, string html, int words, bool addElipsis) + public static IHtmlContent TruncateByWords(this HtmlHelper helper, string html, int words, bool addElipsis) { int length = StringUtilities.WordsToLength(html, words); @@ -1022,7 +1025,7 @@ namespace Umbraco.Web /// /// Truncates a string to a given amount of words, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them /// - public static IHtmlString TruncateByWords(this HtmlHelper helper, IHtmlString html, int words) + public static IHtmlContent TruncateByWords(this HtmlHelper helper, IHtmlContent html, int words) { int length = StringUtilities.WordsToLength(html.ToHtmlString(), words); @@ -1032,7 +1035,7 @@ namespace Umbraco.Web /// /// Truncates a string to a given amount of words, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them /// - public static IHtmlString TruncateByWords(this HtmlHelper helper, IHtmlString html, int words, bool addElipsis) + public static IHtmlContent TruncateByWords(this HtmlHelper helper, IHtmlContent html, int words, bool addElipsis) { int length = StringUtilities.WordsToLength(html.ToHtmlString(), words); diff --git a/src/Umbraco.Web.Website/Extensions/TypeLoaderExtensions.cs b/src/Umbraco.Web.Website/Extensions/TypeLoaderExtensions.cs new file mode 100644 index 0000000000..c01bdf7804 --- /dev/null +++ b/src/Umbraco.Web.Website/Extensions/TypeLoaderExtensions.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Composing; +using Umbraco.Web.Common.Controllers; +using Umbraco.Web.Website.Controllers; + + +namespace Umbraco.Extensions +{ + /// + /// Provides extension methods for the class. + /// + // Migrated to .NET Core + public static class TypeLoaderExtensions + { + /// + /// Gets all types implementing . + /// + internal static IEnumerable GetSurfaceControllers(this TypeLoader typeLoader) + => typeLoader.GetTypes(); + + /// + /// Gets all types implementing . + /// + internal static IEnumerable GetUmbracoApiControllers(this TypeLoader typeLoader) + => typeLoader.GetTypes(); + } +} diff --git a/src/Umbraco.Web.Website/Extensions/UmbracoWebstiteServiceCollectionExtensions.cs b/src/Umbraco.Web.Website/Extensions/UmbracoWebstiteServiceCollectionExtensions.cs index b47679a1bf..a5d729f20f 100644 --- a/src/Umbraco.Web.Website/Extensions/UmbracoWebstiteServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Website/Extensions/UmbracoWebstiteServiceCollectionExtensions.cs @@ -25,6 +25,8 @@ namespace Umbraco.Extensions // Wraps all existing view engines in a ProfilerViewEngine services.AddTransient, ProfilingViewEngineWrapperMvcViewOptionsSetup>(); + //TODO figure out if we need more to work on load balanced setups + services.AddDataProtection(); } } diff --git a/src/Umbraco.Web.Website/Runtime/WebsiteComposer.cs b/src/Umbraco.Web.Website/Runtime/WebsiteComposer.cs index 11204483bd..3cbb24b0e0 100644 --- a/src/Umbraco.Web.Website/Runtime/WebsiteComposer.cs +++ b/src/Umbraco.Web.Website/Runtime/WebsiteComposer.cs @@ -3,6 +3,7 @@ using Umbraco.Core.Composing; using Umbraco.Extensions; using Umbraco.Web.Website.Routing; using Umbraco.Web.Common.Runtime; +using Umbraco.Web.Website.Collections; namespace Umbraco.Web.Website.Runtime { @@ -19,6 +20,10 @@ namespace Umbraco.Web.Website.Runtime .ComposeWebsiteUmbracoControllers() //.SetDefaultRenderMvcController()// default controller for template views ; + + + composition.WithCollectionBuilder() + .Add(composition.TypeLoader.GetSurfaceControllers()); } } } diff --git a/src/Umbraco.Web.Website/UmbracoHelper.cs b/src/Umbraco.Web.Website/UmbracoHelper.cs new file mode 100644 index 0000000000..d44ca4e5fe --- /dev/null +++ b/src/Umbraco.Web.Website/UmbracoHelper.cs @@ -0,0 +1,445 @@ +using System; +using System.Collections.Generic; +using System.Xml.XPath; +using Umbraco.Core; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Templates; +using Umbraco.Core.Strings; +using Umbraco.Core.Xml; + +namespace Umbraco.Web.Website +{ + /// + /// A helper class that provides many useful methods and functionality for using Umbraco in templates + /// + /// + /// This object is a request based lifetime + /// + public class UmbracoHelper + { + private readonly IPublishedContentQuery _publishedContentQuery; + private readonly IUmbracoComponentRenderer _componentRenderer; + private readonly ICultureDictionaryFactory _cultureDictionaryFactory; + + private IPublishedContent _currentPage; + private ICultureDictionary _cultureDictionary; + + #region Constructors + + /// + /// Initializes a new instance of . + /// + /// The item assigned to the helper. + /// + /// + /// + /// Sets the current page to the context's published content request's content item. + public UmbracoHelper(IPublishedContent currentPage, + ICultureDictionaryFactory cultureDictionary, + IUmbracoComponentRenderer componentRenderer, + IPublishedContentQuery publishedContentQuery) + { + _cultureDictionaryFactory = cultureDictionary ?? throw new ArgumentNullException(nameof(cultureDictionary)); + _componentRenderer = componentRenderer ?? throw new ArgumentNullException(nameof(componentRenderer)); + _publishedContentQuery = publishedContentQuery ?? throw new ArgumentNullException(nameof(publishedContentQuery)); + _currentPage = currentPage; + } + + /// + /// Initializes a new empty instance of . + /// + /// For tests - nothing is initialized. + internal UmbracoHelper() + { } + + #endregion + + + /// + /// Gets (or sets) the current item assigned to the UmbracoHelper. + /// + /// + /// + /// Note that this is the assigned IPublishedContent item to the + /// UmbracoHelper, this is not necessarily the Current IPublishedContent + /// item being rendered that is assigned to the UmbracoContext. + /// This IPublishedContent object is contextual to the current UmbracoHelper instance. + /// + /// + /// In some cases accessing this property will throw an exception if + /// there is not IPublishedContent assigned to the Helper this will + /// only ever happen if the Helper is constructed via DI during a non front-end request. + /// + /// + /// Thrown if the + /// UmbracoHelper is constructed with an UmbracoContext and it is not a + /// front-end request. + public IPublishedContent AssignedContentItem + { + get + { + if (_currentPage != null) + { + return _currentPage; + } + + throw new InvalidOperationException( + $"Cannot return the {nameof(IPublishedContent)} because the {nameof(UmbracoHelper)} was not constructed with an {nameof(IPublishedContent)}." + ); + + } + set => _currentPage = value; + } + + /// + /// Renders the template for the specified pageId and an optional altTemplateId + /// + /// + /// If not specified, will use the template assigned to the node + /// + public IHtmlEncodedString RenderTemplate(int contentId, int? altTemplateId = null) + { + return _componentRenderer.RenderTemplate(contentId, altTemplateId); + } + + #region RenderMacro + + /// + /// Renders the macro with the specified alias. + /// + /// The alias. + /// + public IHtmlEncodedString RenderMacro(string alias) + { + return _componentRenderer.RenderMacro(AssignedContentItem?.Id ?? 0, alias, null); + } + + /// + /// Renders the macro with the specified alias, passing in the specified parameters. + /// + /// The alias. + /// The parameters. + /// + public IHtmlEncodedString RenderMacro(string alias, object parameters) + { + return _componentRenderer.RenderMacro(AssignedContentItem?.Id ?? 0, alias, parameters?.ToDictionary()); + } + + /// + /// Renders the macro with the specified alias, passing in the specified parameters. + /// + /// The alias. + /// The parameters. + /// + public IHtmlEncodedString RenderMacro(string alias, IDictionary parameters) + { + return _componentRenderer.RenderMacro(AssignedContentItem?.Id ?? 0, alias, parameters); + } + + #endregion + + #region Dictionary + + /// + /// Returns the dictionary value for the key specified + /// + /// + /// + public string GetDictionaryValue(string key) + { + return CultureDictionary[key]; + } + + /// + /// Returns the dictionary value for the key specified, and if empty returns the specified default fall back value + /// + /// key of dictionary item + /// fall back text if dictionary item is empty - Name altText to match Umbraco.Field + /// + public string GetDictionaryValue(string key, string altText) + { + var dictionaryValue = GetDictionaryValue(key); + if (String.IsNullOrWhiteSpace(dictionaryValue)) + { + dictionaryValue = altText; + } + return dictionaryValue; + } + + /// + /// Returns the ICultureDictionary for access to dictionary items + /// + public ICultureDictionary CultureDictionary => _cultureDictionary ??= _cultureDictionaryFactory.CreateDictionary(); + + #endregion + + + + #region Content + + /// + /// Gets a content item from the cache. + /// + /// The unique identifier, or the key, of the content item. + /// The content, or null of the content item is not in the cache. + public IPublishedContent Content(object id) + { + return ContentForObject(id); + } + + private IPublishedContent ContentForObject(object id) => _publishedContentQuery.Content(id); + + public IPublishedContent ContentSingleAtXPath(string xpath, params XPathVariable[] vars) + { + return _publishedContentQuery.ContentSingleAtXPath(xpath, vars); + } + + /// + /// Gets a content item from the cache. + /// + /// The unique identifier of the content item. + /// The content, or null of the content item is not in the cache. + public IPublishedContent Content(int id) => _publishedContentQuery.Content(id); + + /// + /// Gets a content item from the cache. + /// + /// The key of the content item. + /// The content, or null of the content item is not in the cache. + public IPublishedContent Content(Guid id) => _publishedContentQuery.Content(id); + + /// + /// Gets a content item from the cache. + /// + /// The unique identifier, or the key, of the content item. + /// The content, or null of the content item is not in the cache. + public IPublishedContent Content(string id) => _publishedContentQuery.Content(id); + + public IPublishedContent Content(Udi id) => _publishedContentQuery.Content(id); + + /// + /// Gets content items from the cache. + /// + /// The unique identifiers, or the keys, of the content items. + /// The content items that were found in the cache. + /// Does not support mixing identifiers and keys. + public IEnumerable Content(params object[] ids) => _publishedContentQuery.Content(ids); + + /// + /// Gets the contents corresponding to the identifiers. + /// + /// The content identifiers. + /// The existing contents corresponding to the identifiers. + /// If an identifier does not match an existing content, it will be missing in the returned value. + public IEnumerable Content(params Udi[] ids) => _publishedContentQuery.Content(ids); + + /// + /// Gets the contents corresponding to the identifiers. + /// + /// The content identifiers. + /// The existing contents corresponding to the identifiers. + /// If an identifier does not match an existing content, it will be missing in the returned value. + public IEnumerable Content(params GuidUdi[] ids) => _publishedContentQuery.Content(ids); + + private IEnumerable ContentForObjects(IEnumerable ids) => _publishedContentQuery.Content(ids); + + /// + /// Gets content items from the cache. + /// + /// The unique identifiers of the content items. + /// The content items that were found in the cache. + public IEnumerable Content(params int[] ids) => _publishedContentQuery.Content(ids); + + /// + /// Gets content items from the cache. + /// + /// The keys of the content items. + /// The content items that were found in the cache. + public IEnumerable Content(params Guid[] ids) => _publishedContentQuery.Content(ids); + + /// + /// Gets content items from the cache. + /// + /// The unique identifiers, or the keys, of the content items. + /// The content items that were found in the cache. + /// Does not support mixing identifiers and keys. + public IEnumerable Content(params string[] ids) => _publishedContentQuery.Content(ids); + + /// + /// Gets the contents corresponding to the identifiers. + /// + /// The content identifiers. + /// The existing contents corresponding to the identifiers. + /// If an identifier does not match an existing content, it will be missing in the returned value. + public IEnumerable Content(IEnumerable ids) => _publishedContentQuery.Content(ids); + + /// + /// Gets the contents corresponding to the identifiers. + /// + /// The content identifiers. + /// The existing contents corresponding to the identifiers. + /// If an identifier does not match an existing content, it will be missing in the returned value. + public IEnumerable Content(IEnumerable ids) => _publishedContentQuery.Content(ids); + + /// + /// Gets the contents corresponding to the identifiers. + /// + /// The content identifiers. + /// The existing contents corresponding to the identifiers. + /// If an identifier does not match an existing content, it will be missing in the returned value. + public IEnumerable Content(IEnumerable ids) => _publishedContentQuery.Content(ids); + + /// + /// Gets the contents corresponding to the identifiers. + /// + /// The content identifiers. + /// The existing contents corresponding to the identifiers. + /// If an identifier does not match an existing content, it will be missing in the returned value. + public IEnumerable Content(IEnumerable ids) => _publishedContentQuery.Content(ids); + + /// + /// Gets the contents corresponding to the identifiers. + /// + /// The content identifiers. + /// The existing contents corresponding to the identifiers. + /// If an identifier does not match an existing content, it will be missing in the returned value. + public IEnumerable Content(IEnumerable ids) => _publishedContentQuery.Content(ids); + + public IEnumerable ContentAtXPath(string xpath, params XPathVariable[] vars) + { + return _publishedContentQuery.ContentAtXPath(xpath, vars); + } + + public IEnumerable ContentAtXPath(XPathExpression xpath, params XPathVariable[] vars) + { + return _publishedContentQuery.ContentAtXPath(xpath, vars); + } + + public IEnumerable ContentAtRoot() + { + return _publishedContentQuery.ContentAtRoot(); + } + + + + + #endregion + #region Media + + public IPublishedContent Media(Udi id) => _publishedContentQuery.Media(id); + + public IPublishedContent Media(Guid id) => _publishedContentQuery.Media(id); + + /// + /// Overloaded method accepting an 'object' type + /// + /// + /// + /// + /// We accept an object type because GetPropertyValue now returns an 'object', we still want to allow people to pass + /// this result in to this method. + /// This method will throw an exception if the value is not of type int or string. + /// + public IPublishedContent Media(object id) + { + return MediaForObject(id); + } + + private IPublishedContent MediaForObject(object id) => _publishedContentQuery.Media(id); + + public IPublishedContent Media(int id) => _publishedContentQuery.Media(id); + + public IPublishedContent Media(string id) => _publishedContentQuery.Media(id); + + /// + /// Gets the medias corresponding to the identifiers. + /// + /// The media identifiers. + /// The existing medias corresponding to the identifiers. + /// If an identifier does not match an existing media, it will be missing in the returned value. + public IEnumerable Media(params object[] ids) => _publishedContentQuery.Media(ids); + + private IEnumerable MediaForObjects(IEnumerable ids) => _publishedContentQuery.Media(ids); + + /// + /// Gets the medias corresponding to the identifiers. + /// + /// The media identifiers. + /// The existing medias corresponding to the identifiers. + /// If an identifier does not match an existing media, it will be missing in the returned value. + public IEnumerable Media(params int[] ids) => _publishedContentQuery.Media(ids); + + /// + /// Gets the medias corresponding to the identifiers. + /// + /// The media identifiers. + /// The existing medias corresponding to the identifiers. + /// If an identifier does not match an existing media, it will be missing in the returned value. + public IEnumerable Media(params string[] ids) => _publishedContentQuery.Media(ids); + + + /// + /// Gets the medias corresponding to the identifiers. + /// + /// The media identifiers. + /// The existing medias corresponding to the identifiers. + /// If an identifier does not match an existing media, it will be missing in the returned value. + public IEnumerable Media(params Udi[] ids) => _publishedContentQuery.Media(ids); + + /// + /// Gets the medias corresponding to the identifiers. + /// + /// The media identifiers. + /// The existing medias corresponding to the identifiers. + /// If an identifier does not match an existing media, it will be missing in the returned value. + public IEnumerable Media(params GuidUdi[] ids) => _publishedContentQuery.Media(ids); + + /// + /// Gets the medias corresponding to the identifiers. + /// + /// The media identifiers. + /// The existing medias corresponding to the identifiers. + /// If an identifier does not match an existing media, it will be missing in the returned value. + public IEnumerable Media(IEnumerable ids) => _publishedContentQuery.Media(ids); + + /// + /// Gets the medias corresponding to the identifiers. + /// + /// The media identifiers. + /// The existing medias corresponding to the identifiers. + /// If an identifier does not match an existing media, it will be missing in the returned value. + public IEnumerable Media(IEnumerable ids) => _publishedContentQuery.Media(ids); + + /// + /// Gets the medias corresponding to the identifiers. + /// + /// The media identifiers. + /// The existing medias corresponding to the identifiers. + /// If an identifier does not match an existing media, it will be missing in the returned value. + public IEnumerable Media(IEnumerable ids) => _publishedContentQuery.Media(ids); + + /// + /// Gets the medias corresponding to the identifiers. + /// + /// The media identifiers. + /// The existing medias corresponding to the identifiers. + /// If an identifier does not match an existing media, it will be missing in the returned value. + public IEnumerable Media(IEnumerable ids) => _publishedContentQuery.Media(ids); + + /// + /// Gets the medias corresponding to the identifiers. + /// + /// The media identifiers. + /// The existing medias corresponding to the identifiers. + /// If an identifier does not match an existing media, it will be missing in the returned value. + public IEnumerable Media(IEnumerable ids) => _publishedContentQuery.Media(ids); + + public IEnumerable MediaAtRoot() + { + return _publishedContentQuery.MediaAtRoot(); + } + + #endregion + } +} diff --git a/src/Umbraco.Web/AreaRegistrationContextExtensions.cs b/src/Umbraco.Web/AreaRegistrationContextExtensions.cs deleted file mode 100644 index 1321d3722c..0000000000 --- a/src/Umbraco.Web/AreaRegistrationContextExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Web.Http; -using System.Web.Mvc; -using System.Web.Routing; - -namespace Umbraco.Web -{ - internal static class AreaRegistrationContextExtensions - { - public static Route MapHttpRoute(this AreaRegistrationContext context, string name, string url, object defaults, string[] namespaces) - { - var apiRoute = context.Routes.MapHttpRoute( - name, - url, - defaults); - - //web api routes don't set the data tokens object - if (apiRoute.DataTokens == null) - { - apiRoute.DataTokens = new RouteValueDictionary(); - } - apiRoute.DataTokens.Add("area", context.AreaName); - apiRoute.DataTokens.Add("Namespaces", namespaces); //look in this namespace to create the controller - apiRoute.DataTokens.Add("UseNamespaceFallback", false); //Don't look anywhere else except this namespace! - - return apiRoute; - } - } -} diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index 1d50154214..b1e13745d8 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -133,9 +133,6 @@ namespace Umbraco.Web.Composing internal static UmbracoApiControllerTypeCollection UmbracoApiControllerTypes => Factory.GetRequiredService(); - internal static SurfaceControllerTypeCollection SurfaceControllerTypes - => Factory.GetRequiredService(); - internal static IPublishedSnapshotService PublishedSnapshotService => Factory.GetRequiredService(); diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 1f7203270f..668a5d6873 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -28,7 +28,7 @@ namespace Umbraco.Web.Editors /// Represents a controller user to render out the default back office view and JS results. /// [UmbracoRequireHttps] - [DisableBrowserCache] + // [DisableBrowserCache] public class BackOfficeController : Controller { private BackOfficeOwinUserManager _userManager; diff --git a/src/Umbraco.Web/Mvc/ActionExecutedEventArgs.cs b/src/Umbraco.Web/Mvc/ActionExecutedEventArgs.cs deleted file mode 100644 index 6904aa103a..0000000000 --- a/src/Umbraco.Web/Mvc/ActionExecutedEventArgs.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Web.Mvc; - -namespace Umbraco.Web.Mvc -{ - /// Migrated already to .Net Core - public class ActionExecutedEventArgs : EventArgs - { - public Controller Controller { get; set; } - public object Model { get; set; } - - public ActionExecutedEventArgs(Controller controller, object model) - { - Controller = controller; - Model = model; - } - } -} diff --git a/src/Umbraco.Web/Mvc/AdminTokenAuthorizeAttribute.cs b/src/Umbraco.Web/Mvc/AdminTokenAuthorizeAttribute.cs deleted file mode 100644 index 25a2f958f8..0000000000 --- a/src/Umbraco.Web/Mvc/AdminTokenAuthorizeAttribute.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Net.Http.Headers; -using System.Text; -using System.Text.RegularExpressions; -using System.Web; -using System.Web.Mvc; -using Microsoft.Extensions.Logging; -using Umbraco.Core; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.Mvc -{ - /// - /// Used for authorizing scheduled tasks - /// - public sealed class AdminTokenAuthorizeAttribute : AuthorizeAttribute - { - // see note in HttpInstallAuthorizeAttribute - private readonly IUserService _userService; - private readonly IRuntimeState _runtimeState; - private readonly ILogger _logger; - - private IUserService UserService => _userService ?? Current.Services.UserService; - - private IRuntimeState RuntimeState => _runtimeState ?? Current.RuntimeState; - - private ILogger Logger => _logger ?? Current.Logger; - - /// - /// THIS SHOULD BE ONLY USED FOR UNIT TESTS - /// - /// - /// - public AdminTokenAuthorizeAttribute(IUserService userService, IRuntimeState runtimeState) - { - if (userService == null) throw new ArgumentNullException(nameof(userService)); - if (runtimeState == null) throw new ArgumentNullException(nameof(runtimeState)); - _userService = userService; - _runtimeState = runtimeState; - } - - public AdminTokenAuthorizeAttribute() - { } - - public const string AuthorizationType = "AToken"; - - /// - /// Used to return the full value that needs to go in the Authorization header - /// - /// - /// - public static string GetAuthHeaderTokenVal(IUserService userService) - { - return $"{AuthorizationType} {GetAuthHeaderVal(userService)}"; - } - - public static AuthenticationHeaderValue GetAuthenticationHeaderValue(IUserService userService) - { - return new AuthenticationHeaderValue(AuthorizationType, GetAuthHeaderVal(userService)); - } - - private static string GetAuthHeaderVal(IUserService userService) - { - var admin = userService.GetUserById(Core.Constants.Security.SuperUserId); - - var token = $"{admin.Email}u____u{admin.Username}u____u{admin.RawPasswordValue}"; - - var encrypted = token.EncryptWithMachineKey(); - var bytes = Encoding.UTF8.GetBytes(encrypted); - var base64 = Convert.ToBase64String(bytes); - return $"val=\"{base64}\""; - } - - /// - /// Ensures that the user must be in the Administrator or the Install role - /// - /// - /// - protected override bool AuthorizeCore(HttpContextBase httpContext) - { - if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); - - // we need to that the app is configured and that a user is logged in - if (RuntimeState.Level != RuntimeLevel.Run) return false; - - // need the auth header - if (httpContext.Request.Headers["Authorization"] == null || httpContext.Request.Headers["Authorization"].IsNullOrWhiteSpace()) return false; - - var header = httpContext.Request.Headers["Authorization"]; - if (header.StartsWith("AToken ") == false) return false; - - var keyVal = Regex.Matches(header, "AToken val=(.*?)(?:$|\\s)"); - if (keyVal.Count != 1) return false; - if (keyVal[0].Groups.Count != 2) return false; - - var admin = UserService.GetUserById(Core.Constants.Security.SuperUserId); - if (admin == null) return false; - - try - { - //get the token value from the header - var val = keyVal[0].Groups[1].Value.Trim("\""); - //un-base 64 the string - var bytes = Convert.FromBase64String(val); - var encrypted = Encoding.UTF8.GetString(bytes); - //decrypt the string - var text = encrypted.DecryptWithMachineKey(); - - //split - some users have not set an email, don't strip out empty entries - var split = text.Split(new[] {"u____u"}, StringSplitOptions.None); - if (split.Length != 3) return false; - - //compare - return - split[0] == admin.Email - && split[1] == admin.Username - && split[2] == admin.RawPasswordValue; - } - catch (Exception ex) - { - Logger.LogError(ex, "Failed to format passed in token value"); - return false; - } - } - } -} diff --git a/src/Umbraco.Web/Mvc/BackOfficeArea.cs b/src/Umbraco.Web/Mvc/BackOfficeArea.cs deleted file mode 100644 index eeb48c3b38..0000000000 --- a/src/Umbraco.Web/Mvc/BackOfficeArea.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Web.Mvc; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.Models; -using Umbraco.Core.Hosting; -using Umbraco.Web.Editors; - -namespace Umbraco.Web.Mvc -{ - // TODO: This has been ported to netcore, can be removed - internal class BackOfficeArea : AreaRegistration - { - private readonly GlobalSettings _globalSettings; - private readonly IHostingEnvironment _hostingEnvironment; - - public BackOfficeArea(GlobalSettings globalSettings, IHostingEnvironment hostingEnvironment) - { - _globalSettings = globalSettings; - _hostingEnvironment = hostingEnvironment; - } - - /// - /// Create the routes for the area - /// - /// - /// - /// By using the context to register the routes it means that the area is already applied to them all - /// and that the namespaces searched for the controllers are ONLY the ones specified. - /// - public override void RegisterArea(AreaRegistrationContext context) - { - - context.MapRoute( - "Umbraco_preview", - AreaName + "/preview/{action}/{editor}", - new {controller = "Preview", action = "Index", editor = UrlParameter.Optional}, - new[] { "Umbraco.Web.Editors" }); - - context.MapRoute( - "Umbraco_back_office", - AreaName + "/{action}/{id}", - new {controller = "BackOffice", action = "Default", id = UrlParameter.Optional}, - //limit the action/id to only allow characters - this is so this route doesn't hog all other - // routes like: /umbraco/channels/word.aspx, etc... - new - { - action = @"[a-zA-Z]*", - id = @"[a-zA-Z]*" - }, - new[] {typeof (BackOfficeController).Namespace}); - } - - public override string AreaName => _globalSettings.GetUmbracoMvcArea(_hostingEnvironment); - } -} diff --git a/src/Umbraco.Web/Mvc/Constants.cs b/src/Umbraco.Web/Mvc/Constants.cs deleted file mode 100644 index c71ed6b104..0000000000 --- a/src/Umbraco.Web/Mvc/Constants.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Umbraco.Web.Mvc -{ - /// - /// constants - /// - /// Migrated already to .Net Core - internal static class Constants - { - internal const string ViewLocation = "~/Views"; - - internal const string DataTokenCurrentViewContext = "umbraco-current-view-context"; - } -} diff --git a/src/Umbraco.Web/Mvc/DisableBrowserCacheAttribute.cs b/src/Umbraco.Web/Mvc/DisableBrowserCacheAttribute.cs deleted file mode 100644 index 567e9ca145..0000000000 --- a/src/Umbraco.Web/Mvc/DisableBrowserCacheAttribute.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Web; -using System.Web.Mvc; - -namespace Umbraco.Web.Mvc -{ - /// - /// Ensures that the request is not cached by the browser - /// - public class DisableBrowserCacheAttribute : ActionFilterAttribute - { - public override void OnResultExecuting(ResultExecutingContext filterContext) - { - base.OnResultExecuting(filterContext); - - // could happens if exception (but AFAIK this wouldn't happen in MVC) - if (filterContext.HttpContext == null || filterContext.HttpContext.Response == null || - filterContext.HttpContext.Response.Cache == null) - { - return; - } - - if (filterContext.IsChildAction) - { - return; - } - - if (filterContext.HttpContext.Response.StatusCode != 200) - { - return; - } - - filterContext.HttpContext.Response.Cache.SetLastModified(DateTime.Now); - filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false); - filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache); - filterContext.HttpContext.Response.Cache.SetMaxAge(TimeSpan.Zero); - filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches); - filterContext.HttpContext.Response.Cache.SetNoStore(); - filterContext.HttpContext.Response.AddHeader("Pragma", "no-cache"); - filterContext.HttpContext.Response.Cache.SetExpires(new DateTime(1990, 1, 1, 0, 0, 0)); - } - } -} diff --git a/src/Umbraco.Web/Mvc/HtmlTagWrapper.cs b/src/Umbraco.Web/Mvc/HtmlTagWrapper.cs deleted file mode 100644 index 518121eef9..0000000000 --- a/src/Umbraco.Web/Mvc/HtmlTagWrapper.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Web.UI; -using System.IO; -using System.Web; -using Umbraco.Core; - -namespace Umbraco.Web.Mvc -{ - public class HtmlTagWrapper : IHtmlTagWrapper, IHtmlString - { - public HtmlTagWrapper Parent; - - private readonly List _children; - public IEnumerable Children - { - get { return _children; } - } - - private List> _attributes; - public IEnumerable> Attributes - { - get { return _attributes; } - } - - public void ReflectAttributesFromAnonymousType(List> newAttributes) - { - List> mergedAttributes = - newAttributes - .Concat(Attributes) - .GroupBy(kvp => kvp.Key, kvp => kvp.Value) - .Select(g => new KeyValuePair(g.Key, string.Join(" ", g.ToArray()))) - .ToList(); - - _attributes = mergedAttributes; - } - public void ReflectAttributesFromAnonymousType(object anonymousAttributes) - { - var newAttributes = - anonymousAttributes - .GetType() - .GetProperties() - .Where(prop => !string.IsNullOrWhiteSpace(string.Format("{0}", prop.GetValue(anonymousAttributes, null)))) - .ToList() - .ConvertAll(prop => - new KeyValuePair( - prop.Name, - string.Format("{0}", prop.GetValue(anonymousAttributes, null)) - ) - ); - ReflectAttributesFromAnonymousType(newAttributes); - - } - - public List CssClasses; - public string Tag; - public bool Visible; - - public HtmlTagWrapper(string tag) - { - this.Tag = tag; - this._children = new List(); - this.CssClasses = new List(); - this._attributes = new List>(); - this.Visible = true; - } - public HtmlString Write() - { - if ((Children.Any() || Attributes.Any() || CssClasses.Count > 0) && Visible) - { - using (MemoryStream ms = new MemoryStream()) - { - using (TextWriter tw = new StreamWriter(ms)) - { - HtmlTextWriter html = new HtmlTextWriter(tw); - this.WriteToHtmlTextWriter(html); - tw.Flush(); - ms.Position = 0; - using (TextReader tr = new StreamReader(ms)) - { - string result = tr.ReadToEnd(); - return new HtmlString(result); - } - } - } - } - return new HtmlString(string.Empty); - } - public override string ToString() - { - return "Use @item.Write() to emit the HTML rather than @item"; - } - public IHtmlString ToHtml() - { - return this.Write(); - } - public void WriteToHtmlTextWriter(HtmlTextWriter html) - { - html.WriteBeginTag(Tag); - string cssClassNames = string.Join(" ", CssClasses.ToArray()).Trim(); - foreach (var attribute in Attributes) - { - html.WriteAttribute(attribute.Key, attribute.Value); - } - if (!string.IsNullOrWhiteSpace(cssClassNames)) - { - html.WriteAttribute("class", cssClassNames); - } - html.Write(HtmlTextWriter.TagRightChar); - foreach (var child in Children) - { - child.WriteToHtmlTextWriter(html); - } - html.WriteEndTag(Tag); - } - - public HtmlTagWrapper AddClassName(string className) - { - className = className.Trim(); - if (!this.CssClasses.Contains(className)) - { - this.CssClasses.Add(className); - } - return this; - } - - public HtmlTagWrapper RemoveClassName(string className) - { - className = className.Trim(); - if (this.CssClasses.Contains(className)) - { - this.CssClasses.Remove(className); - } - return this; - } - - public bool HasClassName(string className) - { - className = className.Trim(); - return (this.CssClasses.Contains(className)); - } - - public HtmlTagWrapper Attr(object newAttributes) - { - this.ReflectAttributesFromAnonymousType(newAttributes); - return this; - } - public HtmlTagWrapper Attr(string name, string value) - { - if (!string.IsNullOrWhiteSpace(value)) - { - var newAttributes = new List> {new KeyValuePair(name, value)}; - this.ReflectAttributesFromAnonymousType(newAttributes); - } - else - { - var existingKey = this._attributes.Find(item => item.Key == name); - _attributes.Remove(existingKey); - } - return this; - } - - public HtmlTagWrapper AddChild(IHtmlTagWrapper newChild) - { - _children.Add(newChild); - return this; - } - public HtmlTagWrapper AddChildren(params IHtmlTagWrapper[] collection) - { - _children.AddRange(collection); - return this; - } - public HtmlTagWrapper AddChild(string text) - { - _children.Add(new HtmlTagWrapperTextNode(text)); - return this; - } - public HtmlTagWrapper AddChildAt(int index, IHtmlTagWrapper newChild) - { - _children.Insert(index, newChild); - return this; - } - public HtmlTagWrapper AddChildAt(int index, string text) - { - _children.Insert(index, new HtmlTagWrapperTextNode(text)); - return this; - } - public HtmlTagWrapper AddChildrenAt(int index, params IHtmlTagWrapper[] collection) - { - _children.InsertRange(index, collection); - return this; - } - public HtmlTagWrapper RemoveChildAt(int index) - { - return this; - } - public int CountChildren() - { - return this.Children.Count(); - } - public HtmlTagWrapper ClearChildren() - { - return this; - } - - public string ToHtmlString() - { - return this.Write().ToHtmlString(); - } - } - -} diff --git a/src/Umbraco.Web/Mvc/HtmlTagWrapperTextNode.cs b/src/Umbraco.Web/Mvc/HtmlTagWrapperTextNode.cs deleted file mode 100644 index 1085c2a279..0000000000 --- a/src/Umbraco.Web/Mvc/HtmlTagWrapperTextNode.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Umbraco.Core; - -namespace Umbraco.Web.Mvc -{ - public class HtmlTagWrapperTextNode : IHtmlTagWrapper - { - public string Content { get; private set; } - public HtmlTagWrapperTextNode(string content) - { - this.Content = content; - } - - public void WriteToHtmlTextWriter(System.Web.UI.HtmlTextWriter html) - { - html.WriteEncodedText(Content); - } - } -} diff --git a/src/Umbraco.Web/Mvc/IHtmlTagWrapper.cs b/src/Umbraco.Web/Mvc/IHtmlTagWrapper.cs deleted file mode 100644 index 1c0ff9ca47..0000000000 --- a/src/Umbraco.Web/Mvc/IHtmlTagWrapper.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Web.UI; - -namespace Umbraco.Web.Mvc -{ - public interface IHtmlTagWrapper - { - void WriteToHtmlTextWriter(HtmlTextWriter html); - } -} diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs index 503ea59550..18e1fb8a1a 100644 --- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs +++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs @@ -124,102 +124,5 @@ namespace Umbraco.Web.Mvc _contentSettings = contentSettings.Value; } - // view logic below: - - /// - /// Ensure that the current view context is added to the route data tokens so we can extract it if we like - /// - /// - /// Currently this is required by mvc macro engines - /// - protected override void InitializePage() - { - base.InitializePage(); - - if (ViewContext.IsChildAction) return; - - // this is used purely for partial view macros that contain forms and mostly - // just when rendered within the RTE - this should already be set with the - // EnsurePartialViewMacroViewContextFilterAttribute - if (ViewContext.RouteData.DataTokens.ContainsKey(Constants.DataTokenCurrentViewContext) == false) - ViewContext.RouteData.DataTokens.Add(Constants.DataTokenCurrentViewContext, ViewContext); - } - - /// - /// This will detect the end /body tag and insert the preview badge if in preview mode - /// - /// - public override void WriteLiteral(object value) - { - // filter / add preview banner - if (Response.ContentType.InvariantEquals("text/html")) // ASP.NET default value - { - if (Current.UmbracoContext.IsDebug || Current.UmbracoContext.InPreviewMode) - { - var text = value.ToString(); - var pos = text.IndexOf("", StringComparison.InvariantCultureIgnoreCase); - - if (pos > -1) - { - string markupToInject; - - if (Current.UmbracoContext.InPreviewMode) - { - // creating previewBadge markup - markupToInject = - string.Format(_contentSettings.PreviewBadge, - Current.IOHelper.ResolveUrl(_globalSettings.UmbracoPath), - Server.UrlEncode(HttpContext.Current.Request.Url?.PathAndQuery), - Current.UmbracoContext.PublishedRequest.PublishedContent.Id); - } - else - { - // creating mini-profiler markup - markupToInject = Html.RenderProfiler().ToHtmlString(); - } - - var sb = new StringBuilder(text); - sb.Insert(pos, markupToInject); - - base.WriteLiteral(sb.ToString()); - return; - } - } - } - - base.WriteLiteral(value); - } - - public override void Write(object value) - { - if (value is IHtmlEncodedString htmlEncodedString) - { - base.WriteLiteral(htmlEncodedString.ToHtmlString()); - } - else - { - base.Write(value); - } - } - - public HelperResult RenderSection(string name, Func defaultContents) - { - return WebViewPageExtensions.RenderSection(this, name, defaultContents); - } - - public HelperResult RenderSection(string name, HelperResult defaultContents) - { - return WebViewPageExtensions.RenderSection(this, name, defaultContents); - } - - public HelperResult RenderSection(string name, string defaultContents) - { - return WebViewPageExtensions.RenderSection(this, name, defaultContents); - } - - public HelperResult RenderSection(string name, IHtmlString defaultContents) - { - return WebViewPageExtensions.RenderSection(this, name, defaultContents); - } } } diff --git a/src/Umbraco.Web/Mvc/ValidateUmbracoFormRouteStringAttribute.cs b/src/Umbraco.Web/Mvc/ValidateUmbracoFormRouteStringAttribute.cs index 8d929197e1..7ee44a6abc 100644 --- a/src/Umbraco.Web/Mvc/ValidateUmbracoFormRouteStringAttribute.cs +++ b/src/Umbraco.Web/Mvc/ValidateUmbracoFormRouteStringAttribute.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Web.Mvc; @@ -13,7 +14,7 @@ namespace Umbraco.Web.Mvc /// /// /// Applying this attribute/filter to a or SurfaceController Action will ensure that the Action can only be executed - /// when it is routed to from within Umbraco, typically when rendering a form with BegingUmbracoForm. It will mean that the natural MVC route for this Action + /// when it is routed to from within Umbraco, typically when rendering a form with BeginUmbracoForm. It will mean that the natural MVC route for this Action /// will fail with a . /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] @@ -37,7 +38,6 @@ namespace Umbraco.Web.Mvc var ufprt = filterContext.HttpContext.Request["ufprt"]; ValidateRouteString(ufprt, filterContext.ActionDescriptor?.ControllerDescriptor.ControllerName, filterContext.ActionDescriptor?.ActionName, filterContext.RouteData?.DataTokens["area"]?.ToString()); } - public void ValidateRouteString(string ufprt, string currentController, string currentAction, string currentArea) { if (ufprt.IsNullOrWhiteSpace()) diff --git a/src/Umbraco.Web/Mvc/ViewContextExtensions.cs b/src/Umbraco.Web/Mvc/ViewContextExtensions.cs deleted file mode 100644 index 5131f63f9e..0000000000 --- a/src/Umbraco.Web/Mvc/ViewContextExtensions.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.IO; -using System.Web.Mvc; - -namespace Umbraco.Web.Mvc -{ - internal static class ViewContextExtensions - { - /// - /// Creates a new ViewContext from an existing one but specifies a new Model for the ViewData - /// - /// - /// - /// - public static ViewContext CopyWithModel(this ViewContext vc, object model) - { - return new ViewContext - { - Controller = vc.Controller, - HttpContext = vc.HttpContext, - RequestContext = vc.RequestContext, - RouteData = vc.RouteData, - TempData = vc.TempData, - View = vc.View, - ViewData = new ViewDataDictionary(vc) - { - Model = model - }, - FormContext = vc.FormContext, - ClientValidationEnabled = vc.ClientValidationEnabled, - UnobtrusiveJavaScriptEnabled = vc.UnobtrusiveJavaScriptEnabled, - Writer = vc.Writer - }; - } - - //public static ViewContext CloneWithWriter(this ViewContext vc, TextWriter writer) - //{ - // return new ViewContext - // { - // Controller = vc.Controller, - // HttpContext = vc.HttpContext, - // RequestContext = vc.RequestContext, - // RouteData = vc.RouteData, - // TempData = vc.TempData, - // View = vc.View, - // ViewData = vc.ViewData, - // FormContext = vc.FormContext, - // ClientValidationEnabled = vc.ClientValidationEnabled, - // UnobtrusiveJavaScriptEnabled = vc.UnobtrusiveJavaScriptEnabled, - // Writer = writer - // }; - //} - } -} diff --git a/src/Umbraco.Web/Runtime/WebInitialComponent.cs b/src/Umbraco.Web/Runtime/WebInitialComponent.cs index f07914f2d2..1daf950537 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComponent.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComponent.cs @@ -21,7 +21,6 @@ namespace Umbraco.Web.Runtime public sealed class WebInitialComponent : IComponent { private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly SurfaceControllerTypeCollection _surfaceControllerTypes; private readonly UmbracoApiControllerTypeCollection _apiControllerTypes; private readonly GlobalSettings _globalSettings; private readonly IHostingEnvironment _hostingEnvironment; @@ -29,14 +28,12 @@ namespace Umbraco.Web.Runtime public WebInitialComponent( IUmbracoContextAccessor umbracoContextAccessor, - SurfaceControllerTypeCollection surfaceControllerTypes, UmbracoApiControllerTypeCollection apiControllerTypes, GlobalSettings globalSettings, IHostingEnvironment hostingEnvironment, IShortStringHelper shortStringHelper) { _umbracoContextAccessor = umbracoContextAccessor; - _surfaceControllerTypes = surfaceControllerTypes; _apiControllerTypes = apiControllerTypes; _globalSettings = globalSettings; _hostingEnvironment = hostingEnvironment; @@ -58,7 +55,7 @@ namespace Umbraco.Web.Runtime ConfigureGlobalFilters(); // set routes - CreateRoutes(_umbracoContextAccessor, _globalSettings, _shortStringHelper, _surfaceControllerTypes, _apiControllerTypes, _hostingEnvironment); + CreateRoutes(_umbracoContextAccessor, _globalSettings, _shortStringHelper, _apiControllerTypes, _hostingEnvironment); } public void Terminate() @@ -86,7 +83,7 @@ namespace Umbraco.Web.Runtime private void SetupMvcAndWebApi() { //don't output the MVC version header (security) - MvcHandler.DisableMvcResponseHeader = true; + //MvcHandler.DisableMvcResponseHeader = true; // set master controller factory // var controllerFactory = new MasterControllerFactory(() => Current.FilteredControllerFactories); @@ -108,7 +105,6 @@ namespace Umbraco.Web.Runtime IUmbracoContextAccessor umbracoContextAccessor, GlobalSettings globalSettings, IShortStringHelper shortStringHelper, - SurfaceControllerTypeCollection surfaceControllerTypes, UmbracoApiControllerTypeCollection apiControllerTypes, IHostingEnvironment hostingEnvironment) { @@ -126,22 +122,21 @@ namespace Umbraco.Web.Runtime // RouteTable.Routes.RegisterArea(); // register all back office routes - RouteTable.Routes.RegisterArea(new BackOfficeArea(globalSettings, hostingEnvironment)); + // RouteTable.Routes.RegisterArea(new BackOfficeArea(globalSettings, hostingEnvironment)); // plugin controllers must come first because the next route will catch many things - RoutePluginControllers(globalSettings, surfaceControllerTypes, apiControllerTypes, hostingEnvironment); + RoutePluginControllers(globalSettings, apiControllerTypes, hostingEnvironment); } private static void RoutePluginControllers( GlobalSettings globalSettings, - SurfaceControllerTypeCollection surfaceControllerTypes, UmbracoApiControllerTypeCollection apiControllerTypes, IHostingEnvironment hostingEnvironment) { var umbracoPath = globalSettings.GetUmbracoMvcArea(hostingEnvironment); // need to find the plugin controllers and route them - var pluginControllers = surfaceControllerTypes.Concat(apiControllerTypes).ToArray(); + var pluginControllers = apiControllerTypes; //TODO was: surfaceControllerTypes.Concat(apiControllerTypes).ToArray(); // local controllers do not contain the attribute var localControllers = pluginControllers.Where(x => PluginController.GetMetadata(x).AreaName.IsNullOrWhiteSpace()); diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 5f227b37ad..32e7835ffb 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -66,8 +66,8 @@ namespace Umbraco.Web.Runtime //.SetDefaultRenderMvcController(); // default controller for template views //we need to eagerly scan controller types since they will need to be routed - composition.WithCollectionBuilder() - .Add(composition.TypeLoader.GetSurfaceControllers()); + // composition.WithCollectionBuilder() + // .Add(composition.TypeLoader.GetSurfaceControllers()); // auto-register views diff --git a/src/Umbraco.Web/TypeLoaderExtensions.cs b/src/Umbraco.Web/TypeLoaderExtensions.cs index 1cf8bc124c..8dac034fbf 100644 --- a/src/Umbraco.Web/TypeLoaderExtensions.cs +++ b/src/Umbraco.Web/TypeLoaderExtensions.cs @@ -10,6 +10,7 @@ namespace Umbraco.Web /// /// Provides extension methods for the class. /// + // Migrated to .NET Core public static class TypeLoaderExtensions { /// diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e331d4c5f4..e83c29b7de 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -128,7 +128,6 @@ Properties\SolutionInfo.cs - @@ -136,7 +135,6 @@ - @@ -156,7 +154,6 @@ - @@ -201,7 +198,6 @@ - @@ -213,7 +209,6 @@ - @@ -226,15 +221,11 @@ - - - - @@ -268,7 +259,6 @@ - @@ -285,11 +275,7 @@ - - - - @@ -305,10 +291,8 @@ - - @@ -328,7 +312,6 @@ Component - @@ -344,4 +327,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index c55e5ccb51..3ca0a58c00 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -446,6 +446,8 @@ namespace Umbraco.Web #endregion + + //Migrated to EncryptionHelper internal static bool DecryptAndValidateEncryptedRouteString(string ufprt, out IDictionary parts) { string decryptedString; diff --git a/src/Umbraco.Web/UrlHelperRenderExtensions.cs b/src/Umbraco.Web/UrlHelperRenderExtensions.cs index ba6a0540f0..dfa2632ea0 100644 --- a/src/Umbraco.Web/UrlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/UrlHelperRenderExtensions.cs @@ -312,76 +312,81 @@ namespace Umbraco.Web return result; } - /// - /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - /// - /// - /// - /// - /// - public static string SurfaceAction(this UrlHelper url, string action, Type surfaceType) - { - return url.SurfaceAction(action, surfaceType, null); - } + //TODO Move to LinkGenerator + // /// + // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + // /// + // /// + // /// + // /// + // /// + // public static string SurfaceAction(this UrlHelper url, string action, Type surfaceType) + // { + // return url.SurfaceAction(action, surfaceType, null); + // } - /// - /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - /// - /// - /// - /// - /// - /// - public static string SurfaceAction(this UrlHelper url, string action, Type surfaceType, object additionalRouteVals) - { - if (action == null) throw new ArgumentNullException(nameof(action)); - if (string.IsNullOrEmpty(action)) throw new ArgumentException("Value can't be empty.", nameof(action)); - if (surfaceType == null) throw new ArgumentNullException(nameof(surfaceType)); + //TODO Move to LinkGenerator + // /// + // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + // /// + // /// + // /// + // /// + // /// + // /// + // public static string SurfaceAction(this UrlHelper url, string action, Type surfaceType, object additionalRouteVals) + // { + // if (action == null) throw new ArgumentNullException(nameof(action)); + // if (string.IsNullOrEmpty(action)) throw new ArgumentException("Value can't be empty.", nameof(action)); + // if (surfaceType == null) throw new ArgumentNullException(nameof(surfaceType)); + // + // var area = ""; + // + // + // var surfaceController = Current.SurfaceControllerTypes.SingleOrDefault(x => x == surfaceType); + // if (surfaceController == null) + // throw new InvalidOperationException("Could not find the surface controller of type " + surfaceType.FullName); + // var metaData = PluginController.GetMetadata(surfaceController); + // if (metaData.AreaName.IsNullOrWhiteSpace() == false) + // { + // //set the area to the plugin area + // area = metaData.AreaName; + // } + // + // var encryptedRoute = CreateEncryptedRouteString(metaData.ControllerName, action, area, additionalRouteVals); + // + // var result = Current.UmbracoContext.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; + // return result; + // } - var area = ""; + // /// + // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + // /// + // /// + // /// + // /// + // /// + // //TODO Move to LinkGenerator + // public static string SurfaceAction(this UrlHelper url, string action) + // where T : SurfaceController + // { + // return url.SurfaceAction(action, typeof (T)); + // } - var surfaceController = Current.SurfaceControllerTypes.SingleOrDefault(x => x == surfaceType); - if (surfaceController == null) - throw new InvalidOperationException("Could not find the surface controller of type " + surfaceType.FullName); - var metaData = PluginController.GetMetadata(surfaceController); - if (metaData.AreaName.IsNullOrWhiteSpace() == false) - { - //set the area to the plugin area - area = metaData.AreaName; - } - - var encryptedRoute = CreateEncryptedRouteString(metaData.ControllerName, action, area, additionalRouteVals); - - var result = Current.UmbracoContext.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; - return result; - } - - /// - /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - /// - /// - /// - /// - /// - public static string SurfaceAction(this UrlHelper url, string action) - where T : SurfaceController - { - return url.SurfaceAction(action, typeof (T)); - } - - /// - /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - /// - /// - /// - /// - /// - /// - public static string SurfaceAction(this UrlHelper url, string action, object additionalRouteVals) - where T : SurfaceController - { - return url.SurfaceAction(action, typeof (T), additionalRouteVals); - } + // /// + // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController + // /// + // /// + // /// + // /// + // /// + // /// + // //TODO Move to LinkGenerator + // public static string SurfaceAction(this UrlHelper url, string action, object additionalRouteVals) + // where T : SurfaceController + // { + // return url.SurfaceAction(action, typeof (T), additionalRouteVals); + // } /// /// This is used in methods like BeginUmbracoForm and SurfaceAction to generate an encrypted string which gets submitted in a request for which diff --git a/src/Umbraco.Web/WebViewPageExtensions.cs b/src/Umbraco.Web/WebViewPageExtensions.cs deleted file mode 100644 index 51f811061c..0000000000 --- a/src/Umbraco.Web/WebViewPageExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Web; -using System.Web.Mvc; -using System.Web.WebPages; - -namespace Umbraco.Web -{ - public static class WebViewPageExtensions - { - public static HelperResult RenderSection(this WebPageBase webPage, string name, Func defaultContents) - { - return webPage.IsSectionDefined(name) ? webPage.RenderSection(name) : defaultContents(null); - } - - public static HelperResult RenderSection(this WebPageBase webPage, string name, HelperResult defaultContents) - { - return webPage.IsSectionDefined(name) ? webPage.RenderSection(name) : defaultContents; - } - - public static HelperResult RenderSection(this WebPageBase webPage, string name, string defaultContents) - { - return webPage.IsSectionDefined(name) ? webPage.RenderSection(name) : new HelperResult(text => text.Write(defaultContents)); - } - - public static HelperResult RenderSection(this WebPageBase webPage, string name, IHtmlString defaultContents) - { - return webPage.IsSectionDefined(name) ? webPage.RenderSection(name) : new HelperResult(text => text.Write(defaultContents)); - } - } -} From a1f9bc9b173e46be7c42eb27f174fee6e2c00c7b Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Date: Fri, 13 Nov 2020 08:50:35 +0100 Subject: [PATCH 2/2] NetCore: Install page db checks (#9388) * Making more explicit checks for the db type * Fixed confusion with both ints and strings Co-authored-by: Bjarke Berg --- .../Install/InstallSteps/DatabaseConfigureStep.cs | 8 ++++---- .../src/installer/steps/database.html | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseConfigureStep.cs b/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseConfigureStep.cs index 7727b6b760..4b3268d4c5 100644 --- a/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseConfigureStep.cs +++ b/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseConfigureStep.cs @@ -73,14 +73,14 @@ namespace Umbraco.Web.Install.InstallSteps { var databases = new List() { - new { name = "Microsoft SQL Server", id = 1 }, - new { name = "Microsoft SQL Azure", id = 3 }, - new { name = "Custom connection string", id = -1 }, + new { name = "Microsoft SQL Server", id = DatabaseType.SqlServer.ToString() }, + new { name = "Microsoft SQL Azure", id = DatabaseType.SqlAzure.ToString() }, + new { name = "Custom connection string", id = DatabaseType.Custom.ToString() }, }; if (IsSqlCeAvailable()) { - databases.Insert(0, new { name = "Microsoft SQL Server Compact (SQL CE)", id = 0 }); + databases.Insert(0, new { name = "Microsoft SQL Server Compact (SQL CE)", id = DatabaseType.SqlCe.ToString() }); } return new diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/database.html b/src/Umbraco.Web.UI.Client/src/installer/steps/database.html index 2c81f3ddf3..eee933b561 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/steps/database.html +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/database.html @@ -17,11 +17,11 @@ -
+

Great! No need to configure anything, you can simply click the continue button below to continue to the next step

-
+
What is the exact connection string we should use?
@@ -32,7 +32,7 @@
-
+
Where do we find your database?
@@ -40,7 +40,7 @@
Enter server domain or IP
@@ -86,7 +86,7 @@
-
+