diff --git a/src/Umbraco.Abstractions/Cache/DictionaryAppCache.cs b/src/Umbraco.Abstractions/Cache/DictionaryAppCache.cs index fd360b303d..d372916240 100644 --- a/src/Umbraco.Abstractions/Cache/DictionaryAppCache.cs +++ b/src/Umbraco.Abstractions/Cache/DictionaryAppCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Text.RegularExpressions; @@ -101,5 +102,9 @@ namespace Umbraco.Core.Cache var compiled = new Regex(regex, RegexOptions.Compiled); _items.RemoveAll(kvp => compiled.IsMatch(kvp.Key)); } + + public IEnumerator> GetEnumerator() => _items.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs b/src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs index 87d87ad1c9..e698d93ebe 100644 --- a/src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs +++ b/src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs @@ -171,5 +171,20 @@ namespace Umbraco.Core.Cache } #endregion + + public IEnumerator> GetEnumerator() + { + if (!TryGetContextItems(out var items)) + { + yield break; + } + + foreach (DictionaryEntry item in items) + { + yield return new KeyValuePair(item.Key.ToString(), item.Value); + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/src/Umbraco.Abstractions/Cache/IRequestCache.cs b/src/Umbraco.Abstractions/Cache/IRequestCache.cs index 5ed32b5ba0..7ed7f8251c 100644 --- a/src/Umbraco.Abstractions/Cache/IRequestCache.cs +++ b/src/Umbraco.Abstractions/Cache/IRequestCache.cs @@ -1,6 +1,8 @@ +using System.Collections.Generic; + namespace Umbraco.Core.Cache { - public interface IRequestCache : IAppCache + public interface IRequestCache : IAppCache, IEnumerable> { bool Set(string key, object value); bool Remove(string key); diff --git a/src/Umbraco.Abstractions/Cache/NoAppCache.cs b/src/Umbraco.Abstractions/Cache/NoAppCache.cs index 5ca8f47059..60bc6fb8b8 100644 --- a/src/Umbraco.Abstractions/Cache/NoAppCache.cs +++ b/src/Umbraco.Abstractions/Cache/NoAppCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; @@ -84,5 +85,9 @@ namespace Umbraco.Core.Cache /// public virtual void ClearByRegex(string regex) { } + + public IEnumerator> GetEnumerator() => new Dictionary().GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/src/Umbraco.Web/HybridAccessorBase.cs b/src/Umbraco.Abstractions/HybridAccessorBase.cs similarity index 71% rename from src/Umbraco.Web/HybridAccessorBase.cs rename to src/Umbraco.Abstractions/HybridAccessorBase.cs index ec18b6f3d4..3e17f7b757 100644 --- a/src/Umbraco.Web/HybridAccessorBase.cs +++ b/src/Umbraco.Abstractions/HybridAccessorBase.cs @@ -1,6 +1,7 @@ using System; -using System.Runtime.Remoting.Messaging; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Scoping; namespace Umbraco.Web { @@ -16,12 +17,14 @@ namespace Umbraco.Web public abstract class HybridAccessorBase where T : class { + private readonly IRequestCache _requestCache; + // ReSharper disable StaticMemberInGenericType private static readonly object Locker = new object(); private static bool _registered; // ReSharper restore StaticMemberInGenericType - private readonly IHttpContextAccessor _httpContextAccessor; + // private readonly IHttpContextAccessor _httpContextAccessor; protected abstract string ItemKey { get; } @@ -42,17 +45,16 @@ namespace Umbraco.Web // yes! flows with async! private T NonContextValue { - get => (T) CallContext.LogicalGetData(ItemKey); + get => CallContext.GetData(ItemKey); set { - if (value == null) CallContext.FreeNamedDataSlot(ItemKey); - else CallContext.LogicalSetData(ItemKey, value); + CallContext.SetData(ItemKey, value); } } - protected HybridAccessorBase(IHttpContextAccessor httpContextAccessor) + protected HybridAccessorBase(IRequestCache requestCache) { - _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); + _requestCache = requestCache ?? throw new ArgumentNullException(nameof(requestCache)); lock (Locker) { @@ -65,15 +67,14 @@ namespace Umbraco.Web var itemKey = ItemKey; // virtual SafeCallContext.Register(() => { - var value = CallContext.LogicalGetData(itemKey); - CallContext.FreeNamedDataSlot(itemKey); + var value = CallContext.GetData(itemKey); return value; }, o => { if (o == null) return; var value = o as T; if (value == null) throw new ArgumentException($"Expected type {typeof(T).FullName}, got {o.GetType().FullName}", nameof(o)); - CallContext.LogicalSetData(itemKey, value); + CallContext.SetData(itemKey, value); }); } @@ -81,20 +82,23 @@ namespace Umbraco.Web { get { - var httpContext = _httpContextAccessor.HttpContext; - if (httpContext == null) return NonContextValue; - return (T) httpContext.Items[ItemKey]; + if (!_requestCache.IsAvailable) + { + return NonContextValue; + } + return (T) _requestCache.Get(ItemKey); } set { - var httpContext = _httpContextAccessor.HttpContext; - if (httpContext == null) + if (!_requestCache.IsAvailable) + { NonContextValue = value; + } else if (value == null) - httpContext.Items.Remove(ItemKey); + _requestCache.Remove(ItemKey); else - httpContext.Items[ItemKey] = value; + _requestCache.Set(ItemKey, value); } } } diff --git a/src/Umbraco.Web/HybridEventMessagesAccessor.cs b/src/Umbraco.Abstractions/HybridEventMessagesAccessor.cs similarity index 70% rename from src/Umbraco.Web/HybridEventMessagesAccessor.cs rename to src/Umbraco.Abstractions/HybridEventMessagesAccessor.cs index fddde403d8..b2700eb137 100644 --- a/src/Umbraco.Web/HybridEventMessagesAccessor.cs +++ b/src/Umbraco.Abstractions/HybridEventMessagesAccessor.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Events; +using Umbraco.Core.Cache; +using Umbraco.Core.Events; namespace Umbraco.Web { @@ -6,8 +7,8 @@ namespace Umbraco.Web { protected override string ItemKey => "Umbraco.Core.Events.HybridEventMessagesAccessor"; - public HybridEventMessagesAccessor(IHttpContextAccessor httpContextAccessor) - : base(httpContextAccessor) + public HybridEventMessagesAccessor(IRequestCache requestCache) + : base(requestCache) { } public EventMessages EventMessages diff --git a/src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs b/src/Umbraco.Abstractions/Models/PublishedContent/HttpContextVariationContextAccessor.cs similarity index 50% rename from src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs rename to src/Umbraco.Abstractions/Models/PublishedContent/HttpContextVariationContextAccessor.cs index 0007b346c5..41b81a4a6e 100644 --- a/src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs +++ b/src/Umbraco.Abstractions/Models/PublishedContent/HttpContextVariationContextAccessor.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Cache; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Models.PublishedContent { @@ -7,22 +8,22 @@ namespace Umbraco.Web.Models.PublishedContent /// public class HttpContextVariationContextAccessor : IVariationContextAccessor { - public const string ContextKey = "Umbraco.Web.Models.PublishedContent.DefaultVariationContextAccessor"; - public readonly IHttpContextAccessor HttpContextAccessor; + private readonly IRequestCache _requestCache; + private const string _contextKey = "Umbraco.Web.Models.PublishedContent.DefaultVariationContextAccessor"; /// /// Initializes a new instance of the class. /// - public HttpContextVariationContextAccessor(IHttpContextAccessor httpContextAccessor) + public HttpContextVariationContextAccessor(IRequestCache requestCache) { - HttpContextAccessor = httpContextAccessor; + _requestCache = requestCache; } /// public VariationContext VariationContext { - get => (VariationContext) HttpContextAccessor.HttpContext?.Items[ContextKey]; - set => HttpContextAccessor.HttpContext.Items[ContextKey] = value; + get => (VariationContext) _requestCache.Get(_contextKey); + set => _requestCache.Set(_contextKey, value); } } } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Abstractions/Models/PublishedContent/PublishedValueFallback.cs similarity index 94% rename from src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs rename to src/Umbraco.Abstractions/Models/PublishedContent/PublishedValueFallback.cs index 7b467b6d15..6a3cae9120 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Abstractions/Models/PublishedContent/PublishedValueFallback.cs @@ -14,14 +14,16 @@ namespace Umbraco.Web.Models.PublishedContent { private readonly ILocalizationService _localizationService; private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IPublishedValueFallback _publishedValueFallback; /// /// Initializes a new instance of the class. /// - public PublishedValueFallback(ServiceContext serviceContext, IVariationContextAccessor variationContextAccessor) + public PublishedValueFallback(ServiceContext serviceContext, IVariationContextAccessor variationContextAccessor, IPublishedValueFallback publishedValueFallback) { _localizationService = serviceContext.LocalizationService; _variationContextAccessor = variationContextAccessor; + _publishedValueFallback = publishedValueFallback; } /// @@ -182,7 +184,7 @@ namespace Umbraco.Web.Models.PublishedContent // if we found a content with the property having a value, return that property value if (property != null && property.HasValue(culture, segment)) { - value = property.Value(culture, segment); + value = property.Value(_publishedValueFallback, culture, segment); return true; } @@ -216,7 +218,7 @@ namespace Umbraco.Web.Models.PublishedContent if (property.HasValue(culture2, segment)) { - value = property.Value(culture2, segment); + value = property.Value(_publishedValueFallback, culture2, segment); return true; } @@ -250,7 +252,7 @@ namespace Umbraco.Web.Models.PublishedContent if (content.HasValue(alias, culture2, segment)) { - value = content.Value(alias, culture2, segment); + value = content.Value(_publishedValueFallback, alias, culture2, segment); return true; } @@ -287,7 +289,7 @@ namespace Umbraco.Web.Models.PublishedContent if (content.HasValue(alias, culture2, segment)) { - value = content.Value(alias, culture2, segment); + value = content.Value(_publishedValueFallback, alias, culture2, segment); return true; } diff --git a/src/Umbraco.Infrastructure/Models/PublishedContent/VariationContextAccessorExtensions.cs b/src/Umbraco.Abstractions/Models/PublishedContent/VariationContextAccessorExtensions.cs similarity index 100% rename from src/Umbraco.Infrastructure/Models/PublishedContent/VariationContextAccessorExtensions.cs rename to src/Umbraco.Abstractions/Models/PublishedContent/VariationContextAccessorExtensions.cs diff --git a/src/Umbraco.Abstractions/Models/Trees/ActionMenuItem.cs b/src/Umbraco.Abstractions/Models/Trees/ActionMenuItem.cs index 9354417155..fe760fb94d 100644 --- a/src/Umbraco.Abstractions/Models/Trees/ActionMenuItem.cs +++ b/src/Umbraco.Abstractions/Models/Trees/ActionMenuItem.cs @@ -1,6 +1,4 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using Umbraco.Core; +using Umbraco.Core; using Umbraco.Core.Services; namespace Umbraco.Web.Models.Trees diff --git a/src/Umbraco.Web/Models/Trees/CreateChildEntity.cs b/src/Umbraco.Abstractions/Models/Trees/CreateChildEntity.cs similarity index 100% rename from src/Umbraco.Web/Models/Trees/CreateChildEntity.cs rename to src/Umbraco.Abstractions/Models/Trees/CreateChildEntity.cs diff --git a/src/Umbraco.Web/Models/Trees/ExportMember.cs b/src/Umbraco.Abstractions/Models/Trees/ExportMember.cs similarity index 100% rename from src/Umbraco.Web/Models/Trees/ExportMember.cs rename to src/Umbraco.Abstractions/Models/Trees/ExportMember.cs diff --git a/src/Umbraco.Infrastructure/Scoping/CallContext.cs b/src/Umbraco.Abstractions/Scoping/CallContext.cs similarity index 100% rename from src/Umbraco.Infrastructure/Scoping/CallContext.cs rename to src/Umbraco.Abstractions/Scoping/CallContext.cs diff --git a/src/Umbraco.Abstractions/Umbraco.Abstractions.csproj b/src/Umbraco.Abstractions/Umbraco.Abstractions.csproj index 2eff63c4b8..52322f979a 100644 --- a/src/Umbraco.Abstractions/Umbraco.Abstractions.csproj +++ b/src/Umbraco.Abstractions/Umbraco.Abstractions.csproj @@ -24,9 +24,4 @@ <_Parameter1>Umbraco.Tests.Benchmarks - - - - - diff --git a/src/Umbraco.Web/Models/ContentTypeImportModel.cs b/src/Umbraco.Infrastructure/Models/ContentTypeImportModel.cs similarity index 100% rename from src/Umbraco.Web/Models/ContentTypeImportModel.cs rename to src/Umbraco.Infrastructure/Models/ContentTypeImportModel.cs diff --git a/src/Umbraco.Web/Models/ImageProcessorImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Models/ImageProcessorImageUrlGenerator.cs similarity index 98% rename from src/Umbraco.Web/Models/ImageProcessorImageUrlGenerator.cs rename to src/Umbraco.Infrastructure/Models/ImageProcessorImageUrlGenerator.cs index e471e9aa4a..f88309d7e3 100644 --- a/src/Umbraco.Web/Models/ImageProcessorImageUrlGenerator.cs +++ b/src/Umbraco.Infrastructure/Models/ImageProcessorImageUrlGenerator.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Models; namespace Umbraco.Web.Models { - internal class ImageProcessorImageUrlGenerator : IImageUrlGenerator + public class ImageProcessorImageUrlGenerator : IImageUrlGenerator { public string GetImageUrl(ImageUrlGenerationOptions options) { diff --git a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs b/src/Umbraco.Infrastructure/Models/LocalPackageInstallModel.cs similarity index 100% rename from src/Umbraco.Web/Models/LocalPackageInstallModel.cs rename to src/Umbraco.Infrastructure/Models/LocalPackageInstallModel.cs diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs index e22041a682..ab95768f4b 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs @@ -20,6 +20,7 @@ using Umbraco.Core.Serialization; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Core.Sync; +using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.PublishedCache; using IntegerValidator = Umbraco.Core.PropertyEditors.Validators.IntegerValidator; @@ -141,6 +142,8 @@ namespace Umbraco.Core.Runtime // register the published snapshot accessor - the "current" published snapshot is in the umbraco context composition.RegisterUnique(); + + composition.RegisterUnique(); } } } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index 4d6f57f490..b1ee6a7c4d 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -439,11 +439,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache return (IPublishedContent) appCache.Get(key, () => (new XmlPublishedContent(node, isPreviewing, appCache, contentTypeCache, variationContextAccessor)).CreateModel(Current.PublishedModelFactory)); } - public static void ClearRequest() - { - Current.AppCaches.RequestCache.ClearByKey(CacheKeyPrefix); - } - private const string CacheKeyPrefix = "CONTENTCACHE_XMLPUBLISHEDCONTENT_"; } } diff --git a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs index d4c242a762..c601c48148 100644 --- a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs +++ b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs @@ -42,7 +42,8 @@ namespace Umbraco.Tests.Routing logger, null, // FIXME: PublishedRouter complexities... Mock.Of(), - new RoutableDocumentFilter(globalSettings, IOHelper) + new RoutableDocumentFilter(globalSettings, IOHelper), + AppCaches.RequestCache ); runtime.Level = RuntimeLevel.Run; diff --git a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs index 7ff0776ba8..b16c739553 100644 --- a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs @@ -8,7 +8,6 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Web.Composing; -using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; @@ -39,7 +38,7 @@ namespace Umbraco.Tests.Security var runtime = Mock.Of(x => x.Level == RuntimeLevel.Install); var mgr = new BackOfficeCookieManager( - Mock.Of(accessor => accessor.UmbracoContext == umbracoContext), runtime, TestObjects.GetGlobalSettings(), TestHelper.IOHelper); + Mock.Of(accessor => accessor.UmbracoContext == umbracoContext), runtime, TestObjects.GetGlobalSettings(), IOHelper, AppCaches.RequestCache); var result = mgr.ShouldAuthenticateRequest(Mock.Of(), new Uri("http://localhost/umbraco")); @@ -58,7 +57,7 @@ namespace Umbraco.Tests.Security new TestVariationContextAccessor(), IOHelper); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); - var mgr = new BackOfficeCookieManager(Mock.Of(accessor => accessor.UmbracoContext == umbCtx), runtime, TestObjects.GetGlobalSettings(), TestHelper.IOHelper); + var mgr = new BackOfficeCookieManager(Mock.Of(accessor => accessor.UmbracoContext == umbCtx), runtime, TestObjects.GetGlobalSettings(), IOHelper, AppCaches.RequestCache); var request = new Mock(); request.Setup(owinRequest => owinRequest.Uri).Returns(new Uri("http://localhost/umbraco")); diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index ee922cdfcb..6f07b1ecd2 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -29,6 +29,7 @@ namespace Umbraco.Tests.TestHelpers protected override void Compose() { base.Compose(); + base.Compose(); Composition.RegisterUnique(); Composition.RegisterUnique(); diff --git a/src/Umbraco.Tests/Views/web.config b/src/Umbraco.Tests/Views/web.config new file mode 100644 index 0000000000..efd80424e5 --- /dev/null +++ b/src/Umbraco.Tests/Views/web.config @@ -0,0 +1,74 @@ + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml index e95057efe9..11033aa785 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml @@ -48,7 +48,7 @@ redirectUrl = Url.Action("AuthorizeUpgrade", "BackOffice") }); } - @Html.BareMinimumServerVariablesScript(Url, externalLoginUrl, Model.Features, Model.GlobalSettings, Model.UmbracoVersion, Model.UmbracoSettingsSection, Model.IOHelper, Model.TreeCollection) + @Html.BareMinimumServerVariablesScript(Url, externalLoginUrl, Model.Features, Model.GlobalSettings, Model.UmbracoVersion, Model.UmbracoSettingsSection, Model.IOHelper, Model.TreeCollection, Model.HttpContextAccessor, Model.HostingEnvironment)