From 7b55d2f1b2fce1a3bb8eadbd32028598785658e3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 31 Jan 2019 15:09:31 +1100 Subject: [PATCH 01/15] New IMacroRenderer and ITemplateRenderer and hides underlying logic for these in internal classes. Massively cleans up the macro rendering logic (almost makes sense now), removes unused macro code, injects UmbracoHelper wherever it's needed (not creating manually), fixes UmbracoHelper to have it's services injected, no more empty services, allows setting the AssignedContentItem on the UmbracoHelper and ensures it's lifespan is Transient, updates all corresponding ctors. Fixes macro rendering, ensures the correct culture variation is assigned, and that we can render macros for any given IPublishedContent, not just the one assigned in the request. --- .../Routing/RenderRouteHandlerTests.cs | 3 +- .../TestControllerActivatorBase.cs | 9 +- .../Testing/TestingTests/MockTests.cs | 7 +- .../Controllers/PluginControllerAreaTests.cs | 8 +- .../Web/Mvc/SurfaceControllerTests.cs | 13 +- src/Umbraco.Web/Composing/Current.cs | 3 + src/Umbraco.Web/Controllers/TagsController.cs | 15 +- .../Controllers/UmbLoginController.cs | 9 +- .../Controllers/UmbLoginStatusController.cs | 9 +- .../Controllers/UmbProfileController.cs | 4 +- .../Controllers/UmbRegisterController.cs | 9 +- .../Editors/AuthenticationController.cs | 15 +- .../Editors/BackOfficeController.cs | 4 +- .../Editors/ContentTypeController.cs | 4 +- .../Editors/ContentTypeControllerBase.cs | 4 +- .../Editors/DashboardController.cs | 12 +- src/Umbraco.Web/Editors/EntityController.cs | 4 +- .../Editors/MacroRenderingController.cs | 19 +- .../Editors/MediaTypeController.cs | 3 +- .../Editors/MemberTypeController.cs | 3 +- .../Editors/PackageInstallController.cs | 4 +- src/Umbraco.Web/Editors/SectionController.cs | 6 +- .../UmbracoAuthorizedJsonController.cs | 15 +- src/Umbraco.Web/HtmlHelperRenderExtensions.cs | 4 +- src/Umbraco.Web/IUmbracoComponentRenderer.cs | 13 +- src/Umbraco.Web/Macros/IMacroRenderer.cs | 13 + src/Umbraco.Web/Macros/MacroModel.cs | 15 +- src/Umbraco.Web/Macros/MacroRenderer.cs | 355 ++++-------------- .../PublishedContentHashtableConverter.cs | 10 +- .../Mapping/RedirectUrlMapperProfile.cs | 6 +- .../EnsurePublishedContentRequestAttribute.cs | 3 +- src/Umbraco.Web/Mvc/PluginController.cs | 21 +- src/Umbraco.Web/Mvc/RenderMvcController.cs | 6 +- src/Umbraco.Web/Mvc/SurfaceController.cs | 4 +- .../Mvc/UmbracoAuthorizedController.cs | 9 +- src/Umbraco.Web/Mvc/UmbracoController.cs | 13 +- .../Mvc/UmbracoViewPageOfTModel.cs | 12 +- .../MacroContainerValueConverter.cs | 10 +- .../RteMacroRenderingValueConverter.cs | 10 +- src/Umbraco.Web/Runtime/WebRuntimeComposer.cs | 17 +- .../Templates/ITemplateRenderer.cs | 12 + src/Umbraco.Web/Templates/TemplateRenderer.cs | 93 +++-- .../Trees/ApplicationTreeController.cs | 4 +- src/Umbraco.Web/Trees/TreeController.cs | 4 +- src/Umbraco.Web/Trees/TreeControllerBase.cs | 9 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 + .../UmbracoAuthorizedHttpHandler.cs | 7 +- src/Umbraco.Web/UmbracoComponentRenderer.cs | 161 ++++---- src/Umbraco.Web/UmbracoContext.cs | 26 +- src/Umbraco.Web/UmbracoHelper.cs | 136 +++---- src/Umbraco.Web/UmbracoHttpHandler.cs | 15 +- src/Umbraco.Web/UmbracoWebService.cs | 18 +- .../WebApi/UmbracoApiController.cs | 16 +- .../WebApi/UmbracoApiControllerBase.cs | 13 +- .../WebApi/UmbracoAuthorizedApiController.cs | 16 +- 55 files changed, 457 insertions(+), 778 deletions(-) create mode 100644 src/Umbraco.Web/Macros/IMacroRenderer.cs create mode 100644 src/Umbraco.Web/Templates/ITemplateRenderer.cs diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 71e3836b3d..3f44c6764e 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -172,8 +172,7 @@ 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, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, services, appCaches, logger, profilingLogger, umbracoHelper) { } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 602b5907d8..4363996a46 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -152,13 +152,12 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting var membershipHelper = new MembershipHelper(new TestUmbracoContextAccessor(umbCtx), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), null, 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); } diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 575f14e818..b65e503f34 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(), null, 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(), null, Mock.Of(), Mock.Of())); Assert.Pass(); } 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/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 69f6c5d13e..e80385b533 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -128,12 +128,11 @@ 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(), null, 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(), null, Mock.Of(), Mock.Of())); var ctrl = new TestSurfaceController(umbracoContext, helper); var result = ctrl.GetContent(2) as PublishedContentResult; @@ -185,12 +184,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/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..abe7930693 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, ILogger logger, IProfilingLogger profilingLogger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, services, appCaches, logger, profilingLogger, umbracoHelper) { _manifestParser = manifestParser; _features = features; diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index e2d268450c..faaf14319f 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/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index ca747e83f5..97d800a845 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -45,8 +45,8 @@ namespace Umbraco.Web.Editors private readonly ITreeService _treeService; public EntityController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - ITreeService treeService) - : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState) + ITreeService treeService, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _treeService = treeService; } diff --git a/src/Umbraco.Web/Editors/MacroRenderingController.cs b/src/Umbraco.Web/Editors/MacroRenderingController.cs index 65abde759a..f88a84dd72 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; @@ -103,17 +106,14 @@ namespace Umbraco.Web.Editors 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); //if it isn't supposed to be rendered in the editor then return an empty string - if (macro.RenderInEditor == false) + if (!m.UseInEditor) { var response = Request.CreateResponse(); //need to create a specific content result formatted as HTML since this controller has been configured @@ -132,23 +132,24 @@ namespace Umbraco.Web.Editors // 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(); + _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); + //fixme: don't think we need this anymore + var legacyPage = new 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(doc.Id, m.Alias, macroParams).ToString(), Encoding.UTF8, "text/html"); return result; 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/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/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/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/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..b0ea90f9c1 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,29 +180,17 @@ 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 @@ -226,16 +199,23 @@ namespace Umbraco.Web.Macros // 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 +224,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 +238,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 +280,6 @@ namespace Umbraco.Web.Macros } catch (Exception e) { - Exceptions.Add(e); - _plogger.Warn(e, "Failed {MsgIn}", msgIn); var macroErrorEventArgs = new MacroErrorEventArgs @@ -310,11 +288,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 +317,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 +335,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 +347,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 +356,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 +368,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 +393,7 @@ namespace Umbraco.Web.Macros return attributeValue; } - var context = HttpContext.Current; + var context = _umbracoContextAccessor.UmbracoContext.HttpContext; foreach (var token in tokens) { @@ -458,11 +420,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 +437,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 +456,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 ece76090d8..faf33a74cb 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/Mvc/EnsurePublishedContentRequestAttribute.cs b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs index 62a7c48d2b..bba1522350 100644 --- a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs +++ b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs @@ -79,8 +79,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/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..da7e3b08fc 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, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, services, appCaches, logger, 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..a54958c4f7 100644 --- a/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs +++ b/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs @@ -18,10 +18,11 @@ 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, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, services, appCaches, logger, profilingLogger, umbracoHelper) + { + } } } diff --git a/src/Umbraco.Web/Mvc/UmbracoController.cs b/src/Umbraco.Web/Mvc/UmbracoController.cs index 4d70263cb8..61198bd263 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. @@ -77,12 +74,13 @@ namespace Umbraco.Web.Mvc 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, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) { GlobalSettings = globalSettings; UmbracoContext = umbracoContext; @@ -90,6 +88,7 @@ namespace Umbraco.Web.Mvc AppCaches = appCaches; Logger = logger; ProfilingLogger = profilingLogger; + Umbraco = umbracoHelper; } } } diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs index a16f9661aa..2dcb883ed3 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/Runtime/WebRuntimeComposer.cs b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs index de3ca47b38..27c42e2c91 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.Models.PublishedContent; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; @@ -32,6 +33,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; @@ -85,8 +87,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/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 9279eea124..facafdea63 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 PublishedRouter _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, PublishedRouter 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 PublishedRouter 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 oldPageElements, 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(oldPageElements, 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 @@ -184,31 +175,31 @@ namespace Umbraco.Web.Templates // 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["pageElements"] = request.LegacyContentHashTable.Elements; + _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 object oldPageElements, 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]; + oldPageElements = _umbracoContextAccessor.UmbracoContext.HttpContext.Items["pageElements"]; + 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(object oldPageElements, 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["pageElements"] = oldPageElements; + _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 f078e9d79f..9b685550a8 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -143,6 +143,7 @@ + @@ -194,6 +195,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..4ce25acb29 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(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..73e93300ec 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -8,105 +8,76 @@ 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)); + public IPublishedContentQuery ContentQuery { get; } /// /// Gets the Umbraco context. @@ -124,8 +95,7 @@ namespace Umbraco.Web /// /// Gets the membership helper. /// - public MembershipHelper MembershipHelper => _membershipHelper - ?? (_membershipHelper = Current.Factory.GetInstance()); + public MembershipHelper MembershipHelper { get; } /// /// Gets the url provider. @@ -133,14 +103,7 @@ namespace Umbraco.Web 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 +136,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 +159,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 +170,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 +181,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 +218,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 +251,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 +419,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 +601,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 +654,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/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. From 2f1c39ab96936db1281f9d99ae0ba2f72ac29d57 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 31 Jan 2019 15:44:39 +1100 Subject: [PATCH 02/15] Removes the old/strange "pageElements" thing, it's no longer needed --- src/Umbraco.Web/Editors/MacroRenderingController.cs | 6 ------ src/Umbraco.Web/Macros/MacroRenderer.cs | 4 ---- src/Umbraco.Web/Routing/PublishedRouter.cs | 7 +------ src/Umbraco.Web/Templates/TemplateRenderer.cs | 13 ++++--------- 4 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Web/Editors/MacroRenderingController.cs b/src/Umbraco.Web/Editors/MacroRenderingController.cs index f88a84dd72..0fd88e2b1e 100644 --- a/src/Umbraco.Web/Editors/MacroRenderingController.cs +++ b/src/Umbraco.Web/Editors/MacroRenderingController.cs @@ -139,12 +139,6 @@ namespace Umbraco.Web.Editors _variationContextAccessor.VariationContext = new VariationContext(Thread.CurrentThread.CurrentCulture.Name); } - - //fixme: don't think we need this anymore - var legacyPage = new PublishedContentHashtableConverter(doc, _variationContextAccessor); - UmbracoContext.HttpContext.Items["pageElements"] = legacyPage.Elements; - UmbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = null; - var result = Request.CreateResponse(); //need to create a specific content result formatted as HTML since this controller has been configured //with only json formatters. diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs index b0ea90f9c1..9736aa283b 100755 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -195,10 +195,6 @@ namespace Umbraco.Web.Macros #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(string macroAlias, IPublishedContent content, IDictionary macroParams) { var m = _macroService.GetByAlias(macroAlias); diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index 14c36198d8..27baf86522 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -207,9 +207,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; } @@ -252,9 +249,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/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index facafdea63..dc8ca0b16d 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -97,7 +97,7 @@ namespace Umbraco.Web.Templates //First, save all of the items locally that we know are used in the chain of execution, we'll need to restore these //after this page has rendered. - SaveExistingItems(out var oldPageElements, out var oldPublishedRequest, out var oldAltTemplate); + SaveExistingItems(out var oldPublishedRequest, out var oldAltTemplate); try { @@ -110,7 +110,7 @@ namespace Umbraco.Web.Templates finally { //restore items on context objects to continuing rendering the parent template - RestoreItems(oldPageElements, oldPublishedRequest, oldAltTemplate); + RestoreItems(oldPublishedRequest, oldAltTemplate); } } @@ -172,10 +172,7 @@ 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 - _umbracoContextAccessor.UmbracoContext.HttpContext.Items["pageElements"] = request.LegacyContentHashTable.Elements; _umbracoContextAccessor.UmbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = null; _umbracoContextAccessor.UmbracoContext.PublishedRequest = request; } @@ -183,11 +180,10 @@ namespace Umbraco.Web.Templates /// /// Save all items that we know are used for rendering execution to variables so we can restore after rendering /// - private void SaveExistingItems(out object oldPageElements, out PublishedRequest oldPublishedRequest, out object oldAltTemplate) + 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 = _umbracoContextAccessor.UmbracoContext.HttpContext.Items["pageElements"]; oldPublishedRequest = _umbracoContextAccessor.UmbracoContext.PublishedRequest; oldAltTemplate = _umbracoContextAccessor.UmbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate]; } @@ -195,10 +191,9 @@ namespace Umbraco.Web.Templates /// /// Restores all items back to their context's to continue normal page rendering execution /// - private void RestoreItems(object oldPageElements, PublishedRequest oldPublishedRequest, object oldAltTemplate) + private void RestoreItems(PublishedRequest oldPublishedRequest, object oldAltTemplate) { _umbracoContextAccessor.UmbracoContext.PublishedRequest = oldPublishedRequest; - _umbracoContextAccessor.UmbracoContext.HttpContext.Items["pageElements"] = oldPageElements; _umbracoContextAccessor.UmbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = oldAltTemplate; } } From f9e673ca4c7c26b51c7adcd0b3341d31c8fbc332 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 31 Jan 2019 16:54:17 +1100 Subject: [PATCH 03/15] Updates tinymce, adds back the styles for the rendered macro in the rte, properly uses tinymce's noneditable plugin and removes a ton of code relating to the old work arounds we had, now just need to get the macro re-rendering --- src/Umbraco.Web.UI.Client/package-lock.json | 381 ++++++++++++++---- src/Umbraco.Web.UI.Client/package.json | 2 +- .../src/common/services/tinymce.service.js | 184 +-------- src/Umbraco.Web.UI.Client/src/less/rte.less | 9 + 4 files changed, 317 insertions(+), 259 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index fe657ae470..207eba38c5 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1104,6 +1104,7 @@ "resolved": "https://registry.npmjs.org/archive-type/-/archive-type-3.2.0.tgz", "integrity": "sha1-nNnABpV+vpX62tW9YJiUKoE3N/Y=", "dev": true, + "optional": true, "requires": { "file-type": "^3.1.0" }, @@ -1112,7 +1113,8 @@ "version": "3.9.0", "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true + "dev": true, + "optional": true } } }, @@ -1523,6 +1525,7 @@ "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "dev": true, + "optional": true, "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -1532,13 +1535,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1554,6 +1559,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -1742,7 +1748,8 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true + "dev": true, + "optional": true }, "buffer-fill": { "version": "1.0.0", @@ -1761,6 +1768,7 @@ "resolved": "https://registry.npmjs.org/buffer-to-vinyl/-/buffer-to-vinyl-1.1.0.tgz", "integrity": "sha1-APFfruOreh3aLN5tkSG//dB7ImI=", "dev": true, + "optional": true, "requires": { "file-type": "^3.1.0", "readable-stream": "^2.0.2", @@ -1772,19 +1780,22 @@ "version": "3.9.0", "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true + "dev": true, + "optional": true }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1800,6 +1811,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -1808,13 +1820,15 @@ "version": "2.0.3", "resolved": "http://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", - "dev": true + "dev": true, + "optional": true }, "vinyl": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, + "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -1935,7 +1949,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", - "dev": true + "dev": true, + "optional": true }, "caseless": { "version": "0.12.0", @@ -1948,6 +1963,7 @@ "resolved": "https://registry.npmjs.org/caw/-/caw-1.2.0.tgz", "integrity": "sha1-/7Im/n78VHKI3GLuPpcHPCEtEDQ=", "dev": true, + "optional": true, "requires": { "get-proxy": "^1.0.1", "is-obj": "^1.0.0", @@ -1959,7 +1975,8 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true + "dev": true, + "optional": true } } }, @@ -2220,7 +2237,8 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=", - "dev": true + "dev": true, + "optional": true }, "coa": { "version": "2.0.1", @@ -2316,6 +2334,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, + "optional": true, "requires": { "graceful-readlink": ">= 1.0.0" } @@ -2528,6 +2547,7 @@ "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, + "optional": true, "requires": { "capture-stack-trace": "^1.0.0" } @@ -2782,6 +2802,7 @@ "resolved": "https://registry.npmjs.org/decompress/-/decompress-3.0.0.tgz", "integrity": "sha1-rx3VDQbjv8QyRh033hGzjA2ZG+0=", "dev": true, + "optional": true, "requires": { "buffer-to-vinyl": "^1.0.0", "concat-stream": "^1.4.6", @@ -2799,6 +2820,7 @@ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, + "optional": true, "requires": { "arr-flatten": "^1.0.1" } @@ -2807,13 +2829,15 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true + "dev": true, + "optional": true }, "braces": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, + "optional": true, "requires": { "expand-range": "^1.8.1", "preserve": "^0.2.0", @@ -2825,6 +2849,7 @@ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, + "optional": true, "requires": { "is-posix-bracket": "^0.1.0" } @@ -2834,6 +2859,7 @@ "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -2843,6 +2869,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, + "optional": true, "requires": { "inflight": "^1.0.4", "inherits": "2", @@ -2856,6 +2883,7 @@ "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", "dev": true, + "optional": true, "requires": { "extend": "^3.0.0", "glob": "^5.0.3", @@ -2871,13 +2899,15 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -2889,13 +2919,15 @@ "version": "0.10.31", "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true + "dev": true, + "optional": true }, "through2": { "version": "0.6.5", "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, + "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -2907,19 +2939,22 @@ "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "dev": true, + "optional": true }, "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true + "dev": true, + "optional": true }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -2928,13 +2963,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -2944,6 +2981,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, + "optional": true, "requires": { "arr-diff": "^2.0.0", "array-unique": "^0.2.1", @@ -2964,13 +3002,15 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "dev": true, + "optional": true }, "ordered-read-streams": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", "dev": true, + "optional": true, "requires": { "is-stream": "^1.0.1", "readable-stream": "^2.0.1" @@ -2981,6 +3021,7 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2996,6 +3037,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3005,6 +3047,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, + "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -3014,6 +3057,7 @@ "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", "dev": true, + "optional": true, "requires": { "first-chunk-stream": "^1.0.0", "strip-bom": "^2.0.0" @@ -3024,6 +3068,7 @@ "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "dev": true, + "optional": true, "requires": { "json-stable-stringify-without-jsonify": "^1.0.1", "through2-filter": "^3.0.0" @@ -3034,6 +3079,7 @@ "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, + "optional": true, "requires": { "through2": "~2.0.0", "xtend": "~4.0.0" @@ -3046,6 +3092,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, + "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -3057,6 +3104,7 @@ "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", "dev": true, + "optional": true, "requires": { "duplexify": "^3.2.0", "glob-stream": "^5.3.2", @@ -3084,6 +3132,7 @@ "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-3.1.0.tgz", "integrity": "sha1-IXx4n5uURQ76rcXF5TeXj8MzxGY=", "dev": true, + "optional": true, "requires": { "is-tar": "^1.0.0", "object-assign": "^2.0.0", @@ -3097,19 +3146,22 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3122,6 +3174,7 @@ "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, + "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -3132,6 +3185,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, + "optional": true, "requires": { "clone": "^0.2.0", "clone-stats": "^0.0.1" @@ -3144,6 +3198,7 @@ "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-3.1.0.tgz", "integrity": "sha1-iyOTVoE1X58YnYclag+L3ZbZZm0=", "dev": true, + "optional": true, "requires": { "is-bzip2": "^1.0.0", "object-assign": "^2.0.0", @@ -3158,19 +3213,22 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3183,6 +3241,7 @@ "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, + "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -3193,6 +3252,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, + "optional": true, "requires": { "clone": "^0.2.0", "clone-stats": "^0.0.1" @@ -3205,6 +3265,7 @@ "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-3.1.0.tgz", "integrity": "sha1-ssE9+YFmJomRtxXWRH9kLpaW9aA=", "dev": true, + "optional": true, "requires": { "is-gzip": "^1.0.0", "object-assign": "^2.0.0", @@ -3218,19 +3279,22 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3243,6 +3307,7 @@ "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, + "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -3253,6 +3318,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, + "optional": true, "requires": { "clone": "^0.2.0", "clone-stats": "^0.0.1" @@ -3265,6 +3331,7 @@ "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-3.4.0.tgz", "integrity": "sha1-YUdbQVIGa74/7hL51inRX+ZHjus=", "dev": true, + "optional": true, "requires": { "is-zip": "^1.0.0", "read-all-stream": "^3.0.0", @@ -3280,6 +3347,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, + "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -3292,7 +3360,8 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true + "dev": true, + "optional": true }, "deep-is": { "version": "0.1.3", @@ -3491,6 +3560,7 @@ "resolved": "https://registry.npmjs.org/download/-/download-4.4.3.tgz", "integrity": "sha1-qlX9rTktldS2jowr4D4MKqIbqaw=", "dev": true, + "optional": true, "requires": { "caw": "^1.0.1", "concat-stream": "^1.4.7", @@ -3514,6 +3584,7 @@ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, + "optional": true, "requires": { "arr-flatten": "^1.0.1" } @@ -3522,13 +3593,15 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true + "dev": true, + "optional": true }, "braces": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, + "optional": true, "requires": { "expand-range": "^1.8.1", "preserve": "^0.2.0", @@ -3540,6 +3613,7 @@ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, + "optional": true, "requires": { "is-posix-bracket": "^0.1.0" } @@ -3549,6 +3623,7 @@ "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -3558,6 +3633,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, + "optional": true, "requires": { "inflight": "^1.0.4", "inherits": "2", @@ -3571,6 +3647,7 @@ "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", "dev": true, + "optional": true, "requires": { "extend": "^3.0.0", "glob": "^5.0.3", @@ -3586,13 +3663,15 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3604,13 +3683,15 @@ "version": "0.10.31", "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true + "dev": true, + "optional": true }, "through2": { "version": "0.6.5", "resolved": "http://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, + "optional": true, "requires": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" @@ -3622,19 +3703,22 @@ "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "dev": true, + "optional": true }, "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true + "dev": true, + "optional": true }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -3643,13 +3727,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -3659,6 +3745,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, + "optional": true, "requires": { "arr-diff": "^2.0.0", "array-unique": "^0.2.1", @@ -3679,13 +3766,15 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "dev": true, + "optional": true }, "ordered-read-streams": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", "dev": true, + "optional": true, "requires": { "is-stream": "^1.0.1", "readable-stream": "^2.0.1" @@ -3696,6 +3785,7 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3711,6 +3801,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3720,6 +3811,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, + "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -3729,6 +3821,7 @@ "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", "dev": true, + "optional": true, "requires": { "first-chunk-stream": "^1.0.0", "strip-bom": "^2.0.0" @@ -3739,6 +3832,7 @@ "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "dev": true, + "optional": true, "requires": { "json-stable-stringify-without-jsonify": "^1.0.1", "through2-filter": "^3.0.0" @@ -3749,6 +3843,7 @@ "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, + "optional": true, "requires": { "through2": "~2.0.0", "xtend": "~4.0.0" @@ -3761,6 +3856,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, + "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -3772,6 +3868,7 @@ "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", "dev": true, + "optional": true, "requires": { "duplexify": "^3.2.0", "glob-stream": "^5.3.2", @@ -3814,6 +3911,7 @@ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==", "dev": true, + "optional": true, "requires": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", @@ -3826,6 +3924,7 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, + "optional": true, "requires": { "once": "^1.4.0" } @@ -3834,13 +3933,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3850,6 +3951,7 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3865,6 +3967,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3876,6 +3979,7 @@ "resolved": "https://registry.npmjs.org/each-async/-/each-async-1.1.1.tgz", "integrity": "sha1-3uUim98KtrogEqOV4bhpq/iBNHM=", "dev": true, + "optional": true, "requires": { "onetime": "^1.0.0", "set-immediate-shim": "^1.0.0" @@ -3885,7 +3989,8 @@ "version": "1.1.0", "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true + "dev": true, + "optional": true } } }, @@ -4825,6 +4930,7 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, + "optional": true, "requires": { "pend": "~1.2.0" } @@ -4872,13 +4978,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz", "integrity": "sha1-5hz4BfDeHJhFZ9A4bcXfUO5a9+Q=", - "dev": true + "dev": true, + "optional": true }, "filenamify": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz", "integrity": "sha1-qfL/0RxQO+0wABUCknI3jx8TZaU=", "dev": true, + "optional": true, "requires": { "filename-reserved-regex": "^1.0.0", "strip-outer": "^1.0.0", @@ -5125,7 +5233,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", - "dev": true + "dev": true, + "optional": true }, "fs-extra": { "version": "1.0.0", @@ -5189,7 +5298,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5210,12 +5320,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5230,17 +5342,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5357,7 +5472,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5369,6 +5485,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5383,6 +5500,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5390,12 +5508,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -5414,6 +5534,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5494,7 +5615,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5506,6 +5628,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5591,7 +5714,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -5627,6 +5751,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5646,6 +5771,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5689,12 +5815,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -5730,6 +5858,7 @@ "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-1.1.0.tgz", "integrity": "sha1-iUhUSRvFkbDxR9euVw9cZ4tyVus=", "dev": true, + "optional": true, "requires": { "rc": "^1.1.2" } @@ -6036,6 +6165,7 @@ "resolved": "http://registry.npmjs.org/got/-/got-5.7.1.tgz", "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", "dev": true, + "optional": true, "requires": { "create-error-class": "^3.0.1", "duplexer2": "^0.1.4", @@ -6059,6 +6189,7 @@ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, + "optional": true, "requires": { "readable-stream": "^2.0.2" } @@ -6067,19 +6198,22 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "dev": true, + "optional": true }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, + "optional": true, "requires": { "error-ex": "^1.2.0" } @@ -6089,6 +6223,7 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -6104,6 +6239,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -6123,7 +6259,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true + "dev": true, + "optional": true }, "growly": { "version": "1.3.0", @@ -6419,6 +6556,7 @@ "resolved": "https://registry.npmjs.org/gulp-decompress/-/gulp-decompress-1.2.0.tgz", "integrity": "sha1-jutlpeAV+O2FMsr+KEVJYGJvDcc=", "dev": true, + "optional": true, "requires": { "archive-type": "^3.0.0", "decompress": "^3.0.0", @@ -6430,13 +6568,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -6452,6 +6592,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -7069,6 +7210,7 @@ "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=", "dev": true, + "optional": true, "requires": { "convert-source-map": "^1.1.1", "graceful-fs": "^4.1.2", @@ -7081,13 +7223,15 @@ "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "dev": true, + "optional": true }, "strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, + "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -7097,6 +7241,7 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, + "optional": true, "requires": { "clone": "^1.0.0", "clone-stats": "^0.0.1", @@ -7927,7 +8072,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-bzip2/-/is-bzip2-1.0.0.tgz", "integrity": "sha1-XuWOqlounIDiFAe+3yOuWsCRs/w=", - "dev": true + "dev": true, + "optional": true }, "is-callable": { "version": "1.1.4", @@ -8062,7 +8208,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-gzip/-/is-gzip-1.0.0.tgz", "integrity": "sha1-bKiwe5nHeZgCWQDlVc7Y7YCHmoM=", - "dev": true + "dev": true, + "optional": true }, "is-jpg": { "version": "1.0.1", @@ -8075,7 +8222,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.1.1.tgz", "integrity": "sha1-fUxXKDd+84bD4ZSpkRv1fG3DNec=", - "dev": true + "dev": true, + "optional": true }, "is-number": { "version": "3.0.0", @@ -8141,7 +8289,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", - "dev": true + "dev": true, + "optional": true }, "is-regex": { "version": "1.0.4", @@ -8171,7 +8320,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", - "dev": true + "dev": true, + "optional": true }, "is-stream": { "version": "1.1.0", @@ -8201,7 +8351,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-tar/-/is-tar-1.0.0.tgz", "integrity": "sha1-L2suF5LB9bs2UZrKqdZcDSb+hT0=", - "dev": true + "dev": true, + "optional": true }, "is-typedarray": { "version": "1.0.0", @@ -8222,7 +8373,8 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", - "dev": true + "dev": true, + "optional": true }, "is-utf8": { "version": "0.2.1", @@ -8234,7 +8386,8 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=", - "dev": true + "dev": true, + "optional": true }, "is-windows": { "version": "1.0.2", @@ -8252,7 +8405,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-zip/-/is-zip-1.0.0.tgz", "integrity": "sha1-R7Co/004p2QxzP2ZqOFaTIa6IyU=", - "dev": true + "dev": true, + "optional": true }, "isarray": { "version": "0.0.1", @@ -8573,6 +8727,7 @@ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", "dev": true, + "optional": true, "requires": { "readable-stream": "^2.0.5" }, @@ -8581,13 +8736,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -8603,6 +8760,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -8908,7 +9066,8 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", - "dev": true + "dev": true, + "optional": true }, "lodash.isobject": { "version": "2.4.1", @@ -9105,7 +9264,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", - "dev": true + "dev": true, + "optional": true }, "lpad-align": { "version": "1.1.2", @@ -9501,7 +9661,8 @@ "version": "1.0.0", "resolved": "http://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=", - "dev": true + "dev": true, + "optional": true }, "node.extend": { "version": "1.1.8", @@ -12551,7 +12712,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "dev": true, + "optional": true }, "p-pipe": { "version": "1.2.0", @@ -13262,7 +13424,8 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true + "dev": true, + "optional": true }, "preserve": { "version": "0.2.0", @@ -13394,6 +13557,7 @@ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, + "optional": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -13406,6 +13570,7 @@ "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=", "dev": true, + "optional": true, "requires": { "pinkie-promise": "^2.0.0", "readable-stream": "^2.0.0" @@ -13415,13 +13580,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -13437,6 +13604,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -14053,6 +14221,7 @@ "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", "dev": true, + "optional": true, "requires": { "commander": "~2.8.1" } @@ -14198,7 +14367,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true + "dev": true, + "optional": true }, "set-value": { "version": "2.0.0", @@ -14703,7 +14873,8 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.2.tgz", "integrity": "sha1-5sgLYjEj19gM8TLOU480YokHJQI=", - "dev": true + "dev": true, + "optional": true }, "static-extend": { "version": "0.1.2", @@ -14747,6 +14918,7 @@ "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", "dev": true, + "optional": true, "requires": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" @@ -14757,6 +14929,7 @@ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, + "optional": true, "requires": { "readable-stream": "^2.0.2" } @@ -14765,13 +14938,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -14787,6 +14962,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -14803,7 +14979,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true + "dev": true, + "optional": true }, "streamroller": { "version": "0.7.0", @@ -14981,6 +15158,7 @@ "resolved": "http://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", "integrity": "sha1-lgu9EoeETzl1pFWKoQOoJV4kVqA=", "dev": true, + "optional": true, "requires": { "chalk": "^1.0.0", "get-stdin": "^4.0.1", @@ -14994,13 +15172,15 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "dev": true, + "optional": true }, "chalk": { "version": "1.1.3", "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, + "optional": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -15014,6 +15194,7 @@ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", "integrity": "sha1-hHSREZ/MtftDYhfMc39/qtUPYD8=", "dev": true, + "optional": true, "requires": { "is-relative": "^0.1.0" } @@ -15022,13 +15203,15 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz", "integrity": "sha1-kF/uiuhvRbPsYUvDwVyGnfCHboI=", - "dev": true + "dev": true, + "optional": true }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "dev": true, + "optional": true } } }, @@ -15059,6 +15242,7 @@ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, + "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15092,6 +15276,7 @@ "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz", "integrity": "sha1-HGYfZnBX9jvLeHWqFDi8FiUlFW4=", "dev": true, + "optional": true, "requires": { "chalk": "^1.0.0" }, @@ -15100,13 +15285,15 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "dev": true, + "optional": true }, "chalk": { "version": "1.1.3", "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, + "optional": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -15119,7 +15306,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "dev": true, + "optional": true } } }, @@ -15181,6 +15369,7 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "dev": true, + "optional": true, "requires": { "bl": "^1.0.0", "buffer-alloc": "^1.2.0", @@ -15196,6 +15385,7 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", "dev": true, + "optional": true, "requires": { "once": "^1.4.0" } @@ -15204,13 +15394,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -15220,6 +15412,7 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -15235,6 +15428,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -15333,6 +15527,7 @@ "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", "dev": true, + "optional": true, "requires": { "through2": "~2.0.0", "xtend": "~4.0.0" @@ -15357,7 +15552,8 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc=", - "dev": true + "dev": true, + "optional": true }, "timsort": { "version": "0.3.0", @@ -15402,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", @@ -15420,6 +15616,7 @@ "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=", "dev": true, + "optional": true, "requires": { "extend-shallow": "^2.0.1" }, @@ -15429,6 +15626,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -15445,7 +15643,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", - "dev": true + "dev": true, + "optional": true }, "to-fast-properties": { "version": "2.0.0", @@ -15524,6 +15723,7 @@ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", "dev": true, + "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15772,7 +15972,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=", - "dev": true + "dev": true, + "optional": true }, "upath": { "version": "1.1.0", @@ -15800,6 +16001,7 @@ "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, + "optional": true, "requires": { "prepend-http": "^1.0.1" } @@ -15885,7 +16087,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=", - "dev": true + "dev": true, + "optional": true }, "validate-npm-package-license": { "version": "3.0.4", @@ -15930,6 +16133,7 @@ "resolved": "https://registry.npmjs.org/vinyl-assign/-/vinyl-assign-1.2.1.tgz", "integrity": "sha1-TRmIkbVRWRHXcajNnFSApGoHSkU=", "dev": true, + "optional": true, "requires": { "object-assign": "^4.0.1", "readable-stream": "^2.0.0" @@ -15939,19 +16143,22 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -15967,6 +16174,7 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -16106,6 +16314,7 @@ "resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz", "integrity": "sha1-0bFPOdLiy0q4xAmPdW/ksWTkc9Q=", "dev": true, + "optional": true, "requires": { "wrap-fn": "^0.1.0" } @@ -16196,6 +16405,7 @@ "resolved": "https://registry.npmjs.org/wrap-fn/-/wrap-fn-0.1.5.tgz", "integrity": "sha1-8htuQQFv9KfjFyDbxjoJAWvfmEU=", "dev": true, + "optional": true, "requires": { "co": "3.1.0" } @@ -16293,6 +16503,7 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, + "optional": true, "requires": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" 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/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index 0ba795626a..b2ec94fffe 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 @@ -502,96 +502,22 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s onPostRender: function () { var ctrl = this; - var isOnMacroElement = false; /** - 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 + //set the button active/inactive 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; - } - } + //NOTE: This could be another way to deal with the active/inactive state + //editor.on('ObjectSelected', function (e) {}); + /** when the contents load we need to find any macros declared and load in their content */ editor.on("LoadContent", function (o) { @@ -602,102 +528,9 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s }); - /** - * 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; - } - }); - //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 */ @@ -739,6 +572,10 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s insertMacroInEditor: function (editor, macroObject) { + //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. var macroSyntaxComment = ""; @@ -746,7 +583,8 @@ 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 + ''); diff --git a/src/Umbraco.Web.UI.Client/src/less/rte.less b/src/Umbraco.Web.UI.Client/src/less/rte.less index 9aae9840d1..d9888eb9d3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte.less @@ -26,7 +26,16 @@ } /* loader for macro loading in tinymce*/ +.mce-content-body .umb-macro-holder { + border: 3px dotted @pinkLight; + padding: 7px; + display: block; + margin: 3px; +} .umb-rte .mce-content-body .umb-macro-holder.loading { + + /*fixme: this image doesn't exist*/ + background: url(img/loader.gif) right no-repeat; background-size: 18px; background-position-x: 99%; From 9abd72f6231a9fc517b2cb89776448df41a2453a Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 31 Jan 2019 17:12:44 +1100 Subject: [PATCH 04/15] gets macro content loading on rte load --- .../src/common/services/tinymce.service.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 b2ec94fffe..90cb793a91 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 @@ -474,6 +474,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 () { + createInsertMacroScope.loadMacroContent($(this), null); + }); + }); /** @@ -518,16 +528,6 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s //NOTE: This could be another way to deal with the active/inactive state //editor.on('ObjectSelected', function (e) {}); - /** 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); - }); - - }); - //set onNodeChanged event listener editor.on('NodeChange', onNodeChanged); From f74a49f5a3e1bbf411fa32128526eaa385eae991 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 1 Feb 2019 10:18:53 +1100 Subject: [PATCH 05/15] Gets the macros being able to be updated, now to fix the Classic mode, since this now only works in distraction free mode --- .../src/common/services/tinymce.service.js | 36 +++++++++++-------- src/Umbraco.Web/Routing/PublishedRequest.cs | 1 - 2 files changed, 21 insertions(+), 16 deletions(-) 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 90cb793a91..39d69158c9 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 @@ -458,7 +458,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) { @@ -481,7 +482,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s //get all macro divs and load their content $(editor.dom.select(".umb-macro-holder.mceNonEditable")).each(function () { - createInsertMacroScope.loadMacroContent($(this), null); + self.loadMacroContent($(this), null); }); }); @@ -511,18 +512,18 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s tooltip: 'Insert macro', onPostRender: function () { - var ctrl = this; - + let ctrl = this; + /** * 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"); + activeMacroElement = getRealMacroElem(evt.element); //set the button active/inactive - ctrl.active($macroElement.length !== 0); + ctrl.active(activeMacroElement !== null); } //NOTE: This could be another way to deal with the active/inactive state @@ -543,11 +544,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; }); @@ -557,7 +556,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 }; } @@ -570,7 +570,7 @@ 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 @@ -588,7 +588,13 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s }, 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)); @@ -1126,7 +1132,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) { @@ -1184,7 +1190,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/Routing/PublishedRequest.cs b/src/Umbraco.Web/Routing/PublishedRequest.cs index c5475f8b73..a2ff71e634 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(); From dae8dfdc59cbf3dd423208851ef0769a2a0de991 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 1 Feb 2019 11:34:28 +1100 Subject: [PATCH 06/15] fix merge --- src/Umbraco.Web/Templates/TemplateRenderer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index dc8ca0b16d..df711a084e 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -26,12 +26,12 @@ namespace Umbraco.Web.Templates internal class TemplateRenderer : ITemplateRenderer { private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly PublishedRouter _publishedRouter; + private readonly IPublishedRouter _publishedRouter; private readonly IFileService _fileService; private readonly ILocalizationService _languageService; private readonly IWebRoutingSection _webRoutingSection; - public TemplateRenderer(IUmbracoContextAccessor umbracoContextAccessor, PublishedRouter publishedRouter, IFileService fileService, ILocalizationService textService, IWebRoutingSection webRoutingSection) + public TemplateRenderer(IUmbracoContextAccessor umbracoContextAccessor, IPublishedRouter publishedRouter, IFileService fileService, ILocalizationService textService, IWebRoutingSection webRoutingSection) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _publishedRouter = publishedRouter ?? throw new ArgumentNullException(nameof(publishedRouter)); From 3eb6cd9e217b51e50396f45031260b4caa93d7c7 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 1 Feb 2019 13:50:39 +1100 Subject: [PATCH 07/15] Macros are now working inline in the rte with both the Classic and Distraction free modes --- .../src/common/services/tinymce.service.js | 6 +++++- src/Umbraco.Web.UI.Client/src/less/rte.less | 16 ---------------- 2 files changed, 5 insertions(+), 17 deletions(-) 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 39d69158c9..a0fbbc19d6 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) { diff --git a/src/Umbraco.Web.UI.Client/src/less/rte.less b/src/Umbraco.Web.UI.Client/src/less/rte.less index d9888eb9d3..ba8d02c1e1 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte.less @@ -25,22 +25,6 @@ padding:10px; } -/* loader for macro loading in tinymce*/ -.mce-content-body .umb-macro-holder { - border: 3px dotted @pinkLight; - padding: 7px; - display: block; - margin: 3px; -} -.umb-rte .mce-content-body .umb-macro-holder.loading { - - /*fixme: this image doesn't exist*/ - - background: url(img/loader.gif) right no-repeat; - background-size: 18px; - background-position-x: 99%; -} - .umb-rte .mce-container { box-sizing: border-box; } From d7d82ea98d41206bbe157598cab6ba5709958df0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 1 Feb 2019 14:02:45 +1100 Subject: [PATCH 08/15] Allows grid rte to be distraction-free, the parameter was not being passed but it can be configured --- .../common/directives/components/grid/grid.rte.directive.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From c2ff32aaab2e36204f5b464d80f655cd062142be Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 1 Feb 2019 15:24:07 +1100 Subject: [PATCH 09/15] Fixing tests, don't expose UmbracoContext on UmbracoHelper, fixing all of the editor constructors --- .../TestControllerActivator.cs | 8 +- .../TestControllerActivatorBase.cs | 4 +- .../ControllerTesting/TestRunner.cs | 4 +- .../ControllerTesting/TestStartup.cs | 4 +- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 17 +++ .../Web/Controllers/ContentControllerTests.cs | 114 ++++++++++++++---- .../Web/Controllers/UsersControllerTests.cs | 46 +++++-- .../BackOfficeNotificationsController.cs | 14 ++- src/Umbraco.Web/Editors/CodeFileController.cs | 7 ++ src/Umbraco.Web/Editors/ContentController.cs | 5 +- .../Editors/ContentControllerBase.cs | 7 ++ src/Umbraco.Web/Editors/DataTypeController.cs | 5 +- .../Editors/DictionaryController.cs | 10 ++ src/Umbraco.Web/Editors/MacrosController.cs | 7 +- src/Umbraco.Web/Editors/MediaController.cs | 4 +- src/Umbraco.Web/Editors/MemberController.cs | 5 +- .../RedirectUrlManagementController.cs | 4 +- .../Editors/RelationTypeController.cs | 9 ++ src/Umbraco.Web/Editors/TemplateController.cs | 10 ++ src/Umbraco.Web/Editors/UsersController.cs | 6 + src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 11 +- src/Umbraco.Web/UmbracoHelper.cs | 17 +-- 22 files changed, 245 insertions(+), 73 deletions(-) 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 f576c66291..4e352488be 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -159,9 +159,9 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting 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/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 7c8d1100f8..4294faf4f8 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,21 @@ namespace Umbraco.Tests.Testing .Append() .Append(); Composition.RegisterUnique(); + + //TODO: A lot of this is just copied from the WebRuntimeComposer, maybe we should just compose it all? + 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(); } protected virtual void ComposeWtf() diff --git a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs index cf0bf689a1..7afb41c983 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 @@ -206,17 +211,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"), @@ -232,21 +248,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"), @@ -263,14 +289,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); @@ -283,7 +319,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"), @@ -297,7 +333,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); @@ -306,11 +342,21 @@ namespace Umbraco.Tests.Web.Controllers .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success 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"), @@ -328,7 +374,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); @@ -337,15 +383,25 @@ namespace Umbraco.Tests.Web.Controllers .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success 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"), @@ -363,7 +419,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); @@ -372,15 +428,25 @@ namespace Umbraco.Tests.Web.Controllers .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success 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/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.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 6cded2a253..ad261f0d04 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/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/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 7a787d8848..24b230df13 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) + public MediaController(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/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index f4a8dfbd56..af4061f5cd 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/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/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/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/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/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 73e93300ec..4b1b125a96 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -78,20 +78,7 @@ namespace Umbraco.Web /// Gets the query context. /// public IPublishedContentQuery ContentQuery { get; } - - /// - /// Gets the Umbraco context. - /// - public UmbracoContext UmbracoContext - { - get - { - if (_umbracoContext == null) - throw new NullReferenceException("UmbracoContext has not been set."); - return _umbracoContext; - } - } - + /// /// Gets the membership helper. /// @@ -100,7 +87,7 @@ namespace Umbraco.Web /// /// Gets the url provider. /// - public UrlProvider UrlProvider => UmbracoContext.UrlProvider; + public UrlProvider UrlProvider => _umbracoContext.UrlProvider; /// /// Gets (or sets) the current item assigned to the UmbracoHelper. From 036ca7f1e5a20804860f7e07f98c8f78fadf639b Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 1 Feb 2019 15:30:38 +1100 Subject: [PATCH 10/15] fixing tests --- .../Web/Mvc/SurfaceControllerTests.cs | 32 ++----------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 0ff3662f95..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( @@ -131,13 +104,14 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(), Mock.Of(), Mock.Of(), - Mock.Of(), + 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); } From ad5b166f3f48d085e774ac57f8d10b96bc8d5981 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 1 Feb 2019 15:44:32 +1100 Subject: [PATCH 11/15] Fixes tests, simplifies constructors --- .../Routing/RenderRouteHandlerTests.cs | 18 ++++++++++++++++-- .../TestHelpers/Stubs/TestControllerFactory.cs | 10 ++++++++++ src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 14 -------------- .../Editors/BackOfficeController.cs | 4 ++-- src/Umbraco.Web/Mvc/RenderMvcController.cs | 4 ++-- .../Mvc/UmbracoAuthorizedController.cs | 3 ++- src/Umbraco.Web/Mvc/UmbracoController.cs | 5 ++--- 7 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 3f44c6764e..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,7 +185,8 @@ namespace Umbraco.Tests.Routing /// public class CustomDocumentController : RenderMvcController { - public CustomDocumentController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, services, appCaches, logger, profilingLogger, umbracoHelper) + 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/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/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 4294faf4f8..702a99b510 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -231,20 +231,6 @@ namespace Umbraco.Tests.Testing .Append(); Composition.RegisterUnique(); - //TODO: A lot of this is just copied from the WebRuntimeComposer, maybe we should just compose it all? - 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(); } protected virtual void ComposeWtf() diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index abe7930693..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, UmbracoHelper umbracoHelper) - : base(globalSettings, umbracoContext, services, appCaches, logger, profilingLogger, umbracoHelper) + 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/Mvc/RenderMvcController.cs b/src/Umbraco.Web/Mvc/RenderMvcController.cs index da7e3b08fc..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, UmbracoHelper umbracoHelper) - : base(globalSettings, umbracoContext, services, appCaches, logger, profilingLogger, umbracoHelper) + public RenderMvcController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) + : base(globalSettings, umbracoContext, services, appCaches, profilingLogger, umbracoHelper) { ActionInvoker = new RenderActionInvoker(); } diff --git a/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs b/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs index a54958c4f7..ae97f22dbe 100644 --- a/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs +++ b/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs @@ -21,7 +21,8 @@ namespace Umbraco.Web.Mvc { } - protected UmbracoAuthorizedController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, services, appCaches, logger, profilingLogger, umbracoHelper) + 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 61198bd263..79987ab6b4 100644 --- a/src/Umbraco.Web/Mvc/UmbracoController.cs +++ b/src/Umbraco.Web/Mvc/UmbracoController.cs @@ -73,20 +73,19 @@ namespace Umbraco.Web.Mvc 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, UmbracoHelper umbracoHelper) + 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; } From bb64141207356f9082c621d93fb5d16bca34e2bb Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 1 Feb 2019 16:35:10 +1100 Subject: [PATCH 12/15] removes obsolete method --- .../Mvc/MemberAuthorizeAttribute.cs | 46 ++++--------------- src/Umbraco.Web/Security/MembershipHelper.cs | 5 -- src/Umbraco.Web/Security/WebSecurity.cs | 25 +--------- .../WebApi/MemberAuthorizeAttribute.cs | 46 +++---------------- 4 files changed, 16 insertions(+), 106 deletions(-) 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/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/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); } } From a0cda4075de6e9a023ea0c9cebd84bd25fc24947 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 4 Feb 2019 13:42:07 +1100 Subject: [PATCH 13/15] Fixes issue with rendering macro content when the page doesn't exist --- src/Umbraco.Web/Editors/EntityController.cs | 5 +++++ src/Umbraco.Web/Editors/MacroRenderingController.cs | 13 ++++--------- src/Umbraco.Web/Models/PartialViewMacroModel.cs | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 5dc13e77cf..55604e0eb9 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -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 0fd88e2b1e..87887f0cd7 100644 --- a/src/Umbraco.Web/Editors/MacroRenderingController.cs +++ b/src/Umbraco.Web/Editors/MacroRenderingController.cs @@ -102,18 +102,13 @@ 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); //if it isn't supposed to be rendered in the editor then return an empty string - if (!m.UseInEditor) + //currently we cannot render a macro if the page doesn't yet exist + if (pageId == -1 || !m.UseInEditor) { var response = Request.CreateResponse(); //need to create a specific content result formatted as HTML since this controller has been configured @@ -130,7 +125,7 @@ namespace Umbraco.Web.Editors // 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 publishedContent = UmbracoContext.ContentCache.GetById(pageId); var culture = publishedContent?.GetCulture(); _variationContextAccessor.VariationContext = new VariationContext(); //must have an active variation context! if (culture != null) @@ -143,7 +138,7 @@ namespace Umbraco.Web.Editors //need to create a specific content result formatted as HTML since this controller has been configured //with only json formatters. result.Content = new StringContent( - _componentRenderer.RenderMacro(doc.Id, m.Alias, macroParams).ToString(), + _componentRenderer.RenderMacro(pageId, m.Alias, macroParams).ToString(), Encoding.UTF8, "text/html"); return result; 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; } } } From 9fba7345aa2d5c731e0d6ab615336031fcb5ef31 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 4 Feb 2019 13:48:53 +1100 Subject: [PATCH 14/15] When rendering macro contents, be sure to use the preview published content since the item --- src/Umbraco.Web/Editors/MacroRenderingController.cs | 11 +++++------ src/Umbraco.Web/UmbracoComponentRenderer.cs | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web/Editors/MacroRenderingController.cs b/src/Umbraco.Web/Editors/MacroRenderingController.cs index 87887f0cd7..3cd2e86dd8 100644 --- a/src/Umbraco.Web/Editors/MacroRenderingController.cs +++ b/src/Umbraco.Web/Editors/MacroRenderingController.cs @@ -106,9 +106,11 @@ namespace Umbraco.Web.Editors if (m == null) throw new HttpResponseException(HttpStatusCode.NotFound); + var publishedContent = UmbracoContext.ContentCache.GetById(true, pageId); + //if it isn't supposed to be rendered in the editor then return an empty string //currently we cannot render a macro if the page doesn't yet exist - if (pageId == -1 || !m.UseInEditor) + 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 @@ -118,15 +120,12 @@ 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(pageId); - var culture = publishedContent?.GetCulture(); + + var culture = publishedContent.GetCulture(); _variationContextAccessor.VariationContext = new VariationContext(); //must have an active variation context! if (culture != null) { diff --git a/src/Umbraco.Web/UmbracoComponentRenderer.cs b/src/Umbraco.Web/UmbracoComponentRenderer.cs index 4ce25acb29..25c2d78731 100644 --- a/src/Umbraco.Web/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/UmbracoComponentRenderer.cs @@ -92,7 +92,7 @@ namespace Umbraco.Web if (contentId == default) throw new ArgumentException("Invalid content id " + contentId); - var content = _umbracoContextAccessor.UmbracoContext.ContentCache?.GetById(contentId); + var content = _umbracoContextAccessor.UmbracoContext.ContentCache?.GetById(true, contentId); if (content == null) throw new InvalidOperationException("Cannot render a macro, no content found by id " + contentId); From 30452f0b17f3c6210497fe9de9a7c588d64620e6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 4 Feb 2019 14:00:01 +1100 Subject: [PATCH 15/15] Fixes rte toolbar pinning in the grid --- .../src/common/services/tinymce.service.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 a0fbbc19d6..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 @@ -1015,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;