diff --git a/src/Umbraco.Core/DisposableObject.cs b/src/Umbraco.Core/DisposableObject.cs deleted file mode 100644 index b2c150f96c67..000000000000 --- a/src/Umbraco.Core/DisposableObject.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; - -namespace Umbraco.Core -{ - /// - /// Abstract implementation of IDisposable. - /// - /// - /// This is for objects that DO have unmanaged resources. Use - /// for objects that do NOT have unmanaged resources, and avoid creating a finalizer. - /// - /// Can also be used as a pattern for when inheriting is not possible. - /// - /// See also: https://msdn.microsoft.com/en-us/library/b1yfkh5e%28v=vs.110%29.aspx - /// See also: https://lostechies.com/chrispatterson/2012/11/29/idisposable-done-right/ - /// - /// Note: if an object's ctor throws, it will never be disposed, and so if that ctor - /// has allocated disposable objects, it should take care of disposing them. - /// - public abstract class DisposableObject : IDisposable - { - private readonly object _locko = new object(); - - // gets a value indicating whether this instance is disposed. - // for internal tests only (not thread safe) - public bool Disposed { get; private set; } - - // implements IDisposable - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - // finalizer - ~DisposableObject() - { - Dispose(false); - } - - private void Dispose(bool disposing) - { - // can happen if the object construction failed - if (_locko == null) - return; - - lock (_locko) - { - if (Disposed) return; - Disposed = true; - } - - DisposeUnmanagedResources(); - - if (disposing) - DisposeResources(); - } - - protected abstract void DisposeResources(); - - protected virtual void DisposeUnmanagedResources() - { } - } -} diff --git a/src/Umbraco.Core/Logging/LogProfiler.cs b/src/Umbraco.Core/Logging/LogProfiler.cs index b80e40942ad5..74dae545b469 100644 --- a/src/Umbraco.Core/Logging/LogProfiler.cs +++ b/src/Umbraco.Core/Logging/LogProfiler.cs @@ -41,7 +41,7 @@ public void Stop(bool discardResults = false) } // a lightweight disposable timer - private class LightDisposableTimer : DisposableObject + private class LightDisposableTimer : DisposableObjectSlim { private readonly Action _callback; private readonly Stopwatch _stopwatch = Stopwatch.StartNew(); diff --git a/src/Umbraco.Core/Logging/VoidProfiler.cs b/src/Umbraco.Core/Logging/VoidProfiler.cs index a33f1b2444d6..24eb8e81c3fb 100644 --- a/src/Umbraco.Core/Logging/VoidProfiler.cs +++ b/src/Umbraco.Core/Logging/VoidProfiler.cs @@ -22,7 +22,7 @@ public void Start() public void Stop(bool discardResults = false) { } - private class VoidDisposable : DisposableObject + private class VoidDisposable : DisposableObjectSlim { protected override void DisposeResources() { } diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs index 681eb1232ec6..dc86ff060c4b 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Persistence /// // TODO: these comments are not true anymore // TODO: this class needs not be disposable! - internal class UmbracoDatabaseFactory : DisposableObject, IUmbracoDatabaseFactory + internal class UmbracoDatabaseFactory : DisposableObjectSlim, IUmbracoDatabaseFactory { private readonly Lazy _mappers; private readonly ILogger _logger; diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 7f50d6249b10..bd3d64254d7c 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -561,7 +561,6 @@ - diff --git a/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs b/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs index 014c6b0af5f0..6e8835b9a5df 100644 --- a/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs +++ b/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs @@ -4,11 +4,14 @@ using Moq; using NUnit.Framework; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Tests.Testing; +using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Web; using Umbraco.Web.Cache; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -148,8 +151,18 @@ public void CanHandleEvent() }; + var umbracoContextFactory = new UmbracoContextFactory( + new TestUmbracoContextAccessor(), + Mock.Of(), + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), + TestObjects.GetUmbracoSettings(), + TestObjects.GetGlobalSettings(), + Enumerable.Empty(), + Mock.Of()); + // just assert it does not throw - var refreshers = new DistributedCacheBinder(null, null); + var refreshers = new DistributedCacheBinder(null, umbracoContextFactory, null); refreshers.HandleEvents(definitions); } } diff --git a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs index 2244f9085d45..8d9c1be40a61 100644 --- a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs @@ -473,7 +473,7 @@ public void ShadowScopeComplete() scope.Dispose(); scopedFileSystems = false; Assert.IsTrue(phy.FileExists("sub/f5.txt")); - Assert.IsFalse(Directory.Exists(shadowfs + "/" + id)); + TestHelper.TryAssert(() => Assert.IsFalse(Directory.Exists(shadowfs + "/" + id))); } [Test] diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index 7b22d282f077..c3708dd8f3fe 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -13,6 +13,7 @@ using Umbraco.Tests.Services; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; +using Umbraco.Web; using Umbraco.Web.Cache; using static Umbraco.Tests.Cache.DistributedCache.DistributedCacheTests; @@ -32,7 +33,7 @@ public override void SetUp() { base.SetUp(); - _h1 = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _h1 = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); _h1.BindEvents(true); _events = new List(); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshot.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshot.cs index bfef1873710f..c7be2e6be068 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshot.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshot.cs @@ -51,10 +51,13 @@ public IDisposable ForcedPreview(bool preview, Action callback = null) return new ForcedPreviewObject(); } - private class ForcedPreviewObject : DisposableObject + private class ForcedPreviewObject : DisposableObjectSlim { protected override void DisposeResources() { } } + + public void Dispose() + { } } } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/UmbracoContextCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/UmbracoContextCache.cs index fc6dbe3f3070..5d48e9eae305 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/UmbracoContextCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/UmbracoContextCache.cs @@ -13,7 +13,7 @@ public static ConcurrentDictionary Current { get { - var umbracoContext = UmbracoContext.Current; + var umbracoContext = Umbraco.Web.Composing.Current.UmbracoContext; // will get or create a value // a ConditionalWeakTable is thread-safe diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index ac1c57d40942..108bfb9f18b3 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -4,12 +4,12 @@ using System.Linq; using Moq; using NUnit.Framework; -using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Tests.Testing; using Umbraco.Web; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.PublishedContent { @@ -186,7 +186,7 @@ internal override void PopulateCache(PublishedContentTypeFactory factory, SolidP [Test] public void Can_Get_Content_For_Populated_Requested_Language() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); var value = content.Value("welcomeText", "en-US"); Assert.AreEqual("Welcome", value); } @@ -194,7 +194,7 @@ public void Can_Get_Content_For_Populated_Requested_Language() [Test] public void Can_Get_Content_For_Populated_Requested_Non_Default_Language() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); var value = content.Value("welcomeText", "de"); Assert.AreEqual("Willkommen", value); } @@ -202,7 +202,7 @@ public void Can_Get_Content_For_Populated_Requested_Non_Default_Language() [Test] public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_Without_Fallback() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); var value = content.Value("welcomeText", "fr"); Assert.IsNull(value); } @@ -210,7 +210,7 @@ public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_Without_Fallba [Test] public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Unless_Requested() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); var value = content.Value("welcomeText", "es"); Assert.IsNull(value); } @@ -218,7 +218,7 @@ public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_ [Test] public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); var value = content.Value("welcomeText", "es", fallback: Fallback.ToLanguage); Assert.AreEqual("Welcome", value); } @@ -226,7 +226,7 @@ public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback() [Test] public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Over_Two_Levels() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); var value = content.Value("welcomeText", "it", fallback: Fallback.To(Fallback.Language, Fallback.Ancestors)); Assert.AreEqual("Welcome", value); } @@ -234,7 +234,7 @@ public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Ove [Test] public void Do_Not_GetContent_For_Unpopulated_Requested_Language_With_Fallback_Over_That_Loops() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); var value = content.Value("welcomeText", "no", fallback: Fallback.ToLanguage); Assert.IsNull(value); } @@ -242,7 +242,7 @@ public void Do_Not_GetContent_For_Unpopulated_Requested_Language_With_Fallback_O [Test] public void Do_Not_Get_Content_Recursively_Unless_Requested() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); var value = content.Value("welcomeText2"); Assert.IsNull(value); } @@ -250,7 +250,7 @@ public void Do_Not_Get_Content_Recursively_Unless_Requested() [Test] public void Can_Get_Content_Recursively() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); var value = content.Value("welcomeText2", fallback: Fallback.ToAncestors); Assert.AreEqual("Welcome", value); } @@ -258,7 +258,7 @@ public void Can_Get_Content_Recursively() [Test] public void Do_Not_Get_Content_Recursively_Unless_Requested2() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First().Children().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First().Children().First(); Assert.IsNull(content.GetProperty("welcomeText2")); var value = content.Value("welcomeText2"); Assert.IsNull(value); @@ -267,7 +267,7 @@ public void Do_Not_Get_Content_Recursively_Unless_Requested2() [Test] public void Can_Get_Content_Recursively2() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First().Children().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First().Children().First(); Assert.IsNull(content.GetProperty("welcomeText2")); var value = content.Value("welcomeText2", fallback: Fallback.ToAncestors); Assert.AreEqual("Welcome", value); @@ -276,7 +276,7 @@ public void Can_Get_Content_Recursively2() [Test] public void Can_Get_Content_Recursively3() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First().Children().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First().Children().First(); Assert.IsNull(content.GetProperty("noprop")); var value = content.Value("noprop", fallback: Fallback.ToAncestors); // property has no value but we still get the value (ie, the converter would do something) @@ -287,7 +287,7 @@ public void Can_Get_Content_Recursively3() public void Can_Get_Content_With_Recursive_Priority() { Current.VariationContextAccessor.VariationContext = new VariationContext("nl"); - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); var value = content.Value("welcomeText", "nl", fallback: Fallback.To(Fallback.Ancestors, Fallback.Language)); @@ -298,7 +298,7 @@ public void Can_Get_Content_With_Recursive_Priority() [Test] public void Can_Get_Content_With_Fallback_Language_Priority() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); var value = content.Value("welcomeText", "nl", fallback: Fallback.ToLanguage); // No Dutch value is directly assigned. Check has fallen back to English value from language variant. @@ -308,14 +308,14 @@ public void Can_Get_Content_With_Fallback_Language_Priority() [Test] public void Throws_For_Non_Supported_Fallback() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); Assert.Throws(() => content.Value("welcomeText", "nl", fallback: Fallback.To(999))); } [Test] public void Can_Fallback_To_Default_Value() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); // no Dutch value is assigned, so getting null var value = content.Value("welcomeText", "nl"); @@ -333,7 +333,7 @@ public void Can_Fallback_To_Default_Value() [Test] public void Can_Have_Custom_Default_Value() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First().Children.First(); // HACK: the value, pretend the converter would return something var prop = content.GetProperty("welcomeText") as SolidPublishedPropertyWithLanguageVariants; diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 0dcc4bea99b4..b2f1f311c3bd 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -5,6 +5,7 @@ using Umbraco.Web; using Umbraco.Core; using Umbraco.Tests.Testing; +using Umbraco.Web.Composing; namespace Umbraco.Tests.PublishedContent { @@ -95,14 +96,14 @@ internal override void PopulateCache(PublishedContentTypeFactory factory, SolidP [Test] public void First() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var content = Current.UmbracoContext.ContentCache.GetAtRoot().First(); Assert.AreEqual("Content 1", content.Name); } [Test] public void Distinct() { - var items = UmbracoContext.Current.ContentCache.GetAtRoot() + var items = Current.UmbracoContext.ContentCache.GetAtRoot() .Distinct() .Distinct() .ToIndexedArray(); @@ -126,7 +127,7 @@ public void Distinct() [Test] public void OfType1() { - var items = UmbracoContext.Current.ContentCache.GetAtRoot() + var items = Current.UmbracoContext.ContentCache.GetAtRoot() .OfType() .Distinct() .ToIndexedArray(); @@ -137,7 +138,7 @@ public void OfType1() [Test] public void OfType2() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot() + var content = Current.UmbracoContext.ContentCache.GetAtRoot() .OfType() .Distinct() .ToIndexedArray(); @@ -148,7 +149,7 @@ public void OfType2() [Test] public void OfType() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot() + var content = Current.UmbracoContext.ContentCache.GetAtRoot() .OfType() .First(x => x.Prop1 == 1234); Assert.AreEqual("Content 2", content.Name); @@ -158,7 +159,7 @@ public void OfType() [Test] public void Position() { - var items = UmbracoContext.Current.ContentCache.GetAtRoot() + var items = Current.UmbracoContext.ContentCache.GetAtRoot() .Where(x => x.Value("prop1") == 1234) .ToIndexedArray(); @@ -173,7 +174,7 @@ public void Position() [Test] public void Issue() { - var content = UmbracoContext.Current.ContentCache.GetAtRoot() + var content = Current.UmbracoContext.ContentCache.GetAtRoot() .Distinct() .OfType(); @@ -181,12 +182,12 @@ public void Issue() var first = where.First(); Assert.AreEqual(1234, first.Prop1); - var content2 = UmbracoContext.Current.ContentCache.GetAtRoot() + var content2 = Current.UmbracoContext.ContentCache.GetAtRoot() .OfType() .First(x => x.Prop1 == 1234); Assert.AreEqual(1234, content2.Prop1); - var content3 = UmbracoContext.Current.ContentCache.GetAtRoot() + var content3 = Current.UmbracoContext.ContentCache.GetAtRoot() .OfType() .First(); Assert.AreEqual(1234, content3.Prop1); @@ -195,7 +196,7 @@ public void Issue() [Test] public void PublishedContentQueryTypedContentList() { - var query = new PublishedContentQuery(UmbracoContext.Current.PublishedSnapshot, UmbracoContext.Current.VariationContextAccessor); + var query = new PublishedContentQuery(Current.UmbracoContext.PublishedSnapshot, Current.UmbracoContext.VariationContextAccessor); var result = query.Content(new[] { 1, 2, 4 }).ToArray(); Assert.AreEqual(2, result.Length); Assert.AreEqual(1, result[0].Id); diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index a838e06a9a1e..86017be82045 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -39,6 +39,9 @@ public void Resync() public IAppCache SnapshotCache => null; public IAppCache ElementsCache => null; + + public void Dispose() + { } } class SolidPublishedContentCache : PublishedCacheBase, IPublishedContentCache, IPublishedMediaCache diff --git a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs index ff610cbc0019..3e4c4f1ba9f5 100644 --- a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs +++ b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs @@ -45,7 +45,8 @@ public override void SetUp() runtime, logger, null, // FIXME: PublishedRouter complexities... - Mock.Of() + Mock.Of(), + Mock.Of() ); runtime.Level = RuntimeLevel.Run; diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 995e1c75a94d..e191d282cafd 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -100,6 +100,7 @@ public void StandaloneTest() composition.WithCollectionBuilder().Append(); composition.RegisterUnique(); composition.RegisterUnique(f => ExamineManager.Instance); + composition.RegisterUnique(); // initialize some components only/individually composition.WithCollectionBuilder() @@ -178,8 +179,9 @@ public void StandaloneTest() // need an UmbracoCOntext to access the cache // FIXME: not exactly pretty, should not depend on HttpContext var httpContext = Mock.Of(); - var withUmbracoContext = UmbracoContext.EnsureContext(httpContext); - var umbracoContext = Umbraco.Web.Composing.Current.UmbracoContext; + var umbracoContextFactory = factory.GetInstance(); + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(httpContext); + var umbracoContext = umbracoContextReference.UmbracoContext; // assert that there is no published document var pcontent = umbracoContext.ContentCache.GetById(content.Id); @@ -217,7 +219,7 @@ public void StandaloneTest() // and the published document has a url Assert.AreEqual("/test/", pcontent.GetUrl()); - withUmbracoContext.Dispose(); + umbracoContextReference.Dispose(); mainDom.Stop(); components.Terminate(); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 2f0876b9baa6..33f6af86385c 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -131,7 +131,7 @@ public void TestScope(bool complete) var umbracoContext = GetUmbracoContextNu("http://example.com/", setSingleton: true); // wire cache refresher - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); _distributedCacheBinder.BindEvents(true); // create document type, document diff --git a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs index 9134e9f51bab..c7e4ddcb1931 100644 --- a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Sync; +using Umbraco.Web; namespace Umbraco.Tests.Scoping { @@ -74,7 +75,7 @@ public void DefaultRepositoryCachePolicy(bool complete) // get user again - else we'd modify the one that's in the cache user = service.GetUserById(user.Id); - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); _distributedCacheBinder.BindEvents(true); Assert.IsNull(scopeProvider.AmbientScope); @@ -155,7 +156,7 @@ public void FullDataSetRepositoryCachePolicy(bool complete) Assert.AreEqual(lang.Id, globalCached.Id); Assert.AreEqual("fr-FR", globalCached.IsoCode); - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); _distributedCacheBinder.BindEvents(true); Assert.IsNull(scopeProvider.AmbientScope); @@ -247,7 +248,7 @@ public void SingleItemsOnlyRepositoryCachePolicy(bool complete) Assert.AreEqual(item.Id, globalCached.Id); Assert.AreEqual("item-key", globalCached.ItemKey); - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); _distributedCacheBinder.BindEvents(true); Assert.IsNull(scopeProvider.AmbientScope); diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index ad8b8e19a6fe..044965bc7959 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -16,6 +16,7 @@ using Umbraco.Tests.LegacyXmlPublishedCache; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Web; using Umbraco.Web.Cache; using Umbraco.Web.PublishedCache; @@ -92,7 +93,7 @@ public void TestScope(bool complete) var item = new Content("name", -1, contentType); // wire cache refresher - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); _distributedCacheBinder.BindEvents(true); // check xml in context = "before" @@ -205,7 +206,7 @@ public void TestScopeMany(bool complete) Current.Services.ContentTypeService.Save(contentType); // wire cache refresher - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of(), Mock.Of()); _distributedCacheBinder.BindEvents(true); // check xml in context = "before" diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 4e352488bec4..84bd22cc4fcd 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -135,16 +135,16 @@ IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, Http var umbracoContextAccessor = Umbraco.Web.Composing.Current.UmbracoContextAccessor; - var umbCtx = UmbracoContext.EnsureContext( - umbracoContextAccessor, - httpContext, + var umbCtx = new UmbracoContext(httpContext, publishedSnapshotService.Object, webSecurity.Object, - Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == UrlProviderMode.Auto.ToString())), + Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), Enumerable.Empty(), globalSettings, - new TestVariationContextAccessor(), - true); //replace it + new TestVariationContextAccessor()); + + //replace it + umbracoContextAccessor.UmbracoContext = umbCtx; var urlHelper = new Mock(); urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 660d0f201e24..0f8431997651 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -116,11 +116,21 @@ public UmbracoContext GetUmbracoContextMock(IUmbracoContextAccessor accessor = n var umbracoSettings = GetUmbracoSettings(); var globalSettings = GetGlobalSettings(); - var webSecurity = new Mock(null, null, globalSettings).Object; var urlProviders = Enumerable.Empty(); if (accessor == null) accessor = new TestUmbracoContextAccessor(); - return UmbracoContext.EnsureContext(accessor, httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, new TestVariationContextAccessor(), true); + + var umbracoContextFactory = new UmbracoContextFactory( + accessor, + publishedSnapshotService, + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), + umbracoSettings, + globalSettings, + urlProviders, + Mock.Of()); + + return umbracoContextFactory.EnsureUmbracoContext(httpContext).UmbracoContext; } public IUmbracoSettingsSection GetUmbracoSettings() @@ -143,7 +153,7 @@ public IGlobalSettings GetGlobalSettings() public IFileSystems GetFileSystemsMock() { var fileSystems = Mock.Of(); - + MockFs(fileSystems, x => x.MacroPartialsFileSystem); MockFs(fileSystems, x => x.MvcViewsFileSystem); MockFs(fileSystems, x => x.PartialViewsFileSystem); diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 652ac0ab3065..b69451547ef5 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -44,7 +44,7 @@ public void Can_Mock_Service_Context() public void Can_Mock_Umbraco_Context() { var umbracoContext = TestObjects.GetUmbracoContextMock(Current.UmbracoContextAccessor); - Assert.AreEqual(umbracoContext, UmbracoContext.Current); + Assert.AreEqual(umbracoContext, Current.UmbracoContext); } [Test] diff --git a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs index 29deeb31ec83..ce42b8d55f70 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs @@ -63,19 +63,20 @@ public void Matches_Default_Index() var globalSettings = TestObjects.GetGlobalSettings(); var attr = new RenderIndexActionSelectorAttribute(); var req = new RequestContext(); - //var appCtx = new ApplicationContext( - // CacheHelper.CreateDisabledCacheHelper(), - // new ProfilingLogger(Mock.Of(), Mock.Of())); - var umbCtx = UmbracoContext.EnsureContext( + + var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, - Mock.Of(), Mock.Of(), - new Mock(null, null, globalSettings).Object, + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), TestObjects.GetUmbracoSettings(), - Enumerable.Empty(), globalSettings, - new TestVariationContextAccessor(), - true); + Enumerable.Empty(), + Mock.Of()); + + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbCtx = umbracoContextReference.UmbracoContext; + var ctrl = new MatchesDefaultIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); var result = attr.IsValidForRequest(controllerCtx, @@ -90,16 +91,20 @@ public void Matches_Overriden_Index() var globalSettings = TestObjects.GetGlobalSettings(); var attr = new RenderIndexActionSelectorAttribute(); var req = new RequestContext(); - var umbCtx = UmbracoContext.EnsureContext( + + var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, - Mock.Of(), Mock.Of(), - new Mock(null, null, globalSettings).Object, + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), TestObjects.GetUmbracoSettings(), - Enumerable.Empty(), globalSettings, - new TestVariationContextAccessor(), - true); + Enumerable.Empty(), + Mock.Of()); + + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbCtx = umbracoContextReference.UmbracoContext; + var ctrl = new MatchesOverriddenIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); var result = attr.IsValidForRequest(controllerCtx, @@ -114,16 +119,20 @@ public void Matches_Custom_Index() var globalSettings = TestObjects.GetGlobalSettings(); var attr = new RenderIndexActionSelectorAttribute(); var req = new RequestContext(); - var umbCtx = UmbracoContext.EnsureContext( + + var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, - Mock.Of(), Mock.Of(), - new Mock(null, null, globalSettings).Object, + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), TestObjects.GetUmbracoSettings(), - Enumerable.Empty(), globalSettings, - new TestVariationContextAccessor(), - true); + Enumerable.Empty(), + Mock.Of()); + + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbCtx = umbracoContextReference.UmbracoContext; + var ctrl = new MatchesCustomIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); var result = attr.IsValidForRequest(controllerCtx, @@ -138,16 +147,20 @@ public void Matches_Async_Index_Same_Signature() var globalSettings = TestObjects.GetGlobalSettings(); var attr = new RenderIndexActionSelectorAttribute(); var req = new RequestContext(); - var umbCtx = UmbracoContext.EnsureContext( + + var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, - Mock.Of(), Mock.Of(), - new Mock(null, null, globalSettings).Object, + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), TestObjects.GetUmbracoSettings(), - Enumerable.Empty(), globalSettings, - new TestVariationContextAccessor(), - true); + Enumerable.Empty(), + Mock.Of()); + + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbCtx = umbracoContextReference.UmbracoContext; + var ctrl = new MatchesAsyncIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); var result = attr.IsValidForRequest(controllerCtx, diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index b3ae7e3dd6e2..e845b829f823 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -40,16 +40,19 @@ public override void SetUp() public void Can_Construct_And_Get_Result() { var globalSettings = TestObjects.GetGlobalSettings(); - var umbracoContext = UmbracoContext.EnsureContext( + + var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, - new Mock().Object, Mock.Of(), - new Mock(null, null, globalSettings).Object, + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), TestObjects.GetUmbracoSettings(), - Enumerable.Empty(), globalSettings, - new TestVariationContextAccessor(), - true); + Enumerable.Empty(), + Mock.Of()); + + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbracoContext = umbracoContextReference.UmbracoContext; var ctrl = new TestSurfaceController(umbracoContext); @@ -62,22 +65,25 @@ public void Can_Construct_And_Get_Result() public void Umbraco_Context_Not_Null() { var globalSettings = TestObjects.GetGlobalSettings(); - var umbCtx = UmbracoContext.EnsureContext( + + var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, - new Mock().Object, Mock.Of(), - new Mock(null, null, globalSettings).Object, + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), TestObjects.GetUmbracoSettings(), - Enumerable.Empty(), globalSettings, - new TestVariationContextAccessor(), - true); + Enumerable.Empty(), + Mock.Of()); + + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbCtx = umbracoContextReference.UmbracoContext; var ctrl = new TestSurfaceController(umbCtx); Assert.IsNotNull(ctrl.UmbracoContext); } - + [Test] public void Can_Lookup_Content() { @@ -88,16 +94,18 @@ public void Can_Lookup_Content() var publishedSnapshotService = new Mock(); var globalSettings = TestObjects.GetGlobalSettings(); - var umbracoContext = UmbracoContext.EnsureContext( + var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, - new Mock().Object, publishedSnapshotService.Object, - new Mock(null, null, globalSettings).Object, + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), - Enumerable.Empty(), globalSettings, - new TestVariationContextAccessor(), - true); + Enumerable.Empty(), + Mock.Of()); + + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbracoContext = umbracoContextReference.UmbracoContext; var helper = new UmbracoHelper( umbracoContext, @@ -121,16 +129,18 @@ public void Mock_Current_Page() var webRoutingSettings = Mock.Of(section => section.UrlProviderMode == "Auto"); var globalSettings = TestObjects.GetGlobalSettings(); - var umbracoContext = UmbracoContext.EnsureContext( + var umbracoContextFactory = new UmbracoContextFactory( Current.UmbracoContextAccessor, - new Mock().Object, Mock.Of(), - new Mock(null, null, globalSettings).Object, + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), Mock.Of(section => section.WebRouting == webRoutingSettings), - Enumerable.Empty(), globalSettings, - new TestVariationContextAccessor(), - true); + Enumerable.Empty(), + Mock.Of()); + + var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of()); + var umbracoContext = umbracoContextReference.UmbracoContext; var content = Mock.Of(publishedContent => publishedContent.Id == 12345); diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index aa44e7d08590..d4945dfc58a3 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -96,20 +96,19 @@ public void ParseLocalLinks(string input, string result) var snapshotService = Mock.Of(); Mock.Get(snapshotService).Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(snapshot); - using (var umbCtx = UmbracoContext.EnsureContext( + var umbracoContextFactory = new UmbracoContextFactory( Umbraco.Web.Composing.Current.UmbracoContextAccessor, - Mock.Of(), snapshotService, - new Mock(null, null, globalSettings).Object, - //setup a quick mock of the WebRouting section + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), - //pass in the custom url provider - new[]{ testUrlProvider.Object }, globalSettings, - new TestVariationContextAccessor(), - true)) + new[] { testUrlProvider.Object }, + Mock.Of()); + + using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of())) { - var output = TemplateUtilities.ParseInternalLinks(input, umbCtx.UrlProvider); + var output = TemplateUtilities.ParseInternalLinks(input, reference.UmbracoContext.UrlProvider); Assert.AreEqual(result, output); } diff --git a/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs b/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs index 3dc1a2debfdf..3db0f0371bd2 100644 --- a/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs +++ b/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Web; using System.Web.WebPages; +using Umbraco.Web.Composing; namespace Umbraco.Web.UI.Config.Splashes { @@ -13,7 +14,7 @@ protected override void OnInit(EventArgs e) { base.OnInit(e); - var store = UmbracoContext.Current.ContentCache; + var store = Current.UmbracoContext.ContentCache; if (store.HasContent()) { //if there is actually content, go to the root diff --git a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs b/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs index ce0be7fbe6d1..76d7565862b2 100644 --- a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs @@ -12,6 +12,7 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Scoping; +using Umbraco.Web.Composing; namespace Umbraco.Web { @@ -102,7 +103,7 @@ protected ICollection GetBatch(bool create) // try get the http context from the UmbracoContext, we do this because in the case we are launching an async // thread and we know that the cache refreshers will execute, we will ensure the UmbracoContext and therefore we // can get the http context from it - var httpContext = (UmbracoContext.Current == null ? null : UmbracoContext.Current.HttpContext) + var httpContext = (Current.UmbracoContext == null ? null : Current.UmbracoContext.HttpContext) // if this is null, it could be that an async thread is calling this method that we weren't aware of and the UmbracoContext // wasn't ensured at the beginning of the thread. We can try to see if the HttpContext.Current is available which might be // the case if the asp.net synchronization context has kicked in diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinder.cs b/src/Umbraco.Web/Cache/DistributedCacheBinder.cs index c64951810a74..92ed7de881c7 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheBinder.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheBinder.cs @@ -16,17 +16,17 @@ public partial class DistributedCacheBinder : IDistributedCacheBinder { private static readonly ConcurrentDictionary FoundHandlers = new ConcurrentDictionary(); private readonly DistributedCache _distributedCache; + private readonly IUmbracoContextFactory _umbracoContextFactory; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// - /// - /// - public DistributedCacheBinder(DistributedCache distributedCache, ILogger logger) + public DistributedCacheBinder(DistributedCache distributedCache, IUmbracoContextFactory umbracoContextFactory, ILogger logger) { _distributedCache = distributedCache; _logger = logger; + _umbracoContextFactory = umbracoContextFactory; } // internal for tests @@ -64,7 +64,7 @@ public void HandleEvents(IEnumerable events) { // ensure we run with an UmbracoContext, because this may run in a background task, // yet developers may be using the 'current' UmbracoContext in the event handlers - using (UmbracoContext.EnsureContext()) + using (_umbracoContextFactory.EnsureUmbracoContext()) { foreach (var e in events) { diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index d4fe51b3f1b1..5dfd7251b852 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -44,7 +44,10 @@ static Current() CoreCurrent.Resetted += (sender, args) => { if (_umbracoContextAccessor != null) - ClearUmbracoContext(); + { + var umbracoContext = _umbracoContextAccessor.UmbracoContext; + umbracoContext?.Dispose(); + } _umbracoContextAccessor = null; }; } @@ -75,18 +78,6 @@ public static IUmbracoContextAccessor UmbracoContextAccessor set => _umbracoContextAccessor = value; // for tests } - // clears the "current" umbraco context - // at the moment the "current" umbraco context can end up being disposed and should get cleared - // in the accessor - this should be done differently but for the time being we have to support it - public static void ClearUmbracoContext() - { - lock (Locker) - { - UmbracoContextAccessor.UmbracoContext?.Dispose(); // dispose the one that is being cleared, if any - UmbracoContextAccessor.UmbracoContext = null; - } - } - #endregion #region Web Getters diff --git a/src/Umbraco.Web/Editors/Filters/IsCurrentUserModelFilterAttribute.cs b/src/Umbraco.Web/Editors/Filters/IsCurrentUserModelFilterAttribute.cs index 59a383dca626..b9c6eb45eb2b 100644 --- a/src/Umbraco.Web/Editors/Filters/IsCurrentUserModelFilterAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/IsCurrentUserModelFilterAttribute.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Net.Http; using System.Web.Http.Filters; +using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Editors.Filters @@ -14,7 +15,7 @@ public override void OnActionExecuted(HttpActionExecutedContext actionExecutedCo { if (actionExecutedContext.Response == null) return; - var user = UmbracoContext.Current.Security.CurrentUser; + var user = Current.UmbracoContext.Security.CurrentUser; if (user == null) return; var objectContent = actionExecutedContext.Response.Content as ObjectContent; diff --git a/src/Umbraco.Web/Editors/Filters/UserGroupAuthorizationAttribute.cs b/src/Umbraco.Web/Editors/Filters/UserGroupAuthorizationAttribute.cs index 4293c31660e3..3fc4c8cedc5b 100644 --- a/src/Umbraco.Web/Editors/Filters/UserGroupAuthorizationAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/UserGroupAuthorizationAttribute.cs @@ -35,7 +35,7 @@ public UserGroupAuthorizationAttribute(string paramName) private UmbracoContext GetUmbracoContext() { - return _umbracoContext ?? UmbracoContext.Current; + return _umbracoContext ?? Composing.Current.UmbracoContext; } protected override bool IsAuthorized(HttpActionContext actionContext) diff --git a/src/Umbraco.Web/Editors/TourController.cs b/src/Umbraco.Web/Editors/TourController.cs index f2a546e407eb..a61926740a40 100644 --- a/src/Umbraco.Web/Editors/TourController.cs +++ b/src/Umbraco.Web/Editors/TourController.cs @@ -71,7 +71,7 @@ public IEnumerable GetTours() } } //Get all allowed sections for the current user - var allowedSections = UmbracoContext.Current.Security.CurrentUser.AllowedSections.ToList(); + var allowedSections = Composing.Current.UmbracoContext.Security.CurrentUser.AllowedSections.ToList(); var toursToBeRemoved = new List(); diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index 44d0cc27ca90..dccc5594fa65 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -63,12 +63,12 @@ public static MvcHtmlString AreaPartial(this HtmlHelper helper, string partial, /// public static MvcHtmlString PreviewBadge(this HtmlHelper helper) { - if (UmbracoContext.Current.InPreviewMode) + if (Current.UmbracoContext.InPreviewMode) { var htmlBadge = String.Format(Current.Configs.Settings().Content.PreviewBadge, IOHelper.ResolveUrl(SystemDirectories.Umbraco), - UmbracoContext.Current.HttpContext.Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Path)); + Current.UmbracoContext.HttpContext.Server.UrlEncode(Current.UmbracoContext.HttpContext.Request.Path)); return new MvcHtmlString(htmlBadge); } return new MvcHtmlString(""); @@ -88,11 +88,11 @@ public static IHtmlString CachedPartial( var cacheKey = new StringBuilder(partialViewName); if (cacheByPage) { - if (UmbracoContext.Current == null) + if (Current.UmbracoContext == null) { throw new InvalidOperationException("Cannot cache by page if the UmbracoContext has not been initialized, this parameter can only be used in the context of an Umbraco request"); } - cacheKey.AppendFormat("{0}-", UmbracoContext.Current.PublishedRequest?.PublishedContent?.Id ?? 0); + cacheKey.AppendFormat("{0}-", Current.UmbracoContext.PublishedRequest?.PublishedContent?.Id ?? 0); } if (cacheByMember) { @@ -690,7 +690,7 @@ public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, stri if (string.IsNullOrEmpty(action)) throw new ArgumentNullOrEmptyException(nameof(action)); if (string.IsNullOrEmpty(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName)); - var formAction = UmbracoContext.Current.OriginalRequestUrl.PathAndQuery; + var formAction = Current.UmbracoContext.OriginalRequestUrl.PathAndQuery; return html.RenderForm(formAction, method, htmlAttributes, controllerName, action, area, additionalRouteVals); } diff --git a/src/Umbraco.Web/IUmbracoContextFactory.cs b/src/Umbraco.Web/IUmbracoContextFactory.cs new file mode 100644 index 000000000000..6d89578da7c2 --- /dev/null +++ b/src/Umbraco.Web/IUmbracoContextFactory.cs @@ -0,0 +1,32 @@ +using System.Web; + +namespace Umbraco.Web +{ + /// + /// Creates and manages instances. + /// + public interface IUmbracoContextFactory + { + /// + /// Ensures that a current exists. + /// + /// + /// If an is already registered in the + /// , returns a non-root reference to it. + /// Otherwise, create a new instance, registers it, and return a root reference + /// to it. + /// If is null, the factory tries to use + /// if it exists. Otherwise, it uses a dummy + /// . + /// + /// + /// using (var contextReference = contextFactory.EnsureUmbracoContext()) + /// { + /// var umbracoContext = contextReference.UmbracoContext; + /// // use umbracoContext... + /// } + /// + /// An optional http context. + UmbracoContextReference EnsureUmbracoContext(HttpContextBase httpContext = null); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs b/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs index d1f3071e95a9..44ee77507bac 100644 --- a/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs +++ b/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs @@ -6,6 +6,7 @@ using Umbraco.Web.Mvc; using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.Composing; namespace Umbraco.Web.Macros { @@ -28,9 +29,9 @@ public PartialViewMacroEngine() _getUmbracoContext = () => { - if (UmbracoContext.Current == null) + if (Current.UmbracoContext == null) throw new InvalidOperationException($"The {GetType()} cannot execute with a null UmbracoContext.Current reference."); - return UmbracoContext.Current; + return Current.UmbracoContext; }; } diff --git a/src/Umbraco.Web/Models/Mapping/ActionButtonsResolver.cs b/src/Umbraco.Web/Models/Mapping/ActionButtonsResolver.cs index c2f7abd48723..9c5a4a0b8845 100644 --- a/src/Umbraco.Web/Models/Mapping/ActionButtonsResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ActionButtonsResolver.cs @@ -3,6 +3,7 @@ using System.Linq; using Umbraco.Core.Models; using Umbraco.Core.Services; +using Umbraco.Web.Composing; namespace Umbraco.Web.Models.Mapping { @@ -23,7 +24,7 @@ public ActionButtonsResolver(IUserService userService, IContentService contentSe public IEnumerable Resolve(IContent source) { //cannot check permissions without a context - if (UmbracoContext.Current == null) + if (Current.UmbracoContext == null) return Enumerable.Empty(); string path; @@ -38,7 +39,7 @@ public IEnumerable Resolve(IContent source) // TODO: This is certainly not ideal usage here - perhaps the best way to deal with this in the future is // with the IUmbracoContextAccessor. In the meantime, if used outside of a web app this will throw a null // reference exception :( - return UserService.GetPermissionsForPath(UmbracoContext.Current.Security.CurrentUser, path).GetAllPermissions(); + return UserService.GetPermissionsForPath(Current.UmbracoContext.Security.CurrentUser, path).GetAllPermissions(); } } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeBasicResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeBasicResolver.cs index aa97b2526091..8b5f3d429652 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeBasicResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeBasicResolver.cs @@ -5,6 +5,7 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; +using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping @@ -27,8 +28,8 @@ public ContentTypeBasic Resolve(TSource source, TDestination destination, Conten { // TODO: We can resolve the UmbracoContext from the IValueResolver options! // OMG - if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null - && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) + if (HttpContext.Current != null && Current.UmbracoContext != null && Current.UmbracoContext.Security.CurrentUser != null + && Current.UmbracoContext.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) { var contentType = _contentTypeBaseServiceProvider.GetContentTypeOf(source); var contentTypeBasic = Mapper.Map(contentType); diff --git a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs index c6fc1d907fb0..3eaad90ca4dc 100644 --- a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs +++ b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs @@ -69,7 +69,7 @@ public EnsurePublishedContentRequestAttribute(UmbracoContext umbracoContext, str /// /// Exposes the UmbracoContext /// - protected UmbracoContext UmbracoContext => _umbracoContext ?? (_umbracoContext = UmbracoContext.Current); + protected UmbracoContext UmbracoContext => _umbracoContext ?? (_umbracoContext = Current.UmbracoContext); // TODO: try lazy property injection? private IPublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance(); @@ -84,13 +84,13 @@ public override void OnActionExecuted(ActionExecutedContext filterContext) base.OnActionExecuted(filterContext); // First we need to check if the published content request has been set, if it has we're going to ignore this and not actually do anything - if (UmbracoContext.Current.PublishedRequest != null) + if (Current.UmbracoContext.PublishedRequest != null) { return; } - UmbracoContext.Current.PublishedRequest = PublishedRouter.CreateRequest(UmbracoContext.Current); - ConfigurePublishedContentRequest(UmbracoContext.Current.PublishedRequest, filterContext); + Current.UmbracoContext.PublishedRequest = PublishedRouter.CreateRequest(Current.UmbracoContext); + ConfigurePublishedContentRequest(Current.UmbracoContext.PublishedRequest, filterContext); } /// diff --git a/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs b/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs index 8dd9dafec0bc..6e5c12ee64e8 100644 --- a/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs +++ b/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs @@ -6,6 +6,7 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.Composing; namespace Umbraco.Web.Mvc { @@ -55,7 +56,7 @@ public IPublishedContent PublishedContent if (_publishedContent != null) return _publishedContent; //need to get the URL for the page - _publishedContent = UmbracoContext.Current.ContentCache.GetById(_pageId); + _publishedContent = Current.UmbracoContext.ContentCache.GetById(_pageId); return _publishedContent; } @@ -66,7 +67,7 @@ public IPublishedContent PublishedContent /// /// public RedirectToUmbracoPageResult(int pageId) - : this(pageId, UmbracoContext.Current) + : this(pageId, Current.UmbracoContext) { } @@ -76,7 +77,7 @@ public RedirectToUmbracoPageResult(int pageId) /// /// public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringValues) - : this(pageId, queryStringValues, UmbracoContext.Current) + : this(pageId, queryStringValues, Current.UmbracoContext) { } @@ -86,7 +87,7 @@ public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringVa /// /// public RedirectToUmbracoPageResult(int pageId, string queryString) - : this(pageId, queryString, UmbracoContext.Current) + : this(pageId, queryString, Current.UmbracoContext) { } @@ -95,7 +96,7 @@ public RedirectToUmbracoPageResult(int pageId, string queryString) /// /// public RedirectToUmbracoPageResult(IPublishedContent publishedContent) - : this(publishedContent, UmbracoContext.Current) + : this(publishedContent, Current.UmbracoContext) { } @@ -105,7 +106,7 @@ public RedirectToUmbracoPageResult(IPublishedContent publishedContent) /// /// public RedirectToUmbracoPageResult(IPublishedContent publishedContent, NameValueCollection queryStringValues) - : this(publishedContent, queryStringValues, UmbracoContext.Current) + : this(publishedContent, queryStringValues, Current.UmbracoContext) { } @@ -115,7 +116,7 @@ public RedirectToUmbracoPageResult(IPublishedContent publishedContent, NameValue /// /// public RedirectToUmbracoPageResult(IPublishedContent publishedContent, string queryString) - : this(publishedContent, queryString, UmbracoContext.Current) + : this(publishedContent, queryString, Current.UmbracoContext) { } diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs index 854b59787f87..68359252d4af 100644 --- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs +++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs @@ -206,7 +206,7 @@ public override void WriteLiteral(object value) // filter / add preview banner if (Response.ContentType.InvariantEquals("text/html")) // ASP.NET default value { - if (UmbracoContext.Current.IsDebug || UmbracoContext.Current.InPreviewMode) + if (Current.UmbracoContext.IsDebug || Current.UmbracoContext.InPreviewMode) { var text = value.ToString(); var pos = text.IndexOf("", StringComparison.InvariantCultureIgnoreCase); @@ -215,13 +215,13 @@ public override void WriteLiteral(object value) { string markupToInject; - if (UmbracoContext.Current.InPreviewMode) + if (Current.UmbracoContext.InPreviewMode) { // creating previewBadge markup markupToInject = string.Format(Current.Configs.Settings().Content.PreviewBadge, IOHelper.ResolveUrl(SystemDirectories.Umbraco), - Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Url?.PathAndQuery)); + Server.UrlEncode(Current.UmbracoContext.HttpContext.Request.Url?.PathAndQuery)); } else { diff --git a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs index 1af434e94eb5..6f5dcab45f61 100644 --- a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs @@ -40,7 +40,7 @@ public abstract class UmbracoVirtualNodeRouteHandler : IRouteHandler /// protected virtual UmbracoContext GetUmbracoContext(RequestContext requestContext) { - return UmbracoContext.Current; + return Composing.Current.UmbracoContext; } public IHttpHandler GetHttpHandler(RequestContext requestContext) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index c96e9ff9a317..cb9709157f87 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -4,6 +4,7 @@ using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; +using Umbraco.Web.Composing; using Umbraco.Web.Templates; namespace Umbraco.Web.PropertyEditors.ValueConverters @@ -26,7 +27,7 @@ public override object ConvertSourceToIntermediate(IPublishedElement owner, Publ var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls are resolved correctly - sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview, UmbracoContext.Current); + sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview, Current.UmbracoContext); sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); return sourceString; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs index 3abdc0702a25..b4c7f99a7555 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerValueConverter.cs @@ -2,6 +2,7 @@ using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; +using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; using Umbraco.Web.Security; @@ -44,7 +45,7 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, Publ if (source == null) return null; - if (UmbracoContext.Current != null) + if (Current.UmbracoContext != null) { IPublishedContent member; if (source is int id) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs index beb22bb57e25..a210aa62c667 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs @@ -9,6 +9,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Services; +using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; namespace Umbraco.Web.PropertyEditors.ValueConverters @@ -67,7 +68,7 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, Publ } // TODO: Inject an UmbracoHelper and create a GetUmbracoHelper method based on either injected or singleton - if (UmbracoContext.Current != null) + if (Current.UmbracoContext != null) { if (propertyType.EditorAlias.Equals(Constants.PropertyEditors.Aliases.MultiNodeTreePicker)) { diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index e636369c74e3..79bfa0aeaa41 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -9,6 +9,7 @@ using HtmlAgilityPack; using Umbraco.Core.Cache; using Umbraco.Core.Services; +using Umbraco.Web.Composing; using Umbraco.Web.Macros; namespace Umbraco.Web.PropertyEditors.ValueConverters @@ -72,7 +73,7 @@ public override object ConvertSourceToIntermediate(IPublishedElement owner, Publ var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls are resolved correctly - sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview, UmbracoContext.Current); + sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview, Current.UmbracoContext); sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); // ensure string is parsed for macros and macros are executed correctly diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs index 5fe731ffb303..2d5c322f5840 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -3,6 +3,7 @@ using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; +using Umbraco.Web.Composing; using Umbraco.Web.Templates; namespace Umbraco.Web.PropertyEditors.ValueConverters @@ -31,7 +32,7 @@ public override object ConvertSourceToIntermediate(IPublishedElement owner, Publ var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls are resolved correctly - sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview, UmbracoContext.Current); + sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview, Current.UmbracoContext); sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); return sourceString; diff --git a/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs b/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs index 0865e61f087d..e823e09eaedc 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs @@ -8,7 +8,7 @@ namespace Umbraco.Web.PublishedCache /// /// A published snapshot is a point-in-time capture of the current state of /// everything that is "published". - public interface IPublishedSnapshot + public interface IPublishedSnapshot : IDisposable { /// /// Gets the . diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs index 2ceced75eb0b..3f5c1aa4d5fa 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs @@ -69,7 +69,7 @@ public IDisposable ForcedPreview(bool preview, Action callback = null) return new ForcedPreviewObject(this, preview, callback); } - private class ForcedPreviewObject : DisposableObject + private class ForcedPreviewObject : DisposableObjectSlim { private readonly PublishedSnapshot _publishedSnapshot; private readonly bool _origPreview; diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 5d47d7b6a53c..c77594eabe63 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -46,11 +46,11 @@ public static string UrlAbsolute(this IPublishedContent content) switch (content.ItemType) { case PublishedItemType.Content: - if (UmbracoContext.Current == null) - throw new InvalidOperationException("Cannot resolve a Url for a content item when UmbracoContext.Current is null."); - if (UmbracoContext.Current.UrlProvider == null) - throw new InvalidOperationException("Cannot resolve a Url for a content item when UmbracoContext.Current.UrlProvider is null."); - return UmbracoContext.Current.UrlProvider.GetUrl(content.Id, true); + if (Current.UmbracoContext == null) + throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext is null."); + if (Current.UmbracoContext.UrlProvider == null) + throw new InvalidOperationException("Cannot resolve a Url for a content item when Current.UmbracoContext.UrlProvider is null."); + return Current.UmbracoContext.UrlProvider.GetUrl(content.Id, true); case PublishedItemType.Media: throw new NotSupportedException("AbsoluteUrl is not supported for media types."); default: @@ -267,7 +267,7 @@ public static IEnumerable SearchDescendants(this IPublish .And() .ManagedQuery(term); - return query.Execute().ToPublishedSearchResults(UmbracoContext.Current.ContentCache); + return query.Execute().ToPublishedSearchResults(Current.UmbracoContext.ContentCache); } public static IEnumerable SearchChildren(this IPublishedContent content, string term, string indexName = null) @@ -288,7 +288,7 @@ public static IEnumerable SearchChildren(this IPublishedC .And() .ManagedQuery(term); - return query.Execute().ToPublishedSearchResults(UmbracoContext.Current.ContentCache); + return query.Execute().ToPublishedSearchResults(Current.UmbracoContext.ContentCache); } #endregion diff --git a/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs b/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs index c577ec82cfc1..ae8da1fc6c6d 100644 --- a/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs +++ b/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs @@ -1,4 +1,5 @@ using System.Web; +using Umbraco.Web.Composing; namespace Umbraco.Web.Routing { @@ -28,7 +29,7 @@ internal void WriteOutput(HttpContext context) response.Clear(); - var frequest = UmbracoContext.Current.PublishedRequest; + var frequest = Current.UmbracoContext.PublishedRequest; var reason = "Cannot render the page at url '{0}'."; if (frequest.HasPublishedContent == false) reason = "No umbraco document matches the url '{0}'."; @@ -37,7 +38,7 @@ internal void WriteOutput(HttpContext context) response.Write("

Page not found

"); response.Write("

"); - response.Write(string.Format(reason, HttpUtility.HtmlEncode(UmbracoContext.Current.OriginalRequestUrl.PathAndQuery))); + response.Write(string.Format(reason, HttpUtility.HtmlEncode(Current.UmbracoContext.OriginalRequestUrl.PathAndQuery))); response.Write("

"); if (string.IsNullOrWhiteSpace(_message) == false) response.Write("

" + _message + "

"); diff --git a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs b/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs index 6db90e31909f..cf8702454be6 100644 --- a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs +++ b/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs @@ -38,9 +38,9 @@ private static Dictionary OldRoutes { get { - var oldRoutes = (Dictionary) UmbracoContext.Current.HttpContext.Items[ContextKey3]; + var oldRoutes = (Dictionary) Current.UmbracoContext.HttpContext.Items[ContextKey3]; if (oldRoutes == null) - UmbracoContext.Current.HttpContext.Items[ContextKey3] = oldRoutes = new Dictionary(); + Current.UmbracoContext.HttpContext.Items[ContextKey3] = oldRoutes = new Dictionary(); return oldRoutes; } } @@ -58,27 +58,27 @@ private static bool HasOldRoutes private static bool LockedEvents { - get => Moving && UmbracoContext.Current.HttpContext.Items[ContextKey2] != null; + get => Moving && Current.UmbracoContext.HttpContext.Items[ContextKey2] != null; set { if (Moving && value) - UmbracoContext.Current.HttpContext.Items[ContextKey2] = true; + Current.UmbracoContext.HttpContext.Items[ContextKey2] = true; else - UmbracoContext.Current.HttpContext.Items.Remove(ContextKey2); + Current.UmbracoContext.HttpContext.Items.Remove(ContextKey2); } } private static bool Moving { - get => UmbracoContext.Current.HttpContext.Items[ContextKey1] != null; + get => Current.UmbracoContext.HttpContext.Items[ContextKey1] != null; set { if (value) - UmbracoContext.Current.HttpContext.Items[ContextKey1] = true; + Current.UmbracoContext.HttpContext.Items[ContextKey1] = true; else { - UmbracoContext.Current.HttpContext.Items.Remove(ContextKey1); - UmbracoContext.Current.HttpContext.Items.Remove(ContextKey2); + Current.UmbracoContext.HttpContext.Items.Remove(ContextKey1); + Current.UmbracoContext.HttpContext.Items.Remove(ContextKey2); } } } @@ -164,7 +164,7 @@ private static void ContentService_Publishing(IContentService sender, PublishEve { if (LockedEvents) return; - var contentCache = UmbracoContext.Current.ContentCache; + var contentCache = Current.UmbracoContext.ContentCache; foreach (var entity in args.PublishedEntities) { var entityContent = contentCache.GetById(entity.Id); @@ -205,7 +205,7 @@ private static void ContentService_Moved(IContentService sender, MoveEventArgs().FirstOrDefault(); var url = provider == null ? route // what else? - : provider.GetUrlFromRoute(route, UmbracoContext.Current, id, _umbracoContext.CleanedUmbracoUrl, Mode, culture)?.Text; + : provider.GetUrlFromRoute(route, Current.UmbracoContext, id, _umbracoContext.CleanedUmbracoUrl, Mode, culture)?.Text; return url ?? "#"; } diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index e2bdd7e84d96..56e48fea7147 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -68,7 +68,7 @@ public WebRuntimeComponent( } public void Initialize() - { + { // setup mvc and webapi services SetupMvcAndWebApi(); @@ -87,23 +87,6 @@ public void Initialize() // set routes CreateRoutes(_umbracoContextAccessor, _globalSettings, _surfaceControllerTypes, _apiControllerTypes); - // get an http context - // at that moment, HttpContext.Current != null but its .Request property is null - var httpContext = new HttpContextWrapper(HttpContext.Current); - - // ensure there is an UmbracoContext - // (also sets the accessor) - // this is a *temp* UmbracoContext - UmbracoContext.EnsureContext( - _umbracoContextAccessor, - new HttpContextWrapper(HttpContext.Current), - _publishedSnapshotService, - new WebSecurity(httpContext, _userService, _globalSettings), - _umbracoSettings, - _urlProviders, - _globalSettings, - _variationContextAccessor); - // ensure WebAPI is initialized, after everything GlobalConfiguration.Configuration.EnsureInitialized(); } diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs index 8a0f47dec3c5..599ace1bce51 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs @@ -76,6 +76,9 @@ public override void Compose(Composition composition) // let's use an hybrid accessor that can fall back to a ThreadStatic context. composition.RegisterUnique(); + // register the umbraco context factory + composition.RegisterUnique(); + // register a per-request HttpContextBase object // is per-request so only one wrapper is created per request composition.Register(factory => new HttpContextWrapper(factory.GetInstance().HttpContext), Lifetime.Request); diff --git a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs index 4326ac328793..2e79e40d7a7b 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs @@ -11,14 +11,16 @@ internal class ScheduledPublishing : RecurringTaskBase { private readonly IRuntimeState _runtime; private readonly IContentService _contentService; + private readonly IUmbracoContextFactory _umbracoContextFactory; private readonly ILogger _logger; public ScheduledPublishing(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IContentService contentService, ILogger logger) + IRuntimeState runtime, IContentService contentService, IUmbracoContextFactory umbracoContextFactory, ILogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; _contentService = contentService; + _umbracoContextFactory = umbracoContextFactory; _logger = logger; } @@ -62,7 +64,7 @@ public override bool PerformRun() // but then what should be its "scope"? could we attach it to scopes? // - and we should definitively *not* have to flush it here (should be auto) // - using (var tempContext = UmbracoContext.EnsureContext()) + using (var contextReference = _umbracoContextFactory.EnsureUmbracoContext()) { try { @@ -74,7 +76,7 @@ public override bool PerformRun() finally { // if running on a temp context, we have to flush the messenger - if (tempContext != null && Composing.Current.ServerMessenger is BatchedDatabaseServerMessenger m) + if (contextReference.IsRoot && Composing.Current.ServerMessenger is BatchedDatabaseServerMessenger m) m.FlushBatch(); } } diff --git a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs index ee1fc3d7bb1f..5e29b17f5682 100644 --- a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs @@ -23,6 +23,7 @@ internal sealed class SchedulerComponent : IComponent private readonly IScopeProvider _scopeProvider; private readonly HealthCheckCollection _healthChecks; private readonly HealthCheckNotificationMethodCollection _notifications; + private readonly IUmbracoContextFactory _umbracoContextFactory; private BackgroundTaskRunner _keepAliveRunner; private BackgroundTaskRunner _publishingRunner; @@ -37,13 +38,14 @@ internal sealed class SchedulerComponent : IComponent public SchedulerComponent(IRuntimeState runtime, IContentService contentService, IAuditService auditService, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, - IScopeProvider scopeProvider, IProfilingLogger logger) + IScopeProvider scopeProvider, IUmbracoContextFactory umbracoContextFactory, IProfilingLogger logger) { _runtime = runtime; _contentService = contentService; _auditService = auditService; _scopeProvider = scopeProvider; _logger = logger; + _umbracoContextFactory = umbracoContextFactory; _healthChecks = healthChecks; _notifications = notifications; @@ -113,11 +115,11 @@ private IBackgroundTask RegisterScheduledPublishing() { // scheduled publishing/unpublishing // install on all, will only run on non-replica servers - var task = new ScheduledPublishing(_publishingRunner, 60000, 60000, _runtime, _contentService, _logger); + var task = new ScheduledPublishing(_publishingRunner, 60000, 60000, _runtime, _contentService, _umbracoContextFactory, _logger); _publishingRunner.TryAdd(task); return task; } - + private IBackgroundTask RegisterHealthCheckNotifier(IHealthChecks healthCheckConfig, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, IProfilingLogger logger) diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index 83a0b15b8281..7e00304c8c07 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -703,7 +703,7 @@ public virtual Attempt ChangePassword(string username, Cha /// public virtual Attempt ChangePassword(string username, ChangingPasswordModel passwordModel, MembershipProvider membershipProvider) { - var passwordChanger = new PasswordChanger(_logger, _userService, Web.Composing.Current.UmbracoContext.HttpContext); + var passwordChanger = new PasswordChanger(_logger, _userService, UmbracoContext.HttpContext); return passwordChanger.ChangePasswordWithMembershipProvider(username, passwordModel, membershipProvider); } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 8024fcbb970d..133da122a19f 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -138,6 +138,7 @@ + @@ -208,6 +209,8 @@ + + diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index a1b443c3ee91..c67c1da02d48 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Web; -using System.Web.Hosting; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Composing; @@ -18,7 +16,7 @@ namespace Umbraco.Web /// /// Class that encapsulates Umbraco information of a specific HTTP request /// - public class UmbracoContext : DisposableObject, IDisposeOnRequestEnd + public class UmbracoContext : DisposableObjectSlim, IDisposeOnRequestEnd { private readonly IGlobalSettings _globalSettings; private readonly Lazy _publishedSnapshot; @@ -26,102 +24,6 @@ public class UmbracoContext : DisposableObject, IDisposeOnRequestEnd private string _previewToken; private bool? _previewing; - #region Ensure Context - - /// - /// Ensures that there is a "current" UmbracoContext. - /// - /// - /// An http context. - /// A published snapshot service. - /// A security helper. - /// The umbraco settings. - /// Some url providers. - /// - /// A value indicating whether to replace the existing context. - /// The "current" UmbracoContext. - /// - /// TODO: this needs to be clarified - /// - /// If is true then the "current" UmbracoContext is replaced - /// with a new one even if there is one already. See . Has to do with - /// creating a context at startup and not being able to access httpContext.Request at that time, so - /// the OriginalRequestUrl remains unspecified until replaces the context. - /// - /// This *has* to be done differently! - /// - /// See http://issues.umbraco.org/issue/U4-1890, http://issues.umbraco.org/issue/U4-1717 - /// - /// - // used by - // UmbracoModule BeginRequest (since it's a request it has an UmbracoContext) - // in BeginRequest so *late* ie *after* the HttpApplication has started (+ init? check!) - // WebRuntimeComponent (and I'm not quite sure why) - // -> because an UmbracoContext seems to be required by UrlProvider to get the "current" published snapshot? - // note: at startup not sure we have an HttpContext.Current - // at startup not sure we have an httpContext.Request => hard to tell "current" url - // should we have a post-boot event of some sort for ppl that *need* ?! - // can we have issues w/ routing context? - // and tests - // can .ContentRequest be null? of course! - public static UmbracoContext EnsureContext( - IUmbracoContextAccessor umbracoContextAccessor, - HttpContextBase httpContext, - IPublishedSnapshotService publishedSnapshotService, - WebSecurity webSecurity, - IUmbracoSettingsSection umbracoSettings, - IEnumerable urlProviders, - IGlobalSettings globalSettings, - IVariationContextAccessor variationContextAccessor, - bool replace = false) - { - if (umbracoContextAccessor == null) throw new ArgumentNullException(nameof(umbracoContextAccessor)); - if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); - if (publishedSnapshotService == null) throw new ArgumentNullException(nameof(publishedSnapshotService)); - if (webSecurity == null) throw new ArgumentNullException(nameof(webSecurity)); - if (umbracoSettings == null) throw new ArgumentNullException(nameof(umbracoSettings)); - if (urlProviders == null) throw new ArgumentNullException(nameof(urlProviders)); - if (globalSettings == null) throw new ArgumentNullException(nameof(globalSettings)); - - // if there is already a current context, return if not replacing - var current = umbracoContextAccessor.UmbracoContext; - if (current != null && replace == false) - return current; - - // create & assign to accessor, dispose existing if any - umbracoContextAccessor.UmbracoContext?.Dispose(); - return umbracoContextAccessor.UmbracoContext = new UmbracoContext(httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, variationContextAccessor); - } - - /// - /// Gets a disposable object representing the presence of a current UmbracoContext. - /// - /// - /// The disposable object should be used in a using block: using (UmbracoContext.EnsureContext()) { ... }. - /// If an actual current UmbracoContext is already present, the disposable object is null and this method does nothing. - /// Otherwise, a temporary, dummy UmbracoContext is created and registered in the accessor. And disposed and removed from the accessor. - /// - internal static IDisposable EnsureContext(HttpContextBase httpContext = null) // keep this internal for now! - { - if (Composing.Current.UmbracoContext != null) return null; - - httpContext = httpContext ?? new HttpContextWrapper(System.Web.HttpContext.Current ?? new HttpContext(new SimpleWorkerRequest("temp.aspx", "", new StringWriter()))); - - return EnsureContext( - Composing.Current.UmbracoContextAccessor, - httpContext, - Composing.Current.PublishedSnapshotService, - new WebSecurity(httpContext, Composing.Current.Services.UserService, Composing.Current.Configs.Global()), - Composing.Current.Configs.Settings(), - Composing.Current.UrlProviders, - Composing.Current.Configs.Global(), - Composing.Current.Factory.GetInstance(), - true); - - // when the context will be disposed, it will be removed from the accessor - // (see DisposeResources) - } - // initializes a new instance of the UmbracoContext class // internal for unit tests // otherwise it's used by EnsureContext above @@ -172,14 +74,6 @@ internal UmbracoContext(HttpContextBase httpContext, UrlProvider = new UrlProvider(this, umbracoSettings.WebRouting, urlProviders, variationContextAccessor); } - #endregion - - /// - /// Gets the current Umbraco Context. - /// - // note: obsolete, use Current.UmbracoContext... then obsolete Current too, and inject! - public static UmbracoContext Current => Composing.Current.UmbracoContext; - /// /// This is used internally for performance calculations, the ObjectCreated DateTime is set as soon as this /// object is instantiated which in the web site is created during the BeginRequest phase. @@ -281,7 +175,7 @@ public bool IsDebug || string.IsNullOrEmpty(request["umbdebug"]) == false); } } - + /// /// Determines whether the current user is in a preview mode and browsing the site (ie. not in the admin UI) /// @@ -345,15 +239,11 @@ protected override void DisposeResources() Security.DisposeIfDisposable(); - // reset - important when running outside of http context - // also takes care of the accessor - Composing.Current.ClearUmbracoContext(); - // help caches release resources // (but don't create caches just to dispose them) // context is not multi-threaded if (_publishedSnapshot.IsValueCreated) - _publishedSnapshot.Value.DisposeIfDisposable(); + _publishedSnapshot.Value.Dispose(); } } } diff --git a/src/Umbraco.Web/UmbracoContextFactory.cs b/src/Umbraco.Web/UmbracoContextFactory.cs new file mode 100644 index 000000000000..a4acd49f1e8c --- /dev/null +++ b/src/Umbraco.Web/UmbracoContextFactory.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Web; +using System.Web.Hosting; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; + +namespace Umbraco.Web +{ + /// + /// Creates and manages instances. + /// + public class UmbracoContextFactory : IUmbracoContextFactory + { + private static readonly NullWriter NullWriterInstance = new NullWriter(); + + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IPublishedSnapshotService _publishedSnapshotService; + private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IDefaultCultureAccessor _defaultCultureAccessor; + + private readonly IUmbracoSettingsSection _umbracoSettings; + private readonly IGlobalSettings _globalSettings; + private readonly IEnumerable _urlProviders; + private readonly IUserService _userService; + + /// + /// Initializes a new instance of the class. + /// + public UmbracoContextFactory(IUmbracoContextAccessor umbracoContextAccessor, IPublishedSnapshotService publishedSnapshotService, IVariationContextAccessor variationContextAccessor, IDefaultCultureAccessor defaultCultureAccessor, IUmbracoSettingsSection umbracoSettings, IGlobalSettings globalSettings, IEnumerable urlProviders, IUserService userService) + { + _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); + _publishedSnapshotService = publishedSnapshotService ?? throw new ArgumentNullException(nameof(publishedSnapshotService)); + _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); + _defaultCultureAccessor = defaultCultureAccessor ?? throw new ArgumentNullException(nameof(defaultCultureAccessor)); + + _umbracoSettings = umbracoSettings ?? throw new ArgumentNullException(nameof(umbracoSettings)); + _globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings)); + _urlProviders = urlProviders ?? throw new ArgumentNullException(nameof(urlProviders)); + _userService = userService ?? throw new ArgumentNullException(nameof(userService)); + } + + private UmbracoContext CreateUmbracoContext(HttpContextBase httpContext) + { + // make sure we have a variation context + if (_variationContextAccessor.VariationContext == null) + _variationContextAccessor.VariationContext = new VariationContext(_defaultCultureAccessor.DefaultCulture); + + var webSecurity = new WebSecurity(httpContext, _userService, _globalSettings); + + return new UmbracoContext(httpContext, _publishedSnapshotService, webSecurity, _umbracoSettings, _urlProviders, _globalSettings, _variationContextAccessor); + } + + /// + public UmbracoContextReference EnsureUmbracoContext(HttpContextBase httpContext = null) + { + var currentUmbracoContext = _umbracoContextAccessor.UmbracoContext; + if (currentUmbracoContext != null) + return new UmbracoContextReference(currentUmbracoContext, false, _umbracoContextAccessor); + + + httpContext = httpContext ?? new HttpContextWrapper(HttpContext.Current ?? new HttpContext(new SimpleWorkerRequest("null.aspx", "", NullWriterInstance))); + + var umbracoContext = CreateUmbracoContext(httpContext); + _umbracoContextAccessor.UmbracoContext = umbracoContext; + + return new UmbracoContextReference(umbracoContext, true, _umbracoContextAccessor); + } + + // dummy TextWriter that does not write + private class NullWriter : TextWriter + { + public override Encoding Encoding => Encoding.UTF8; + } + } +} diff --git a/src/Umbraco.Web/UmbracoContextReference.cs b/src/Umbraco.Web/UmbracoContextReference.cs new file mode 100644 index 000000000000..6c4ac7e54a7b --- /dev/null +++ b/src/Umbraco.Web/UmbracoContextReference.cs @@ -0,0 +1,60 @@ +using System; + +namespace Umbraco.Web +{ + /// + /// Represents a reference to an instance. + /// + /// + /// A reference points to an and it may own it (when it + /// is a root reference) or just reference it. A reference must be disposed after it has + /// been used. Disposing does nothing if the reference is not a root reference. Otherwise, + /// it disposes the and clears the + /// . + /// + public class UmbracoContextReference : IDisposable //fixme - should we inherit from DisposableObjectSlim? + { + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private bool _disposed; + + /// + /// Initializes a new instance of the class. + /// + internal UmbracoContextReference(UmbracoContext umbracoContext, bool isRoot, IUmbracoContextAccessor umbracoContextAccessor) + { + UmbracoContext = umbracoContext; + IsRoot = isRoot; + + _umbracoContextAccessor = umbracoContextAccessor; + } + + /// + /// Gets the . + /// + public UmbracoContext UmbracoContext { get; } + + /// + /// Gets a value indicating whether the reference is a root reference. + /// + /// + /// + /// + public bool IsRoot { get; } + + /// + public void Dispose() + { + if (_disposed) + return; + _disposed = true; + + if (IsRoot) + { + UmbracoContext.Dispose(); + _umbracoContextAccessor.UmbracoContext = null; + } + + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Umbraco.Web/UmbracoInjectedModule.cs b/src/Umbraco.Web/UmbracoInjectedModule.cs index ba1a38448433..14ae45fe40f6 100644 --- a/src/Umbraco.Web/UmbracoInjectedModule.cs +++ b/src/Umbraco.Web/UmbracoInjectedModule.cs @@ -47,6 +47,7 @@ public class UmbracoInjectedModule : IHttpModule private readonly ILogger _logger; private readonly IPublishedRouter _publishedRouter; private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IUmbracoContextFactory _umbracoContextFactory; public UmbracoInjectedModule( IGlobalSettings globalSettings, @@ -57,7 +58,8 @@ public UmbracoInjectedModule( IRuntimeState runtime, ILogger logger, IPublishedRouter publishedRouter, - IVariationContextAccessor variationContextAccessor) + IVariationContextAccessor variationContextAccessor, + IUmbracoContextFactory umbracoContextFactory) { _combinedRouteCollection = new Lazy(CreateRouteCollection); @@ -70,6 +72,7 @@ public UmbracoInjectedModule( _logger = logger; _publishedRouter = publishedRouter; _variationContextAccessor = variationContextAccessor; + _umbracoContextFactory = umbracoContextFactory; } #region HttpModule event handlers @@ -92,18 +95,10 @@ private void BeginRequest(HttpContextBase httpContext) // ok, process - // create the UmbracoContext singleton, one per request, and assign - // replace existing if any (eg during app startup, a temp one is created) - UmbracoContext.EnsureContext( - _umbracoContextAccessor, - httpContext, - _publishedSnapshotService, - new WebSecurity(httpContext, _userService, _globalSettings), - Current.Configs.Settings(), - _urlProviders, - _globalSettings, - _variationContextAccessor, - true); + // ensure there's an UmbracoContext registered for the current request + // registers the context reference so its disposed at end of request + var umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext(httpContext); + httpContext.DisposeOnPipelineCompleted(umbracoContextReference); } /// @@ -124,10 +119,10 @@ void ProcessRequest(HttpContextBase httpContext) if (httpContext.Request.Url.IsClientSideRequest()) return; - if (UmbracoContext.Current == null) - throw new InvalidOperationException("The UmbracoContext.Current is null, ProcessRequest cannot proceed unless there is a current UmbracoContext"); + if (Current.UmbracoContext == null) + throw new InvalidOperationException("The Current.UmbracoContext is null, ProcessRequest cannot proceed unless there is a current UmbracoContext"); - var umbracoContext = UmbracoContext.Current; + var umbracoContext = Current.UmbracoContext; // re-write for the default back office path if (httpContext.Request.Url.IsDefaultBackOfficeRequest(_globalSettings)) @@ -491,14 +486,14 @@ public void Init(HttpApplication app) { var httpContext = ((HttpApplication) sender).Context; - if (UmbracoContext.Current != null && UmbracoContext.Current.IsFrontEndUmbracoRequest) + if (Current.UmbracoContext != null && Current.UmbracoContext.IsFrontEndUmbracoRequest) { LogHttpRequest.TryGetCurrentHttpRequestId(out var httpRequestId); - _logger.Verbose("End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", httpRequestId, httpContext.Request.Url, DateTime.Now.Subtract(UmbracoContext.Current.ObjectCreated).TotalMilliseconds); + _logger.Verbose("End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", httpRequestId, httpContext.Request.Url, DateTime.Now.Subtract(Current.UmbracoContext.ObjectCreated).TotalMilliseconds); } - UmbracoModule.OnEndRequest(this, new UmbracoRequestEventArgs(UmbracoContext.Current, new HttpContextWrapper(httpContext))); + UmbracoModule.OnEndRequest(this, new UmbracoRequestEventArgs(Current.UmbracoContext, new HttpContextWrapper(httpContext))); DisposeHttpContextItems(httpContext); }; diff --git a/src/Umbraco.Web/UrlHelperRenderExtensions.cs b/src/Umbraco.Web/UrlHelperRenderExtensions.cs index 2835bc02dcc0..cc5000bb194a 100644 --- a/src/Umbraco.Web/UrlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/UrlHelperRenderExtensions.cs @@ -296,7 +296,7 @@ public static string SurfaceAction(this UrlHelper url, string action, string con var encryptedRoute = UmbracoHelper.CreateEncryptedRouteString(controllerName, action, area, additionalRouteVals); - var result = UmbracoContext.Current.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; + var result = Current.UmbracoContext.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; return result; } @@ -339,7 +339,7 @@ public static string SurfaceAction(this UrlHelper url, string action, Type surfa var encryptedRoute = UmbracoHelper.CreateEncryptedRouteString(metaData.ControllerName, action, area, additionalRouteVals); - var result = UmbracoContext.Current.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; + var result = Current.UmbracoContext.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; return result; } diff --git a/src/Umbraco.Web/WebApi/Filters/AppendCurrentEventMessagesAttribute.cs b/src/Umbraco.Web/WebApi/Filters/AppendCurrentEventMessagesAttribute.cs index 779a559a162d..8669533e7879 100644 --- a/src/Umbraco.Web/WebApi/Filters/AppendCurrentEventMessagesAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/AppendCurrentEventMessagesAttribute.cs @@ -18,7 +18,7 @@ public override void OnActionExecuted(HttpActionExecutedContext context) { if (context.Response == null) return; if (context.Request.Method == HttpMethod.Get) return; - if (UmbracoContext.Current == null) return; + if (Current.UmbracoContext == null) return; var obj = context.Response.Content as ObjectContent; if (obj == null) return; diff --git a/src/Umbraco.Web/WebApi/Filters/AppendUserModifiedHeaderAttribute.cs b/src/Umbraco.Web/WebApi/Filters/AppendUserModifiedHeaderAttribute.cs index 3b2ee30aa05d..5ac8d886f068 100644 --- a/src/Umbraco.Web/WebApi/Filters/AppendUserModifiedHeaderAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/AppendUserModifiedHeaderAttribute.cs @@ -2,6 +2,7 @@ using System.Web.Http.Filters; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Web.Composing; namespace Umbraco.Web.WebApi.Filters { @@ -53,7 +54,7 @@ public override void OnActionExecuted(HttpActionExecutedContext actionExecutedCo throw new InvalidOperationException("No argument found for the current action with the name: " + _userIdParameter); } - var user = UmbracoContext.Current.Security.CurrentUser; + var user = Current.UmbracoContext.Security.CurrentUser; if (user == null) return; var userId = GetUserIdFromParameter(actionContext.ActionArguments[_userIdParameter]); diff --git a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs index ae42d54787a0..efee0458906f 100644 --- a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs @@ -61,7 +61,7 @@ public EnsureUserPermissionForContentAttribute(string paramName, char permission public override void OnActionExecuting(HttpActionContext actionContext) { - if (UmbracoContext.Current.Security.CurrentUser == null) + if (Current.UmbracoContext.Security.CurrentUser == null) { //not logged in throw new HttpResponseException(System.Net.HttpStatusCode.Unauthorized); diff --git a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForMediaAttribute.cs b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForMediaAttribute.cs index 026b43a01bed..24bf2ea9a877 100644 --- a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForMediaAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForMediaAttribute.cs @@ -80,7 +80,7 @@ private int GetNodeIdFromParameter(object parameterValue) public override void OnActionExecuting(HttpActionContext actionContext) { - if (UmbracoContext.Current.Security.CurrentUser == null) + if (Current.UmbracoContext.Security.CurrentUser == null) { throw new HttpResponseException(System.Net.HttpStatusCode.Unauthorized); } @@ -118,7 +118,7 @@ public override void OnActionExecuting(HttpActionContext actionContext) if (MediaController.CheckPermissions( actionContext.Request.Properties, - UmbracoContext.Current.Security.CurrentUser, + Current.UmbracoContext.Security.CurrentUser, Current.Services.MediaService, Current.Services.EntityService, nodeId)) diff --git a/src/Umbraco.Web/WebApi/Filters/FilterAllowedOutgoingMediaAttribute.cs b/src/Umbraco.Web/WebApi/Filters/FilterAllowedOutgoingMediaAttribute.cs index 4a4c5b98a0ea..21dc60e6cc2b 100644 --- a/src/Umbraco.Web/WebApi/Filters/FilterAllowedOutgoingMediaAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/FilterAllowedOutgoingMediaAttribute.cs @@ -48,7 +48,7 @@ public override void OnActionExecuted(HttpActionExecutedContext actionExecutedCo { if (actionExecutedContext.Response == null) return; - var user = UmbracoContext.Current.Security.CurrentUser; + var user = Composing.Current.UmbracoContext.Security.CurrentUser; if (user == null) return; var objectContent = actionExecutedContext.Response.Content as ObjectContent; diff --git a/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs b/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs index 8410891a5dc4..e2a6f155d0fe 100644 --- a/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs @@ -2,6 +2,7 @@ using System.Net.Http; using System.Web.Http.Filters; using Umbraco.Core; +using Umbraco.Web.Composing; using Umbraco.Web.Editors; using Umbraco.Web.Models.ContentEditing; @@ -16,7 +17,7 @@ public override void OnActionExecuted(HttpActionExecutedContext actionExecutedCo { if (actionExecutedContext.Response == null) return; - var user = UmbracoContext.Current.Security.CurrentUser; + var user = Current.UmbracoContext.Security.CurrentUser; if (user == null) return; if (actionExecutedContext.Response.Content is ObjectContent objectContent) @@ -27,7 +28,7 @@ public override void OnActionExecuted(HttpActionExecutedContext actionExecutedCo { var args = new EditorModelEventArgs( model, - UmbracoContext.Current); + Current.UmbracoContext); EditorModelEventManager.EmitEvent(actionExecutedContext, args); objectContent.Value = args.Model; }