diff --git a/src/Umbraco.Core/Actions/ActionCollectionBuilder.cs b/src/Umbraco.Core/Actions/ActionCollectionBuilder.cs index ec1a9210a7..eaea63b3a3 100644 --- a/src/Umbraco.Core/Actions/ActionCollectionBuilder.cs +++ b/src/Umbraco.Core/Actions/ActionCollectionBuilder.cs @@ -4,7 +4,7 @@ using System.Linq; using Umbraco.Core.Composing; namespace Umbraco.Web.Actions { - internal class ActionCollectionBuilder : LazyCollectionBuilderBase + public class ActionCollectionBuilder : LazyCollectionBuilderBase { protected override ActionCollectionBuilder This => this; diff --git a/src/Umbraco.Core/CompositionExtensions.cs b/src/Umbraco.Core/CompositionExtensions.cs index a16abf76ac..bbea868f55 100644 --- a/src/Umbraco.Core/CompositionExtensions.cs +++ b/src/Umbraco.Core/CompositionExtensions.cs @@ -21,7 +21,7 @@ namespace Umbraco.Core /// /// The composition. /// - internal static ActionCollectionBuilder Actions(this Composition composition) + public static ActionCollectionBuilder Actions(this Composition composition) => composition.WithCollectionBuilder(); /// @@ -45,7 +45,7 @@ namespace Umbraco.Core /// /// The composition. /// - internal static EditorValidatorCollectionBuilder EditorValidators(this Composition composition) + public static EditorValidatorCollectionBuilder EditorValidators(this Composition composition) => composition.WithCollectionBuilder(); /// @@ -81,7 +81,7 @@ namespace Umbraco.Core /// The composition. public static SectionCollectionBuilder Sections(this Composition composition) => composition.WithCollectionBuilder(); - + /// /// Gets the components collection builder. /// diff --git a/src/Umbraco.Core/CompositionExtensions_Uniques.cs b/src/Umbraco.Core/CompositionExtensions_Uniques.cs index 52f1ce7ecd..8352eb33ec 100644 --- a/src/Umbraco.Core/CompositionExtensions_Uniques.cs +++ b/src/Umbraco.Core/CompositionExtensions_Uniques.cs @@ -31,5 +31,20 @@ namespace Umbraco.Core /// public static void RegisterUnique(this Composition composition, TService instance) => composition.RegisterUnique(typeof(TService), instance); + + + + /// + /// Registers a unique service with an implementation type. + /// + public static void RegisterMultipleUnique(this Composition composition) + where TImplementing : class, TService1, TService2 + where TService1 : class + where TService2 : class + { + composition.RegisterUnique(); + composition.RegisterUnique(factory => factory.GetInstance()); + composition.RegisterUnique(factory => factory.GetInstance()); + } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/ContentApps/ContentEditorContentAppFactory.cs b/src/Umbraco.Core/ContentApps/ContentEditorContentAppFactory.cs index add7e2f16a..467bc6c29f 100644 --- a/src/Umbraco.Core/ContentApps/ContentEditorContentAppFactory.cs +++ b/src/Umbraco.Core/ContentApps/ContentEditorContentAppFactory.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Models.Membership; namespace Umbraco.Web.ContentApps { - internal class ContentEditorContentAppFactory : IContentAppFactory + public class ContentEditorContentAppFactory : IContentAppFactory { // see note on ContentApp internal const int Weight = -100; diff --git a/src/Umbraco.Core/ContentApps/ListViewContentAppFactory.cs b/src/Umbraco.Core/ContentApps/ListViewContentAppFactory.cs index 3e0dea0f5e..d66de3b238 100644 --- a/src/Umbraco.Core/ContentApps/ListViewContentAppFactory.cs +++ b/src/Umbraco.Core/ContentApps/ListViewContentAppFactory.cs @@ -10,7 +10,7 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.ContentApps { - internal class ListViewContentAppFactory : IContentAppFactory + public class ListViewContentAppFactory : IContentAppFactory { // see note on ContentApp private const int Weight = -666; diff --git a/src/Umbraco.Web/DefaultEventMessagesFactory.cs b/src/Umbraco.Core/DefaultEventMessagesFactory.cs similarity index 92% rename from src/Umbraco.Web/DefaultEventMessagesFactory.cs rename to src/Umbraco.Core/DefaultEventMessagesFactory.cs index 39be829a7d..0d53645b5e 100644 --- a/src/Umbraco.Web/DefaultEventMessagesFactory.cs +++ b/src/Umbraco.Core/DefaultEventMessagesFactory.cs @@ -3,7 +3,7 @@ using Umbraco.Core.Events; namespace Umbraco.Web { - internal class DefaultEventMessagesFactory : IEventMessagesFactory + public class DefaultEventMessagesFactory : IEventMessagesFactory { private readonly IEventMessagesAccessor _eventMessagesAccessor; diff --git a/src/Umbraco.Core/HybridEventMessagesAccessor.cs b/src/Umbraco.Core/HybridEventMessagesAccessor.cs index b2700eb137..82e784e093 100644 --- a/src/Umbraco.Core/HybridEventMessagesAccessor.cs +++ b/src/Umbraco.Core/HybridEventMessagesAccessor.cs @@ -3,7 +3,7 @@ using Umbraco.Core.Events; namespace Umbraco.Web { - internal class HybridEventMessagesAccessor : HybridAccessorBase, IEventMessagesAccessor + public class HybridEventMessagesAccessor : HybridAccessorBase, IEventMessagesAccessor { protected override string ItemKey => "Umbraco.Core.Events.HybridEventMessagesAccessor"; diff --git a/src/Umbraco.Core/Services/SectionService.cs b/src/Umbraco.Core/Services/SectionService.cs index 7f97b7b71a..f66ab5ef62 100644 --- a/src/Umbraco.Core/Services/SectionService.cs +++ b/src/Umbraco.Core/Services/SectionService.cs @@ -7,7 +7,7 @@ using Umbraco.Web.Sections; namespace Umbraco.Web.Services { - internal class SectionService : ISectionService + public class SectionService : ISectionService { private readonly IUserService _userService; private readonly SectionCollection _sectionCollection; diff --git a/src/Umbraco.Core/Services/TreeService.cs b/src/Umbraco.Core/Services/TreeService.cs index dc500f2583..48cc98b2db 100644 --- a/src/Umbraco.Core/Services/TreeService.cs +++ b/src/Umbraco.Core/Services/TreeService.cs @@ -9,7 +9,7 @@ namespace Umbraco.Web.Services /// /// Implements . /// - internal class TreeService : ITreeService + public class TreeService : ITreeService { private readonly TreeCollection _treeCollection; diff --git a/src/Umbraco.Core/Web/ISessionManager.cs b/src/Umbraco.Core/Web/ISessionManager.cs index fdcebbb05a..e7bee5012d 100644 --- a/src/Umbraco.Core/Web/ISessionManager.cs +++ b/src/Umbraco.Core/Web/ISessionManager.cs @@ -2,7 +2,7 @@ namespace Umbraco.Web { public interface ISessionManager { - object GetSessionValue(string sessionName); - void SetSessionValue(string sessionName, object value); + string GetSessionValue(string sessionName); + void SetSessionValue(string sessionName, string value); } } diff --git a/src/Umbraco.Infrastructure/CompositionExtensions.cs b/src/Umbraco.Infrastructure/CompositionExtensions.cs index bd221e6fd3..d55effcd7a 100644 --- a/src/Umbraco.Infrastructure/CompositionExtensions.cs +++ b/src/Umbraco.Infrastructure/CompositionExtensions.cs @@ -11,6 +11,8 @@ using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Strings; using Umbraco.Core.Sync; +using Umbraco.Web.Media.EmbedProviders; +using Umbraco.Web.Search; namespace Umbraco.Core { @@ -84,7 +86,20 @@ namespace Umbraco.Core public static ManifestFilterCollectionBuilder ManifestFilters(this Composition composition) => composition.WithCollectionBuilder(); - + /// + /// Gets the backoffice OEmbed Providers collection builder. + /// + /// The composition. + public static EmbedProvidersCollectionBuilder OEmbedProviders(this Composition composition) + => composition.WithCollectionBuilder(); + + /// + /// Gets the back office searchable tree collection builder + /// + /// + /// + public static SearchableTreeCollectionBuilder SearchableTrees(this Composition composition) + => composition.WithCollectionBuilder(); #endregion @@ -98,7 +113,7 @@ namespace Umbraco.Core public static void SetCultureDictionaryFactory(this Composition composition) where T : ICultureDictionaryFactory { - composition.RegisterUnique(); + composition.RegisterUnique(); } /// diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs index aa9ccb958f..1ea08e3118 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs @@ -1,4 +1,5 @@ using System; +using Examine; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Composing.CompositionExtensions; @@ -7,9 +8,11 @@ using Umbraco.Core.Configuration.Grid; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Dashboards; using Umbraco.Core.Dictionary; +using Umbraco.Core.Events; using Umbraco.Core.Hosting; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; +using Umbraco.Core.Media; using Umbraco.Core.Migrations; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Migrations.PostMigrations; @@ -17,19 +20,35 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; +using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Scoping; using Umbraco.Core.Serialization; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; using Umbraco.Core.Sync; +using Umbraco.Examine; +using Umbraco.Infrastructure.Media; using Umbraco.Web; +using Umbraco.Web.Actions; +using Umbraco.Web.Cache; +using Umbraco.Web.ContentApps; +using Umbraco.Web.Editors; +using Umbraco.Web.Features; +using Umbraco.Web.HealthCheck; +using Umbraco.Web.HealthCheck.NotificationMethods; using Umbraco.Web.Install; +using Umbraco.Web.Macros; +using Umbraco.Web.Media.EmbedProviders; using Umbraco.Web.Migrations.PostMigrations; using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.PropertyEditors; using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Web.Search; +using Umbraco.Web.Sections; using Umbraco.Web.Services; +using Umbraco.Web.Templates; using Umbraco.Web.Trees; using IntegerValidator = Umbraco.Core.PropertyEditors.Validators.IntegerValidator; @@ -42,7 +61,7 @@ namespace Umbraco.Core.Runtime public override void Compose(Composition composition) { base.Compose(composition); - + // composers composition .ComposeRepositories() @@ -177,6 +196,165 @@ namespace Umbraco.Core.Runtime // Config manipulator composition.RegisterUnique(); + + + // register the http context and umbraco context accessors + // we *should* use the HttpContextUmbracoContextAccessor, however there are cases when + // we have no http context, eg when booting Umbraco or in background threads, so instead + // let's use an hybrid accessor that can fall back to a ThreadStatic context. + composition.RegisterUnique(); + + // register the umbraco context factory + // composition.RegisterUnique(); + composition.RegisterUnique(); + + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + + // both TinyMceValueConverter (in Core) and RteMacroRenderingValueConverter (in Web) will be + // discovered when CoreBootManager configures the converters. We HAVE to remove one of them + // here because there cannot be two converters for one property editor - and we want the full + // RteMacroRenderingValueConverter that converts macros, etc. So remove TinyMceValueConverter. + // (the limited one, defined in Core, is there for tests) - same for others + composition.PropertyValueConverters() + .Remove() + .Remove() + .Remove(); + + composition.UrlProviders() + .Append() + .Append(); + + composition.MediaUrlProviders() + .Append(); + + composition.RegisterUnique(); + + // register properties fallback + composition.RegisterUnique(); + + composition.RegisterUnique(); + + composition.RegisterUnique(); + + composition.Actions() + .Add(() => composition.TypeLoader.GetTypes()); + + composition.EditorValidators() + .Add(() => composition.TypeLoader.GetTypes()); + + + composition.TourFilters(); + + // replace with web implementation + composition.RegisterUnique(); + + // register OEmbed providers - no type scanning - all explicit opt-in of adding types + // note: IEmbedProvider is not IDiscoverable - think about it if going for type scanning + composition.OEmbedProviders() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append(); + + // register back office sections in the order we want them rendered + composition.Sections() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append(); + + // register known content apps + composition.ContentApps() + .Append() + .Append() + .Append(); + + // register published router + composition.RegisterUnique(); + + // register *all* checks, except those marked [HideFromTypeFinder] of course + composition.HealthChecks() + .Add(() => composition.TypeLoader.GetTypes()); + + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); + + composition.RegisterUnique(); + + composition.ContentFinders() + // all built-in finders in the correct order, + // devs can then modify this list on application startup + .Append() + .Append() + .Append() + //.Append() // disabled, this is an odd finder + .Append() + .Append(); + + composition.Register(Lifetime.Request); + + composition.SearchableTrees() + .Add(() => composition.TypeLoader.GetTypes()); + + // replace some services + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + + composition.RegisterUnique(); + + // register distributed cache + composition.RegisterUnique(f => new DistributedCache(f.GetInstance(), f.GetInstance())); + + + composition.Register(Lifetime.Request); + + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + + composition.RegisterUnique(); + composition.Register(factory => + { + var umbCtx = factory.GetInstance(); + return new PublishedContentQuery(umbCtx.UmbracoContext.PublishedSnapshot, factory.GetInstance(), factory.GetInstance()); + }, Lifetime.Request); + + + composition.RegisterUnique(); + + // register the http context and umbraco context accessors + // we *should* use the HttpContextUmbracoContextAccessor, however there are cases when + // we have no http context, eg when booting Umbraco or in background threads, so instead + // let's use an hybrid accessor that can fall back to a ThreadStatic context. + composition.RegisterUnique(); + + // register accessors for cultures + composition.RegisterUnique(); + + + + } } } diff --git a/src/Umbraco.Tests/Web/Mvc/RenderNoContentControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderNoContentControllerTests.cs index e728d75dc5..d33ce3bfcc 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderNoContentControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderNoContentControllerTests.cs @@ -3,6 +3,7 @@ using Moq; using NUnit.Framework; using Umbraco.Core.Configuration; using Umbraco.Core.IO; +using Umbraco.Tests.Common; using Umbraco.Web; using Umbraco.Web.Models; using Umbraco.Web.Mvc; @@ -15,11 +16,12 @@ namespace Umbraco.Tests.Web.Mvc [Test] public void Redirects_To_Root_When_Content_Published() { + var mockUmbracoContext = new Mock(); mockUmbracoContext.Setup(x => x.Content.HasContent()).Returns(true); var mockIOHelper = new Mock(); var mockGlobalSettings = new Mock(); - var controller = new RenderNoContentController(mockUmbracoContext.Object, mockIOHelper.Object, mockGlobalSettings.Object); + var controller = new RenderNoContentController(new TestUmbracoContextAccessor(mockUmbracoContext.Object), mockIOHelper.Object, mockGlobalSettings.Object); var result = controller.Index() as RedirectResult; @@ -40,7 +42,7 @@ namespace Umbraco.Tests.Web.Mvc var mockGlobalSettings = new Mock(); mockGlobalSettings.SetupGet(x => x.UmbracoPath).Returns(UmbracoPathSetting); mockGlobalSettings.SetupGet(x => x.NoNodesViewPath).Returns(ViewPath); - var controller = new RenderNoContentController(mockUmbracoContext.Object, mockIOHelper.Object, mockGlobalSettings.Object); + var controller = new RenderNoContentController(new TestUmbracoContextAccessor(mockUmbracoContext.Object), mockIOHelper.Object, mockGlobalSettings.Object); var result = controller.Index() as ViewResult; Assert.IsNotNull(result); diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreCookieManager.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreCookieManager.cs new file mode 100644 index 0000000000..15ac06dcf5 --- /dev/null +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreCookieManager.cs @@ -0,0 +1,45 @@ +using System; +using Microsoft.AspNetCore.Http; + +namespace Umbraco.Web.Common.AspNetCore +{ + public class AspNetCoreCookieManager : ICookieManager + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetCoreCookieManager(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public void ExpireCookie(string cookieName) + { + var httpContext = _httpContextAccessor.HttpContext; + + if (httpContext is null) return; + + var cookieValue = httpContext.Request.Cookies[cookieName]; + + httpContext.Response.Cookies.Append(cookieName, cookieValue, new CookieOptions() + { + Expires = DateTime.Now.AddYears(-1) + }); + } + + public string GetCookieValue(string cookieName) + { + return _httpContextAccessor.HttpContext?.Request.Cookies[cookieName]; + } + + public void SetCookieValue(string cookieName, string value) + { + _httpContextAccessor.HttpContext?.Response.Cookies.Append(cookieName, value); + } + + public bool HasCookie(string cookieName) + { + return !(GetCookieValue(cookieName) is null); + } + + } +} diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreSessionIdResolver.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreSessionIdResolver.cs deleted file mode 100644 index 818a39fac5..0000000000 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreSessionIdResolver.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Umbraco.Net; - -namespace Umbraco.Web.Common.AspNetCore -{ - internal class AspNetCoreSessionIdResolver : ISessionIdResolver - { - private readonly IHttpContextAccessor _httpContextAccessor; - - public AspNetCoreSessionIdResolver(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } - - - public string SessionId - { - get - { - var httpContext = _httpContextAccessor?.HttpContext; - // If session isn't enabled this will throw an exception so we check - var sessionFeature = httpContext?.Features.Get(); - return sessionFeature != null - ? httpContext?.Session?.Id - : "0"; - } - } - } -} diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreSessionManager.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreSessionManager.cs new file mode 100644 index 0000000000..a7f42bb888 --- /dev/null +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreSessionManager.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Umbraco.Net; + +namespace Umbraco.Web.Common.AspNetCore +{ + internal class AspNetCoreSessionManager : ISessionIdResolver, ISessionManager + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetCoreSessionManager(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + + /// + /// If session isn't enabled this will throw an exception so we check + /// + private bool IsSessionsAvailable => !(_httpContextAccessor.HttpContext?.Features.Get() is null); + + public string SessionId + { + get + { + var httpContext = _httpContextAccessor?.HttpContext; + + return IsSessionsAvailable + ? httpContext?.Session?.Id + : "0"; + } + } + + public string GetSessionValue(string sessionName) + { + if(!IsSessionsAvailable) return null; + return _httpContextAccessor.HttpContext?.Session.GetString(sessionName); + } + + + public void SetSessionValue(string sessionName, string value) + { + if(!IsSessionsAvailable) return; + _httpContextAccessor.HttpContext?.Session.SetString(sessionName, value); + } + } +} diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs index f82144f9a7..843620d571 100644 --- a/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs @@ -171,7 +171,7 @@ namespace Umbraco.Web.Common.Extensions hostingEnvironment = new AspNetCoreHostingEnvironment(hostingSettings, webHostEnvironment); ioHelper = new IOHelper(hostingEnvironment, globalSettings); logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, - new AspNetCoreSessionIdResolver(httpContextAccessor), + new AspNetCoreSessionManager(httpContextAccessor), // TODO: We need to avoid this, surely there's a way? See ContainerTests.BuildServiceProvider_Before_Host_Is_Configured () => services.BuildServiceProvider().GetService(), coreDebug, ioHelper, new AspNetCoreMarchal()); diff --git a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs index e7c589bc17..79c7d3ec25 100644 --- a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs +++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs @@ -2,18 +2,11 @@ using Microsoft.AspNetCore.Http; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Hosting; -using Umbraco.Core.Media; -using Umbraco.Core.Models.PublishedContent; using Umbraco.Net; using Umbraco.Core.Runtime; using Umbraco.Core.Security; -using Umbraco.Infrastructure.Media; using Umbraco.Web.Common.AspNetCore; using Umbraco.Web.Common.Lifetime; -using Umbraco.Web.Models.PublishedContent; -using Umbraco.Web.PropertyEditors; -using Umbraco.Web.Routing; -using Umbraco.Web.Templates; namespace Umbraco.Web.Common.Runtime { @@ -39,41 +32,16 @@ namespace Umbraco.Web.Common.Runtime composition.RegisterUnique(); // The umbraco request lifetime - composition.RegisterUnique(); - composition.RegisterUnique(factory => factory.GetInstance()); - composition.RegisterUnique(factory => factory.GetInstance()); + composition.RegisterMultipleUnique(); + + //Password hasher composition.RegisterUnique(); - // register the http context and umbraco context accessors - // we *should* use the HttpContextUmbracoContextAccessor, however there are cases when - // we have no http context, eg when booting Umbraco or in background threads, so instead - // let's use an hybrid accessor that can fall back to a ThreadStatic context. - composition.RegisterUnique(); + composition.RegisterUnique(); - // register the umbraco context factory - // composition.RegisterUnique(); - composition.RegisterUnique(); - - composition.RegisterUnique(); - composition.RegisterUnique(); - composition.RegisterUnique(); - composition.RegisterUnique(); - - composition.UrlProviders() - .Append() - .Append(); - - composition.MediaUrlProviders() - .Append(); - - composition.RegisterUnique(); - - // register properties fallback - composition.RegisterUnique(); - - composition.RegisterUnique(); + composition.RegisterMultipleUnique(); } } } diff --git a/src/Umbraco.Web/AspNet/AspNetSessionManager.cs b/src/Umbraco.Web/AspNet/AspNetSessionManager.cs index 0e91fe7b5b..50fe70488b 100644 --- a/src/Umbraco.Web/AspNet/AspNetSessionManager.cs +++ b/src/Umbraco.Web/AspNet/AspNetSessionManager.cs @@ -10,13 +10,15 @@ namespace Umbraco.Web.AspNet { } - public object GetSessionValue(string sessionName) + public string GetSessionValue(string sessionName) { - return HttpContext.Current.Session[sessionName]; + return HttpContext.Current?.Session[sessionName]?.ToString(); } - public void SetSessionValue(string sessionName, object value) + public void SetSessionValue(string sessionName, string value) { + var httpContext = HttpContext.Current; + if (httpContext is null) return; HttpContext.Current.Session[sessionName] = value; } diff --git a/src/Umbraco.Web/Composing/CompositionExtensions/WebMappingProfiles.cs b/src/Umbraco.Web/Composing/CompositionExtensions/WebMappingProfiles.cs index d5f3ba244b..21a242ee17 100644 --- a/src/Umbraco.Web/Composing/CompositionExtensions/WebMappingProfiles.cs +++ b/src/Umbraco.Web/Composing/CompositionExtensions/WebMappingProfiles.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web.Composing.CompositionExtensions .Add() .Add() .Add() - .Add();; + .Add(); composition.Register(); composition.Register(); diff --git a/src/Umbraco.Web/CompositionExtensions.cs b/src/Umbraco.Web/CompositionExtensions.cs index 35e60740dd..29a892f1d5 100644 --- a/src/Umbraco.Web/CompositionExtensions.cs +++ b/src/Umbraco.Web/CompositionExtensions.cs @@ -40,12 +40,7 @@ namespace Umbraco.Web => composition.WithCollectionBuilder(); - /// - /// Gets the backoffice OEmbed Providers collection builder. - /// - /// The composition. - public static EmbedProvidersCollectionBuilder OEmbedProviders(this Composition composition) - => composition.WithCollectionBuilder(); + /// /// Gets the back office tree collection builder @@ -55,14 +50,6 @@ namespace Umbraco.Web public static TreeCollectionBuilder Trees(this Composition composition) => composition.WithCollectionBuilder(); - /// - /// Gets the back office searchable tree collection builder - /// - /// - /// - public static SearchableTreeCollectionBuilder SearchableTrees(this Composition composition) - => composition.WithCollectionBuilder(); - #endregion diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs index de1d82be7d..7f6a1cdbf3 100644 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -400,7 +400,7 @@ namespace Umbraco.Web.Macros attributeValue = _requestAccessor.GetRequestValue(name); break; case '%': - attributeValue = _sessionManager.GetSessionValue(name)?.ToString(); + attributeValue = _sessionManager.GetSessionValue(name); if (string.IsNullOrEmpty(attributeValue)) attributeValue = _cookieManager.GetCookieValue(name); break; diff --git a/src/Umbraco.Web/Mvc/RenderNoContentController.cs b/src/Umbraco.Web/Mvc/RenderNoContentController.cs index 2ffd323440..9334591fbb 100644 --- a/src/Umbraco.Web/Mvc/RenderNoContentController.cs +++ b/src/Umbraco.Web/Mvc/RenderNoContentController.cs @@ -8,20 +8,20 @@ namespace Umbraco.Web.Mvc { public class RenderNoContentController : Controller { - private readonly IUmbracoContext _umbracoContext; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IIOHelper _ioHelper; private readonly IGlobalSettings _globalSettings; - public RenderNoContentController(IUmbracoContext umbracoContext, IIOHelper ioHelper, IGlobalSettings globalSettings) + public RenderNoContentController(IUmbracoContextAccessor umbracoContextAccessor, IIOHelper ioHelper, IGlobalSettings globalSettings) { - _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); + _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); _globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings)); } public ActionResult Index() { - var store = _umbracoContext.Content; + var store = _umbracoContextAccessor.UmbracoContext.Content; if (store.HasContent()) { // If there is actually content, go to the root. diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 4d5d4198b6..ec3b463d4c 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -3,50 +3,28 @@ using System.Web.Security; using Examine; using Microsoft.AspNet.SignalR; using Umbraco.Core; -using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; -using Umbraco.Core.Events; using Umbraco.Core.Hosting; using Umbraco.Core.Install; -using Umbraco.Core.Migrations.PostMigrations; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Runtime; using Umbraco.Core.Security; using Umbraco.Core.Services; -using Umbraco.Core.Sync; -using Umbraco.Web.Actions; -using Umbraco.Web.Cache; using Umbraco.Web.Composing.CompositionExtensions; -using Umbraco.Web.ContentApps; -using Umbraco.Web.Editors; -using Umbraco.Web.Features; -using Umbraco.Web.HealthCheck; using Umbraco.Web.Hosting; using Umbraco.Web.Install; using Umbraco.Web.Macros; -using Umbraco.Web.Media.EmbedProviders; -using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; -using Umbraco.Web.Routing; -using Umbraco.Web.Search; -using Umbraco.Web.Sections; using Umbraco.Web.Security; using Umbraco.Web.Security.Providers; -using Umbraco.Web.Services; using Umbraco.Web.SignalR; using Umbraco.Web.Templates; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using Umbraco.Web.PropertyEditors; -using Umbraco.Examine; using Umbraco.Net; using Umbraco.Web.AspNet; -using Umbraco.Core.Media; -using Umbraco.Infrastructure.Media; namespace Umbraco.Web.Runtime { @@ -63,9 +41,7 @@ namespace Umbraco.Web.Runtime composition.Register(); composition.Register(); - composition.Register(Lifetime.Singleton); - composition.Register(factory => factory.GetInstance(), Lifetime.Singleton); - composition.Register(factory => factory.GetInstance(), Lifetime.Singleton); + composition.Register(Lifetime.Singleton); @@ -73,8 +49,7 @@ namespace Umbraco.Web.Runtime composition.Register(); composition.Register(Lifetime.Singleton); - composition.RegisterUnique(); // required for hybrid accessors - composition.RegisterUnique(); + composition.ComposeWebMappingProfiles(); @@ -92,43 +67,14 @@ namespace Umbraco.Web.Runtime composition.RegisterUnique(); composition.RegisterUnique(); - // register accessors for cultures - composition.RegisterUnique(); - - - // register the http context and umbraco context accessors - // we *should* use the HttpContextUmbracoContextAccessor, however there are cases when - // we have no http context, eg when booting Umbraco or in background threads, so instead - // let's use an hybrid accessor that can fall back to a ThreadStatic context. - composition.RegisterUnique(); - // register the umbraco context factory composition.RegisterUnique(); - composition.RegisterUnique(); - - // we should stop injecting UmbracoContext and always inject IUmbracoContextAccessor, however at the moment - // there are tons of places (controllers...) which require UmbracoContext in their ctor - so let's register - // a way to inject the UmbracoContext - DO NOT register this as Lifetime.Request since LI will dispose the context - // in it's own way but we don't want that to happen, we manage its lifetime ourselves. - composition.Register(factory => factory.GetInstance().UmbracoContext); - composition.RegisterUnique(); - composition.Register(factory => - { - var umbCtx = factory.GetInstance(); - return new PublishedContentQuery(umbCtx.UmbracoContext.PublishedSnapshot, factory.GetInstance(), factory.GetInstance()); - }, Lifetime.Request); - composition.Register(Lifetime.Request); composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(); - composition.RegisterUnique(); - composition.RegisterUnique(); - composition.RegisterUnique(); - composition.RegisterUnique(); - // register the umbraco helper - this is Transient! very important! // also, if not level.Run, we cannot really use the helper (during upgrade...) // so inject a "void" helper (not exactly pretty but...) @@ -142,19 +88,8 @@ namespace Umbraco.Web.Runtime else composition.Register(_ => new UmbracoHelper()); - // register distributed cache - composition.RegisterUnique(f => new DistributedCache(f.GetInstance(), f.GetInstance())); - composition.RegisterUnique(); - // replace some services - composition.RegisterUnique(); - composition.RegisterUnique(); - composition.RegisterUnique(); - composition.RegisterUnique(); - - composition.RegisterUnique(); - // configure the container for web composition.ConfigureForWeb(); @@ -162,21 +97,6 @@ namespace Umbraco.Web.Runtime .ComposeUmbracoControllers(GetType().Assembly) .SetDefaultRenderMvcController(); // default controller for template views - composition.SearchableTrees() - .Add(() => composition.TypeLoader.GetTypes()); - - composition.Register(Lifetime.Request); - - composition.EditorValidators() - .Add(() => composition.TypeLoader.GetTypes()); - - composition.TourFilters(); - - composition.RegisterUnique(); - - composition.Actions() - .Add(() => composition.TypeLoader.GetTypes()); - //we need to eagerly scan controller types since they will need to be routed composition.WithCollectionBuilder() .Add(composition.TypeLoader.GetSurfaceControllers()); @@ -184,113 +104,37 @@ namespace Umbraco.Web.Runtime composition.WithCollectionBuilder() .Add(umbracoApiControllerTypes); - // both TinyMceValueConverter (in Core) and RteMacroRenderingValueConverter (in Web) will be - // discovered when CoreBootManager configures the converters. We HAVE to remove one of them - // here because there cannot be two converters for one property editor - and we want the full - // RteMacroRenderingValueConverter that converts macros, etc. So remove TinyMceValueConverter. - // (the limited one, defined in Core, is there for tests) - same for others - composition.PropertyValueConverters() - .Remove() - .Remove() - .Remove(); // add all known factories, devs can then modify this list on application // startup either by binding to events or in their own global.asax composition.FilteredControllerFactory() .Append(); - composition.UrlProviders() - .Append() - .Append(); - - composition.MediaUrlProviders() - .Append(); - - composition.RegisterUnique(); - - composition.RegisterUnique(); - - composition.ContentFinders() - // all built-in finders in the correct order, - // devs can then modify this list on application startup - .Append() - .Append() - .Append() - //.Append() // disabled, this is an odd finder - .Append() - .Append(); - - composition.RegisterUnique(); - - // register *all* checks, except those marked [HideFromTypeFinder] of course - composition.HealthChecks() - .Add(() => composition.TypeLoader.GetTypes()); - - composition.WithCollectionBuilder() - .Add(() => composition.TypeLoader.GetTypes()); - // auto-register views composition.RegisterAuto(typeof(UmbracoViewPage<>)); - // register published router - composition.RegisterUnique(); - // register preview SignalR hub composition.RegisterUnique(_ => GlobalHost.ConnectionManager.GetHubContext()); - // register properties fallback - composition.RegisterUnique(); - - // register known content apps - composition.ContentApps() - .Append() - .Append() - .Append(); - - // register back office sections in the order we want them rendered - composition.Sections() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append(); - // register back office trees // the collection builder only accepts types inheriting from TreeControllerBase // and will filter out those that are not attributed with TreeAttribute composition.Trees() .AddTreeControllers(umbracoApiControllerTypes.Where(x => typeof(TreeControllerBase).IsAssignableFrom(x))); - // register OEmbed providers - no type scanning - all explicit opt-in of adding types - // note: IEmbedProvider is not IDiscoverable - think about it if going for type scanning - composition.OEmbedProviders() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append(); - // replace with web implementation - composition.RegisterUnique(); - - // Config manipulator - composition.RegisterUnique(); - - //ApplicationShutdownRegistry + // STUFF that do not have to be moved to .NET CORE + //---------------------------------------- + composition.RegisterUnique(); composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); // required for hybrid accessors + + composition.Register(Lifetime.Singleton); + composition.Register(factory => factory.GetInstance(), Lifetime.Singleton); + composition.Register(factory => factory.GetInstance(), Lifetime.Singleton); + } } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 407af7d366..9e1179a8f4 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -323,7 +323,6 @@ -