diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 71e3836b3d..c7282ee9c8 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Web.Mvc; using System.Web.Routing; +using System.Web.Security; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -17,12 +18,15 @@ using Umbraco.Web.WebApi; using Umbraco.Core.Strings; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; +using Umbraco.Core.Dictionary; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.Testing; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Runtime; +using Umbraco.Web.Security; using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Routing @@ -136,7 +140,16 @@ namespace Umbraco.Tests.Routing var type = new AutoPublishedContentType(22, "CustomDocument", new PublishedPropertyType[] { }); ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; - var handler = new RenderRouteHandler(umbracoContext, new TestControllerFactory(umbracoContext, Mock.Of())); + var handler = new RenderRouteHandler(umbracoContext, new TestControllerFactory(umbracoContext, Mock.Of(), context => + { + var membershipHelper = new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()); + return new CustomDocumentController(Factory.GetInstance(), + umbracoContext, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + new UmbracoHelper(umbracoContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), membershipHelper)); + })); handler.GetHandlerForRoute(umbracoContext.HttpContext.Request.RequestContext, frequest); Assert.AreEqual("CustomDocument", routeData.Values["controller"].ToString()); @@ -172,8 +185,8 @@ namespace Umbraco.Tests.Routing /// public class CustomDocumentController : RenderMvcController { - public CustomDocumentController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger) - : base(globalSettings, umbracoContext, services, appCaches, logger, profilingLogger) + public CustomDocumentController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, services, appCaches, profilingLogger, umbracoHelper) { } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs index 73762d4e91..2f7e50ea1e 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs @@ -7,16 +7,16 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting { public class TestControllerActivator : TestControllerActivatorBase { - private readonly Func _factory; + private readonly Func _factory; - public TestControllerActivator(Func factory) + public TestControllerActivator(Func factory) { _factory = factory; } - protected override ApiController CreateController(Type controllerType, HttpRequestMessage msg, UmbracoHelper helper) + protected override ApiController CreateController(Type controllerType, HttpRequestMessage msg, UmbracoContext umbracoContext, UmbracoHelper helper) { - return _factory(msg, helper); + return _factory(msg, umbracoContext, helper); } } } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 6414ea469d..4e352488be 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -152,17 +152,16 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting var membershipHelper = new MembershipHelper(new TestUmbracoContextAccessor(umbCtx), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()); - var umbHelper = new UmbracoHelper(umbCtx, - Mock.Of(), + var umbHelper = new UmbracoHelper(umbCtx, Mock.Of(), - Mock.Of(), + Mock.Of(), Mock.Of(), - membershipHelper, - serviceContext); + Mock.Of(), + membershipHelper); - return CreateController(controllerType, request, umbHelper); + return CreateController(controllerType, request, umbCtx, umbHelper); } - protected abstract ApiController CreateController(Type controllerType, HttpRequestMessage msg, UmbracoHelper helper); + protected abstract ApiController CreateController(Type controllerType, HttpRequestMessage msg, UmbracoContext umbracoContext, UmbracoHelper helper); } } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs index 5834415568..86d8cbdd5e 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs @@ -15,9 +15,9 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting { public class TestRunner { - private readonly Func _controllerFactory; + private readonly Func _controllerFactory; - public TestRunner(Func controllerFactory) + public TestRunner(Func controllerFactory) { _controllerFactory = controllerFactory; } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs index 95b5a3bfeb..6143432faf 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs @@ -16,10 +16,10 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting /// public class TestStartup { - private readonly Func _controllerFactory; + private readonly Func _controllerFactory; private readonly Action _initialize; - public TestStartup(Action initialize, Func controllerFactory) + public TestStartup(Action initialize, Func controllerFactory) { _controllerFactory = controllerFactory; _initialize = initialize; diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs index 36c5961b9f..9889b8d0c2 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs @@ -19,6 +19,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs { private readonly UmbracoContext _umbracoContext; private readonly ILogger _logger; + private readonly Func _factory; public TestControllerFactory(UmbracoContext umbracoContext, ILogger logger) { @@ -26,8 +27,17 @@ namespace Umbraco.Tests.TestHelpers.Stubs _logger = logger; } + public TestControllerFactory(UmbracoContext umbracoContext, ILogger logger, Func factory) + { + _umbracoContext = umbracoContext; + _logger = logger; + _factory = factory; + } + public IController CreateController(RequestContext requestContext, string controllerName) { + if (_factory != null) return _factory(requestContext); + var types = TypeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 622186a547..652ac0ab30 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -61,12 +61,11 @@ namespace Umbraco.Tests.Testing.TestingTests // ReSharper disable once UnusedVariable var helper = new UmbracoHelper(umbracoContext, - Mock.Of(), Mock.Of(), - Mock.Of(), + Mock.Of(), Mock.Of(), - new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()), - ServiceContext.CreatePartial()); + Mock.Of(), + new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of())); Assert.Pass(); } diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 7c8d1100f8..702a99b510 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -38,9 +38,11 @@ using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Actions; using Umbraco.Web.Composing.Composers; using Umbraco.Web.ContentApps; +using Umbraco.Web.Macros; using Umbraco.Web.PublishedCache; using Current = Umbraco.Core.Composing.Current; using Umbraco.Web.Routing; +using Umbraco.Web.Templates; using Umbraco.Web.Trees; namespace Umbraco.Tests.Testing @@ -228,6 +230,7 @@ namespace Umbraco.Tests.Testing .Append() .Append(); Composition.RegisterUnique(); + } protected virtual void ComposeWtf() diff --git a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs index 5b4b20b7b9..37600e2f89 100644 --- a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs @@ -30,6 +30,11 @@ using System; using Umbraco.Web.WebApi; using Umbraco.Web.Trees; using System.Globalization; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; using Umbraco.Web.Actions; namespace Umbraco.Tests.Web.Controllers @@ -207,17 +212,28 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async Task PostSave_Validate_Existing_Content() { - ApiController Factory(HttpRequestMessage message, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, UmbracoContext umbracoContext, UmbracoHelper helper) { var contentServiceMock = Mock.Get(Current.Services.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => null); //do not find it var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - var usersController = new ContentController(propertyEditorCollection); - return usersController; + + var controller = new ContentController( + propertyEditorCollection, + Factory.GetInstance(), + umbracoContext, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + helper); + + return controller; } - var runner = new TestRunner(Factory); + var runner = new TestRunner(CtrlFactory); var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, content: GetMultiPartRequestContent(PublishJsonInvariant), mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), @@ -233,21 +249,31 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async Task PostSave_Validate_At_Least_One_Variant_Flagged_For_Saving() { - ApiController Factory(HttpRequestMessage message, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, UmbracoContext umbracoContext, UmbracoHelper helper) { var contentServiceMock = Mock.Get(Current.Services.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => GetMockedContent()); var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - var usersController = new ContentController(propertyEditorCollection); - return usersController; + var controller = new ContentController( + propertyEditorCollection, + Factory.GetInstance(), + umbracoContext, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + helper); + + return controller; } var json = JsonConvert.DeserializeObject(PublishJsonInvariant); //remove all save flaggs ((JArray)json["variants"])[0]["save"] = false; - var runner = new TestRunner(Factory); + var runner = new TestRunner(CtrlFactory); var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), @@ -264,14 +290,24 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async Task PostSave_Validate_Properties_Exist() { - ApiController Factory(HttpRequestMessage message, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, UmbracoContext umbracoContext, UmbracoHelper helper) { var contentServiceMock = Mock.Get(Current.Services.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => GetMockedContent()); var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - var usersController = new ContentController(propertyEditorCollection); - return usersController; + var controller = new ContentController( + propertyEditorCollection, + Factory.GetInstance(), + umbracoContext, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + helper); + + return controller; } var json = JsonConvert.DeserializeObject(PublishJsonInvariant); @@ -284,7 +320,7 @@ namespace Umbraco.Tests.Web.Controllers value = "hello" })); - var runner = new TestRunner(Factory); + var runner = new TestRunner(CtrlFactory); var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), @@ -298,7 +334,7 @@ namespace Umbraco.Tests.Web.Controllers { var content = GetMockedContent(); - ApiController Factory(HttpRequestMessage message, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, UmbracoContext umbracoContext, UmbracoHelper helper) { var contentServiceMock = Mock.Get(Current.Services.ContentService); @@ -310,11 +346,21 @@ namespace Umbraco.Tests.Web.Controllers contentTypeServiceMock.Setup(x => x.Get(content.ContentTypeId)).Returns(() => MockedContentTypes.CreateSimpleContentType()); var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - var usersController = new ContentController(propertyEditorCollection); - return usersController; + var controller = new ContentController( + propertyEditorCollection, + Factory.GetInstance(), + umbracoContext, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + helper); + + return controller; } - var runner = new TestRunner(Factory); + var runner = new TestRunner(CtrlFactory); var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, content: GetMultiPartRequestContent(PublishJsonInvariant), mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), @@ -332,7 +378,7 @@ namespace Umbraco.Tests.Web.Controllers { var content = GetMockedContent(); - ApiController Factory(HttpRequestMessage message, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, UmbracoContext umbracoContext, UmbracoHelper helper) { var contentServiceMock = Mock.Get(Current.Services.ContentService); @@ -344,15 +390,25 @@ namespace Umbraco.Tests.Web.Controllers contentTypeServiceMock.Setup(x => x.Get(content.ContentTypeId)).Returns(() => MockedContentTypes.CreateSimpleContentType()); var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - var usersController = new ContentController(propertyEditorCollection); - return usersController; + var controller = new ContentController( + propertyEditorCollection, + Factory.GetInstance(), + umbracoContext, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + helper); + + return controller; } //clear out the name var json = JsonConvert.DeserializeObject(PublishJsonInvariant); json["variants"].ElementAt(0)["name"] = null; - var runner = new TestRunner(Factory); + var runner = new TestRunner(CtrlFactory); var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), @@ -370,7 +426,7 @@ namespace Umbraco.Tests.Web.Controllers { var content = GetMockedContent(); - ApiController Factory(HttpRequestMessage message, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, UmbracoContext umbracoContext, UmbracoHelper helper) { var contentServiceMock = Mock.Get(Current.Services.ContentService); @@ -382,15 +438,25 @@ namespace Umbraco.Tests.Web.Controllers contentTypeServiceMock.Setup(x => x.Get(content.ContentTypeId)).Returns(() => MockedContentTypes.CreateSimpleContentType()); var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - var usersController = new ContentController(propertyEditorCollection); - return usersController; + var controller = new ContentController( + propertyEditorCollection, + Factory.GetInstance(), + umbracoContext, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + helper); + + return controller; } //clear out one of the names var json = JsonConvert.DeserializeObject(PublishJsonVariant); json["variants"].ElementAt(0)["name"] = null; - var runner = new TestRunner(Factory); + var runner = new TestRunner(CtrlFactory); var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), diff --git a/src/Umbraco.Tests/Web/Controllers/PluginControllerAreaTests.cs b/src/Umbraco.Tests/Web/Controllers/PluginControllerAreaTests.cs index 01525f12da..4c738df003 100644 --- a/src/Umbraco.Tests/Web/Controllers/PluginControllerAreaTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/PluginControllerAreaTests.cs @@ -54,7 +54,7 @@ namespace Umbraco.Tests.Web.Controllers public class Plugin1Controller : PluginController { public Plugin1Controller(UmbracoContext umbracoContext) - : base(umbracoContext, null, null, null, null, null) + : base(umbracoContext, null, null, null, null, null, null) { } } @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Web.Controllers public class Plugin2Controller : PluginController { public Plugin2Controller(UmbracoContext umbracoContext) - : base(umbracoContext, null, null, null, null, null) + : base(umbracoContext, null, null, null, null, null, null) { } } @@ -72,7 +72,7 @@ namespace Umbraco.Tests.Web.Controllers public class Plugin3Controller : PluginController { public Plugin3Controller(UmbracoContext umbracoContext) - : base(umbracoContext, null, null, null, null, null) + : base(umbracoContext, null, null, null, null, null, null) { } } @@ -80,7 +80,7 @@ namespace Umbraco.Tests.Web.Controllers public class Plugin4Controller : PluginController { public Plugin4Controller(UmbracoContext umbracoContext) - : base(umbracoContext, null, null, null, null, null) + : base(umbracoContext, null, null, null, null, null, null) { } } diff --git a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs index 857e922ac9..3905e62037 100644 --- a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs @@ -7,9 +7,13 @@ using Moq; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; @@ -49,7 +53,7 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async System.Threading.Tasks.Task Save_User() { - ApiController Factory(HttpRequestMessage message, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, UmbracoContext umbracoContext, UmbracoHelper helper) { //setup some mocks Umbraco.Core.Configuration.GlobalSettings.HasSmtpServer = true; @@ -68,7 +72,15 @@ namespace Umbraco.Tests.Web.Controllers userServiceMock.Setup(service => service.GetUserById(It.IsAny())) .Returns((int id) => id == 1234 ? new User(1234, "Test", "test@test.com", "test@test.com", "", new List(), new int[0], new int[0]) : null); - var usersController = new UsersController(); + var usersController = new UsersController( + Factory.GetInstance(), + umbracoContext, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + helper); return usersController; } @@ -82,7 +94,7 @@ namespace Umbraco.Tests.Web.Controllers UserGroups = new[] { "writers" } }; - var runner = new TestRunner(Factory); + var runner = new TestRunner(CtrlFactory); var response = await runner.Execute("Users", "PostSaveUser", HttpMethod.Post, new ObjectContent(userSave, new JsonMediaTypeFormatter())); var obj = JsonConvert.DeserializeObject(response.Item2); @@ -122,15 +134,23 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async System.Threading.Tasks.Task GetPagedUsers_Empty() { - ApiController Factory(HttpRequestMessage message, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, UmbracoContext umbracoContext, UmbracoHelper helper) { - var usersController = new UsersController(); + var usersController = new UsersController( + Factory.GetInstance(), + umbracoContext, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + helper); return usersController; } MockForGetPagedUsers(); - var runner = new TestRunner(Factory); + var runner = new TestRunner(CtrlFactory); var response = await runner.Execute("Users", "GetPagedUsers", HttpMethod.Get); var obj = JsonConvert.DeserializeObject>(response.Item2); @@ -140,7 +160,7 @@ namespace Umbraco.Tests.Web.Controllers [Test] public async System.Threading.Tasks.Task GetPagedUsers_10() { - ApiController Factory(HttpRequestMessage message, UmbracoHelper helper) + ApiController CtrlFactory(HttpRequestMessage message, UmbracoContext umbracoContext, UmbracoHelper helper) { //setup some mocks var userServiceMock = Mock.Get(Current.Services.UserService); @@ -151,13 +171,21 @@ namespace Umbraco.Tests.Web.Controllers It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) .Returns(() => users); - var usersController = new UsersController(); + var usersController = new UsersController( + Factory.GetInstance(), + umbracoContext, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + helper); return usersController; } MockForGetPagedUsers(); - var runner = new TestRunner(Factory); + var runner = new TestRunner(CtrlFactory); var response = await runner.Execute("Users", "GetPagedUsers", HttpMethod.Get); var obj = JsonConvert.DeserializeObject>(response.Item2); diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index ec37793c10..b3ae7e3dd6 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -77,42 +77,15 @@ namespace Umbraco.Tests.Web.Mvc Assert.IsNotNull(ctrl.UmbracoContext); } - - [Test] - public void Umbraco_Helper_Not_Null() - { - var globalSettings = TestObjects.GetGlobalSettings(); - var umbracoContext = UmbracoContext.EnsureContext( - Current.UmbracoContextAccessor, - new Mock().Object, - Mock.Of(), - new Mock(null, null, globalSettings).Object, - TestObjects.GetUmbracoSettings(), - Enumerable.Empty(), - globalSettings, - new TestVariationContextAccessor(), - true); - - var controller = new TestSurfaceController(umbracoContext); - Composition.Register(_ => umbracoContext); - - Assert.IsNotNull(controller.Umbraco); - } - + [Test] public void Can_Lookup_Content() { var publishedSnapshot = new Mock(); publishedSnapshot.Setup(x => x.Members).Returns(Mock.Of()); - var contentCache = new Mock(); var content = new Mock(); content.Setup(x => x.Id).Returns(2); - contentCache.Setup(x => x.GetById(It.IsAny())).Returns(content.Object); - var mediaCache = new Mock(); - publishedSnapshot.Setup(x => x.Content).Returns(contentCache.Object); - publishedSnapshot.Setup(x => x.Media).Returns(mediaCache.Object); var publishedSnapshotService = new Mock(); - publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot.Object); var globalSettings = TestObjects.GetGlobalSettings(); var umbracoContext = UmbracoContext.EnsureContext( @@ -128,17 +101,17 @@ namespace Umbraco.Tests.Web.Mvc var helper = new UmbracoHelper( umbracoContext, - Mock.Of(), Mock.Of(), - Mock.Of(), + Mock.Of(), Mock.Of(), - new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()), - ServiceContext.CreatePartial()); + Mock.Of(query => query.Content(2) == content.Object), + new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of())); var ctrl = new TestSurfaceController(umbracoContext, helper); var result = ctrl.GetContent(2) as PublishedContentResult; Assert.IsNotNull(result); + Assert.IsNotNull(result.Content); Assert.AreEqual(2, result.Content.Id); } @@ -185,12 +158,8 @@ namespace Umbraco.Tests.Web.Mvc public class TestSurfaceController : SurfaceController { public TestSurfaceController(UmbracoContext ctx, UmbracoHelper helper = null) - : base(ctx, null, ServiceContext.CreatePartial(), Mock.Of(), null, null) + : base(ctx, null, ServiceContext.CreatePartial(), Mock.Of(), null, null, helper) { - if (helper != null) - { - Umbraco = helper; - } } public ActionResult Index() diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 390e23f1e9..207eba38c5 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -15598,9 +15598,9 @@ } }, "tinymce": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.9.0.tgz", - "integrity": "sha512-hrPeCLXY/sVCo3i64CTW8P5xbDiEI8Uii/vWpcmQWAMhex6GWWd2U+L8WIMj5tKKGdfcIQAJfpfQthc/92bcKw==" + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.9.2.tgz", + "integrity": "sha512-ZRoTGG4GAsOI73QPSNkabO7nkoYw9H6cglRB44W2mMkxSiqxYi8WJlgkUphk0fDqo6ZD6r3E+NSP4UHxF2lySg==" }, "tmp": { "version": "0.0.33", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 3f4314db0e..f24275420a 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -39,7 +39,7 @@ "npm": "^6.4.1", "signalr": "2.4.0", "spectrum-colorpicker": "1.8.0", - "tinymce": "4.9.0", + "tinymce": "4.9.2", "typeahead.js": "0.11.1", "underscore": "1.9.1" }, diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js index 0a8846f975..6472dd3d38 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js @@ -30,7 +30,8 @@ angular.module("umbraco.directives") promises.push(tinyMceService.getTinyMceEditorConfig({ htmlId: scope.uniqueId, stylesheets: scope.configuration ? scope.configuration.stylesheets : null, - toolbar: toolbar + toolbar: toolbar, + mode: scope.configuration.mode })); // pin toolbar to top of screen if we have focus and it scrolls off the screen diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index 0ba795626a..6b4554624f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -232,7 +232,11 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s body_class: 'umb-rte', //see http://archive.tinymce.com/wiki.php/Configuration:cache_suffix - cache_suffix: "?umb__rnd=" + Umbraco.Sys.ServerVariables.application.cacheBuster + cache_suffix: "?umb__rnd=" + Umbraco.Sys.ServerVariables.application.cacheBuster, + + //this is used to style the inline macro bits, sorry hard coding this form now since we don't have a standalone + //stylesheet to load in for this with only these styles (the color is @pinkLight) + content_style: ".mce-content-body .umb-macro-holder { border: 3px dotted #f5c1bc; padding: 7px; display: block; margin: 3px; } .umb-rte .mce-content-body .umb-macro-holder.loading {background: url(assets/img/loader.gif) right no-repeat; background-size: 18px; background-position-x: 99%;}" }; if (tinyMceConfig.customConfig) { @@ -458,7 +462,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s */ createInsertMacro: function (editor, callback) { - var createInsertMacroScope = this; + let self = this; + let activeMacroElement = null; //track an active macro element /** Adds custom rules for the macro plugin and custom serialization */ editor.on('preInit', function (args) { @@ -474,6 +479,16 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s } }); + }); + + /** when the contents load we need to find any macros declared and load in their content */ + editor.on("SetContent", function (o) { + + //get all macro divs and load their content + $(editor.dom.select(".umb-macro-holder.mceNonEditable")).each(function () { + self.loadMacroContent($(this), null); + }); + }); /** @@ -501,203 +516,26 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s tooltip: 'Insert macro', onPostRender: function () { - var ctrl = this; - var isOnMacroElement = false; - + let ctrl = this; + /** - if the selection comes from a different element that is not the macro's - we need to check if the selection includes part of the macro, if so we'll force the selection - to clear to the next element since if people can select part of the macro markup they can then modify it. - */ - function handleSelectionChange() { - - if (!editor.selection.isCollapsed()) { - var endSelection = tinymce.activeEditor.selection.getEnd(); - var startSelection = tinymce.activeEditor.selection.getStart(); - //don't proceed if it's an entire element selected - if (endSelection !== startSelection) { - - //if the end selection is a macro then move the cursor - //NOTE: we don't have to handle when the selection comes from a previous parent because - // that is automatically taken care of with the normal onNodeChanged logic since the - // evt.element will be the macro once it becomes part of the selection. - var $testForMacro = $(endSelection).closest(".umb-macro-holder"); - if ($testForMacro.length > 0) { - - //it came from before so move after, if there is no after then select ourselves - var next = $testForMacro.next(); - if (next.length > 0) { - editor.selection.setCursorLocation($testForMacro.next().get(0)); - } else { - selectMacroElement($testForMacro.get(0)); - } - - } - } - } - } - - /** helper method to select the macro element */ - function selectMacroElement(macroElement) { - - // move selection to top element to ensure we can't edit this - editor.selection.select(macroElement); - - // check if the current selection *is* the element (ie bug) - var currentSelection = editor.selection.getStart(); - if (tinymce.isIE) { - if (!editor.dom.hasClass(currentSelection, 'umb-macro-holder')) { - while (!editor.dom.hasClass(currentSelection, 'umb-macro-holder') && currentSelection.parentNode) { - currentSelection = currentSelection.parentNode; - } - editor.selection.select(currentSelection); - } - } - } - - /** - * Add a node change handler, test if we're editing a macro and select the whole thing, then set our isOnMacroElement flag. - * If we change the selection inside this method, then we end up in an infinite loop, so we have to remove ourselves - * from the event listener before changing selection, however, it seems that putting a break point in this method - * will always cause an 'infinite' loop as the caret keeps changing. - * - * TODO: I don't think we need this anymore with recent tinymce fixes: https://www.tiny.cloud/docs/plugins/noneditable/ + * Check if the macro is currently selected and toggle the menu button */ function onNodeChanged(evt) { //set our macro button active when on a node of class umb-macro-holder - var $macroElement = $(evt.element).closest(".umb-macro-holder"); - - handleSelectionChange(); - - //set the button active - ctrl.active($macroElement.length !== 0); - - if ($macroElement.length > 0) { - var macroElement = $macroElement.get(0); - - //remove the event listener before re-selecting - editor.off('NodeChange', onNodeChanged); - - selectMacroElement(macroElement); - - //set the flag - isOnMacroElement = true; - - //re-add the event listener - editor.on('NodeChange', onNodeChanged); - } else { - isOnMacroElement = false; - } + activeMacroElement = getRealMacroElem(evt.element); + //set the button active/inactive + ctrl.active(activeMacroElement !== null); } - /** when the contents load we need to find any macros declared and load in their content */ - editor.on("LoadContent", function (o) { - - //get all macro divs and load their content - $(editor.dom.select(".umb-macro-holder.mceNonEditable")).each(function () { - createInsertMacroScope.loadMacroContent($(this), null); - }); - - }); - - /** - * This prevents any other commands from executing when the current element is the macro so the content cannot be edited - * - * TODO: I don't think we need this anymore with recent tinymce fixes: https://www.tiny.cloud/docs/plugins/noneditable/ - */ - editor.on('BeforeExecCommand', function (o) { - if (isOnMacroElement) { - if (o.preventDefault) { - o.preventDefault(); - } - if (o.stopImmediatePropagation) { - o.stopImmediatePropagation(); - } - return; - } - }); - - /** - * This double checks and ensures you can't paste content into the rendered macro - * - * TODO: I don't think we need this anymore with recent tinymce fixes: https://www.tiny.cloud/docs/plugins/noneditable/ - */ - editor.on("Paste", function (o) { - if (isOnMacroElement) { - if (o.preventDefault) { - o.preventDefault(); - } - if (o.stopImmediatePropagation) { - o.stopImmediatePropagation(); - } - return; - } - }); + //NOTE: This could be another way to deal with the active/inactive state + //editor.on('ObjectSelected', function (e) {}); //set onNodeChanged event listener editor.on('NodeChange', onNodeChanged); - /** - * Listen for the keydown in the editor, we'll check if we are currently on a macro element, if so - * we'll check if the key down is a supported key which requires an action, otherwise we ignore the request - * so the macro cannot be edited. - * - * TODO: I don't think we need this anymore with recent tinymce fixes: https://www.tiny.cloud/docs/plugins/noneditable/ - */ - editor.on('KeyDown', function (e) { - if (isOnMacroElement) { - var macroElement = editor.selection.getNode(); - - //get the 'real' element (either p or the real one) - macroElement = getRealMacroElem(macroElement); - - //prevent editing - e.preventDefault(); - e.stopPropagation(); - - var moveSibling = function (element, isNext) { - var $e = $(element); - var $sibling = isNext ? $e.next() : $e.prev(); - if ($sibling.length > 0) { - editor.selection.select($sibling.get(0)); - editor.selection.collapse(true); - } else { - //if we're moving previous and there is no sibling, then lets recurse and just select the next one - if (!isNext) { - moveSibling(element, true); - return; - } - - //if there is no sibling we'll generate a new p at the end and select it - editor.setContent(editor.getContent() + "

 

"); - editor.selection.select($(editor.dom.getRoot()).children().last().get(0)); - editor.selection.collapse(true); - - } - }; - - //supported keys to move to the next or prev element (13-enter, 27-esc, 38-up, 40-down, 39-right, 37-left) - //supported keys to remove the macro (8-backspace, 46-delete) - // TODO: Should we make the enter key insert a line break before or leave it as moving to the next element? - if ($.inArray(e.keyCode, [13, 40, 39]) !== -1) { - //move to next element - moveSibling(macroElement, true); - } else if ($.inArray(e.keyCode, [27, 38, 37]) !== -1) { - //move to prev element - moveSibling(macroElement, false); - } else if ($.inArray(e.keyCode, [8, 46]) !== -1) { - //delete macro element - - //move first, then delete - moveSibling(macroElement, false); - editor.dom.remove(macroElement); - } - return; - } - }); - }, /** The insert macro button click event handler */ @@ -710,11 +548,9 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s //when we click we could have a macro already selected and in that case we'll want to edit the current parameters //so we'll need to extract them and submit them to the dialog. - var macroElement = editor.selection.getNode(); - macroElement = getRealMacroElem(macroElement); - if (macroElement) { + if (activeMacroElement) { //we have a macro selected so we'll need to parse it's alias and parameters - var contents = $(macroElement).contents(); + var contents = $(activeMacroElement).contents(); var comment = _.find(contents, function (item) { return item.nodeType === 8; }); @@ -724,7 +560,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s var syntax = comment.textContent.trim(); var parsed = macroService.parseMacroSyntax(syntax); dialogData = { - macroData: parsed + macroData: parsed, + activeMacroElement: activeMacroElement //pass the active element along so we can retrieve it later }; } @@ -737,7 +574,11 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s }); }, - insertMacroInEditor: function (editor, macroObject) { + insertMacroInEditor: function (editor, macroObject, activeMacroElement) { + + //Important note: the TinyMce plugin "noneditable" is used here so that the macro cannot be edited, + // for this to work the mceNonEditable class needs to come last and we also need to use the attribute contenteditable = false + // (even though all the docs and examples say that is not necessary) //put the macro syntax in comments, we will parse this out on the server side to be used //for persisting. @@ -746,11 +587,18 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s var uniqueId = "umb-macro-" + editor.dom.uniqueId(); var macroDiv = editor.dom.create('div', { - 'class': 'umb-macro-holder ' + macroObject.macroAlias + ' mceNonEditable ' + uniqueId + 'class': 'umb-macro-holder ' + macroObject.macroAlias + " " + uniqueId + ' mceNonEditable', + 'contenteditable': 'false' }, macroSyntaxComment + 'Macro alias: ' + macroObject.macroAlias + ''); - editor.selection.setNode(macroDiv); + //if there's an activeMacroElement then replace it, otherwise set the contents of the selected node + if (activeMacroElement) { + activeMacroElement.replaceWith(macroDiv); //directly replaces the html node + } + else { + editor.selection.setNode(macroDiv); + } var $macroDiv = $(editor.dom.select("div.umb-macro-holder." + uniqueId)); @@ -1167,10 +1015,15 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s pinToolbar : function (editor) { + //we can't pin the toolbar if this doesn't exist (i.e. when in distraction free mode) + if (!editor.editorContainer) { + return; + } + var tinyMce = $(editor.editorContainer); var toolbar = tinyMce.find(".mce-toolbar"); var toolbarHeight = toolbar.height(); - var tinyMceRect = tinyMce[0].getBoundingClientRect(); + var tinyMceRect = editor.editorContainer.getBoundingClientRect(); var tinyMceTop = tinyMceRect.top; var tinyMceBottom = tinyMceRect.bottom; var tinyMceWidth = tinyMceRect.width; @@ -1288,7 +1141,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s } }); - var self = this; + let self = this; //create link picker self.createLinkPicker(args.editor, function (currentTarget, anchorElement) { @@ -1346,7 +1199,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s dialogData: dialogData, submit: function (model) { var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine); - self.insertMacroInEditor(args.editor, macroObject); + self.insertMacroInEditor(args.editor, macroObject, dialogData.activeMacroElement); editorService.close(); }, close: function () { diff --git a/src/Umbraco.Web.UI.Client/src/less/rte.less b/src/Umbraco.Web.UI.Client/src/less/rte.less index 9aae9840d1..ba8d02c1e1 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte.less @@ -25,13 +25,6 @@ padding:10px; } -/* loader for macro loading in tinymce*/ -.umb-rte .mce-content-body .umb-macro-holder.loading { - background: url(img/loader.gif) right no-repeat; - background-size: 18px; - background-position-x: 99%; -} - .umb-rte .mce-container { box-sizing: border-box; } diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index b394684f56..d4fe51b3f1 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -94,6 +94,9 @@ namespace Umbraco.Web.Composing public static UmbracoContext UmbracoContext => UmbracoContextAccessor.UmbracoContext; + public static UmbracoHelper UmbracoHelper + => Factory.GetInstance(); + public static DistributedCache DistributedCache => Factory.GetInstance(); diff --git a/src/Umbraco.Web/Controllers/TagsController.cs b/src/Umbraco.Web/Controllers/TagsController.cs index 29518a594d..5dca84eee1 100644 --- a/src/Umbraco.Web/Controllers/TagsController.cs +++ b/src/Umbraco.Web/Controllers/TagsController.cs @@ -20,18 +20,13 @@ namespace Umbraco.Web.Controllers // TODO: This controller should be moved to a more suitable place. public class TagsController : UmbracoApiController { - /// - /// Initializes a new instance of the with auto dependencies. - /// public TagsController() - { } + { + } - /// - /// Initializes a new instance of the with all its dependencies. - /// - public TagsController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) - { } + public TagsController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } /// /// Get every tag stored in the database (with optional group) diff --git a/src/Umbraco.Web/Controllers/UmbLoginController.cs b/src/Umbraco.Web/Controllers/UmbLoginController.cs index 97444dbc6a..ea526f47a9 100644 --- a/src/Umbraco.Web/Controllers/UmbLoginController.cs +++ b/src/Umbraco.Web/Controllers/UmbLoginController.cs @@ -12,11 +12,12 @@ namespace Umbraco.Web.Controllers public class UmbLoginController : SurfaceController { public UmbLoginController() - { } + { + } - public UmbLoginController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger) - : base(umbracoContext, databaseFactory, services, appCaches, logger, profilingLogger) - { } + public UmbLoginController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) : base(umbracoContext, databaseFactory, services, appCaches, logger, profilingLogger, umbracoHelper) + { + } [HttpPost] [ValidateAntiForgeryToken] diff --git a/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs b/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs index fddefbe4a9..e63f15b9c9 100644 --- a/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs +++ b/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs @@ -14,11 +14,12 @@ namespace Umbraco.Web.Controllers public class UmbLoginStatusController : SurfaceController { public UmbLoginStatusController() - { } + { + } - public UmbLoginStatusController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger) - : base(umbracoContext, databaseFactory, services, appCaches, logger, profilingLogger) - { } + public UmbLoginStatusController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) : base(umbracoContext, databaseFactory, services, appCaches, logger, profilingLogger, umbracoHelper) + { + } [HttpPost] [ValidateAntiForgeryToken] diff --git a/src/Umbraco.Web/Controllers/UmbProfileController.cs b/src/Umbraco.Web/Controllers/UmbProfileController.cs index f22192a1cc..d30f08040e 100644 --- a/src/Umbraco.Web/Controllers/UmbProfileController.cs +++ b/src/Umbraco.Web/Controllers/UmbProfileController.cs @@ -17,8 +17,8 @@ namespace Umbraco.Web.Controllers public UmbProfileController() { } - public UmbProfileController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger) - : base(umbracoContext, databaseFactory, services, appCaches, logger, profilingLogger) + public UmbProfileController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) + : base(umbracoContext, databaseFactory, services, appCaches, logger, profilingLogger, umbracoHelper) { } [HttpPost] diff --git a/src/Umbraco.Web/Controllers/UmbRegisterController.cs b/src/Umbraco.Web/Controllers/UmbRegisterController.cs index 726797c26a..a60b5cffdb 100644 --- a/src/Umbraco.Web/Controllers/UmbRegisterController.cs +++ b/src/Umbraco.Web/Controllers/UmbRegisterController.cs @@ -14,11 +14,12 @@ namespace Umbraco.Web.Controllers public class UmbRegisterController : SurfaceController { public UmbRegisterController() - { } + { + } - public UmbRegisterController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger) - : base(umbracoContext, databaseFactory, services, appCaches, logger, profilingLogger) - { } + public UmbRegisterController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) : base(umbracoContext, databaseFactory, services, appCaches, logger, profilingLogger, umbracoHelper) + { + } [HttpPost] [ValidateAntiForgeryToken] diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs index 5804a00a79..f5652cc5fb 100644 --- a/src/Umbraco.Web/Editors/AuthenticationController.cs +++ b/src/Umbraco.Web/Editors/AuthenticationController.cs @@ -43,18 +43,9 @@ namespace Umbraco.Web.Editors private BackOfficeUserManager _userManager; private BackOfficeSignInManager _signInManager; - /// - /// Initializes a new instance of the new class with auto dependencies. - /// - public AuthenticationController() - { } - - /// - /// Initializes a new instance of the class with all its dependencies. - /// - public AuthenticationController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) - { } + public AuthenticationController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } protected BackOfficeUserManager UserManager => _userManager ?? (_userManager = TryGetOwinContext().Result.GetBackOfficeUserManager()); diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 462b5bbc9c..91a3f6778b 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -49,8 +49,8 @@ namespace Umbraco.Web.Editors private const string TokenPasswordResetCode = "PasswordResetCode"; private static readonly string[] TempDataTokenNames = { TokenExternalSignInError, TokenPasswordResetCode }; - public BackOfficeController(ManifestParser manifestParser, UmbracoFeatures features, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, IRuntimeState runtimeState) - : base(globalSettings, umbracoContext, services, appCaches, logger, profilingLogger) + public BackOfficeController(ManifestParser manifestParser, UmbracoFeatures features, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, services, appCaches, profilingLogger, umbracoHelper) { _manifestParser = manifestParser; _features = features; diff --git a/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs b/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs index 66e7a4734a..d83c5c8fb6 100644 --- a/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs @@ -1,4 +1,10 @@ -using Umbraco.Web.WebApi; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; +using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Editors @@ -11,5 +17,9 @@ namespace Umbraco.Web.Editors [AppendCurrentEventMessages] [PrefixlessBodyModelValidator] public abstract class BackOfficeNotificationsController : UmbracoAuthorizedJsonController - { } + { + protected BackOfficeNotificationsController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } + } } diff --git a/src/Umbraco.Web/Editors/CodeFileController.cs b/src/Umbraco.Web/Editors/CodeFileController.cs index 719f7521cb..4a29530068 100644 --- a/src/Umbraco.Web/Editors/CodeFileController.cs +++ b/src/Umbraco.Web/Editors/CodeFileController.cs @@ -8,8 +8,12 @@ using System.Net.Http; using System.Web.Http; using ClientDependency.Core; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings.Css; using Umbraco.Web.Composing; @@ -30,6 +34,9 @@ namespace Umbraco.Web.Editors [UmbracoApplicationAuthorize(Core.Constants.Applications.Settings)] public class CodeFileController : BackOfficeNotificationsController { + public CodeFileController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } /// /// Used to create a brand new file diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index ee056e38f0..b429c7d9bd 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -11,6 +11,8 @@ using System.Web.Http.ModelBinding; using System.Web.Security; using AutoMapper; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -33,6 +35,7 @@ using Umbraco.Web.ContentApps; using Umbraco.Web.Editors.Binders; using Umbraco.Web.Editors.Filters; using Umbraco.Core.Models.Entities; +using Umbraco.Core.Persistence; using Umbraco.Core.Security; namespace Umbraco.Web.Editors @@ -54,7 +57,7 @@ namespace Umbraco.Web.Editors public object Domains { get; private set; } - public ContentController(PropertyEditorCollection propertyEditors) + public ContentController(PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors)); _allLangs = new Lazy>(() => Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.IsoCode, x => x, StringComparer.InvariantCultureIgnoreCase)); diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs index d7c4d4f7f7..2a4d38d6f4 100644 --- a/src/Umbraco.Web/Editors/ContentControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs @@ -4,9 +4,12 @@ using System.Net; using System.Net.Http; using System.Web.Http; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; +using Umbraco.Core.Persistence; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web.Composing; @@ -23,6 +26,10 @@ namespace Umbraco.Web.Editors [JsonDateTimeFormatAttribute] public abstract class ContentControllerBase : BackOfficeNotificationsController { + protected ContentControllerBase(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } + protected HttpResponseMessage HandleContentNotFound(object id, bool throwException = true) { ModelState.AddModelError("id", $"content with id: {id} was not found"); diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 0aa7b75143..1f5feb8ad9 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -54,8 +54,8 @@ namespace Umbraco.Web.Editors UmbracoContext umbracoContext, ISqlContext sqlContext, PropertyEditorCollection propertyEditors, ServiceContext services, AppCaches appCaches, - IProfilingLogger logger, IRuntimeState runtimeState) - : base(cultureDictionaryFactory, globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) + IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) + : base(cultureDictionaryFactory, globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _serializer = serializer; _propertyEditors = propertyEditors; diff --git a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs index fe91f9fff6..b3baaf3e03 100644 --- a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs @@ -33,8 +33,8 @@ namespace Umbraco.Web.Editors private readonly ICultureDictionaryFactory _cultureDictionaryFactory; private ICultureDictionary _cultureDictionary; - protected ContentTypeControllerBase(ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) + protected ContentTypeControllerBase(ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _cultureDictionaryFactory = cultureDictionaryFactory; } diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index 8ae59b974c..9a9ab72845 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -31,18 +31,12 @@ namespace Umbraco.Web.Editors public class DashboardController : UmbracoApiController { private readonly IDashboardService _dashboardService; - - /// - /// Initializes a new instance of the with auto dependencies. - /// - public DashboardController() - { } - + /// /// Initializes a new instance of the with all its dependencies. /// - public DashboardController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, IDashboardService dashboardService) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) + public DashboardController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, IDashboardService dashboardService, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _dashboardService = dashboardService; } diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 4446373cd3..117bc33cc4 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -16,8 +16,11 @@ using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; using System.Net.Http; using System.Text; +using Umbraco.Core.Cache; using Umbraco.Web.Composing; using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; namespace Umbraco.Web.Editors { @@ -36,7 +39,7 @@ namespace Umbraco.Web.Editors { private readonly PropertyEditorCollection _propertyEditors; - public DataTypeController(PropertyEditorCollection propertyEditors) + public DataTypeController(PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _propertyEditors = propertyEditors; } diff --git a/src/Umbraco.Web/Editors/DictionaryController.cs b/src/Umbraco.Web/Editors/DictionaryController.cs index 484dd47a4e..186b3ac4a4 100644 --- a/src/Umbraco.Web/Editors/DictionaryController.cs +++ b/src/Umbraco.Web/Editors/DictionaryController.cs @@ -5,7 +5,13 @@ using System.Net; using System.Net.Http; using System.Web.Http; using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; @@ -28,6 +34,10 @@ namespace Umbraco.Web.Editors [EnableOverrideAuthorization] public class DictionaryController : BackOfficeNotificationsController { + public DictionaryController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } + /// /// Deletes a data type with a given ID /// diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 915b73e39a..55604e0eb9 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -47,8 +47,8 @@ namespace Umbraco.Web.Editors private readonly SearchableTreeCollection _searchableTreeCollection; public EntityController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - ITreeService treeService, SearchableTreeCollection searchableTreeCollection, UmbracoTreeSearcher treeSearcher) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) + ITreeService treeService, UmbracoHelper umbracoHelper, SearchableTreeCollection searchableTreeCollection, UmbracoTreeSearcher treeSearcher) + : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _treeService = treeService; _searchableTreeCollection = searchableTreeCollection; @@ -108,6 +108,8 @@ namespace Umbraco.Web.Editors if (string.IsNullOrEmpty(query)) return Enumerable.Empty(); + //TODO: This uses the internal UmbracoTreeSearcher, this instead should delgate to the ISearchableTree implementation for the type + return ExamineSearch(query, type, searchFrom); } @@ -451,6 +453,9 @@ namespace Umbraco.Web.Editors //the EntityService cannot search members of a certain type, this is currently not supported and would require //quite a bit of plumbing to do in the Services/Repository, we'll revert to a paged search + //TODO: We should really fix this in the EntityService but if we don't we should allow the ISearchableTree for the members controller + // to be used for this search instead of the built in/internal searcher + var searchResult = _treeSearcher.ExamineSearch(filter ?? "", type, pageSize, pageNumber - 1, out long total, id); return new PagedResult(total, pageNumber, pageSize) diff --git a/src/Umbraco.Web/Editors/MacroRenderingController.cs b/src/Umbraco.Web/Editors/MacroRenderingController.cs index 65abde759a..3cd2e86dd8 100644 --- a/src/Umbraco.Web/Editors/MacroRenderingController.cs +++ b/src/Umbraco.Web/Editors/MacroRenderingController.cs @@ -32,9 +32,12 @@ namespace Umbraco.Web.Editors { private readonly IMacroService _macroService; private readonly IContentService _contentService; + private readonly IUmbracoComponentRenderer _componentRenderer; private readonly IVariationContextAccessor _variationContextAccessor; - public MacroRenderingController(IVariationContextAccessor variationContextAccessor, IMacroService macroService, IContentService contentService) + + public MacroRenderingController(IUmbracoComponentRenderer componentRenderer, IVariationContextAccessor variationContextAccessor, IMacroService macroService, IContentService contentService) { + _componentRenderer = componentRenderer; _variationContextAccessor = variationContextAccessor; _macroService = macroService; _contentService = contentService; @@ -99,21 +102,15 @@ namespace Umbraco.Web.Editors private HttpResponseMessage GetMacroResultAsHtml(string macroAlias, int pageId, IDictionary macroParams) { - // note - here we should be using the cache, provided that the preview content is in the cache... - - var doc = _contentService.GetById(pageId); - if (doc == null) - { - throw new HttpResponseException(HttpStatusCode.NotFound); - } - var m = _macroService.GetByAlias(macroAlias); if (m == null) throw new HttpResponseException(HttpStatusCode.NotFound); - var macro = new MacroModel(m); + + var publishedContent = UmbracoContext.ContentCache.GetById(true, pageId); //if it isn't supposed to be rendered in the editor then return an empty string - if (macro.RenderInEditor == false) + //currently we cannot render a macro if the page doesn't yet exist + if (pageId == -1 || publishedContent == null || !m.UseInEditor) { var response = Request.CreateResponse(); //need to create a specific content result formatted as HTML since this controller has been configured @@ -123,32 +120,24 @@ namespace Umbraco.Web.Editors return response; } - //because macro's are filled with insane legacy bits and pieces we need all sorts of weirdness to make them render. - //the 'easiest' way might be to create an IPublishedContent manually and populate the legacy 'page' object with that - //and then set the legacy parameters. // When rendering the macro in the backoffice the default setting would be to use the Culture of the logged in user. // Since a Macro might contain thing thats related to the culture of the "IPublishedContent" (ie Dictionary keys) we want // to set the current culture to the culture related to the content item. This is hacky but it works. - var publishedContent = UmbracoContext.ContentCache.GetById(doc.Id); - var culture = publishedContent?.GetCulture(); + + var culture = publishedContent.GetCulture(); + _variationContextAccessor.VariationContext = new VariationContext(); //must have an active variation context! if (culture != null) { Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture.Culture); + _variationContextAccessor.VariationContext = new VariationContext(Thread.CurrentThread.CurrentCulture.Name); } - var legacyPage = new global::Umbraco.Web.Macros.PublishedContentHashtableConverter(doc, _variationContextAccessor); - - UmbracoContext.HttpContext.Items["pageElements"] = legacyPage.Elements; - UmbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = null; - - var renderer = new UmbracoComponentRenderer(UmbracoContext); - var result = Request.CreateResponse(); //need to create a specific content result formatted as HTML since this controller has been configured //with only json formatters. result.Content = new StringContent( - renderer.RenderMacro(macro, macroParams, legacyPage).ToString(), + _componentRenderer.RenderMacro(pageId, m.Alias, macroParams).ToString(), Encoding.UTF8, "text/html"); return result; diff --git a/src/Umbraco.Web/Editors/MacrosController.cs b/src/Umbraco.Web/Editors/MacrosController.cs index 7fbcdbf98a..38ec0ffc1b 100644 --- a/src/Umbraco.Web/Editors/MacrosController.cs +++ b/src/Umbraco.Web/Editors/MacrosController.cs @@ -7,9 +7,12 @@ using System.Net; using System.Net.Http; using System.Web.Http; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Persistence; using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; @@ -29,9 +32,9 @@ namespace Umbraco.Web.Editors { private readonly IMacroService _macroService; - public MacrosController(IMacroService macroService) + public MacrosController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { - _macroService = macroService; + _macroService = Services.MacroService; } /// diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 5662680a8a..ee7f1cc9cd 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -21,7 +21,9 @@ using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using System.Linq; using System.Web.Http.Controllers; +using Umbraco.Core.Cache; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; using Umbraco.Core.Persistence.Querying; @@ -47,7 +49,7 @@ namespace Umbraco.Web.Editors [MediaControllerControllerConfiguration] public class MediaController : ContentControllerBase { - public MediaController(PropertyEditorCollection propertyEditors, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) + public MediaController(PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors)); _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index bb126ed1dd..d041db1862 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -37,8 +37,7 @@ namespace Umbraco.Web.Editors [MediaTypeControllerControllerConfiguration] public class MediaTypeController : ContentTypeControllerBase { - public MediaTypeController(ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState) - : base(cultureDictionaryFactory, globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) + public MediaTypeController(ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(cultureDictionaryFactory, globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { } diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index 338e38fbe6..5a11f6cda9 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -26,7 +26,10 @@ using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; using System.Collections.Generic; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; using Umbraco.Core.Models.ContentEditing; +using Umbraco.Core.Persistence; using Umbraco.Core.PropertyEditors; using Umbraco.Web.ContentApps; using Umbraco.Web.Editors.Binders; @@ -43,7 +46,7 @@ namespace Umbraco.Web.Editors [OutgoingNoHyphenGuidFormat] public class MemberController : ContentControllerBase { - public MemberController(PropertyEditorCollection propertyEditors) + public MemberController(PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors)); } diff --git a/src/Umbraco.Web/Editors/MemberTypeController.cs b/src/Umbraco.Web/Editors/MemberTypeController.cs index b200f17372..a1a9a97572 100644 --- a/src/Umbraco.Web/Editors/MemberTypeController.cs +++ b/src/Umbraco.Web/Editors/MemberTypeController.cs @@ -30,8 +30,7 @@ namespace Umbraco.Web.Editors [UmbracoTreeAuthorize(new string[] { Constants.Trees.MemberTypes, Constants.Trees.Members})] public class MemberTypeController : ContentTypeControllerBase { - public MemberTypeController(ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState) - : base(cultureDictionaryFactory, globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) + public MemberTypeController(ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(cultureDictionaryFactory, globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { } diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index e96665ebb5..63be647a12 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -36,8 +36,8 @@ namespace Umbraco.Web.Editors { public PackageInstallController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, - IProfilingLogger logger, IRuntimeState runtimeState) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) + IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { } diff --git a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs index aa2626ca3b..04a19ec437 100644 --- a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs +++ b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs @@ -35,7 +35,7 @@ namespace Umbraco.Web.Editors public IHttpActionResult GetEnableState() { var enabled = Current.Configs.Settings().WebRouting.DisableRedirectUrlTracking == false; - var userIsAdmin = Umbraco.UmbracoContext.Security.CurrentUser.IsAdmin(); + var userIsAdmin = UmbracoContext.Security.CurrentUser.IsAdmin(); return Ok(new { enabled, userIsAdmin }); } @@ -92,7 +92,7 @@ namespace Umbraco.Web.Editors [HttpPost] public IHttpActionResult ToggleUrlTracker(bool disable) { - var userIsAdmin = Umbraco.UmbracoContext.Security.CurrentUser.IsAdmin(); + var userIsAdmin = UmbracoContext.Security.CurrentUser.IsAdmin(); if (userIsAdmin == false) { var errorMessage = "User is not a member of the administrators group and so is not allowed to toggle the URL tracker"; diff --git a/src/Umbraco.Web/Editors/RelationTypeController.cs b/src/Umbraco.Web/Editors/RelationTypeController.cs index ef3def1889..7113e30909 100644 --- a/src/Umbraco.Web/Editors/RelationTypeController.cs +++ b/src/Umbraco.Web/Editors/RelationTypeController.cs @@ -5,7 +5,12 @@ using System.Net.Http; using System.Web.Http; using AutoMapper; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; @@ -22,6 +27,10 @@ namespace Umbraco.Web.Editors [EnableOverrideAuthorization] public class RelationTypeController : BackOfficeNotificationsController { + public RelationTypeController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } + /// /// Gets a relation type by ID. /// diff --git a/src/Umbraco.Web/Editors/SectionController.cs b/src/Umbraco.Web/Editors/SectionController.cs index f6973fcbb9..0a2f17cd15 100644 --- a/src/Umbraco.Web/Editors/SectionController.cs +++ b/src/Umbraco.Web/Editors/SectionController.cs @@ -27,8 +27,8 @@ namespace Umbraco.Web.Editors private readonly ITreeService _treeService; public SectionController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - IDashboardService dashboardService, ISectionService sectionService, ITreeService treeService) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) + IDashboardService dashboardService, ISectionService sectionService, ITreeService treeService, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _dashboardService = dashboardService; _sectionService = sectionService; @@ -43,7 +43,7 @@ namespace Umbraco.Web.Editors // this is a bit nasty since we'll be proxying via the app tree controller but we sort of have to do that // since tree's by nature are controllers and require request contextual data - var appTreeController = new ApplicationTreeController(GlobalSettings, UmbracoContext, SqlContext, Services, AppCaches, Logger, RuntimeState, _treeService) + var appTreeController = new ApplicationTreeController(GlobalSettings, UmbracoContext, SqlContext, Services, AppCaches, Logger, RuntimeState, _treeService, Umbraco) { ControllerContext = ControllerContext }; diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index f91d27ceae..17faf4349f 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -4,8 +4,14 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; @@ -17,6 +23,10 @@ namespace Umbraco.Web.Editors [UmbracoTreeAuthorize(Constants.Trees.Templates)] public class TemplateController : BackOfficeNotificationsController { + public TemplateController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } + /// /// Gets data type by alias /// diff --git a/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs b/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs index f76f62a99b..32e56965b3 100644 --- a/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs +++ b/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs @@ -20,24 +20,11 @@ namespace Umbraco.Web.Editors [AngularJsonOnlyConfiguration] public abstract class UmbracoAuthorizedJsonController : UmbracoAuthorizedApiController { - /// - /// Initializes a new instance of the with auto dependencies. - /// protected UmbracoAuthorizedJsonController() { } - /// - /// Initializes a new instance of the class with all its dependencies. - /// - /// - /// - /// - /// - /// - /// - /// - protected UmbracoAuthorizedJsonController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) + protected UmbracoAuthorizedJsonController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { } } diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index c31d005ea6..3789e22b49 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -17,10 +17,12 @@ using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; using Umbraco.Core.Models.Identity; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Security; @@ -43,6 +45,10 @@ namespace Umbraco.Web.Editors [IsCurrentUserModelFilter] public class UsersController : UmbracoAuthorizedJsonController { + public UsersController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } + /// /// Returns a list of the sizes of gravatar urls for the user or null if the gravatar server cannot be reached /// diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index c5d2a65cb9..44d0cc27ca 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -92,13 +92,13 @@ namespace Umbraco.Web { 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}-", UmbracoContext.Current.PageId); + cacheKey.AppendFormat("{0}-", UmbracoContext.Current.PublishedRequest?.PublishedContent?.Id ?? 0); } if (cacheByMember) { var helper = Current.Factory.GetInstance(); var currentMember = helper.GetCurrentMember(); - cacheKey.AppendFormat("m{0}-", currentMember == null ? 0 : currentMember.Id); + cacheKey.AppendFormat("m{0}-", currentMember?.Id ?? 0); } if (contextualKeyBuilder != null) { diff --git a/src/Umbraco.Web/IUmbracoComponentRenderer.cs b/src/Umbraco.Web/IUmbracoComponentRenderer.cs index dc91cacdc0..4dc9036e6b 100644 --- a/src/Umbraco.Web/IUmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/IUmbracoComponentRenderer.cs @@ -12,32 +12,35 @@ namespace Umbraco.Web /// /// Renders the template for the specified pageId and an optional altTemplateId /// - /// + /// /// If not specified, will use the template assigned to the node /// - IHtmlString RenderTemplate(int pageId, int? altTemplateId = null); + IHtmlString RenderTemplate(int contentId, int? altTemplateId = null); /// /// Renders the macro with the specified alias. /// + /// /// The alias. /// - IHtmlString RenderMacro(string alias); + IHtmlString RenderMacro(int contentId, string alias); /// /// Renders the macro with the specified alias, passing in the specified parameters. /// + /// /// The alias. /// The parameters. /// - IHtmlString RenderMacro(string alias, object parameters); + IHtmlString RenderMacro(int contentId, string alias, object parameters); /// /// Renders the macro with the specified alias, passing in the specified parameters. /// + /// /// The alias. /// The parameters. /// - IHtmlString RenderMacro(string alias, IDictionary parameters); + IHtmlString RenderMacro(int contentId, string alias, IDictionary parameters); } } diff --git a/src/Umbraco.Web/Macros/IMacroRenderer.cs b/src/Umbraco.Web/Macros/IMacroRenderer.cs new file mode 100644 index 0000000000..d858315403 --- /dev/null +++ b/src/Umbraco.Web/Macros/IMacroRenderer.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Web.Macros +{ + /// + /// Renders a macro + /// + public interface IMacroRenderer + { + MacroContent Render(string macroAlias, IPublishedContent content, IDictionary macroParams); + } +} diff --git a/src/Umbraco.Web/Macros/MacroModel.cs b/src/Umbraco.Web/Macros/MacroModel.cs index bea07cebc5..dd9a73b147 100644 --- a/src/Umbraco.Web/Macros/MacroModel.cs +++ b/src/Umbraco.Web/Macros/MacroModel.cs @@ -1,22 +1,25 @@ using System.Collections.Generic; -using System.Text.RegularExpressions; -using Umbraco.Core; -using Umbraco.Core.IO; using Umbraco.Core.Models; -using Umbraco.Core.Services.Implement; namespace Umbraco.Web.Macros { public class MacroModel { + /// + /// The Macro Id + /// public int Id { get; set; } + /// + /// The Macro Name + /// public string Name { get; set; } + /// + /// The Macro Alias + /// public string Alias { get; set; } - public string MacroControlIdentifier { get; set; } - public MacroTypes MacroType { get; set; } public string MacroSource { get; set; } diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs index c975c4a60b..9736aa283b 100755 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -3,45 +3,44 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Net; -using System.Net.Security; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; using System.Text; -using System.Text.RegularExpressions; -using System.Web; using System.Web.Caching; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Macros; using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; -using Umbraco.Web.Composing; namespace Umbraco.Web.Macros { - public class MacroRenderer + internal class MacroRenderer : IMacroRenderer { private readonly IProfilingLogger _plogger; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IContentSection _contentSection; + private readonly ILocalizedTextService _textService; + private readonly AppCaches _appCaches; + private readonly IMacroService _macroService; - // TODO: there are many more things that would need to be injected in here - - public MacroRenderer(IProfilingLogger plogger) + public MacroRenderer(IProfilingLogger plogger, IUmbracoContextAccessor umbracoContextAccessor, IContentSection contentSection, ILocalizedTextService textService, AppCaches appCaches, IMacroService macroService) { - _plogger = plogger; + _plogger = plogger ?? throw new ArgumentNullException(nameof(plogger)); + _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); + _contentSection = contentSection ?? throw new ArgumentNullException(nameof(contentSection)); + _textService = textService; + _appCaches = appCaches ?? throw new ArgumentNullException(nameof(appCaches)); + _macroService = macroService ?? throw new ArgumentNullException(nameof(macroService)); } - // probably can do better - just porting from v7 - public IList Exceptions { get; } = new List(); - #region MacroContent cache // gets this macro content cache identifier - private static string GetContentCacheIdentifier(MacroModel model, int pageId) + private string GetContentCacheIdentifier(MacroModel model, int pageId) { var id = new StringBuilder(); @@ -55,8 +54,9 @@ namespace Umbraco.Web.Macros { object key = 0; - if (HttpContext.Current.User.Identity.IsAuthenticated) + if (_umbracoContextAccessor.UmbracoContext.HttpContext?.User?.Identity?.IsAuthenticated ?? false) { + //ugh, membershipproviders :( var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); var member = Core.Security.MembershipProviderExtensions.GetCurrentUser(provider); key = member?.ProviderUserKey ?? 0; @@ -71,34 +71,19 @@ namespace Umbraco.Web.Macros return id.ToString(); } - private static string GenerateCacheKeyFromCode(string input) - { - if (string.IsNullOrEmpty(input)) throw new ArgumentNullException(nameof(input)); - - // step 1, calculate MD5 hash from input - var md5 = MD5.Create(); - var inputBytes = Encoding.ASCII.GetBytes(input); - var hash = md5.ComputeHash(inputBytes); - - // step 2, convert byte array to hex string - var sb = new StringBuilder(); - foreach (var h in hash) sb.Append(h.ToString("X2")); - return sb.ToString(); - } - // gets this macro content from the cache // ensuring that it is appropriate to use the cache - private static MacroContent GetMacroContentFromCache(MacroModel model) + private MacroContent GetMacroContentFromCache(MacroModel model) { // only if cache is enabled - if (UmbracoContext.Current.InPreviewMode || model.CacheDuration <= 0) return null; + if (_umbracoContextAccessor.UmbracoContext.InPreviewMode || model.CacheDuration <= 0) return null; - var cache = Current.AppCaches.RuntimeCache; + var cache = _appCaches.RuntimeCache; var macroContent = cache.GetCacheItem(CacheKeys.MacroContentCacheKey + model.CacheIdentifier); if (macroContent == null) return null; - Current.Logger.Debug("Macro content loaded from cache '{MacroCacheId}'", model.CacheIdentifier); + _plogger.Debug("Macro content loaded from cache '{MacroCacheId}'", model.CacheIdentifier); // ensure that the source has not changed // note: does not handle dependencies, and never has @@ -107,13 +92,13 @@ namespace Umbraco.Web.Macros { if (macroSource.Exists == false) { - Current.Logger.Debug("Macro source does not exist anymore, ignore cache."); + _plogger.Debug("Macro source does not exist anymore, ignore cache."); return null; } if (macroContent.Date < macroSource.LastWriteTime) { - Current.Logger.Debug("Macro source has changed, ignore cache."); + _plogger.Debug("Macro source has changed, ignore cache."); return null; } } @@ -126,10 +111,10 @@ namespace Umbraco.Web.Macros } // stores macro content into the cache - private static void AddMacroContentToCache(MacroModel model, MacroContent macroContent) + private void AddMacroContentToCache(MacroModel model, MacroContent macroContent) { // only if cache is enabled - if (UmbracoContext.Current.InPreviewMode || model.CacheDuration <= 0) return; + if (_umbracoContextAccessor.UmbracoContext.InPreviewMode || model.CacheDuration <= 0) return; // just make sure... if (macroContent == null) return; @@ -150,7 +135,7 @@ namespace Umbraco.Web.Macros // remember when we cache the content macroContent.Date = DateTime.Now; - var cache = Current.AppCaches.RuntimeCache; + var cache = _appCaches.RuntimeCache; cache.Insert( CacheKeys.MacroContentCacheKey + model.CacheIdentifier, () => macroContent, @@ -158,7 +143,7 @@ namespace Umbraco.Web.Macros priority: CacheItemPriority.NotRemovable ); - Current.Logger.Debug("Macro content saved to cache '{MacroCacheId}'", model.CacheIdentifier); + _plogger.Debug("Macro content saved to cache '{MacroCacheId}'", model.CacheIdentifier); } // gets the macro source file name @@ -183,7 +168,7 @@ namespace Umbraco.Web.Macros // gets the macro source file // null if macro is not file-based - internal static FileInfo GetMacroFile(MacroModel model) + private static FileInfo GetMacroFile(MacroModel model) { var filename = GetMacroFileName(model); if (filename == null) return null; @@ -195,47 +180,38 @@ namespace Umbraco.Web.Macros return file.Exists ? file : null; } - #endregion - - #region MacroModel properties - // updates the model properties values according to the attributes - private static void UpdateMacroModelProperties(MacroModel model, Hashtable attributes) + private static void UpdateMacroModelProperties(MacroModel model, IDictionary macroParams) { foreach (var prop in model.Properties) { var key = prop.Key.ToLowerInvariant(); - prop.Value = attributes.ContainsKey(key) - ? attributes[key]?.ToString() ?? string.Empty + prop.Value = macroParams.ContainsKey(key) + ? macroParams[key]?.ToString() ?? string.Empty : string.Empty; } - } - - // generates the model properties according to the attributes - public static void GenerateMacroModelPropertiesFromAttributes(MacroModel model, Hashtable attributes) - { - foreach (string key in attributes.Keys) - model.Properties.Add(new MacroPropertyModel(key, attributes[key].ToString())); - } - + } #endregion #region Render/Execute - // still, this is ugly. The macro should have a Content property - // referring to IPublishedContent we're rendering the macro against, - // this is all so convoluted ;-( - - public MacroContent Render(MacroModel macro, Hashtable pageElements, int pageId, Hashtable attributes) + public MacroContent Render(string macroAlias, IPublishedContent content, IDictionary macroParams) { - UpdateMacroModelProperties(macro, attributes); - return Render(macro, pageElements, pageId); + var m = _macroService.GetByAlias(macroAlias); + if (m == null) + throw new InvalidOperationException("No macro found by alias " + macroAlias); + + var page = new PublishedContentHashtableConverter(content); + + var macro = new MacroModel(m); + + UpdateMacroModelProperties(macro, macroParams); + return Render(macro, content, page.Elements); } - public MacroContent Render(MacroModel macro, Hashtable pageElements, int pageId) + private MacroContent Render(MacroModel macro, IPublishedContent content, IDictionary pageElements) { - // trigger MacroRendering event so that the model can be manipulated before rendering - OnMacroRendering(new MacroRenderingEventArgs(pageElements, pageId)); + if (content == null) throw new ArgumentNullException(nameof(content)); var macroInfo = $"Render Macro: {macro.Name}, type: {macro.MacroType}, cache: {macro.CacheDuration}"; using (_plogger.DebugDuration(macroInfo, "Rendered Macro.")) @@ -244,7 +220,7 @@ namespace Umbraco.Web.Macros foreach (var prop in macro.Properties) prop.Value = ParseAttribute(pageElements, prop.Value); - macro.CacheIdentifier = GetContentCacheIdentifier(macro, pageId); + macro.CacheIdentifier = GetContentCacheIdentifier(macro, content.Id); // get the macro from cache if it is there var macroContent = GetMacroContentFromCache(macro); @@ -258,7 +234,7 @@ namespace Umbraco.Web.Macros // this will take care of errors // it may throw, if we actually want to throw, so better not // catch anything here and let the exception be thrown - var attempt = ExecuteMacroOfType(macro); + var attempt = ExecuteMacroOfType(macro, content); // by convention ExecuteMacroByType must either throw or return a result // just check to avoid internal errors @@ -300,8 +276,6 @@ namespace Umbraco.Web.Macros } catch (Exception e) { - Exceptions.Add(e); - _plogger.Warn(e, "Failed {MsgIn}", msgIn); var macroErrorEventArgs = new MacroErrorEventArgs @@ -310,11 +284,9 @@ namespace Umbraco.Web.Macros Alias = macro.Alias, MacroSource = macro.MacroSource, Exception = e, - Behaviour = Current.Configs.Settings().Content.MacroErrorBehaviour + Behaviour = _contentSection.MacroErrorBehaviour }; - OnError(macroErrorEventArgs); - switch (macroErrorEventArgs.Behaviour) { case MacroErrorBehaviour.Inline: @@ -341,16 +313,17 @@ namespace Umbraco.Web.Macros /// Returns an attempt that is successful if the macro ran successfully. If the macro failed /// to run properly, the attempt fails, though it may contain a content. But for instance that content /// should not be cached. In that case the attempt may also contain an exception. - private Attempt ExecuteMacroOfType(MacroModel model) + private Attempt ExecuteMacroOfType(MacroModel model, IPublishedContent content) { + if (model == null) throw new ArgumentNullException(nameof(model)); + // ensure that we are running against a published node (ie available in XML) // that may not be the case if the macro is embedded in a RTE of an unpublished document - if (UmbracoContext.Current.PublishedRequest == null - || UmbracoContext.Current.PublishedRequest.HasPublishedContent == false) - return Attempt.Fail(new MacroContent { Text = "[macro]" }); + if (content == null) + return Attempt.Fail(new MacroContent { Text = "[macro failed (no content)]" }); - var textService = Current.Services.TextService; + var textService = _textService; switch (model.MacroType) { @@ -358,7 +331,7 @@ namespace Umbraco.Web.Macros return ExecuteMacroWithErrorWrapper(model, $"Executing PartialView: MacroSource=\"{model.MacroSource}\".", "Executed PartialView.", - () => ExecutePartialView(model), + () => ExecutePartialView(model, content), () => textService.Localize("errors/macroErrorLoadingPartialView", new[] { model.MacroSource })); default: @@ -370,21 +343,6 @@ namespace Umbraco.Web.Macros } } - // raised when a macro triggers an error - public static event TypedEventHandler Error; - - protected void OnError(MacroErrorEventArgs e) - { - Error?.Invoke(this, e); - } - - // raised before the macro renders, allowing devs to modify it - public static event TypedEventHandler MacroRendering; - - protected void OnMacroRendering(MacroRenderingEventArgs e) - { - MacroRendering?.Invoke(this, e); - } #endregion @@ -394,10 +352,9 @@ namespace Umbraco.Web.Macros /// Renders a PartialView Macro. /// /// The text output of the macro execution. - private static MacroContent ExecutePartialView(MacroModel macro) + private MacroContent ExecutePartialView(MacroModel macro, IPublishedContent content) { var engine = new PartialViewMacroEngine(); - var content = UmbracoContext.Current.PublishedRequest.PublishedContent; return engine.Execute(macro, content); } @@ -407,8 +364,9 @@ namespace Umbraco.Web.Macros // parses attribute value looking for [@requestKey], [%sessionKey], [#pageElement], [$recursiveValue] // supports fallbacks eg "[@requestKey],[%sessionKey],1234" - public static string ParseAttribute(IDictionary pageElements, string attributeValue) + private string ParseAttribute(IDictionary pageElements, string attributeValue) { + if (pageElements == null) throw new ArgumentNullException(nameof(pageElements)); // check for potential querystring/cookie variables attributeValue = attributeValue.Trim(); @@ -431,7 +389,7 @@ namespace Umbraco.Web.Macros return attributeValue; } - var context = HttpContext.Current; + var context = _umbracoContextAccessor.UmbracoContext.HttpContext; foreach (var token in tokens) { @@ -458,11 +416,9 @@ namespace Umbraco.Web.Macros attributeValue = context?.Request.GetCookieValue(name); break; case '#': - if (pageElements == null) pageElements = GetPageElements(); attributeValue = pageElements[name]?.ToString(); break; case '$': - if (pageElements == null) pageElements = GetPageElements(); attributeValue = pageElements[name]?.ToString(); if (string.IsNullOrEmpty(attributeValue)) attributeValue = ParseAttributeOnParents(pageElements, name); @@ -477,12 +433,13 @@ namespace Umbraco.Web.Macros return attributeValue; } - private static string ParseAttributeOnParents(IDictionary pageElements, string name) + private string ParseAttributeOnParents(IDictionary pageElements, string name) { + if (pageElements == null) throw new ArgumentNullException(nameof(pageElements)); // this was, and still is, an ugly piece of nonsense var value = string.Empty; - var cache = UmbracoContext.Current.ContentCache; // should be injected + var cache = _umbracoContextAccessor.UmbracoContext.ContentCache; var splitpath = (string[])pageElements["splitpath"]; for (var i = splitpath.Length - 1; i > 0; i--) // at 0 we have root (-1) @@ -495,195 +452,9 @@ namespace Umbraco.Web.Macros return value; } - - private static IDictionary GetPageElements() - { - IDictionary pageElements = null; - if (HttpContext.Current.Items["pageElements"] != null) - pageElements = (IDictionary)HttpContext.Current.Items["pageElements"]; - return pageElements; - } - - #endregion - - #region RTE macros - - public static string RenderMacroStartTag(Hashtable attributes, int pageId, Guid versionId) - { - var div = "
"; - - return div; - } - - private static string EncodeMacroAttribute(string attributeContents) - { - // replace line breaks - attributeContents = attributeContents.Replace("\n", "\\n").Replace("\r", "\\r"); - - // replace quotes - attributeContents = attributeContents.Replace("\"", """); - - // replace tag start/ends - attributeContents = attributeContents.Replace("<", "<").Replace(">", ">"); - - return attributeContents; - } - - public static string RenderMacroEndTag() - { - return "
"; - } - - private static readonly Regex HrefRegex = new Regex("href=\"([^\"]*)\"", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - - public static string GetRenderedMacro(int macroId, Hashtable elements, Hashtable attributes, int pageId, IMacroService macroService, IProfilingLogger plogger) - { - var m = macroService.GetById(macroId); - if (m == null) return string.Empty; - var model = new MacroModel(m); - - // get as text, will render the control if any - var renderer = new MacroRenderer(plogger); - var macroContent = renderer.Render(model, elements, pageId); - var text = macroContent.GetAsText(); - - // remove hrefs - text = HrefRegex.Replace(text, match => "href=\"javascript:void(0)\""); - - return text; - } - - public static string MacroContentByHttp(int pageId, Guid pageVersion, Hashtable attributes, IMacroService macroService) - { - // though... we only support FullTrust now? - if (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted) - return "Cannot render macro content in the rich text editor when the application is running in a Partial Trust environment"; - - var tempAlias = attributes["macroalias"]?.ToString() ?? attributes["macroAlias"].ToString(); - - var m = macroService.GetByAlias(tempAlias); - if (m == null) return string.Empty; - var macro = new MacroModel(m); - if (macro.RenderInEditor == false) - return ShowNoMacroContent(macro); - - var querystring = $"umbPageId={pageId}&umbVersionId={pageVersion}"; - var ide = attributes.GetEnumerator(); - while (ide.MoveNext()) - querystring += $"&umb_{ide.Key}={HttpContext.Current.Server.UrlEncode((ide.Value ?? String.Empty).ToString())}"; - - // create a new 'HttpWebRequest' object to the mentioned URL. - var useSsl = Current.Configs.Global().UseHttps; - var protocol = useSsl ? "https" : "http"; - var currentRequest = HttpContext.Current.Request; - var serverVars = currentRequest.ServerVariables; - var umbracoDir = IOHelper.ResolveUrl(SystemDirectories.Umbraco); - var url = $"{protocol}://{serverVars["SERVER_NAME"]}:{serverVars["SERVER_PORT"]}{umbracoDir}/macroResultWrapper.aspx?{querystring}"; - - var myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url); - - // allows for validation of SSL conversations (to bypass SSL errors in debug mode!) - ServicePointManager.ServerCertificateValidationCallback += ValidateRemoteCertificate; - - // propagate the user's context - // TODO: this is the worst thing ever. - // also will not work if people decide to put their own custom auth system in place. - var inCookie = currentRequest.Cookies[Current.Configs.Settings().Security.AuthCookieName]; - if (inCookie == null) throw new NullReferenceException("No auth cookie found"); - var cookie = new Cookie(inCookie.Name, inCookie.Value, inCookie.Path, serverVars["SERVER_NAME"]); - myHttpWebRequest.CookieContainer = new CookieContainer(); - myHttpWebRequest.CookieContainer.Add(cookie); - - // assign the response object of 'HttpWebRequest' to a 'HttpWebResponse' variable. - HttpWebResponse myHttpWebResponse = null; - var text = string.Empty; - try - { - myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse(); - if (myHttpWebResponse.StatusCode == HttpStatusCode.OK) - { - var streamResponse = myHttpWebResponse.GetResponseStream(); - if (streamResponse == null) - throw new Exception("Internal error, no response stream."); - var streamRead = new StreamReader(streamResponse); - var readBuff = new char[256]; - var count = streamRead.Read(readBuff, 0, 256); - while (count > 0) - { - var outputData = new string(readBuff, 0, count); - text += outputData; - count = streamRead.Read(readBuff, 0, 256); - } - - streamResponse.Close(); - streamRead.Close(); - - // find the content of a form - const string grabStart = ""; - const string grabEnd = ""; - - var grabStartPos = text.InvariantIndexOf(grabStart) + grabStart.Length; - var grabEndPos = text.InvariantIndexOf(grabEnd) - grabStartPos; - text = text.Substring(grabStartPos, grabEndPos); - } - else - { - text = ShowNoMacroContent(macro); - } - } - catch (Exception) - { - text = ShowNoMacroContent(macro); - } - finally - { - // release the HttpWebResponse Resource. - myHttpWebResponse?.Close(); - } - - return text.Replace("\n", string.Empty).Replace("\r", string.Empty); - } - - private static string ShowNoMacroContent(MacroModel model) - { - var name = HttpUtility.HtmlEncode(model.Name); // safe - return $"{name}
No macro content available for WYSIWYG editing
"; - } - - private static bool ValidateRemoteCertificate( - object sender, - X509Certificate certificate, - X509Chain chain, - SslPolicyErrors policyErrors - ) - { - // allow any old dodgy certificate in debug mode - return GlobalSettings.DebugMode || policyErrors == SslPolicyErrors.None; - } - + #endregion + } - public class MacroRenderingEventArgs : EventArgs - { - public MacroRenderingEventArgs(Hashtable pageElements, int pageId) - { - PageElements = pageElements; - PageId = pageId; - } - - public int PageId { get; } - - public Hashtable PageElements { get; } - } } diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index a871fef1b0..f40c8a7d98 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -30,14 +30,13 @@ namespace Umbraco.Web.Macros /// internal PublishedContentHashtableConverter(PublishedRequest frequest) { - if (!frequest.HasPublishedContent) - throw new ArgumentException("Document request has no node.", "frequest"); + throw new ArgumentException("Document request has no node.", nameof(frequest)); PopulatePageData(frequest.PublishedContent.Id, frequest.PublishedContent.Name, frequest.PublishedContent.ContentType.Id, frequest.PublishedContent.ContentType.Alias, frequest.PublishedContent.WriterName, frequest.PublishedContent.CreatorName, frequest.PublishedContent.CreateDate, frequest.PublishedContent.UpdateDate, - frequest.PublishedContent.Path, frequest.PublishedContent.Parent == null ? -1 : frequest.PublishedContent.Parent.Id); + frequest.PublishedContent.Path, frequest.PublishedContent.Parent?.Id ?? -1); if (frequest.HasTemplate) { @@ -54,12 +53,12 @@ namespace Umbraco.Web.Macros /// internal PublishedContentHashtableConverter(IPublishedContent doc) { - if (doc == null) throw new ArgumentNullException("doc"); + if (doc == null) throw new ArgumentNullException(nameof(doc)); PopulatePageData(doc.Id, doc.Name, doc.ContentType.Id, doc.ContentType.Alias, doc.WriterName, doc.CreatorName, doc.CreateDate, doc.UpdateDate, - doc.Path, doc.Parent == null ? -1 : doc.Parent.Id); + doc.Path, doc.Parent?.Id ?? -1); if (doc.TemplateId.HasValue) { @@ -132,6 +131,7 @@ namespace Umbraco.Web.Macros ///
public Hashtable Elements { get; } = new Hashtable(); + #region PublishedContent private class PagePublishedProperty : PublishedPropertyBase diff --git a/src/Umbraco.Web/Models/Mapping/RedirectUrlMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/RedirectUrlMapperProfile.cs index 53baa13379..5899c79b72 100644 --- a/src/Umbraco.Web/Models/Mapping/RedirectUrlMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/RedirectUrlMapperProfile.cs @@ -9,12 +9,14 @@ namespace Umbraco.Web.Models.Mapping internal class RedirectUrlMapperProfile : Profile { - public RedirectUrlMapperProfile() + public RedirectUrlMapperProfile(IUmbracoContextAccessor umbracoContextAccessor) { CreateMap() .ForMember(x => x.OriginalUrl, expression => expression.MapFrom(item => Current.UmbracoContext.UrlProvider.GetUrlFromRoute(item.ContentId, item.Url, item.Culture))) - .ForMember(x => x.DestinationUrl, expression => expression.MapFrom(item => item.ContentId > 0 ? new UmbracoHelper(Current.UmbracoContext, Current.Services).Url(item.ContentId, item.Culture) : "#")) + .ForMember(x => x.DestinationUrl, expression => expression.MapFrom(item => item.ContentId > 0 ? GetUrl(umbracoContextAccessor, item) : "#")) .ForMember(x => x.RedirectId, expression => expression.MapFrom(item => item.Key)); } + + private static string GetUrl(IUmbracoContextAccessor umbracoContextAccessor, IRedirectUrl item) => umbracoContextAccessor?.UmbracoContext?.UrlProvider?.GetUrl(item.Id, item.Culture); } } diff --git a/src/Umbraco.Web/Models/PartialViewMacroModel.cs b/src/Umbraco.Web/Models/PartialViewMacroModel.cs index ae4becc7cf..9964877cba 100644 --- a/src/Umbraco.Web/Models/PartialViewMacroModel.cs +++ b/src/Umbraco.Web/Models/PartialViewMacroModel.cs @@ -22,10 +22,10 @@ namespace Umbraco.Web.Models MacroId = macroId; } - public IPublishedContent Content { get; private set; } - public string MacroName { get; private set; } - public string MacroAlias { get; private set; } - public int MacroId { get; private set; } - public IDictionary MacroParameters { get; private set; } + public IPublishedContent Content { get; } + public string MacroName { get; } + public string MacroAlias { get; } + public int MacroId { get; } + public IDictionary MacroParameters { get; } } } diff --git a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs index 471cec6db7..c6fc1d907f 100644 --- a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs +++ b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs @@ -77,8 +77,7 @@ namespace Umbraco.Web.Mvc /// /// Exposes an UmbracoHelper /// - protected UmbracoHelper Umbraco => _helper - ?? (_helper = new UmbracoHelper(Current.UmbracoContext, Current.Services)); + protected UmbracoHelper Umbraco => _helper ?? (_helper = Current.Factory.GetInstance()); public override void OnActionExecuted(ActionExecutedContext filterContext) { diff --git a/src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs b/src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs index 287edd3bce..5f81ced3f0 100644 --- a/src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs +++ b/src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs @@ -1,10 +1,11 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Web; using System.Web.Mvc; using AuthorizeAttribute = System.Web.Mvc.AuthorizeAttribute; using Umbraco.Core; -using Umbraco.Web.Composing; +using Umbraco.Web.Security; +using Umbraco.Core.Composing; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Mvc { @@ -14,35 +15,6 @@ namespace Umbraco.Web.Mvc ///
public sealed class MemberAuthorizeAttribute : AuthorizeAttribute { - // see note in HttpInstallAuthorizeAttribute - private readonly UmbracoContext _umbracoContext; - - private UmbracoContext UmbracoContext => _umbracoContext ?? Current.UmbracoContext; - - /// - /// THIS SHOULD BE ONLY USED FOR UNIT TESTS - /// - /// - public MemberAuthorizeAttribute(UmbracoContext umbracoContext) - { - if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); - _umbracoContext = umbracoContext; - } - - public MemberAuthorizeAttribute() - { - - } - - /// - /// Flag for whether to allow all site visitors or just authenticated members - /// - /// - /// This is the same as applying the [AllowAnonymous] attribute - /// - [Obsolete("Use [AllowAnonymous] instead")] - public bool AllowAll { get; set; } - /// /// Comma delimited list of allowed member types /// @@ -70,17 +42,15 @@ namespace Umbraco.Web.Mvc var members = new List(); foreach (var s in AllowMembers.Split(',')) { - int id; - if (int.TryParse(s, out id)) + if (int.TryParse(s, out var id)) { members.Add(id); } } - return UmbracoContext.Security.IsMemberAuthorized(AllowAll, - AllowType.Split(','), - AllowGroup.Split(','), - members); + var helper = Current.Factory.GetInstance(); + return helper.IsMemberAuthorized(AllowType.Split(','), AllowGroup.Split(','), members); + } /// diff --git a/src/Umbraco.Web/Mvc/PluginController.cs b/src/Umbraco.Web/Mvc/PluginController.cs index e3f4b45ce6..8f113dc0a4 100644 --- a/src/Umbraco.Web/Mvc/PluginController.cs +++ b/src/Umbraco.Web/Mvc/PluginController.cs @@ -20,8 +20,6 @@ namespace Umbraco.Web.Mvc private static readonly ConcurrentDictionary MetadataStorage = new ConcurrentDictionary(); - private UmbracoHelper _umbracoHelper; - // for debugging purposes internal Guid InstanceId { get; } = Guid.NewGuid(); @@ -70,18 +68,7 @@ namespace Umbraco.Web.Mvc /// /// Gets the Umbraco helper. /// - public UmbracoHelper Umbraco - { - get - { - return _umbracoHelper - ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services)); - } - internal set // tests - { - _umbracoHelper = value; - } - } + public UmbracoHelper Umbraco { get; } /// /// Gets metadata for this instance. @@ -95,12 +82,13 @@ namespace Umbraco.Web.Mvc Current.Factory.GetInstance(), Current.Factory.GetInstance(), Current.Factory.GetInstance(), - Current.Factory.GetInstance() + Current.Factory.GetInstance(), + Current.Factory.GetInstance() ) { } - protected PluginController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger) + protected PluginController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) { UmbracoContext = umbracoContext; DatabaseFactory = databaseFactory; @@ -108,6 +96,7 @@ namespace Umbraco.Web.Mvc AppCaches = appCaches; Logger = logger; ProfilingLogger = profilingLogger; + Umbraco = umbracoHelper; } /// diff --git a/src/Umbraco.Web/Mvc/RenderMvcController.cs b/src/Umbraco.Web/Mvc/RenderMvcController.cs index e5ad2bb01e..e67aa3948e 100644 --- a/src/Umbraco.Web/Mvc/RenderMvcController.cs +++ b/src/Umbraco.Web/Mvc/RenderMvcController.cs @@ -23,8 +23,8 @@ namespace Umbraco.Web.Mvc ActionInvoker = new RenderActionInvoker(); } - public RenderMvcController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger) - : base(globalSettings, umbracoContext, services, appCaches, logger, profilingLogger) + public RenderMvcController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, services, appCaches, profilingLogger, umbracoHelper) { ActionInvoker = new RenderActionInvoker(); } @@ -32,7 +32,7 @@ namespace Umbraco.Web.Mvc /// /// Gets the Umbraco context. /// - public override UmbracoContext UmbracoContext => PublishedRequest.UmbracoContext; + public override UmbracoContext UmbracoContext => PublishedRequest.UmbracoContext; //TODO: Why? /// /// Gets the current content item. diff --git a/src/Umbraco.Web/Mvc/SurfaceController.cs b/src/Umbraco.Web/Mvc/SurfaceController.cs index 2bf73b7dd1..05c095940c 100644 --- a/src/Umbraco.Web/Mvc/SurfaceController.cs +++ b/src/Umbraco.Web/Mvc/SurfaceController.cs @@ -19,8 +19,8 @@ namespace Umbraco.Web.Mvc protected SurfaceController() { } - protected SurfaceController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger) - : base(umbracoContext, databaseFactory, services, appCaches, logger, profilingLogger) + protected SurfaceController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) + : base(umbracoContext, databaseFactory, services, appCaches, logger, profilingLogger, umbracoHelper) { } /// diff --git a/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs b/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs index 89364fe9cf..ae97f22dbe 100644 --- a/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs +++ b/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs @@ -18,10 +18,12 @@ namespace Umbraco.Web.Mvc public abstract class UmbracoAuthorizedController : UmbracoController { protected UmbracoAuthorizedController() - { } + { + } - protected UmbracoAuthorizedController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger) - : base(globalSettings, umbracoContext, services, appCaches, logger, profilingLogger) - { } + protected UmbracoAuthorizedController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, services, appCaches, profilingLogger, umbracoHelper) + { + } } } diff --git a/src/Umbraco.Web/Mvc/UmbracoController.cs b/src/Umbraco.Web/Mvc/UmbracoController.cs index 4d70263cb8..79987ab6b4 100644 --- a/src/Umbraco.Web/Mvc/UmbracoController.cs +++ b/src/Umbraco.Web/Mvc/UmbracoController.cs @@ -17,15 +17,13 @@ namespace Umbraco.Web.Mvc /// public abstract class UmbracoController : Controller { - private UmbracoHelper _umbracoHelper; - // for debugging purposes internal Guid InstanceId { get; } = Guid.NewGuid(); /// /// Gets or sets the Umbraco context. /// - public virtual IGlobalSettings GlobalSettings { get; set; } + public IGlobalSettings GlobalSettings { get; set; } /// /// Gets or sets the Umbraco context. @@ -62,8 +60,7 @@ namespace Umbraco.Web.Mvc /// /// Gets the Umbraco helper. /// - public UmbracoHelper Umbraco => _umbracoHelper - ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services)); + public UmbracoHelper Umbraco { get; } /// /// Gets the web security helper. @@ -76,20 +73,21 @@ namespace Umbraco.Web.Mvc Current.Factory.GetInstance(), Current.Factory.GetInstance(), Current.Factory.GetInstance(), - Current.Factory.GetInstance(), - Current.Factory.GetInstance() + Current.Factory.GetInstance(), + Current.Factory.GetInstance() ) { } - protected UmbracoController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger) + protected UmbracoController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) { GlobalSettings = globalSettings; UmbracoContext = umbracoContext; Services = services; AppCaches = appCaches; - Logger = logger; + Logger = profilingLogger; ProfilingLogger = profilingLogger; + Umbraco = umbracoHelper; } } } diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs index 748efbb27a..854b59787f 100644 --- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs +++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs @@ -7,6 +7,7 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; +using Umbraco.Core.Dictionary; using Umbraco.Core.IO; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; @@ -84,7 +85,7 @@ namespace Umbraco.Web.Mvc /// /// Gets the Umbraco helper. /// - public virtual UmbracoHelper Umbraco + public UmbracoHelper Umbraco { get { @@ -94,9 +95,12 @@ namespace Umbraco.Web.Mvc var content = model as IPublishedContent; if (content == null && model is IContentModel) content = ((IContentModel) model).Content; - _helper = content == null - ? new UmbracoHelper(UmbracoContext, Services) - : new UmbracoHelper(UmbracoContext, Services, content); + + _helper = Current.UmbracoHelper; + + if (content != null) + _helper.AssignedContentItem = content; + return _helper; } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs index 5a0fc87cb0..e6e66b79ff 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs @@ -20,12 +20,12 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters public class MacroContainerValueConverter : PropertyValueConverterBase { private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly ServiceContext _services; + private readonly IMacroRenderer _macroRenderer; - public MacroContainerValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services) + public MacroContainerValueConverter(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); - _services = services ?? throw new ArgumentNullException(nameof(services)); + _macroRenderer = macroRenderer ?? throw new ArgumentNullException(nameof(macroRenderer)); } public override bool IsConverter(PublishedPropertyType propertyType) @@ -48,14 +48,14 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { var sb = new StringBuilder(); - var umbracoHelper = new UmbracoHelper(umbracoContext, _services); MacroTagParser.ParseMacros( source, //callback for when text block is found textBlock => sb.Append(textBlock), //callback for when macro syntax is found - (macroAlias, macroAttributes) => sb.Append(umbracoHelper.RenderMacro( + (macroAlias, macroAttributes) => sb.Append(_macroRenderer.Render( macroAlias, + umbracoContext.PublishedRequest?.PublishedContent, //needs to be explicitly casted to Dictionary macroAttributes.ConvertTo(x => (string)x, x => x)).ToString())); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index 7a089b6f9e..e636369c74 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters public class RteMacroRenderingValueConverter : TinyMceValueConverter { private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly ServiceContext _services; + private readonly IMacroRenderer _macroRenderer; public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) { @@ -30,10 +30,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return PropertyCacheLevel.Snapshot; } - public RteMacroRenderingValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services) + public RteMacroRenderingValueConverter(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer) { _umbracoContextAccessor = umbracoContextAccessor; - _services = services; + _macroRenderer = macroRenderer; } // NOT thread-safe over a request because it modifies the @@ -47,14 +47,14 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { var sb = new StringBuilder(); - var umbracoHelper = new UmbracoHelper(umbracoContext, _services); MacroTagParser.ParseMacros( source, //callback for when text block is found textBlock => sb.Append(textBlock), //callback for when macro syntax is found - (macroAlias, macroAttributes) => sb.Append(umbracoHelper.RenderMacro( + (macroAlias, macroAttributes) => sb.Append(_macroRenderer.Render( macroAlias, + umbracoContext.PublishedRequest?.PublishedContent, //needs to be explicitly casted to Dictionary macroAttributes.ConvertTo(x => (string)x, x => x)).ToString())); diff --git a/src/Umbraco.Web/Routing/PublishedRequest.cs b/src/Umbraco.Web/Routing/PublishedRequest.cs index 52f5820744..d2a930fa96 100644 --- a/src/Umbraco.Web/Routing/PublishedRequest.cs +++ b/src/Umbraco.Web/Routing/PublishedRequest.cs @@ -279,7 +279,6 @@ namespace Umbraco.Web.Routing /// /// Resets the template. /// - /// The RenderingEngine becomes unknown. public void ResetTemplate() { EnsureWriteable(); diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index 7dd1ec9f1a..0ce6a9b768 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -197,9 +197,6 @@ namespace Umbraco.Web.Routing // handlers like default.aspx will want it and most macros currently need it frequest.LegacyContentHashTable = new PublishedContentHashtableConverter(frequest); - // used by many legacy objects - frequest.UmbracoContext.HttpContext.Items["pageElements"] = frequest.LegacyContentHashTable.Elements; - return true; } @@ -239,9 +236,7 @@ namespace Umbraco.Web.Routing // assign the legacy page back to the docrequest // handlers like default.aspx will want it and most macros currently need it request.LegacyContentHashTable = new PublishedContentHashtableConverter(request); - - // this is used by many legacy objects - request.UmbracoContext.HttpContext.Items["pageElements"] = request.LegacyContentHashTable.Elements; + } #endregion diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs index b81f2db486..e1a840c17b 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs @@ -23,6 +23,7 @@ using Umbraco.Web.Dictionary; using Umbraco.Web.Editors; using Umbraco.Web.Features; using Umbraco.Web.HealthCheck; +using Umbraco.Web.Macros; using Umbraco.Web.Media.EmbedProviders; using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.Mvc; @@ -33,6 +34,7 @@ using Umbraco.Web.Security; using Umbraco.Web.Security.Providers; using Umbraco.Web.Services; using Umbraco.Web.SignalR; +using Umbraco.Web.Templates; using Umbraco.Web.Tour; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; @@ -86,8 +88,19 @@ namespace Umbraco.Web.Runtime // TODO: stop doing this composition.Register(factory => factory.GetInstance().UmbracoContext, Lifetime.Request); - // register the umbraco helper - composition.RegisterUnique(); + composition.Register(factory => + { + var umbCtx = factory.GetInstance(); + return new PublishedContentQuery(umbCtx.UmbracoContext.ContentCache, umbCtx.UmbracoContext.MediaCache, factory.GetInstance()); + }, Lifetime.Request); + composition.Register(Lifetime.Request); + + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + + // register the umbraco helper - this is Transient! very important! + composition.Register(); // register distributed cache composition.RegisterUnique(f => new DistributedCache()); diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index 2bb093a659..cd87d9dd37 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -23,16 +23,19 @@ namespace Umbraco.Web.Search public class UmbracoTreeSearcher { private readonly IExamineManager _examineManager; + private readonly UmbracoContext _umbracoContext; private readonly UmbracoHelper _umbracoHelper; private readonly ILocalizationService _languageService; private readonly IEntityService _entityService; public UmbracoTreeSearcher(IExamineManager examineManager, + UmbracoContext umbracoContext, UmbracoHelper umbracoHelper, ILocalizationService languageService, IEntityService entityService) { _examineManager = examineManager ?? throw new ArgumentNullException(nameof(examineManager)); + _umbracoContext = umbracoContext; _umbracoHelper = umbracoHelper ?? throw new ArgumentNullException(nameof(umbracoHelper)); _languageService = languageService; _entityService = entityService; @@ -61,9 +64,7 @@ namespace Umbraco.Web.Search string type; var indexName = Constants.UmbracoIndexes.InternalIndexName; var fields = new[] { "id", "__NodeId" }; - - var umbracoContext = _umbracoHelper.UmbracoContext; - + // TODO: WE should try to allow passing in a lucene raw query, however we will still need to do some manual string // manipulation for things like start paths, member types, etc... //if (Examine.ExamineExtensions.TryParseLuceneQuery(query)) @@ -86,12 +87,12 @@ namespace Umbraco.Web.Search break; case UmbracoEntityTypes.Media: type = "media"; - var allMediaStartNodes = umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(_entityService); + var allMediaStartNodes = _umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(_entityService); AppendPath(sb, UmbracoObjectTypes.Media, allMediaStartNodes, searchFrom, _entityService); break; case UmbracoEntityTypes.Document: type = "content"; - var allContentStartNodes = umbracoContext.Security.CurrentUser.CalculateContentStartNodeIds(_entityService); + var allContentStartNodes = _umbracoContext.Security.CurrentUser.CalculateContentStartNodeIds(_entityService); AppendPath(sb, UmbracoObjectTypes.Document, allContentStartNodes, searchFrom, _entityService); break; default: diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index daec4bb1f7..8de42fc12b 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -599,20 +599,15 @@ namespace Umbraco.Web.Security /// /// Returns true or false if the currently logged in member is authorized based on the parameters provided /// - /// /// /// /// /// public virtual bool IsMemberAuthorized( - bool allowAll = false, IEnumerable allowTypes = null, IEnumerable allowGroups = null, IEnumerable allowMembers = null) { - if (allowAll) - return true; - if (allowTypes == null) allowTypes = Enumerable.Empty(); if (allowGroups == null) diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index 54ff1bba3f..ef6193694c 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -33,30 +33,7 @@ namespace Umbraco.Web.Security _userService = userService; _globalSettings = globalSettings; } - - /// - /// Returns true or false if the currently logged in member is authorized based on the parameters provided - /// - /// - /// - /// - /// - /// - [Obsolete("Use MembershipHelper.IsMemberAuthorized instead")] - public bool IsMemberAuthorized( - bool allowAll = false, - IEnumerable allowTypes = null, - IEnumerable allowGroups = null, - IEnumerable allowMembers = null) - { - if (Current.UmbracoContext == null) - { - return false; - } - var helper = Current.Factory.GetInstance(); - return helper.IsMemberAuthorized(allowAll, allowTypes, allowGroups, allowMembers); - } - + private IUser _currentUser; /// diff --git a/src/Umbraco.Web/Templates/ITemplateRenderer.cs b/src/Umbraco.Web/Templates/ITemplateRenderer.cs new file mode 100644 index 0000000000..f01edc58ed --- /dev/null +++ b/src/Umbraco.Web/Templates/ITemplateRenderer.cs @@ -0,0 +1,12 @@ +using System.IO; + +namespace Umbraco.Web.Templates +{ + /// + /// This is used purely for the RenderTemplate functionality in Umbraco + /// + public interface ITemplateRenderer + { + void Render(int pageId, int? altTemplateId, StringWriter writer); + } +} diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index 7d678b93ca..df711a084e 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Globalization; using System.IO; using System.Linq; @@ -9,6 +10,7 @@ using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.Routing; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Services; using Umbraco.Web.Macros; using Current = Umbraco.Web.Composing.Current; @@ -21,92 +23,81 @@ namespace Umbraco.Web.Templates /// /// This allows you to render an MVC template based purely off of a node id and an optional alttemplate id as string output. /// - internal class TemplateRenderer + internal class TemplateRenderer : ITemplateRenderer { - private readonly UmbracoContext _umbracoContext; - private object _oldPageElements; - private PublishedRequest _oldPublishedRequest; - private object _oldAltTemplate; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IPublishedRouter _publishedRouter; + private readonly IFileService _fileService; + private readonly ILocalizationService _languageService; + private readonly IWebRoutingSection _webRoutingSection; - public TemplateRenderer(UmbracoContext umbracoContext, int pageId, int? altTemplateId) + public TemplateRenderer(IUmbracoContextAccessor umbracoContextAccessor, IPublishedRouter publishedRouter, IFileService fileService, ILocalizationService textService, IWebRoutingSection webRoutingSection) { - PageId = pageId; - AltTemplateId = altTemplateId; - _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); + _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); + _publishedRouter = publishedRouter ?? throw new ArgumentNullException(nameof(publishedRouter)); + _fileService = fileService ?? throw new ArgumentNullException(nameof(fileService)); + _languageService = textService ?? throw new ArgumentNullException(nameof(textService)); + _webRoutingSection = webRoutingSection ?? throw new ArgumentNullException(nameof(webRoutingSection)); } - - private IFileService FileService => Current.Services.FileService; // TODO: inject - private IPublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance(); // TODO: inject - - - /// - /// Gets/sets the page id for the template to render - /// - public int PageId { get; } - - /// - /// Gets/sets the alt template to render if there is one - /// - public int? AltTemplateId { get; } - - public void Render(StringWriter writer) + + public void Render(int pageId, int? altTemplateId, StringWriter writer) { if (writer == null) throw new ArgumentNullException(nameof(writer)); // instantiate a request and process // important to use CleanedUmbracoUrl - lowercase path-only version of the current url, though this isn't going to matter // terribly much for this implementation since we are just creating a doc content request to modify it's properties manually. - var contentRequest = PublishedRouter.CreateRequest(_umbracoContext); + var contentRequest = _publishedRouter.CreateRequest(_umbracoContextAccessor.UmbracoContext); - var doc = contentRequest.UmbracoContext.ContentCache.GetById(PageId); + var doc = contentRequest.UmbracoContext.ContentCache.GetById(pageId); if (doc == null) { - writer.Write("", PageId); + writer.Write("", pageId); return; } //in some cases the UmbracoContext will not have a PublishedContentRequest assigned to it if we are not in the //execution of a front-end rendered page. In this case set the culture to the default. //set the culture to the same as is currently rendering - if (_umbracoContext.PublishedRequest == null) + if (_umbracoContextAccessor.UmbracoContext.PublishedRequest == null) { - var defaultLanguage = Current.Services.LocalizationService.GetAllLanguages().FirstOrDefault(); + var defaultLanguage = _languageService.GetAllLanguages().FirstOrDefault(); contentRequest.Culture = defaultLanguage == null ? CultureInfo.CurrentUICulture : defaultLanguage.CultureInfo; } else { - contentRequest.Culture = _umbracoContext.PublishedRequest.Culture; + contentRequest.Culture = _umbracoContextAccessor.UmbracoContext.PublishedRequest.Culture; } //set the doc that was found by id contentRequest.PublishedContent = doc; //set the template, either based on the AltTemplate found or the standard template of the doc - var templateId = Current.Configs.Settings().WebRouting.DisableAlternativeTemplates || !AltTemplateId.HasValue + var templateId = _webRoutingSection.DisableAlternativeTemplates || !altTemplateId.HasValue ? doc.TemplateId - : AltTemplateId.Value; + : altTemplateId.Value; if (templateId.HasValue) - contentRequest.TemplateModel = FileService.GetTemplate(templateId.Value); + contentRequest.TemplateModel = _fileService.GetTemplate(templateId.Value); //if there is not template then exit if (contentRequest.HasTemplate == false) { - if (AltTemplateId.HasValue == false) + if (altTemplateId.HasValue == false) { writer.Write("", doc.TemplateId); } else { - writer.Write("", AltTemplateId); + writer.Write("", altTemplateId); } return; } //First, save all of the items locally that we know are used in the chain of execution, we'll need to restore these //after this page has rendered. - SaveExistingItems(); + SaveExistingItems(out var oldPublishedRequest, out var oldAltTemplate); try { @@ -119,7 +110,7 @@ namespace Umbraco.Web.Templates finally { //restore items on context objects to continuing rendering the parent template - RestoreItems(); + RestoreItems(oldPublishedRequest, oldAltTemplate); } } @@ -134,11 +125,11 @@ namespace Umbraco.Web.Templates //var queryString = _umbracoContext.HttpContext.Request.QueryString.AllKeys // .ToDictionary(key => key, key => context.Request.QueryString[key]); - var requestContext = new RequestContext(_umbracoContext.HttpContext, new RouteData() + var requestContext = new RequestContext(_umbracoContextAccessor.UmbracoContext.HttpContext, new RouteData() { Route = RouteTable.Routes["Umbraco_default"] }); - var routeHandler = new RenderRouteHandler(_umbracoContext, ControllerBuilder.Current.GetControllerFactory()); + var routeHandler = new RenderRouteHandler(_umbracoContextAccessor, ControllerBuilder.Current.GetControllerFactory()); var routeDef = routeHandler.GetUmbracoRouteDefinition(requestContext, request); var renderModel = new ContentModel(request.PublishedContent); //manually add the action/controller, this is required by mvc @@ -181,34 +172,29 @@ namespace Umbraco.Web.Templates private void SetNewItemsOnContextObjects(PublishedRequest request) { - // handlers like default.aspx will want it and most macros currently need it - request.LegacyContentHashTable = new PublishedContentHashtableConverter(request); //now, set the new ones for this page execution - _umbracoContext.HttpContext.Items["pageElements"] = request.LegacyContentHashTable.Elements; - _umbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = null; - _umbracoContext.PublishedRequest = request; + _umbracoContextAccessor.UmbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = null; + _umbracoContextAccessor.UmbracoContext.PublishedRequest = request; } /// /// Save all items that we know are used for rendering execution to variables so we can restore after rendering /// - private void SaveExistingItems() + private void SaveExistingItems(out PublishedRequest oldPublishedRequest, out object oldAltTemplate) { //Many objects require that these legacy items are in the http context items... before we render this template we need to first //save the values in them so that we can re-set them after we render so the rest of the execution works as per normal - _oldPageElements = _umbracoContext.HttpContext.Items["pageElements"]; - _oldPublishedRequest = _umbracoContext.PublishedRequest; - _oldAltTemplate = _umbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate]; + oldPublishedRequest = _umbracoContextAccessor.UmbracoContext.PublishedRequest; + oldAltTemplate = _umbracoContextAccessor.UmbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate]; } /// /// Restores all items back to their context's to continue normal page rendering execution /// - private void RestoreItems() + private void RestoreItems(PublishedRequest oldPublishedRequest, object oldAltTemplate) { - _umbracoContext.PublishedRequest = _oldPublishedRequest; - _umbracoContext.HttpContext.Items["pageElements"] = _oldPageElements; - _umbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = _oldAltTemplate; + _umbracoContextAccessor.UmbracoContext.PublishedRequest = oldPublishedRequest; + _umbracoContextAccessor.UmbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = oldAltTemplate; } } } diff --git a/src/Umbraco.Web/Trees/ApplicationTreeController.cs b/src/Umbraco.Web/Trees/ApplicationTreeController.cs index 605d5e4352..68d7fbb3fd 100644 --- a/src/Umbraco.Web/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web/Trees/ApplicationTreeController.cs @@ -36,8 +36,8 @@ namespace Umbraco.Web.Trees public ApplicationTreeController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, - IRuntimeState runtimeState, ITreeService treeService) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) + IRuntimeState runtimeState, ITreeService treeService, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _treeService = treeService; } diff --git a/src/Umbraco.Web/Trees/TreeController.cs b/src/Umbraco.Web/Trees/TreeController.cs index e6d948e19a..be0b862988 100644 --- a/src/Umbraco.Web/Trees/TreeController.cs +++ b/src/Umbraco.Web/Trees/TreeController.cs @@ -18,8 +18,8 @@ namespace Umbraco.Web.Trees private readonly TreeAttribute _treeAttribute; - protected TreeController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) + protected TreeController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _treeAttribute = GetTreeAttribute(); } diff --git a/src/Umbraco.Web/Trees/TreeControllerBase.cs b/src/Umbraco.Web/Trees/TreeControllerBase.cs index 8632c15d24..e3946ce532 100644 --- a/src/Umbraco.Web/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/TreeControllerBase.cs @@ -27,11 +27,12 @@ namespace Umbraco.Web.Trees public abstract class TreeControllerBase : UmbracoAuthorizedApiController, ITree { protected TreeControllerBase() - { } + { + } - protected TreeControllerBase(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) - { } + protected TreeControllerBase(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } /// /// The method called to render the contents of the tree structure diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 63836931a7..f9fa29f7db 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -143,6 +143,7 @@ + @@ -212,6 +213,7 @@ + diff --git a/src/Umbraco.Web/UmbracoAuthorizedHttpHandler.cs b/src/Umbraco.Web/UmbracoAuthorizedHttpHandler.cs index 1d8475111f..b18ba55799 100644 --- a/src/Umbraco.Web/UmbracoAuthorizedHttpHandler.cs +++ b/src/Umbraco.Web/UmbracoAuthorizedHttpHandler.cs @@ -2,6 +2,7 @@ using System.Security; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Logging; using Umbraco.Web.Security; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; @@ -11,10 +12,10 @@ namespace Umbraco.Web public abstract class UmbracoAuthorizedHttpHandler : UmbracoHttpHandler { protected UmbracoAuthorizedHttpHandler() - { } + { + } - protected UmbracoAuthorizedHttpHandler(UmbracoContext umbracoContext, ServiceContext services) - : base(umbracoContext, services) + protected UmbracoAuthorizedHttpHandler(UmbracoContext umbracoContext, UmbracoHelper umbracoHelper, ServiceContext service, IProfilingLogger plogger) : base(umbracoContext, umbracoHelper, service, plogger) { } diff --git a/src/Umbraco.Web/UmbracoComponentRenderer.cs b/src/Umbraco.Web/UmbracoComponentRenderer.cs index f59962a0f2..25c2d78731 100644 --- a/src/Umbraco.Web/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/UmbracoComponentRenderer.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Collections; using System.IO; using System.Web; @@ -6,6 +7,9 @@ using System.Web.UI; using Umbraco.Core; using Umbraco.Web.Templates; using System.Collections.Generic; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.Macros; @@ -20,31 +24,34 @@ namespace Umbraco.Web /// internal class UmbracoComponentRenderer : IUmbracoComponentRenderer { - private readonly UmbracoContext _umbracoContext; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IMacroRenderer _macroRenderer; + private readonly ITemplateRenderer _templateRenderer; - public UmbracoComponentRenderer(UmbracoContext umbracoContext) + public UmbracoComponentRenderer(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer, ITemplateRenderer templateRenderer) { - _umbracoContext = umbracoContext; + _umbracoContextAccessor = umbracoContextAccessor; + _macroRenderer = macroRenderer; + _templateRenderer = templateRenderer ?? throw new ArgumentNullException(nameof(templateRenderer)); } /// /// Renders the template for the specified pageId and an optional altTemplateId /// - /// + /// /// If not specified, will use the template assigned to the node /// - public IHtmlString RenderTemplate(int pageId, int? altTemplateId = null) + public IHtmlString RenderTemplate(int contentId, int? altTemplateId = null) { - var templateRenderer = new TemplateRenderer(_umbracoContext, pageId, altTemplateId); using (var sw = new StringWriter()) { try { - templateRenderer.Render(sw); + _templateRenderer.Render(contentId, altTemplateId, sw); } catch (Exception ex) { - sw.Write("", pageId, ex); + sw.Write("", contentId, ex); } return new HtmlString(sw.ToString()); } @@ -53,129 +60,107 @@ namespace Umbraco.Web /// /// Renders the macro with the specified alias. /// + /// /// The alias. /// - public IHtmlString RenderMacro(string alias) + public IHtmlString RenderMacro(int contentId, string alias) { - return RenderMacro(alias, new { }); + return RenderMacro(contentId, alias, new { }); } /// /// Renders the macro with the specified alias, passing in the specified parameters. /// + /// /// The alias. /// The parameters. /// - public IHtmlString RenderMacro(string alias, object parameters) + public IHtmlString RenderMacro(int contentId, string alias, object parameters) { - return RenderMacro(alias, parameters.ToDictionary()); + return RenderMacro(contentId, alias, parameters.ToDictionary()); } /// /// Renders the macro with the specified alias, passing in the specified parameters. /// + /// /// The alias. /// The parameters. /// - public IHtmlString RenderMacro(string alias, IDictionary parameters) + public IHtmlString RenderMacro(int contentId, string alias, IDictionary parameters) { + if (contentId == default) + throw new ArgumentException("Invalid content id " + contentId); - if (_umbracoContext.PublishedRequest == null) - { - throw new InvalidOperationException("Cannot render a macro when there is no current PublishedContentRequest."); - } + var content = _umbracoContextAccessor.UmbracoContext.ContentCache?.GetById(true, contentId); - return RenderMacro(alias, parameters, _umbracoContext.PublishedRequest.LegacyContentHashTable); + if (content == null) + throw new InvalidOperationException("Cannot render a macro, no content found by id " + contentId); + + return RenderMacro(alias, parameters, content); } /// /// Renders the macro with the specified alias, passing in the specified parameters. /// - /// The alias. + /// The macro alias. /// The parameters. - /// The legacy umbraco page object that is required for some macros + /// The content used for macro rendering /// - internal IHtmlString RenderMacro(string alias, IDictionary parameters, PublishedContentHashtableConverter umbracoPage) + private IHtmlString RenderMacro(string alias, IDictionary parameters, IPublishedContent content) { - if (alias == null) throw new ArgumentNullException("alias"); - if (umbracoPage == null) throw new ArgumentNullException("umbracoPage"); + if (content == null) throw new ArgumentNullException(nameof(content)); - var m = Current.Services.MacroService.GetByAlias(alias); - if (m == null) - throw new KeyNotFoundException("Could not find macro with alias " + alias); - - return RenderMacro(new MacroModel(m), parameters, umbracoPage); - } - - /// - /// Renders the macro with the specified alias, passing in the specified parameters. - /// - /// The macro. - /// The parameters. - /// The legacy umbraco page object that is required for some macros - /// - internal IHtmlString RenderMacro(MacroModel m, IDictionary parameters, PublishedContentHashtableConverter umbracoPage) - { - if (umbracoPage == null) throw new ArgumentNullException(nameof(umbracoPage)); - if (m == null) throw new ArgumentNullException(nameof(m)); - - if (_umbracoContext.PageId == null) - { - throw new InvalidOperationException("Cannot render a macro when UmbracoContext.PageId is null."); - } - - var macroProps = new Hashtable(); - foreach (var i in parameters) - { - // TODO: We are doing at ToLower here because for some insane reason the UpdateMacroModel method of macro.cs - // looks for a lower case match. WTF. the whole macro concept needs to be rewritten. - - - //NOTE: the value could have HTML encoded values, so we need to deal with that - macroProps.Add(i.Key.ToLowerInvariant(), (i.Value is string) ? HttpUtility.HtmlDecode(i.Value.ToString()) : i.Value); - } - var renderer = new MacroRenderer(Current.ProfilingLogger); - var macroControl = renderer.Render(m, umbracoPage.Elements, _umbracoContext.PageId.Value, macroProps).GetAsControl(); + // TODO: We are doing at ToLower here because for some insane reason the UpdateMacroModel method looks for a lower case match. the whole macro concept needs to be rewritten. + //NOTE: the value could have HTML encoded values, so we need to deal with that + var macroProps = parameters.ToDictionary( + x => x.Key.ToLowerInvariant(), + i => (i.Value is string) ? HttpUtility.HtmlDecode(i.Value.ToString()) : i.Value); + + var macroControl = _macroRenderer.Render(alias, content, macroProps).GetAsControl(); string html; - if (macroControl is LiteralControl) + if (macroControl is LiteralControl control) { // no need to execute, we already have text - html = (macroControl as LiteralControl).Text; + html = control.Text; } else { - var containerPage = new FormlessPage(); - containerPage.Controls.Add(macroControl); - - using (var output = new StringWriter()) + using (var containerPage = new FormlessPage()) { - // .Execute() does a PushTraceContext/PopTraceContext and writes trace output straight into 'output' - // and I do not see how we could wire the trace context to the current context... so it creates dirty - // trace output right in the middle of the page. - // - // The only thing we can do is fully disable trace output while .Execute() runs and restore afterwards - // which means trace output is lost if the macro is a control (.ascx or user control) that is invoked - // from within Razor -- which makes sense anyway because the control can _not_ run correctly from - // within Razor since it will never be inserted into the page pipeline (which may even not exist at all - // if we're running MVC). - // - // I'm sure there's more things that will get lost with this context changing but I guess we'll figure - // those out as we go along. One thing we lose is the content type response output. - // http://issues.umbraco.org/issue/U4-1599 if it is setup during the macro execution. So - // here we'll save the content type response and reset it after execute is called. + containerPage.Controls.Add(macroControl); - var contentType = _umbracoContext.HttpContext.Response.ContentType; - var traceIsEnabled = containerPage.Trace.IsEnabled; - containerPage.Trace.IsEnabled = false; - _umbracoContext.HttpContext.Server.Execute(containerPage, output, true); - containerPage.Trace.IsEnabled = traceIsEnabled; - //reset the content type - _umbracoContext.HttpContext.Response.ContentType = contentType; + using (var output = new StringWriter()) + { + // .Execute() does a PushTraceContext/PopTraceContext and writes trace output straight into 'output' + // and I do not see how we could wire the trace context to the current context... so it creates dirty + // trace output right in the middle of the page. + // + // The only thing we can do is fully disable trace output while .Execute() runs and restore afterwards + // which means trace output is lost if the macro is a control (.ascx or user control) that is invoked + // from within Razor -- which makes sense anyway because the control can _not_ run correctly from + // within Razor since it will never be inserted into the page pipeline (which may even not exist at all + // if we're running MVC). + // + // I'm sure there's more things that will get lost with this context changing but I guess we'll figure + // those out as we go along. One thing we lose is the content type response output. + // http://issues.umbraco.org/issue/U4-1599 if it is setup during the macro execution. So + // here we'll save the content type response and reset it after execute is called. - //Now, we need to ensure that local links are parsed - html = TemplateUtilities.ParseInternalLinks(output.ToString(), _umbracoContext.UrlProvider); + var contentType = _umbracoContextAccessor.UmbracoContext.HttpContext.Response.ContentType; + var traceIsEnabled = containerPage.Trace.IsEnabled; + containerPage.Trace.IsEnabled = false; + _umbracoContextAccessor.UmbracoContext.HttpContext.Server.Execute(containerPage, output, true); + containerPage.Trace.IsEnabled = traceIsEnabled; + //reset the content type + _umbracoContextAccessor.UmbracoContext.HttpContext.Response.ContentType = contentType; + + //Now, we need to ensure that local links are parsed + html = TemplateUtilities.ParseInternalLinks(output.ToString(), _umbracoContextAccessor.UmbracoContext.UrlProvider); + } } + } return new HtmlString(html); diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 04a9658ee5..a1b443c3ee 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -281,29 +281,7 @@ namespace Umbraco.Web || string.IsNullOrEmpty(request["umbdebug"]) == false); } } - - /// - /// Gets the current page ID, or null if no page ID is available (e.g. a custom page). - /// - public int? PageId - { - get - { - try - { - // This was changed but the comments used to refer to - // macros in the backoffice not working with this Id - // it's probably not a problem any more though. Old comment: - // https://github.com/umbraco/Umbraco-CMS/blob/7a615133ff9de84ee667fb7794169af65e2b4d7a/src/Umbraco.Web/UmbracoContext.cs#L256 - return Current.PublishedRequest.PublishedContent.Id; - } - catch - { - return null; - } - } - } - + /// /// Determines whether the current user is in a preview mode and browsing the site (ie. not in the admin UI) /// @@ -312,7 +290,7 @@ namespace Umbraco.Web get { if (_previewing.HasValue == false) DetectPreviewMode(); - return _previewing.Value; + return _previewing ?? false; } private set => _previewing = value; } diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 9d55299ecc..4b1b125a96 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -8,139 +8,89 @@ using Umbraco.Core.Dictionary; using Umbraco.Core.Exceptions; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Services; using Umbraco.Core.Xml; -using Umbraco.Core.Composing; using Umbraco.Web.Routing; using Umbraco.Web.Security; -using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web { - using Examine = global::Examine; - /// /// A helper class that provides many useful methods and functionality for using Umbraco in templates /// - public class UmbracoHelper : IUmbracoComponentRenderer + /// + /// This object is a request based lifetime + /// + public class UmbracoHelper { private static readonly HtmlStringUtilities StringUtilities = new HtmlStringUtilities(); private readonly UmbracoContext _umbracoContext; - private readonly IPublishedContent _currentPage; - private readonly ServiceContext _services; + private IPublishedContent _currentPage; - private IUmbracoComponentRenderer _componentRenderer; - private IPublishedContentQuery _query; - private MembershipHelper _membershipHelper; - private ITagQuery _tag; + private readonly IUmbracoComponentRenderer _componentRenderer; + private readonly ICultureDictionaryFactory _cultureDictionaryFactory; private ICultureDictionary _cultureDictionary; #region Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of . /// - /// For tests. - internal UmbracoHelper(UmbracoContext umbracoContext, IPublishedContent content, - ITagQuery tagQuery, - ICultureDictionary cultureDictionary, + /// An Umbraco context. + /// + /// + /// + /// + /// + /// Sets the current page to the context's published content request's content item. + public UmbracoHelper(UmbracoContext umbracoContext, ITagQuery tagQuery, + ICultureDictionaryFactory cultureDictionary, IUmbracoComponentRenderer componentRenderer, - MembershipHelper membershipHelper, - ServiceContext services) + IPublishedContentQuery publishedContentQuery, + MembershipHelper membershipHelper) { _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); - _tag = tagQuery ?? throw new ArgumentNullException(nameof(tagQuery)); - _cultureDictionary = cultureDictionary ?? throw new ArgumentNullException(nameof(cultureDictionary)); + TagQuery = tagQuery ?? throw new ArgumentNullException(nameof(tagQuery)); + _cultureDictionaryFactory = cultureDictionary ?? throw new ArgumentNullException(nameof(cultureDictionary)); _componentRenderer = componentRenderer ?? throw new ArgumentNullException(nameof(componentRenderer)); - _membershipHelper = membershipHelper ?? throw new ArgumentNullException(nameof(membershipHelper)); - _currentPage = content ?? throw new ArgumentNullException(nameof(content)); - _services = services ?? throw new ArgumentNullException(nameof(services)); + MembershipHelper = membershipHelper ?? throw new ArgumentNullException(nameof(membershipHelper)); + ContentQuery = publishedContentQuery ?? throw new ArgumentNullException(nameof(publishedContentQuery)); + + if (_umbracoContext.IsFrontEndUmbracoRequest) + _currentPage = _umbracoContext.PublishedRequest.PublishedContent; } /// - /// Initializes a new instance of the class. + /// Initializes a new empty instance of . /// /// For tests - nothing is initialized. internal UmbracoHelper() { } - - /// - /// Initializes a new instance of the class with an Umbraco context - /// and a specific content item. - /// - /// An Umbraco context. - /// A content item. - /// A services context. - /// Sets the current page to the supplied content item. - public UmbracoHelper(UmbracoContext umbracoContext, ServiceContext services, IPublishedContent content) - : this(umbracoContext, services) - { - _currentPage = content ?? throw new ArgumentNullException(nameof(content)); - } - - /// - /// Initializes a new instance of the class with an Umbraco context. - /// - /// An Umbraco context. - /// A services context. - /// Sets the current page to the context's published content request's content item. - public UmbracoHelper(UmbracoContext umbracoContext, ServiceContext services) - { - _services = services ?? throw new ArgumentNullException(nameof(services)); - _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); - if (_umbracoContext.IsFrontEndUmbracoRequest) - _currentPage = _umbracoContext.PublishedRequest.PublishedContent; - } - #endregion /// /// Gets the tag context. /// - public ITagQuery TagQuery => _tag ?? - (_tag = new TagQuery(_services.TagService, ContentQuery)); + public ITagQuery TagQuery { get; } /// /// Gets the query context. /// - public IPublishedContentQuery ContentQuery => _query ?? - (_query = new PublishedContentQuery(UmbracoContext.ContentCache, UmbracoContext.MediaCache, UmbracoContext.VariationContextAccessor)); - - /// - /// Gets the Umbraco context. - /// - public UmbracoContext UmbracoContext - { - get - { - if (_umbracoContext == null) - throw new NullReferenceException("UmbracoContext has not been set."); - return _umbracoContext; - } - } - + public IPublishedContentQuery ContentQuery { get; } + /// /// Gets the membership helper. /// - public MembershipHelper MembershipHelper => _membershipHelper - ?? (_membershipHelper = Current.Factory.GetInstance()); + public MembershipHelper MembershipHelper { get; } /// /// Gets the url provider. /// - public UrlProvider UrlProvider => UmbracoContext.UrlProvider; + public UrlProvider UrlProvider => _umbracoContext.UrlProvider; /// - /// Gets the component renderer. - /// - public IUmbracoComponentRenderer UmbracoComponentRenderer => _componentRenderer - ?? (_componentRenderer = new UmbracoComponentRenderer(UmbracoContext)); - - /// - /// Returns the current item - /// assigned to the UmbracoHelper. + /// Gets (or sets) the current item assigned to the UmbracoHelper. /// /// /// @@ -173,17 +123,18 @@ namespace Umbraco.Web ); } + 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 IHtmlString RenderTemplate(int pageId, int? altTemplateId = null) + public IHtmlString RenderTemplate(int contentId, int? altTemplateId = null) { - return UmbracoComponentRenderer.RenderTemplate(pageId, altTemplateId); + return _componentRenderer.RenderTemplate(contentId, altTemplateId); } #region RenderMacro @@ -195,7 +146,7 @@ namespace Umbraco.Web /// public IHtmlString RenderMacro(string alias) { - return UmbracoComponentRenderer.RenderMacro(alias, new { }); + return _componentRenderer.RenderMacro(_umbracoContext.PublishedRequest?.PublishedContent?.Id ?? 0, alias, new { }); } /// @@ -206,7 +157,7 @@ namespace Umbraco.Web /// public IHtmlString RenderMacro(string alias, object parameters) { - return UmbracoComponentRenderer.RenderMacro(alias, parameters.ToDictionary()); + return _componentRenderer.RenderMacro(_umbracoContext.PublishedRequest?.PublishedContent?.Id ?? 0, alias, parameters.ToDictionary()); } /// @@ -217,7 +168,7 @@ namespace Umbraco.Web /// public IHtmlString RenderMacro(string alias, IDictionary parameters) { - return UmbracoComponentRenderer.RenderMacro(alias, parameters); + return _componentRenderer.RenderMacro(_umbracoContext.PublishedRequest?.PublishedContent?.Id ?? 0, alias, parameters); } #endregion @@ -254,7 +205,7 @@ namespace Umbraco.Web /// Returns the ICultureDictionary for access to dictionary items /// public ICultureDictionary CultureDictionary => _cultureDictionary - ?? (_cultureDictionary = Current.CultureDictionaryFactory.CreateDictionary()); + ?? (_cultureDictionary = _cultureDictionaryFactory.CreateDictionary()); #endregion @@ -287,6 +238,7 @@ namespace Umbraco.Web /// Gets the url of a content identified by its identifier. /// /// The content identifier. + /// /// The url for the content. public string Url(int contentId, string culture = null) { @@ -454,11 +406,9 @@ namespace Umbraco.Web private IEnumerable ContentForObjects(IEnumerable ids) { var idsA = ids.ToArray(); - IEnumerable intIds; - if (ConvertIdsObjectToInts(idsA, out intIds)) + if (ConvertIdsObjectToInts(idsA, out var intIds)) return ContentQuery.Content(intIds); - IEnumerable guidIds; - if (ConvertIdsObjectToGuids(idsA, out guidIds)) + if (ConvertIdsObjectToGuids(idsA, out var guidIds)) return ContentQuery.Content(guidIds); return Enumerable.Empty(); } @@ -638,12 +588,7 @@ namespace Umbraco.Web public IPublishedContent Media(Guid id) { - // TODO: This is horrible but until the media cache properly supports GUIDs we have no choice here and - // currently there won't be any way to add this method correctly to `ITypedPublishedContentQuery` without breaking an interface and adding GUID support for media - - var entityService = Current.Services.EntityService; // TODO: inject - var mediaAttempt = entityService.GetId(id, UmbracoObjectTypes.Media); - return mediaAttempt.Success ? ContentQuery.Media(mediaAttempt.Result) : null; + return ContentQuery.Media(id); } /// @@ -696,12 +641,10 @@ namespace Umbraco.Web private IEnumerable MediaForObjects(IEnumerable ids) { var idsA = ids.ToArray(); - IEnumerable intIds; - if (ConvertIdsObjectToInts(idsA, out intIds)) + if (ConvertIdsObjectToInts(idsA, out var intIds)) return ContentQuery.Media(intIds); - //IEnumerable guidIds; - //if (ConvertIdsObjectToGuids(idsA, out guidIds)) - // return ContentQuery.Media(guidIds); + if (ConvertIdsObjectToGuids(idsA, out var guidIds)) + return ContentQuery.Media(guidIds); return Enumerable.Empty(); } diff --git a/src/Umbraco.Web/UmbracoHttpHandler.cs b/src/Umbraco.Web/UmbracoHttpHandler.cs index 336b53e9e0..eca3fc303e 100644 --- a/src/Umbraco.Web/UmbracoHttpHandler.cs +++ b/src/Umbraco.Web/UmbracoHttpHandler.cs @@ -14,19 +14,16 @@ namespace Umbraco.Web private UrlHelper _url; protected UmbracoHttpHandler() - : this(Current.UmbracoContext, Current.Services) + : this(Current.UmbracoContext, Current.UmbracoHelper, Current.Services, Current.ProfilingLogger) { } - protected UmbracoHttpHandler(UmbracoContext umbracoContext, ServiceContext services) + protected UmbracoHttpHandler(UmbracoContext umbracoContext, UmbracoHelper umbracoHelper, ServiceContext service, IProfilingLogger plogger) { - if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext)); UmbracoContext = umbracoContext; - Umbraco = new UmbracoHelper(umbracoContext, services); - - // TODO: inject somehow - Logger = Current.Logger; - ProfilingLogger = Current.ProfilingLogger; - Services = Current.Services; + Logger = plogger; + ProfilingLogger = plogger; + Services = service; + Umbraco = umbracoHelper; } public abstract void ProcessRequest(HttpContext context); diff --git a/src/Umbraco.Web/UmbracoWebService.cs b/src/Umbraco.Web/UmbracoWebService.cs index b970a0e8c2..781e521fcd 100644 --- a/src/Umbraco.Web/UmbracoWebService.cs +++ b/src/Umbraco.Web/UmbracoWebService.cs @@ -21,15 +21,19 @@ namespace Umbraco.Web { private UrlHelper _url; - protected UmbracoWebService() + protected UmbracoWebService(IProfilingLogger profilingLogger, UmbracoContext umbracoContext, UmbracoHelper umbraco, ServiceContext services, IGlobalSettings globalSettings) { - UmbracoContext = Current.UmbracoContext; - Umbraco = new UmbracoHelper(UmbracoContext, Current.Services); + Logger = profilingLogger; + ProfilingLogger = profilingLogger; + UmbracoContext = umbracoContext; + Umbraco = umbraco; + Services = services; + GlobalSettings = globalSettings; + } - Logger = Current.Logger; - ProfilingLogger = Current.ProfilingLogger; - Services = Current.Services; - GlobalSettings = Current.Configs.Global(); + protected UmbracoWebService() + : this(Current.ProfilingLogger, Current.UmbracoContext, Current.UmbracoHelper, Current.Services, Current.Configs.Global()) + { } /// diff --git a/src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs b/src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs index 12caa29703..bc1e9a4318 100644 --- a/src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs +++ b/src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs @@ -1,7 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Web.Http; using Umbraco.Core; +using Umbraco.Web.Security; +using Umbraco.Core.Composing; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.WebApi { @@ -11,37 +13,6 @@ namespace Umbraco.Web.WebApi /// public sealed class MemberAuthorizeAttribute : AuthorizeAttribute { - private readonly UmbracoContext _umbracoContext; - - private UmbracoContext GetUmbracoContext() - { - return _umbracoContext ?? UmbracoContext.Current; - } - - /// - /// THIS SHOULD BE ONLY USED FOR UNIT TESTS - /// - /// - public MemberAuthorizeAttribute(UmbracoContext umbracoContext) - { - if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); - _umbracoContext = umbracoContext; - } - - public MemberAuthorizeAttribute() - { - - } - - /// - /// Flag for whether to allow all site visitors or just authenticated members - /// - /// - /// This is the same as applying the [AllowAnonymous] attribute - /// - [Obsolete("Use [AllowAnonymous] instead")] - public bool AllowAll { get; set; } - /// /// Comma delimited list of allowed member types /// @@ -69,17 +40,14 @@ namespace Umbraco.Web.WebApi var members = new List(); foreach (var s in AllowMembers.Split(',')) { - int id; - if (int.TryParse(s, out id)) + if (int.TryParse(s, out var id)) { members.Add(id); } } - return GetUmbracoContext().Security.IsMemberAuthorized(AllowAll, - AllowType.Split(','), - AllowGroup.Split(','), - members); + var helper = Current.Factory.GetInstance(); + return helper.IsMemberAuthorized(AllowType.Split(','), AllowGroup.Split(','), members); } } diff --git a/src/Umbraco.Web/WebApi/UmbracoApiController.cs b/src/Umbraco.Web/WebApi/UmbracoApiController.cs index 31778d4b0c..2091d47202 100644 --- a/src/Umbraco.Web/WebApi/UmbracoApiController.cs +++ b/src/Umbraco.Web/WebApi/UmbracoApiController.cs @@ -13,18 +13,12 @@ namespace Umbraco.Web.WebApi /// public abstract class UmbracoApiController : UmbracoApiControllerBase, IDiscoverable { - /// - /// Initializes a new instance of the with auto dependencies. - /// - /// Dependencies are obtained from the service locator. protected UmbracoApiController() - { } + { + } - /// - /// Initialize a new instance of the with all its dependencies. - /// - protected UmbracoApiController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) - { } + protected UmbracoApiController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } } } diff --git a/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs b/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs index 7e32296e12..c6ac35267d 100644 --- a/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs +++ b/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs @@ -21,8 +21,6 @@ namespace Umbraco.Web.WebApi [FeatureAuthorize] public abstract class UmbracoApiControllerBase : ApiController { - private UmbracoHelper _umbracoHelper; - // note: all Umbraco controllers have two constructors: one with all dependencies, which should be used, // and one with auto dependencies, ie no dependencies - and then dependencies are automatically obtained // here from the Current service locator - this is obviously evil, but it allows us to add new dependencies @@ -40,14 +38,15 @@ namespace Umbraco.Web.WebApi Current.Factory.GetInstance(), Current.Factory.GetInstance(), Current.Factory.GetInstance(), - Current.Factory.GetInstance() + Current.Factory.GetInstance(), + Current.Factory.GetInstance() ) { } /// /// Initializes a new instance of the class with all its dependencies. /// - protected UmbracoApiControllerBase(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState) + protected UmbracoApiControllerBase(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) { GlobalSettings = globalSettings; SqlContext = sqlContext; @@ -56,6 +55,7 @@ namespace Umbraco.Web.WebApi Logger = logger; RuntimeState = runtimeState; UmbracoContext = umbracoContext; + Umbraco = umbracoHelper; } /// @@ -112,9 +112,8 @@ namespace Umbraco.Web.WebApi /// /// Gets the Umbraco helper. /// - public UmbracoHelper Umbraco => _umbracoHelper - ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services)); - + public UmbracoHelper Umbraco { get; } + /// /// Gets the web security helper. /// diff --git a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs index 039e462e0b..e188fb4fef 100644 --- a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs +++ b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs @@ -30,19 +30,13 @@ namespace Umbraco.Web.WebApi { private BackOfficeUserManager _userManager; - /// - /// Initializes a new instance of the with auto dependencies. - /// - /// Dependencies are obtained from the service locator. protected UmbracoAuthorizedApiController() - { } + { + } - /// - /// Initializes a new instance of the class with all its dependencies. - /// - protected UmbracoAuthorizedApiController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) - { } + protected UmbracoAuthorizedApiController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } /// /// Gets the user manager.