From 908589277a4f0d4bb7b22fe06c2d42958adf34e5 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Apr 2018 19:46:47 +0200 Subject: [PATCH 01/22] U4-11227 - default variations and fallback (wip) --- .../PublishedContent/IPublishedProperty.cs | 8 +- .../IPublishedValueFallback.cs | 35 +++ .../IPublishedVariationContextAccessor.cs | 13 ++ .../PublishedContentExtensionsForModels.cs | 5 + .../PublishedContent/PublishedPropertyBase.cs | 8 +- .../PublishedVariationContext.cs | 30 +++ .../PublishedContent/RawValueProperty.cs | 8 +- ...ulturePublishedVariationContextAccessor.cs | 23 ++ ...StaticPublishedVariationContextAccessor.cs | 23 ++ src/Umbraco.Core/Umbraco.Core.csproj | 5 + .../Published/NestedContentTests.cs | 8 +- .../PublishedContentOtherTests.cs | 212 ++++++++++++++++++ .../SolidPublishedSnapshot.cs | 8 +- .../Routing/RenderRouteHandlerTests.cs | 1 + .../Scoping/ScopedNuCacheTests.cs | 3 + .../TestHelpers/TestObjects-Mocks.cs | 1 + .../TestHelpers/TestWithDatabaseBase.cs | 4 +- .../Accessors}/NoHttpContextAccessor.cs | 2 +- .../TestPublishedSnapshotAccessor.cs | 2 +- .../TestPublishedVariationContextAccessor.cs | 13 ++ .../Accessors}/TestUmbracoContextAccessor.cs | 2 +- .../Testing/TestingTests/MockTests.cs | 1 + src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 1 + src/Umbraco.Tests/Umbraco.Tests.csproj | 8 +- ...RenderIndexActionSelectorAttributeTests.cs | 1 + .../Web/Mvc/RenderModelBinderTests.cs | 1 + .../Web/Mvc/SurfaceControllerTests.cs | 1 + .../Web/Mvc/UmbracoViewPageTests.cs | 13 +- .../Web/TemplateUtilitiesTests.cs | 1 + .../Web/WebExtensionMethodTests.cs | 1 + .../PublishedCache/NuCache/ContentNode.cs | 17 +- .../PublishedCache/NuCache/ContentNodeKit.cs | 4 +- .../PublishedCache/NuCache/ContentStore.cs | 10 +- .../{Database.cs => DatabaseDataSource.cs} | 6 +- .../NuCache/DataSource/IDataSource.cs | 21 ++ .../PublishedCache/NuCache/MemberCache.cs | 12 +- .../NuCache/NuCacheComponent.cs | 13 +- .../PublishedCache/NuCache/Property.cs | 33 ++- .../NuCache/PublishedContent.cs | 7 +- .../PublishedCache/NuCache/PublishedMember.cs | 8 +- .../NuCache/PublishedSnapshotService.cs | 22 +- .../PublishedElementPropertyBase.cs | 8 +- .../PublishedSnapshotServiceBase.cs | 5 +- .../PublishedSnapshotService.cs | 14 +- .../XmlPublishedCache/XmlCacheComponent.cs | 1 + .../XmlPublishedCache/XmlPublishedProperty.cs | 6 +- src/Umbraco.Web/PublishedContentExtensions.cs | 47 ++-- .../PublishedContentPropertyExtension.cs | 27 +-- src/Umbraco.Web/PublishedElementExtensions.cs | 100 +++------ src/Umbraco.Web/Routing/PublishedRouter.cs | 4 +- src/Umbraco.Web/Umbraco.Web.csproj | 3 +- src/Umbraco.Web/umbraco.presentation/page.cs | 8 +- 52 files changed, 590 insertions(+), 228 deletions(-) create mode 100644 src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs create mode 100644 src/Umbraco.Core/Models/PublishedContent/IPublishedVariationContextAccessor.cs create mode 100644 src/Umbraco.Core/Models/PublishedContent/PublishedVariationContext.cs create mode 100644 src/Umbraco.Core/Models/PublishedContent/ThreadCulturePublishedVariationContextAccessor.cs create mode 100644 src/Umbraco.Core/Models/PublishedContent/ThreadStaticPublishedVariationContextAccessor.cs create mode 100644 src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs rename src/Umbraco.Tests/{TestHelpers => Testing/Objects/Accessors}/NoHttpContextAccessor.cs (78%) rename src/Umbraco.Tests/{TestHelpers/Stubs => Testing/Objects/Accessors}/TestPublishedSnapshotAccessor.cs (79%) create mode 100644 src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs rename src/Umbraco.Tests/{TestHelpers/Stubs => Testing/Objects/Accessors}/TestUmbracoContextAccessor.cs (77%) rename src/Umbraco.Web/PublishedCache/NuCache/DataSource/{Database.cs => DatabaseDataSource.cs} (97%) create mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs index 9d2cca3e6d..bfe1389921 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs @@ -21,7 +21,7 @@ /// Other caches that get their raw value from the database would consider that a property has "no /// value" if it is missing, null, or an empty string (including whitespace-only). /// - bool HasValue(string culture = null, string segment = null); + bool HasValue(string culture = ".", string segment = "."); /// /// Gets the source value of the property. @@ -35,7 +35,7 @@ /// If you're using that value, you're probably wrong, unless you're doing some internal /// Umbraco stuff. /// - object GetSourceValue(string culture = null, string segment = null); + object GetSourceValue(string culture = ".", string segment = "."); /// /// Gets the object value of the property. @@ -45,7 +45,7 @@ /// It can be null, or any type of CLR object. /// It has been fully prepared and processed by the appropriate converter. /// - object GetValue(string culture = null, string segment = null); + object GetValue(string culture = ".", string segment = "."); /// /// Gets the XPath value of the property. @@ -55,6 +55,6 @@ /// It must be either null, or a string, or an XPathNavigator. /// It has been fully prepared and processed by the appropriate converter. /// - object GetXPathValue(string culture = null, string segment = null); + object GetXPathValue(string culture = ".", string segment = "."); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs new file mode 100644 index 0000000000..d83438ab06 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -0,0 +1,35 @@ +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides a fallback strategy for getting values. + /// + public interface IPublishedValueFallback + { + /// + /// Gets a value. + /// + /// + /// This is invoked when getting a value for the specified and + /// could not return a value, and fallback rules should apply to get the value for another language and/or segment. + /// + TValue GetValue(IPublishedProperty property, string culture, string segment); + } + + // fixme question + // this is working at property level at the moment, should we move it up to element, + // so that the decision can be made based upon the entire element, other properties, etc? + // or, would we need the *two* levels? + + /// + /// Provides a default implementation of that does not fall back at all. + /// + public class NoPublishedValueFallback : IPublishedValueFallback + { + /// + public TValue GetValue(IPublishedProperty property, string culture, string segment) + { + // we don't implement fallback + return default; + } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedVariationContextAccessor.cs new file mode 100644 index 0000000000..2af4230665 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedVariationContextAccessor.cs @@ -0,0 +1,13 @@ +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Gives access to the current . + /// + public interface IPublishedVariationContextAccessor + { + /// + /// Gets or sets the current . + /// + PublishedVariationContext Context { get; set; } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs index 7f42c022a5..df3213eb07 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs @@ -18,6 +18,11 @@ namespace Umbraco.Core.Models.PublishedContent if (content == null) return null; + // in order to provide a nice, "fluent" experience, this extension method + // needs to access Current, which is not always initialized in tests - not + // very elegant, but works + if (!Current.HasContainer) return content; + // get model // if factory returns nothing, throw var model = Current.PublishedModelFactory.CreateModel(content); diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs index 7e2a5b5498..c6626be1b2 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs @@ -53,15 +53,15 @@ namespace Umbraco.Core.Models.PublishedContent public string Alias => PropertyType.Alias; /// - public abstract bool HasValue(string culture = null, string segment = null); + public abstract bool HasValue(string culture = ".", string segment = "."); /// - public abstract object GetSourceValue(string culture = null, string segment = null); + public abstract object GetSourceValue(string culture = ".", string segment = "."); /// - public abstract object GetValue(string culture = null, string segment = null); + public abstract object GetValue(string culture = ".", string segment = "."); /// - public abstract object GetXPathValue(string culture = null, string segment = null); + public abstract object GetXPathValue(string culture = ".", string segment = "."); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedVariationContext.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedVariationContext.cs new file mode 100644 index 0000000000..2440b1dc32 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedVariationContext.cs @@ -0,0 +1,30 @@ +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Represents the published variation context. + /// + /// + /// The published variation context indicates which variation is the current default variation. + /// + public class PublishedVariationContext + { + /// + /// Initializes a new instance of the class. + /// + public PublishedVariationContext(string culture = null, string segment = null) + { + Culture = culture; + Segment = segment; + } + + /// + /// Gets the culture. + /// + public string Culture { get; set; } + + /// + /// Gets the segment. + /// + public string Segment { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs index e20d8cb49c..00a6b940bb 100644 --- a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs +++ b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs @@ -20,19 +20,19 @@ namespace Umbraco.Core.Models.PublishedContent private readonly Lazy _objectValue; private readonly Lazy _xpathValue; - public override object GetSourceValue(string culture = null, string segment = null) + public override object GetSourceValue(string culture = ".", string segment = ".") => culture == null & segment == null ? _sourceValue : null; - public override bool HasValue(string culture = null, string segment = null) + public override bool HasValue(string culture = ".", string segment = ".") { var sourceValue = GetSourceValue(culture, segment); return sourceValue is string s ? !string.IsNullOrWhiteSpace(s) : sourceValue != null; } - public override object GetValue(string culture = null, string segment = null) + public override object GetValue(string culture = ".", string segment = ".") => culture == null & segment == null ? _objectValue.Value : null; - public override object GetXPathValue(string culture = null, string segment = null) + public override object GetXPathValue(string culture = ".", string segment = ".") => culture == null & segment == null ? _xpathValue.Value : null; public RawValueProperty(PublishedPropertyType propertyType, IPublishedElement content, object sourceValue, bool isPreviewing = false) diff --git a/src/Umbraco.Core/Models/PublishedContent/ThreadCulturePublishedVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/ThreadCulturePublishedVariationContextAccessor.cs new file mode 100644 index 0000000000..8bf02e3f9b --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/ThreadCulturePublishedVariationContextAccessor.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides a CurrentUICulture-based implementation of . + /// + /// + /// This accessor does not support segments. There is no need to set the current context. + /// + public class ThreadCulturePublishedVariationContextAccessor : IPublishedVariationContextAccessor + { + private readonly ConcurrentDictionary _contexts = new ConcurrentDictionary(); + + public PublishedVariationContext Context + { + get => _contexts.GetOrAdd(Thread.CurrentThread.CurrentUICulture.Name, culture => new PublishedVariationContext { Culture = culture }); + set => throw new NotSupportedException(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PublishedContent/ThreadStaticPublishedVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/ThreadStaticPublishedVariationContextAccessor.cs new file mode 100644 index 0000000000..b7391e8b0d --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/ThreadStaticPublishedVariationContextAccessor.cs @@ -0,0 +1,23 @@ +using System; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides a ThreadStatic-based implementation of . + /// + /// + /// Something must set the current context. + /// + public class ThreadStaticPublishedVariationContextAccessor : IPublishedVariationContextAccessor + { + [ThreadStatic] + private static PublishedVariationContext _context; + + /// + public PublishedVariationContext Context + { + get => _context; + set => _context = value; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 2a4a79180b..7d6f27083b 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -369,6 +369,11 @@ + + + + + diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index c806930704..2ad5f470ef 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -242,10 +242,10 @@ namespace Umbraco.Tests.Published _owner = owner; } - public override bool HasValue(string culture = null, string segment = null) => _hasValue; - public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue; - public override object GetValue(string culture = null, string segment = null) => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview); - public override object GetXPathValue(string culture = null, string segment = null) => throw new WontImplementException(); + public override bool HasValue(string culture = ".", string segment = ".") => _hasValue; + public override object GetSourceValue(string culture = ".", string segment = ".") => _sourceValue; + public override object GetValue(string culture = ".", string segment = ".") => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview); + public override object GetXPathValue(string culture = ".", string segment = ".") => throw new WontImplementException(); } class TestPublishedContent : PublishedContentBase diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs new file mode 100644 index 0000000000..a131076a3a --- /dev/null +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Events; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Scoping; +using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Stubs; +using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Web; +using Umbraco.Web.PublishedCache.NuCache; +using Umbraco.Web.PublishedCache.NuCache.DataSource; + +namespace Umbraco.Tests.PublishedContent +{ + [TestFixture] + public class PublishedContentOtherTests // FIXME rename! + { + [Test] + public void Test() + { + SettingsForTests.ConfigureSettings(SettingsForTests.GenerateMockUmbracoSettings()); + var globalSettings = UmbracoConfig.For.GlobalSettings(); + + // fixme - missing variant names here, and what else? + var kit = new ContentNodeKit + { + ContentTypeId = 2, + Node = new ContentNode(1, Guid.NewGuid(), 0, "-1,1", 0, -1, DateTime.Now, 0), + DraftData = new ContentData { Name="It Works2!", Published = false, TemplateId = 0, VersionId = 2, VersionDate = DateTime.Now, WriterId = 0, + Properties = new Dictionary { { "prop", new[] + { + new PropertyData { Value = "val2" }, + new PropertyData { Culture = "fr-FR", Value = "val-fr2" }, + new PropertyData { Culture = "en-UK", Value = "val-uk2" } + } } } }, + PublishedData = new ContentData { Name="It Works1!", Published = true, TemplateId = 0, VersionId = 1, VersionDate = DateTime.Now, WriterId = 0, + Properties = new Dictionary { { "prop", new[] + { + new PropertyData { Value = "val1" }, + new PropertyData { Culture = "fr-FR", Value = "val-fr1" }, + new PropertyData { Culture = "en-UK", Value = "val-uk1" } + } } } } + }; + + var dataSource = new TestDataSource(kit); + + var runtime = Mock.Of(); + Mock.Get(runtime).Setup(x => x.Level).Returns(RuntimeLevel.Run); + + var propertyType = new PropertyType("Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.InvariantNeutral | ContentVariation.CultureNeutral }; + var contentType = new ContentType(-1) { Id = 2, Alias = "alias-ct", Variations = ContentVariation.InvariantNeutral | ContentVariation.CultureNeutral }; + contentType.AddPropertyType(propertyType); + + var contentTypes = new[] + { + contentType + }; + + var dataType = new DataType(new VoidEditor("Editor", Mock.Of())) { Id = 3 }; + + var dataTypes = new[] + { + dataType + }; + + var contentTypeService = Mock.Of(); + Mock.Get(contentTypeService).Setup(x => x.GetAll()).Returns(contentTypes); + + var dataTypeService = Mock.Of(); + Mock.Get(dataTypeService).Setup(x => x.GetAll()).Returns(dataTypes); + + var serviceContext = new ServiceContext( + dataTypeService : dataTypeService, + + memberTypeService: Mock.Of(), + memberService: Mock.Of(), + + contentTypeService : contentTypeService, + + localizationService: Mock.Of() + ); + + var contentTypeFactory = new PublishedContentTypeFactory( + Mock.Of(), + new PropertyValueConverterCollection(Array.Empty()), + dataTypeService); + + var documentRepository = Mock.Of(); + var mediaRepository = Mock.Of(); + var memberRepository = Mock.Of(); + + var snapshotAccessor = new TestPublishedSnapshotAccessor(); + + var scopeProvider = Mock.Of(); + Mock.Get(scopeProvider) + .Setup(x => x.CreateScope( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns(() => Mock.Of()); + + var variationAccessor = new TestPublishedVariationContextAccessor(); + + var options = new PublishedSnapshotService.Options { IgnoreLocalDb = true }; + var snapshotService = new PublishedSnapshotService(options, + null, + runtime, + serviceContext, + contentTypeFactory, + null, + snapshotAccessor, + variationAccessor, + Mock.Of(), + scopeProvider, + documentRepository, + mediaRepository, + memberRepository, + dataSource, + globalSettings); + + var snapshot = snapshotService.CreatePublishedSnapshot(previewToken: null); + var publishedContent = snapshot.Content.GetById(1); + + // invariant is the current default + variationAccessor.Context = null; + + Assert.IsNotNull(publishedContent); + Assert.AreEqual("It Works1!", publishedContent.Name); + Assert.AreEqual("val1", publishedContent.Value("prop")); + Assert.AreEqual("val-fr1", publishedContent.Value("prop", "fr-FR")); + Assert.AreEqual("val-uk1", publishedContent.Value("prop", "en-UK")); + + var draftContent = snapshot.Content.GetById(true, 1); + Assert.AreEqual("It Works2!", draftContent.Name); + Assert.AreEqual("val2", draftContent.Value("prop")); + Assert.AreEqual("val-fr2", draftContent.Value("prop", "fr-FR")); + Assert.AreEqual("val-uk2", draftContent.Value("prop", "en-UK")); + + variationAccessor.Context = new PublishedVariationContext("fr-FR"); + Assert.AreEqual("val-fr1", publishedContent.Value("prop")); + variationAccessor.Context = new PublishedVariationContext("en-UK"); + Assert.AreEqual("val-uk1", publishedContent.Value("prop")); + + // invariant needs to be retrieved explicitely, when it's not default + Assert.AreEqual("val1", publishedContent.Value("prop", culture: null)); + + // then, test fallback + } + + internal class TestDataSource : IDataSource + { + private readonly Dictionary _kits; + + public TestDataSource(params ContentNodeKit[] kits) + : this((IEnumerable) kits) + { } + + public TestDataSource(IEnumerable kits) + { + _kits = kits.ToDictionary(x => x.Node.Id, x => x); + } + + public ContentNodeKit GetContentSource(IScope scope, int id) + => _kits.TryGetValue(id, out var kit) ? kit : default; + + public IEnumerable GetAllContentSources(IScope scope) + => _kits.Values; + + public IEnumerable GetBranchContentSources(IScope scope, int id) + { + throw new NotImplementedException(); + } + + public IEnumerable GetTypeContentSources(IScope scope, IEnumerable ids) + => _kits.Values.Where(x => ids.Contains(x.ContentTypeId)); + + public ContentNodeKit GetMediaSource(IScope scope, int id) + { + throw new NotImplementedException(); + } + + public IEnumerable GetAllMediaSources(IScope scope) + { + throw new NotImplementedException(); + } + + public IEnumerable GetBranchMediaSources(IScope scope, int id) + { + throw new NotImplementedException(); + } + + public IEnumerable GetTypeMediaSources(IScope scope, IEnumerable ids) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 4b7a131bd0..2be86640d7 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -258,10 +258,10 @@ namespace Umbraco.Tests.PublishedContent public bool SolidHasValue { get; set; } public object SolidXPathValue { get; set; } - public object GetSourceValue(string culture = null, string segment = null) => SolidSourceValue; - public object GetValue(string culture = null, string segment = null) => SolidValue; - public object GetXPathValue(string culture = null, string segment = null) => SolidXPathValue; - public bool HasValue(string culture = null, string segment = null) => SolidHasValue; + public object GetSourceValue(string culture = ".", string segment = ".") => SolidSourceValue; + public object GetValue(string culture = ".", string segment = ".") => SolidValue; + public object GetXPathValue(string culture = ".", string segment = ".") => SolidXPathValue; + public bool HasValue(string culture = ".", string segment = ".") => SolidHasValue; } [PublishedModel("ContentType2")] diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index d0b3622127..f722906053 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -18,6 +18,7 @@ using Umbraco.Web.WebApi; using Umbraco.Core.Strings; using Umbraco.Core.Composing; using Umbraco.Tests.Testing; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Runtime; using Current = Umbraco.Web.Composing.Current; diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index cc9813cdbd..70ff7d8341 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -25,6 +25,7 @@ using Umbraco.Web; using Umbraco.Web.Cache; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.NuCache; +using Umbraco.Web.PublishedCache.NuCache.DataSource; using Umbraco.Web.Routing; using Umbraco.Web.Security; @@ -88,9 +89,11 @@ namespace Umbraco.Tests.Scoping contentTypeFactory, null, publishedSnapshotAccessor, + Mock.Of(), Logger, ScopeProvider, documentRepository, mediaRepository, memberRepository, + new DatabaseDataSource(), SystemDefaultCultureProvider, Container.GetInstance(), new SiteDomainHelper()); } diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index ee0dd38c45..4952360b6a 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers.Stubs; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 0fcd3c9295..cede329bd0 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -33,6 +33,7 @@ using Umbraco.Core.Migrations.Install; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Repositories; using Umbraco.Tests.Testing.Objects.AccessorsAndProviders; +using Umbraco.Tests.Testing.Objects.Accessors; namespace Umbraco.Tests.TestHelpers { @@ -264,11 +265,12 @@ namespace Umbraco.Tests.TestHelpers // testing=true so XmlStore will not use the file nor the database var publishedSnapshotAccessor = new UmbracoContextPublishedSnapshotAccessor(Umbraco.Web.Composing.Current.UmbracoContextAccessor); + var variationContextAccessor = new TestPublishedVariationContextAccessor(); var service = new PublishedSnapshotService( ServiceContext, Container.GetInstance(), ScopeProvider, - cache, publishedSnapshotAccessor, + cache, publishedSnapshotAccessor, variationContextAccessor, Container.GetInstance(), Container.GetInstance(), Container.GetInstance(), SystemDefaultCultureProvider, Logger, diff --git a/src/Umbraco.Tests/TestHelpers/NoHttpContextAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs similarity index 78% rename from src/Umbraco.Tests/TestHelpers/NoHttpContextAccessor.cs rename to src/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs index b77f8f828c..9b37389241 100644 --- a/src/Umbraco.Tests/TestHelpers/NoHttpContextAccessor.cs +++ b/src/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs @@ -1,7 +1,7 @@ using System.Web; using Umbraco.Web; -namespace Umbraco.Tests.TestHelpers +namespace Umbraco.Tests.Testing.Objects.Accessors { public class NoHttpContextAccessor : IHttpContextAccessor { diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedSnapshotAccessor.cs similarity index 79% rename from src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs rename to src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedSnapshotAccessor.cs index 3768803de2..c46915e3a0 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs +++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedSnapshotAccessor.cs @@ -1,6 +1,6 @@ using Umbraco.Web.PublishedCache; -namespace Umbraco.Tests.TestHelpers.Stubs +namespace Umbraco.Tests.Testing.Objects.Accessors { public class TestPublishedSnapshotAccessor : IPublishedSnapshotAccessor { diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs new file mode 100644 index 0000000000..9327b462f1 --- /dev/null +++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs @@ -0,0 +1,13 @@ +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Tests.Testing.Objects.Accessors +{ + /// + /// Provides an implementation of for tests. + /// + public class TestPublishedVariationContextAccessor : IPublishedVariationContextAccessor + { + /// + public PublishedVariationContext Context { get; set; } + } +} diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestUmbracoContextAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs similarity index 77% rename from src/Umbraco.Tests/TestHelpers/Stubs/TestUmbracoContextAccessor.cs rename to src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs index eaf8912fda..da93218907 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestUmbracoContextAccessor.cs +++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs @@ -1,6 +1,6 @@ using Umbraco.Web; -namespace Umbraco.Tests.TestHelpers.Stubs +namespace Umbraco.Tests.Testing.Objects.Accessors { public class TestUmbracoContextAccessor : IUmbracoContextAccessor { diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 59bc24ed10..9f06d0de17 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Routing; using Umbraco.Web.Security; diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 003ba9ff4d..6f803516cf 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -31,6 +31,7 @@ using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.Services; using Umbraco.Examine; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Composing.CompositionRoots; using Umbraco.Web._Legacy.Actions; using Current = Umbraco.Core.Composing.Current; diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 20bc181017..dc38dd54ee 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -129,6 +129,7 @@ + @@ -176,9 +177,10 @@ - + + @@ -195,10 +197,10 @@ - + - + diff --git a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs index 1f5fe1a6e3..6e8b22378c 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Profiling; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Composing; using Umbraco.Web.Models; diff --git a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs index 3b60f1a7c5..901c737584 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Tests.TestHelpers.Stubs; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Current = Umbraco.Web.Composing.Current; diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 931cc57493..e467889831 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -14,6 +14,7 @@ using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Composing; using Umbraco.Web.Mvc; diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 295f42fee2..8e9e6a3e22 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -424,22 +424,23 @@ namespace Umbraco.Tests.Web.Mvc //var provider = new ScopeUnitOfWorkProvider(databaseFactory, new RepositoryFactory(Mock.Of())); var scopeProvider = TestObjects.GetScopeProvider(Mock.Of()); var factory = Mock.Of(); - _service = new PublishedSnapshotService(svcCtx, factory, scopeProvider, cache, Enumerable.Empty(), null, - null, null, null, + _service = new PublishedSnapshotService(svcCtx, factory, scopeProvider, cache, Enumerable.Empty(), + null, null, + null, null, null, new TestSystemDefaultCultureProvider(), Current.Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), null, true, false); // no events var http = GetHttpContextFactory(url, routeData).HttpContext; - - var globalSettings = TestObjects.GetGlobalSettings(); + + var globalSettings = TestObjects.GetGlobalSettings(); var ctx = new UmbracoContext( http, _service, new WebSecurity(http, Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), - Enumerable.Empty(), - globalSettings, + Enumerable.Empty(), + globalSettings, Mock.Of()); //if (setSingleton) diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index d35b4e5823..165f103d43 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; diff --git a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs index cc97633cde..aa1fce8c85 100644 --- a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs +++ b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs index a7cc1e950c..de658daeec 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs @@ -33,10 +33,11 @@ namespace Umbraco.Web.PublishedCache.NuCache int parentContentId, DateTime createDate, int creatorId, ContentData draftData, ContentData publishedData, - IPublishedSnapshotAccessor publishedSnapshotAccessor) + IPublishedSnapshotAccessor publishedSnapshotAccessor, + IPublishedVariationContextAccessor variationContextAccessor) : this(id, uid, level, path, sortOrder, parentContentId, createDate, creatorId) { - SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor); + SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor, variationContextAccessor); } // 2-phases ctor, phase 1 @@ -58,7 +59,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // two-phase ctor, phase 2 - public void SetContentTypeAndData(PublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor) + public void SetContentTypeAndData(PublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) { ContentType = contentType; @@ -66,9 +67,9 @@ namespace Umbraco.Web.PublishedCache.NuCache throw new ArgumentException("Both draftData and publishedData cannot be null at the same time."); if (draftData != null) - Draft = new PublishedContent(this, draftData, publishedSnapshotAccessor).CreateModel(); + Draft = new PublishedContent(this, draftData, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); if (publishedData != null) - Published = new PublishedContent(this, publishedData, publishedSnapshotAccessor).CreateModel(); + Published = new PublishedContent(this, publishedData, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); } // clone parent @@ -97,7 +98,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // clone with new content type - public ContentNode(ContentNode origin, PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor) + public ContentNode(ContentNode origin, PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) { Id = origin.Id; Uid = origin.Uid; @@ -112,8 +113,8 @@ namespace Umbraco.Web.PublishedCache.NuCache var originDraft = origin.Draft == null ? null : PublishedContent.UnwrapIPublishedContent(origin.Draft); var originPublished = origin.Published == null ? null : PublishedContent.UnwrapIPublishedContent(origin.Published); - Draft = originDraft == null ? null : new PublishedContent(this, originDraft._contentData, publishedSnapshotAccessor).CreateModel(); - Published = originPublished == null ? null : new PublishedContent(this, originPublished._contentData, publishedSnapshotAccessor).CreateModel(); + Draft = originDraft == null ? null : new PublishedContent(this, originDraft._contentData, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); + Published = originPublished == null ? null : new PublishedContent(this, originPublished._contentData, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); ChildContentIds = origin.ChildContentIds; // can be the *same* list FIXME oh really? } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs index 82bfc8766a..19163d5e8d 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs @@ -17,9 +17,9 @@ namespace Umbraco.Web.PublishedCache.NuCache public static ContentNodeKit Null { get; } = new ContentNodeKit { ContentTypeId = -1 }; - public void Build(PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor) + public void Build(PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) { - Node.SetContentTypeAndData(contentType, DraftData, PublishedData, publishedSnapshotAccessor); + Node.SetContentTypeAndData(contentType, DraftData, PublishedData, publishedSnapshotAccessor, variationContextAccessor); } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 74613509ba..95482bb6b1 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -19,6 +19,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // SnapDictionary has unit tests to ensure it all works correctly private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; + private readonly IPublishedVariationContextAccessor _variationContextAccessor; private readonly ConcurrentDictionary> _contentNodes; private readonly ConcurrentDictionary> _contentRootNodes; private readonly ConcurrentDictionary> _contentTypesById; @@ -43,9 +44,10 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Ctor - public ContentStore(IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, BPlusTree localDb = null) + public ContentStore(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, ILogger logger, BPlusTree localDb = null) { _publishedSnapshotAccessor = publishedSnapshotAccessor; + _variationContextAccessor = variationContextAccessor; _logger = logger; _localDb = localDb; @@ -277,7 +279,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (node == null) continue; var contentTypeId = node.ContentType.Id; if (index.TryGetValue(contentTypeId, out PublishedContentType contentType) == false) continue; - SetValueLocked(_contentNodes, node.Id, new ContentNode(node, contentType, _publishedSnapshotAccessor)); + SetValueLocked(_contentNodes, node.Id, new ContentNode(node, contentType, _publishedSnapshotAccessor, _variationContextAccessor)); } } finally @@ -391,7 +393,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _contentNodes.TryGetValue(id, out LinkedNode link); if (link?.Value == null) continue; - var node = new ContentNode(link.Value, contentType, _publishedSnapshotAccessor); + var node = new ContentNode(link.Value, contentType, _publishedSnapshotAccessor, _variationContextAccessor); SetValueLocked(_contentNodes, id, node); if (_localDb != null) RegisterChange(id, node.ToKit()); } @@ -414,7 +416,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return false; // and use - kit.Build(link.Value, _publishedSnapshotAccessor); + kit.Build(link.Value, _publishedSnapshotAccessor, _variationContextAccessor); return true; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs similarity index 97% rename from src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs rename to src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs index 342ad5b59f..16f11aeafd 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource // fixme - use SqlTemplate for these queries else it's going to be horribly slow! // provides efficient database access for NuCache - internal class Database + internal class DatabaseDataSource : IDataSource { // we want arrays, we want them all loaded, not an enumerable @@ -186,7 +186,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource { if (Debugger.IsAttached) throw new Exception("Missing cmsContentNu edited content for node " + dto.Id + ", consider rebuilding."); - Current.Logger.Warn("Missing cmsContentNu edited content for node " + dto.Id + ", consider rebuilding."); + Current.Logger.Warn("Missing cmsContentNu edited content for node " + dto.Id + ", consider rebuilding."); } else { @@ -211,7 +211,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource { if (Debugger.IsAttached) throw new Exception("Missing cmsContentNu published content for node " + dto.Id + ", consider rebuilding."); - Current.Logger.Warn("Missing cmsContentNu published content for node " + dto.Id + ", consider rebuilding."); + Current.Logger.Warn("Missing cmsContentNu published content for node " + dto.Id + ", consider rebuilding."); } else { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs new file mode 100644 index 0000000000..323d954980 --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDataSource.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using Umbraco.Core.Scoping; + +namespace Umbraco.Web.PublishedCache.NuCache.DataSource +{ + /// + /// Defines a data source for NuCache. + /// + internal interface IDataSource + { + ContentNodeKit GetContentSource(IScope scope, int id); + IEnumerable GetAllContentSources(IScope scope); + IEnumerable GetBranchContentSources(IScope scope, int id); + IEnumerable GetTypeContentSources(IScope scope, IEnumerable ids); + + ContentNodeKit GetMediaSource(IScope scope, int id); + IEnumerable GetAllMediaSources(IScope scope); + IEnumerable GetBranchMediaSources(IScope scope, int id); + IEnumerable GetTypeMediaSources(IScope scope, IEnumerable ids); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs index a8cf4a97c0..899fbd6eed 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs @@ -16,6 +16,7 @@ namespace Umbraco.Web.PublishedCache.NuCache class MemberCache : IPublishedMemberCache, INavigableData { private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; + public readonly IPublishedVariationContextAccessor _variationContextAccessor; private readonly ICacheProvider _snapshotCache; private readonly IMemberService _memberService; private readonly IDataTypeService _dataTypeService; @@ -23,10 +24,11 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly PublishedContentTypeCache _contentTypeCache; private readonly bool _previewDefault; - public MemberCache(bool previewDefault, ICacheProvider snapshotCache, IMemberService memberService, IDataTypeService dataTypeService, ILocalizationService localizationService, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor) + public MemberCache(bool previewDefault, ICacheProvider snapshotCache, IMemberService memberService, IDataTypeService dataTypeService, ILocalizationService localizationService, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) { _snapshotCache = snapshotCache; _publishedSnapshotAccessor = publishedSnapshotAccessor; + _variationContextAccessor = variationContextAccessor; _memberService = memberService; _dataTypeService = dataTypeService; _localizationService = localizationService; @@ -63,14 +65,14 @@ namespace Umbraco.Web.PublishedCache.NuCache var member = _memberService.GetById(memberId); return member == null ? null - : PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor); + : PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, _variationContextAccessor); }); } private IPublishedContent /*IPublishedMember*/ GetById(IMember member, bool previewing) { return GetCacheItem(CacheKeys.MemberCacheMember("ById", _previewDefault, member.Id), () => - PublishedMember.Create(member, GetContentType(member.ContentTypeId), previewing, _publishedSnapshotAccessor)); + PublishedMember.Create(member, GetContentType(member.ContentTypeId), previewing, _publishedSnapshotAccessor, _variationContextAccessor)); } public IPublishedContent /*IPublishedMember*/ GetByProviderKey(object key) @@ -105,7 +107,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public IPublishedContent /*IPublishedMember*/ GetByMember(IMember member) { - return PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor); + return PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, _variationContextAccessor); } public IEnumerable GetAtRoot(bool preview) @@ -113,7 +115,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // because members are flat (not a tree) everything is at root // because we're loading everything... let's just not cache? var members = _memberService.GetAllMembers(); - return members.Select(m => PublishedMember.Create(m, GetContentType(m.ContentTypeId), preview, _publishedSnapshotAccessor)); + return members.Select(m => PublishedMember.Create(m, GetContentType(m.ContentTypeId), preview, _publishedSnapshotAccessor, _variationContextAccessor)); } public XPathNavigator CreateNavigator() diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs index 3482a6cf2c..1d497d73e0 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs @@ -1,11 +1,5 @@ -using Umbraco.Core; -using Umbraco.Core.Components; -using Umbraco.Core.Logging; -using Umbraco.Core.Scoping; -using Umbraco.Core.Services; -using LightInject; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Components; +using Umbraco.Web.PublishedCache.NuCache.DataSource; namespace Umbraco.Web.PublishedCache.NuCache { @@ -15,6 +9,9 @@ namespace Umbraco.Web.PublishedCache.NuCache { base.Compose(composition); + // register the NuCache database data source + composition.Container.Register(); + // register the NuCache published snapshot service // must register default options, required in the service ctor composition.Container.Register(factory => new PublishedSnapshotService.Options()); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index f2e3355750..d2f37a1488 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly Guid _contentUid; private readonly bool _isPreviewing; private readonly bool _isMember; - private readonly IPublishedContent _content; + private readonly PublishedContent _content; private readonly object _locko = new object(); @@ -71,7 +71,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // clone for previewing as draft a published content that is published and has no draft - public Property(Property origin, IPublishedContent content) + public Property(Property origin, PublishedContent content) : base(origin.PropertyType, origin.ReferenceCacheLevel) { _sourceValue = origin._sourceValue; @@ -84,7 +84,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; } - public override bool HasValue(string culture = null, string segment = null) => _sourceValue != null + public override bool HasValue(string culture = ".", string segment = ".") => _sourceValue != null && (!(_sourceValue is string) || string.IsNullOrWhiteSpace((string) _sourceValue) == false); // used to cache the recursive *property* for this property @@ -166,8 +166,10 @@ namespace Umbraco.Web.PublishedCache.NuCache return vvalue.InterValue; } - public override object GetSourceValue(string culture = null, string segment = null) + public override object GetSourceValue(string culture = ".", string segment = ".") { + ContextualizeVariation(ref culture, ref segment); + if (culture == null && segment == null) return _sourceValue; @@ -178,8 +180,21 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - public override object GetValue(string culture = null, string segment = null) + private void ContextualizeVariation(ref string culture, ref string segment) { + if (culture != "." && segment != ".") return; + + // use context values + var publishedVariationContext = _content.VariationContextAccessor?.Context; + if (culture == ".") culture = publishedVariationContext?.Culture; + if (segment == ".") segment = publishedVariationContext?.Segment; + } + + public override object GetValue(string culture = ".", string segment = ".") + { + ContextualizeVariation(ref culture, ref segment); + + object value; lock (_locko) { var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment); @@ -190,12 +205,16 @@ namespace Umbraco.Web.PublishedCache.NuCache if (cacheValues.ObjectInitialized) return cacheValues.ObjectValue; cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing); cacheValues.ObjectInitialized = true; - return cacheValues.ObjectValue; + value = cacheValues.ObjectValue; } + + return value; } - public override object GetXPathValue(string culture = null, string segment = null) + public override object GetXPathValue(string culture = ".", string segment = ".") { + ContextualizeVariation(ref culture, ref segment); + lock (_locko) { var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index b5201716ac..a0ec6e4687 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -23,11 +23,12 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Constructors - public PublishedContent(ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor) + public PublishedContent(ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) { _contentNode = contentNode; _contentData = contentData; _publishedSnapshotAccessor = publishedSnapshotAccessor; + VariationContextAccessor = variationContextAccessor; _urlName = _contentData.Name.ToUrlSegment(); IsPreviewing = _contentData.Published == false; @@ -70,6 +71,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { _contentNode = contentNode; _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; + VariationContextAccessor = origin.VariationContextAccessor; _contentData = origin._contentData; _urlName = origin._urlName; @@ -85,6 +87,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private PublishedContent(PublishedContent origin) { _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; + VariationContextAccessor = origin.VariationContextAccessor; _contentNode = origin._contentNode; _contentData = origin._contentData; @@ -308,6 +311,8 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Internal + internal IPublishedVariationContextAccessor VariationContextAccessor { get; } + // used by navigable content internal IPublishedProperty[] PropertiesArray { get; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs index 8b3d01f6e9..63ef1ae5aa 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs @@ -15,13 +15,13 @@ namespace Umbraco.Web.PublishedCache.NuCache { private readonly IMember _member; - private PublishedMember(IMember member, ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor) - : base(contentNode, contentData, publishedSnapshotAccessor) + private PublishedMember(IMember member, ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) + : base(contentNode, contentData, publishedSnapshotAccessor, variationContextAccessor) { _member = member; } - public static IPublishedContent Create(IMember member, PublishedContentType contentType, bool previewing, IPublishedSnapshotAccessor publishedSnapshotAccessor) + public static IPublishedContent Create(IMember member, PublishedContentType contentType, bool previewing, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) { var d = new ContentData { @@ -37,7 +37,7 @@ namespace Umbraco.Web.PublishedCache.NuCache member.Level, member.Path, member.SortOrder, member.ParentId, member.CreateDate, member.CreatorId); - return new PublishedMember(member, n, d, publishedSnapshotAccessor).CreateModel(); + return new PublishedMember(member, n, d, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); } private static Dictionary GetPropertyValues(PublishedContentType contentType, IMember member) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index b498a1a42b..1ca5770060 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -28,7 +28,6 @@ using Umbraco.Web.Install; using Umbraco.Web.PublishedCache.NuCache.DataSource; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; -using Database = Umbraco.Web.PublishedCache.NuCache.DataSource.Database; namespace Umbraco.Web.PublishedCache.NuCache { @@ -37,7 +36,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly ServiceContext _serviceContext; private readonly IPublishedContentTypeFactory _publishedContentTypeFactory; private readonly IScopeProvider _scopeProvider; - private readonly Database _dataSource; + private readonly IDataSource _dataSource; private readonly ILogger _logger; private readonly IDocumentRepository _documentRepository; private readonly IMediaRepository _mediaRepository; @@ -82,18 +81,19 @@ namespace Umbraco.Web.PublishedCache.NuCache public PublishedSnapshotService(Options options, MainDom mainDom, IRuntimeState runtime, ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IdkMap idkMap, - IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, IScopeProvider scopeProvider, + IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, + ILogger logger, IScopeProvider scopeProvider, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, ISystemDefaultCultureProvider systemDefaultCultureProvider, - IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) - : base(publishedSnapshotAccessor) + IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) + : base(publishedSnapshotAccessor, variationContextAccessor) { //if (Interlocked.Increment(ref _singletonCheck) > 1) // throw new Exception("Singleton must be instancianted only once!"); _serviceContext = serviceContext; _publishedContentTypeFactory = publishedContentTypeFactory; - _dataSource = new Database(); + _dataSource = dataSource; _logger = logger; _scopeProvider = scopeProvider; _documentRepository = documentRepository; @@ -145,13 +145,13 @@ namespace Umbraco.Web.PublishedCache.NuCache // stores are created with a db so they can write to it, but they do not read from it, // stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to // figure out whether it can read the dbs or it should populate them from sql - _contentStore = new ContentStore(publishedSnapshotAccessor, logger, _localContentDb); - _mediaStore = new ContentStore(publishedSnapshotAccessor, logger, _localMediaDb); + _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localContentDb); + _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localMediaDb); } else { - _contentStore = new ContentStore(publishedSnapshotAccessor, logger); - _mediaStore = new ContentStore(publishedSnapshotAccessor, logger); + _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger); + _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger); } _domainStore = new SnapDictionary(); @@ -1025,7 +1025,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainHelper, _globalSettings, _serviceContext.LocalizationService), MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache), - MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, _serviceContext.DataTypeService, _serviceContext.LocalizationService, memberTypeCache, PublishedSnapshotAccessor), + MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, _serviceContext.DataTypeService, _serviceContext.LocalizationService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor), DomainCache = domainCache, SnapshotCache = snapshotCache, ElementsCache = elementsCache diff --git a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs b/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs index d8db937ca8..9fa16d184c 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web.PublishedCache IsMember = propertyType.ContentType.ItemType == PublishedItemType.Member; } - public override bool HasValue(string culture = null, string segment = null) + public override bool HasValue(string culture = ".", string segment = ".") => _sourceValue != null && (!(_sourceValue is string s) || !string.IsNullOrWhiteSpace(s)); // used to cache the CacheValues of this property @@ -136,9 +136,9 @@ namespace Umbraco.Web.PublishedCache return _interValue; } - public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue; + public override object GetSourceValue(string culture = ".", string segment = ".") => _sourceValue; - public override object GetValue(string culture = null, string segment = null) + public override object GetValue(string culture = ".", string segment = ".") { GetCacheLevels(out var cacheLevel, out var referenceCacheLevel); @@ -152,7 +152,7 @@ namespace Umbraco.Web.PublishedCache } } - public override object GetXPathValue(string culture = null, string segment = null) + public override object GetXPathValue(string culture = ".", string segment = ".") { GetCacheLevels(out var cacheLevel, out var referenceCacheLevel); diff --git a/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs b/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs index 685c129224..64dda9f20b 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs @@ -1,17 +1,20 @@ using System.Collections.Generic; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.Cache; namespace Umbraco.Web.PublishedCache { abstract class PublishedSnapshotServiceBase : IPublishedSnapshotService { - protected PublishedSnapshotServiceBase(IPublishedSnapshotAccessor publishedSnapshotAccessor) + protected PublishedSnapshotServiceBase(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) { PublishedSnapshotAccessor = publishedSnapshotAccessor; + VariationContextAccessor = variationContextAccessor; } public IPublishedSnapshotAccessor PublishedSnapshotAccessor { get; } + public IPublishedVariationContextAccessor VariationContextAccessor { get; } // note: NOT setting _publishedSnapshotAccessor.PublishedSnapshot here because it is the // responsibility of the caller to manage what the 'current' facade is diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs index 3b9a97d8cc..a0efd14ba9 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs @@ -43,7 +43,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IScopeProvider scopeProvider, ICacheProvider requestCache, IEnumerable segmentProviders, - IPublishedSnapshotAccessor publishedSnapshotAccessor, + IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, ISystemDefaultCultureProvider systemDefaultCultureProvider, ILogger logger, @@ -51,7 +51,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache ISiteDomainHelper siteDomainHelper, MainDom mainDom, bool testing = false, bool enableRepositoryEvents = true) - : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, segmentProviders, publishedSnapshotAccessor, + : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, segmentProviders, + publishedSnapshotAccessor, variationContextAccessor, documentRepository, mediaRepository, memberRepository, systemDefaultCultureProvider, logger, globalSettings, siteDomainHelper, null, mainDom, testing, enableRepositoryEvents) @@ -62,7 +63,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IPublishedContentTypeFactory publishedContentTypeFactory, IScopeProvider scopeProvider, ICacheProvider requestCache, - IPublishedSnapshotAccessor publishedSnapshotAccessor, + IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, ISystemDefaultCultureProvider systemDefaultCultureProvider, ILogger logger, @@ -71,7 +72,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache PublishedContentTypeCache contentTypeCache, MainDom mainDom, bool testing, bool enableRepositoryEvents) - : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, Enumerable.Empty(), publishedSnapshotAccessor, + : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, Enumerable.Empty(), + publishedSnapshotAccessor, variationContextAccessor, documentRepository, mediaRepository, memberRepository, systemDefaultCultureProvider, logger, globalSettings, siteDomainHelper, contentTypeCache, mainDom, testing, enableRepositoryEvents) @@ -82,7 +84,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IScopeProvider scopeProvider, ICacheProvider requestCache, IEnumerable segmentProviders, - IPublishedSnapshotAccessor publishedSnapshotAccessor, + IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, ISystemDefaultCultureProvider systemDefaultCultureProvider, ILogger logger, @@ -91,7 +93,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache PublishedContentTypeCache contentTypeCache, MainDom mainDom, bool testing, bool enableRepositoryEvents) - : base(publishedSnapshotAccessor) + : base(publishedSnapshotAccessor, variationContextAccessor) { _routesCache = new RoutesCache(); _publishedContentTypeFactory = publishedContentTypeFactory; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs index c9794eb99a..aea2fc60c6 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs @@ -29,6 +29,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache factory.GetInstance().RequestCache, factory.GetInstance(), factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs index ea8ab925c6..57b81b8c73 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs @@ -27,13 +27,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache /// /// Gets the raw value of the property. /// - public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue; + public override object GetSourceValue(string culture = ".", string segment = ".") => _sourceValue; // in the Xml cache, everything is a string, and to have a value // you want to have a non-null, non-empty string. - public override bool HasValue(string culture = null, string segment = null) => _sourceValue.Trim().Length > 0; + public override bool HasValue(string culture = ".", string segment = ".") => _sourceValue.Trim().Length > 0; - public override object GetValue(string culture = null, string segment = null) + public override object GetValue(string culture = ".", string segment = ".") { // NOT caching the source (intermediate) value since we'll never need it // everything in Xml cache is per-request anyways diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index e4a3a85d13..4ae03bfc22 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -122,6 +122,8 @@ namespace Umbraco.Web #endregion #region Value + + // fixme missing variations, but recurse/variations/fallback = ? /// /// Recursively gets the value of a content's property identified by its alias. @@ -148,6 +150,8 @@ namespace Umbraco.Web /// /// The content. /// The property alias. + /// The variation language. + /// The variation segment. /// A value indicating whether to recurse. /// The default value. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. @@ -158,22 +162,26 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedContent content, string alias, bool recurse, object defaultValue) - { + public static object Value(this IPublishedContent content, string alias, string culture = ".", string segment = ".", object defaultValue = default, bool recurse = false) + { + // fixme - variations+recurse not implemented here var property = content.GetProperty(alias, recurse); - return property == null || property.HasValue() == false ? defaultValue : property.GetValue(); + return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(); } #endregion #region Value - + /// /// Recursively gets the value of a content's property identified by its alias, converted to a specified type. /// /// The target property type. /// The content. /// The property alias. + /// The variation language. + /// The variation segment. + /// The default value. /// A value indicating whether to recurse. /// The value of the content's property identified by the alias, converted to the specified type. /// @@ -183,38 +191,13 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedContent content, string alias, bool recurse) - { - return content.Value(alias, recurse, false, default(T)); - } - - /// - /// Recursively gets the value of a content's property identified by its alias, converted to a specified type, if it exists, otherwise a default value. - /// - /// The target property type. - /// The content. - /// The property alias. - /// A value indicating whether to recurse. - /// The default value. - /// The value of the content's property identified by the alias, converted to the specified type, if it exists, otherwise a default value. - /// - /// Recursively means: walking up the tree from , get the first value that can be found. - /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. - /// If no property with the specified alias exists, or if the property has no value, or if it could not be converted, returns . - /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. - /// The alias is case-insensitive. - /// - public static T Value(this IPublishedContent content, string alias, bool recurse, T defaultValue) - { - return content.Value(alias, recurse, true, defaultValue); - } - - internal static T Value(this IPublishedContent content, string alias, bool recurse, bool withDefaultValue, T defaultValue) + public static T Value(this IPublishedContent content, string alias, string culture = ".", string segment = ".", T defaultValue = default, bool recurse = false) { + // fixme - variations+recurse not implemented here var property = content.GetProperty(alias, recurse); if (property == null) return defaultValue; - return property.Value(withDefaultValue, defaultValue); + return property.Value(culture, segment, defaultValue); } #endregion diff --git a/src/Umbraco.Web/PublishedContentPropertyExtension.cs b/src/Umbraco.Web/PublishedContentPropertyExtension.cs index 6d397ffaa3..e460445c6c 100644 --- a/src/Umbraco.Web/PublishedContentPropertyExtension.cs +++ b/src/Umbraco.Web/PublishedContentPropertyExtension.cs @@ -10,21 +10,16 @@ namespace Umbraco.Web { #region Value - public static T Value(this IPublishedProperty property, string culture = null, string segment = null) - { - return property.Value(false, default(T), culture, segment); - } - - public static T Value(this IPublishedProperty property, T defaultValue, string culture = null, string segment = null) - { - return property.Value(true, defaultValue, culture, segment); - } - - internal static T Value(this IPublishedProperty property, bool withDefaultValue, T defaultValue, string culture = null, string segment = null) - { - if (property.HasValue(culture, segment) == false && withDefaultValue) return defaultValue; - - // else we use .Value so we give the converter a chance to handle the default value differently + public static T Value(this IPublishedProperty property, string culture = ".", string segment = ".", T defaultValue = default) + { + // for Value when defaultValue is not specified, and HasValue() is false, we still want to convert the result (see below) + // but we have no way to tell whether default value is specified or not - we could do it with overloads, but then defaultValue + // comes right after property and conflicts with culture when T is string - so we're just not doing it - if defaultValue is + // default, whether specified or not, we give a chance to the converter + // + //if (!property.HasValue(culture, segment) && 'defaultValue is explicitely specified') return defaultValue; + + // give the converter a chance to handle the default value differently // eg for IEnumerable it may return Enumerable.Empty instead of null var value = property.GetValue(culture, segment); @@ -34,7 +29,7 @@ namespace Umbraco.Web // failed attempt. So, no need to care for value being null here. // if already the requested type, return - if (value is T) return (T)value; + if (value is T variable) return variable; // if can convert to requested type, return var convert = value.TryConvertTo(); diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index bde74c0ab1..57e71573b5 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; +using System.Reflection; using System.Web; using Umbraco.Core.Models.PublishedContent; @@ -48,12 +50,13 @@ namespace Umbraco.Web /// Gets a value indicating whether the content has a value for a property identified by its alias. /// /// Returns true if GetProperty(alias) is not null and GetProperty(alias).HasValue is true. - public static bool HasValue(this IPublishedElement content, string alias, string culture = null, string segment = null) + public static bool HasValue(this IPublishedElement content, string alias, string culture = ".", string segment = ".") { var prop = content.GetProperty(alias); return prop != null && prop.HasValue(culture, segment); } + // fixme - that one is missing variations /// /// Returns one of two strings depending on whether the content has a value for a property identified by its alias. /// @@ -63,7 +66,7 @@ namespace Umbraco.Web /// The value to return if the content has no value for the property. /// Either or depending on whether the content /// has a value for the property identified by the alias. - public static IHtmlString IfHasValue(this IPublishedElement content, string alias, string valueIfTrue, string valueIfFalse = null) + public static IHtmlString IfValue(this IPublishedElement content, string alias, string valueIfTrue, string valueIfFalse = null) { return content.HasValue(alias) ? new HtmlString(valueIfTrue) @@ -81,27 +84,7 @@ namespace Umbraco.Web /// The property alias. /// The variation language. /// The variation segment. - /// The value of the content's property identified by the alias. - /// - /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. - /// If no property with the specified alias exists, or if the property has no value, returns null. - /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. - /// The alias is case-insensitive. - /// - public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null) - { - var property = content.GetProperty(alias); - return property?.GetValue(culture, segment); - } - - /// - /// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value. - /// - /// The content. - /// The property alias. /// The default value. - /// The variation language. - /// The variation segment. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. @@ -109,31 +92,13 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedElement content, string alias, string defaultValue, string culture = null, string segment = null) // fixme - kill + public static object Value(this IPublishedElement content, string alias, string culture = ".", string segment = ".", object defaultValue = default) { var property = content.GetProperty(alias); - return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(culture, segment); - } + if (property == null || !property.HasValue(culture, segment)) return defaultValue; - /// - /// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value. - /// - /// The content. - /// The property alias. - /// The default value. - /// The variation language. - /// The variation segment. - /// The value of the content's property identified by the alias, if it exists, otherwise a default value. - /// - /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. - /// If no property with the specified alias exists, or if the property has no value, returns . - /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. - /// The alias is case-insensitive. - /// - public static object Value(this IPublishedElement content, string alias, object defaultValue, string culture = null, string segment = null) - { - var property = content.GetProperty(alias); - return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(culture, segment); + // note: supporting the "." notation for 'current' is the responsibility of the IPublishedProperty + return property.GetValue(culture, segment); // tested HasValue() right above } #endregion @@ -148,6 +113,7 @@ namespace Umbraco.Web /// The property alias. /// The variation language. /// The variation segment. + /// The default value. /// The value of the content's property identified by the alias, converted to the specified type. /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. @@ -155,44 +121,20 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null) - { - return content.Value(alias, false, default(T), culture, segment); - } - - /// - /// Gets the value of a content's property identified by its alias, converted to a specified type, if it exists, otherwise a default value. - /// - /// The target property type. - /// The content. - /// The property alias. - /// The default value. - /// The variation language. - /// The variation segment. - /// The value of the content's property identified by the alias, converted to the specified type, if it exists, otherwise a default value. - /// - /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. - /// If no property with the specified alias exists, or if the property has no value, or if it could not be converted, returns . - /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. - /// The alias is case-insensitive. - /// - public static T Value(this IPublishedElement content, string alias, T defaultValue, string culture = null, string segment = null) - { - return content.Value(alias, true, defaultValue, culture, segment); - } - - internal static T Value(this IPublishedElement content, string alias, bool withDefaultValue, T defaultValue, string culture = null, string segment = null) // fixme uh? + public static T Value(this IPublishedElement content, string alias, string culture = ".", string segment = ".", T defaultValue = default) { var property = content.GetProperty(alias); if (property == null) return defaultValue; - return property.Value(withDefaultValue, defaultValue, culture, segment); + // note: supporting the "." notation for 'current' is the responsibility of the IPublishedProperty + return property.Value(culture, segment, defaultValue); } #endregion #region Value or Umbraco.Field - WORK IN PROGRESS + // fixme - more work-in-progress for element.Value() and element.Value() here // trying to reproduce Umbraco.Field so we can get rid of it // // what we want: @@ -211,6 +153,18 @@ namespace Umbraco.Web // TODO: strongly typed properties howto? // there is no strongly typed recurse, etc => needs to be in ModelsBuilder? + // todo - that one can only happen in ModelsBuilder as that's where the attributes are defined + // the attribute that carries the alias is in ModelsBuilder! + //public static TValue Value(this TModel content, Expression> propertySelector, ...) + // where TModel : IPublishedElement + //{ + // PropertyInfo pi = GetPropertyFromExpression(propertySelector); + // var attr = pi.GetCustomAttribute(); + // var alias = attr.Alias; + // return content.Value(alias, ...) + //} + + // todo - that one should be refactored, missing culture and so many things public static IHtmlString Value(this IPublishedElement content, string aliases, Func format, string alt = "") { if (format == null) format = x => x.ToString(); @@ -225,7 +179,7 @@ namespace Umbraco.Web : new HtmlString(alt); } - // fixme - move that one! + // todo - that one should move to PublishedContentExtensions public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", bool recurse = false) { if (format == null) format = x => x.ToString(); diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index 35e5ab1af4..9437a8147d 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -533,7 +533,7 @@ namespace Umbraco.Web.Routing var redirect = false; var valid = false; IPublishedContent internalRedirectNode = null; - var internalRedirectId = request.PublishedContent.Value(Constants.Conventions.Content.InternalRedirectId, -1); + var internalRedirectId = request.PublishedContent.Value(Constants.Conventions.Content.InternalRedirectId, defaultValue: -1); if (internalRedirectId > 0) { @@ -739,7 +739,7 @@ namespace Umbraco.Web.Routing if (request.PublishedContent.HasProperty(Constants.Conventions.Content.Redirect) == false) return; - var redirectId = request.PublishedContent.Value(Constants.Conventions.Content.Redirect, -1); + var redirectId = request.PublishedContent.Value(Constants.Conventions.Content.Redirect, defaultValue: -1); var redirectUrl = "#"; if (redirectId > 0) { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a39495430b..2760229030 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -350,6 +350,7 @@ + @@ -371,7 +372,7 @@ - + diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index 89ac52949f..61f263f02c 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -374,17 +374,17 @@ namespace umbraco _content = content; } - public override bool HasValue(string culture = null, string segment = null) + public override bool HasValue(string culture = ".", string segment = ".") { return _sourceValue != null && ((_sourceValue is string) == false || string.IsNullOrWhiteSpace((string)_sourceValue) == false); } - public override object GetSourceValue(string culture = null, string segment = null) + public override object GetSourceValue(string culture = ".", string segment = ".") { return _sourceValue; } - public override object GetValue(string culture = null, string segment = null) + public override object GetValue(string culture = ".", string segment = ".") { // isPreviewing is true here since we want to preview anyway... const bool isPreviewing = true; @@ -392,7 +392,7 @@ namespace umbraco return PropertyType.ConvertInterToObject(_content, PropertyCacheLevel.Unknown, source, isPreviewing); } - public override object GetXPathValue(string culture = null, string segment = null) + public override object GetXPathValue(string culture = ".", string segment = ".") { throw new NotImplementedException(); } From 923fdf91996a048aa6306f524ac6c03452d91843 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 27 Apr 2018 19:21:29 +0200 Subject: [PATCH 02/22] ISystemDefaultCultureAccessor --- .../PublishedCache/PublishedContentCacheTests.cs | 2 +- .../PublishedContentOtherTests.cs | 6 +++++- src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs | 2 +- .../TestHelpers/TestWithDatabaseBase.cs | 9 ++++----- .../TestSystemDefaultCultureAccessor.cs | 9 +++++++++ .../TestSystemDefaultCultureProvider.cs | 9 --------- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../Web/Mvc/UmbracoViewPageTests.cs | 4 ++-- ...vider.cs => ISystemDefaultCultureAccessor.cs} | 2 +- .../NuCache/PublishedSnapshotService.cs | 8 ++++---- ...ovider.cs => SystemDefaultCultureAccessor.cs} | 8 ++++---- .../XmlPublishedCache/DomainCache.cs | 4 ++-- .../PublishedSnapshotService.cs | 16 ++++++++-------- .../XmlPublishedCache/XmlCacheComponent.cs | 2 +- src/Umbraco.Web/Runtime/WebRuntimeComponent.cs | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 4 ++-- 16 files changed, 46 insertions(+), 43 deletions(-) create mode 100644 src/Umbraco.Tests/Testing/Objects/Accessors/TestSystemDefaultCultureAccessor.cs delete mode 100644 src/Umbraco.Tests/Testing/Objects/AccessorsAndProviders/TestSystemDefaultCultureProvider.cs rename src/Umbraco.Web/PublishedCache/{ISystemDefaultCultureProvider.cs => ISystemDefaultCultureAccessor.cs} (83%) rename src/Umbraco.Web/PublishedCache/{SystemDefaultCultureProvider.cs => SystemDefaultCultureAccessor.cs} (76%) diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index 8212243e26..1068ba3243 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -62,7 +62,7 @@ namespace Umbraco.Tests.Cache.PublishedCache _xml.LoadXml(GetXml()); var xmlStore = new XmlStore(() => _xml, null, null, null); var cacheProvider = new StaticCacheProvider(); - var domainCache = new DomainCache(ServiceContext.DomainService, SystemDefaultCultureProvider); + var domainCache = new DomainCache(ServiceContext.DomainService, SystemDefaultCultureAccessor); var publishedShapshot = new Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedSnapshot( new PublishedContentCache(xmlStore, domainCache, cacheProvider, globalSettings, new SiteDomainHelper(), ContentTypesCache, null, null), new PublishedMediaCache(xmlStore, ServiceContext.MediaService, ServiceContext.UserService, cacheProvider, ContentTypesCache), diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs index a131076a3a..e1c2ed9d5a 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs @@ -18,8 +18,10 @@ using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; +using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.NuCache; using Umbraco.Web.PublishedCache.NuCache.DataSource; +using Umbraco.Web.Routing; namespace Umbraco.Tests.PublishedContent { @@ -129,8 +131,10 @@ namespace Umbraco.Tests.PublishedContent documentRepository, mediaRepository, memberRepository, + new TestSystemDefaultCultureAccessor(), dataSource, - globalSettings); + globalSettings, + new SiteDomainHelper()); var snapshot = snapshotService.CreatePublishedSnapshot(previewToken: null); var publishedContent = snapshot.Content.GetById(1); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 70ff7d8341..fb9b3ee0e4 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -93,8 +93,8 @@ namespace Umbraco.Tests.Scoping Logger, ScopeProvider, documentRepository, mediaRepository, memberRepository, + SystemDefaultCultureAccessor, new DatabaseDataSource(), - SystemDefaultCultureProvider, Container.GetInstance(), new SiteDomainHelper()); } diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index cede329bd0..75b9d6b0fd 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -32,7 +32,6 @@ using LightInject; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Repositories; -using Umbraco.Tests.Testing.Objects.AccessorsAndProviders; using Umbraco.Tests.Testing.Objects.Accessors; namespace Umbraco.Tests.TestHelpers @@ -79,7 +78,7 @@ namespace Umbraco.Tests.TestHelpers Container.Register(); Container.Register(factory => PublishedSnapshotService); - Container.Register(factory => SystemDefaultCultureProvider); + Container.Register(factory => SystemDefaultCultureAccessor); Container.GetInstance() .Clear() @@ -232,7 +231,7 @@ namespace Umbraco.Tests.TestHelpers } } - protected ISystemDefaultCultureProvider SystemDefaultCultureProvider { get; set; } + protected ISystemDefaultCultureAccessor SystemDefaultCultureAccessor { get; set; } protected IPublishedSnapshotService PublishedSnapshotService { get; set; } @@ -240,7 +239,7 @@ namespace Umbraco.Tests.TestHelpers { base.Initialize(); - SystemDefaultCultureProvider = new TestSystemDefaultCultureProvider(); + SystemDefaultCultureAccessor = new TestSystemDefaultCultureAccessor(); CreateAndInitializeDatabase(); @@ -272,7 +271,7 @@ namespace Umbraco.Tests.TestHelpers ScopeProvider, cache, publishedSnapshotAccessor, variationContextAccessor, Container.GetInstance(), Container.GetInstance(), Container.GetInstance(), - SystemDefaultCultureProvider, + SystemDefaultCultureAccessor, Logger, Container.GetInstance(), new SiteDomainHelper(), ContentTypesCache, diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestSystemDefaultCultureAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestSystemDefaultCultureAccessor.cs new file mode 100644 index 0000000000..fe94a0237f --- /dev/null +++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestSystemDefaultCultureAccessor.cs @@ -0,0 +1,9 @@ +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Tests.Testing.Objects.Accessors +{ + public class TestSystemDefaultCultureAccessor : ISystemDefaultCultureAccessor + { + public string DefaultCulture { get; set; } + } +} diff --git a/src/Umbraco.Tests/Testing/Objects/AccessorsAndProviders/TestSystemDefaultCultureProvider.cs b/src/Umbraco.Tests/Testing/Objects/AccessorsAndProviders/TestSystemDefaultCultureProvider.cs deleted file mode 100644 index f7e5484500..0000000000 --- a/src/Umbraco.Tests/Testing/Objects/AccessorsAndProviders/TestSystemDefaultCultureProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Umbraco.Web.PublishedCache; - -namespace Umbraco.Tests.Testing.Objects.AccessorsAndProviders -{ - public class TestSystemDefaultCultureProvider : ISystemDefaultCultureProvider - { - public string DefaultCulture { get; set; } - } -} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index dc38dd54ee..c1d1da0b54 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -182,7 +182,7 @@ - + diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 8e9e6a3e22..a71612823e 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -19,7 +19,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using Umbraco.Tests.Testing.Objects.AccessorsAndProviders; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Models; using Umbraco.Web.Mvc; @@ -427,7 +427,7 @@ namespace Umbraco.Tests.Web.Mvc _service = new PublishedSnapshotService(svcCtx, factory, scopeProvider, cache, Enumerable.Empty(), null, null, null, null, null, - new TestSystemDefaultCultureProvider(), + new TestSystemDefaultCultureAccessor(), Current.Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), null, true, false); // no events var http = GetHttpContextFactory(url, routeData).HttpContext; diff --git a/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureProvider.cs b/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureAccessor.cs similarity index 83% rename from src/Umbraco.Web/PublishedCache/ISystemDefaultCultureProvider.cs rename to src/Umbraco.Web/PublishedCache/ISystemDefaultCultureAccessor.cs index 022b6d3e25..109187c770 100644 --- a/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureProvider.cs +++ b/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureAccessor.cs @@ -3,7 +3,7 @@ /// /// Provides the system default culture. /// - public interface ISystemDefaultCultureProvider + public interface ISystemDefaultCultureAccessor { /// /// Gets the system default culture. diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 1ca5770060..af84f54c11 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -43,7 +43,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IMemberRepository _memberRepository; private readonly IGlobalSettings _globalSettings; private readonly ISiteDomainHelper _siteDomainHelper; - private readonly ISystemDefaultCultureProvider _systemDefaultCultureProvider; + private readonly ISystemDefaultCultureAccessor _systemDefaultCultureAccessor; // volatile because we read it with no lock private volatile bool _isReady; @@ -84,7 +84,7 @@ namespace Umbraco.Web.PublishedCache.NuCache IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, ILogger logger, IScopeProvider scopeProvider, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, - ISystemDefaultCultureProvider systemDefaultCultureProvider, + ISystemDefaultCultureAccessor systemDefaultCultureAccessor, IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) : base(publishedSnapshotAccessor, variationContextAccessor) { @@ -99,7 +99,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _documentRepository = documentRepository; _mediaRepository = mediaRepository; _memberRepository = memberRepository; - _systemDefaultCultureProvider = systemDefaultCultureProvider; + _systemDefaultCultureAccessor = systemDefaultCultureAccessor; _globalSettings = globalSettings; _siteDomainHelper = siteDomainHelper; @@ -1017,7 +1017,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var memberTypeCache = new PublishedContentTypeCache(null, null, _serviceContext.MemberTypeService, _publishedContentTypeFactory, _logger); - var defaultCulture = _systemDefaultCultureProvider.DefaultCulture; + var defaultCulture = _systemDefaultCultureAccessor.DefaultCulture; var domainCache = new DomainCache(domainSnap, defaultCulture); var domainHelper = new DomainHelper(domainCache, _siteDomainHelper); diff --git a/src/Umbraco.Web/PublishedCache/SystemDefaultCultureProvider.cs b/src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs similarity index 76% rename from src/Umbraco.Web/PublishedCache/SystemDefaultCultureProvider.cs rename to src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs index 6838d483b0..34910bfe1b 100644 --- a/src/Umbraco.Web/PublishedCache/SystemDefaultCultureProvider.cs +++ b/src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs @@ -3,17 +3,17 @@ namespace Umbraco.Web.PublishedCache { /// - /// Provides the default implementation of . + /// Provides the default implementation of . /// - public class SystemDefaultCultureProvider : ISystemDefaultCultureProvider + public class SystemDefaultCultureAccessor : ISystemDefaultCultureAccessor { private readonly ILocalizationService _localizationService; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// - public SystemDefaultCultureProvider(ILocalizationService localizationService) + public SystemDefaultCultureAccessor(ILocalizationService localizationService) { _localizationService = localizationService; } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs index 9a82840024..b0c5033c41 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs @@ -11,10 +11,10 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { private readonly IDomainService _domainService; - public DomainCache(IDomainService domainService, ISystemDefaultCultureProvider systemDefaultCultureProvider) + public DomainCache(IDomainService domainService, ISystemDefaultCultureAccessor systemDefaultCultureAccessor) { _domainService = domainService; - DefaultCulture = systemDefaultCultureProvider.DefaultCulture; + DefaultCulture = systemDefaultCultureAccessor.DefaultCulture; } public IEnumerable GetAll(bool includeWildcards) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs index a0efd14ba9..259229a600 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs @@ -32,7 +32,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private readonly IUserService _userService; private readonly ICacheProvider _requestCache; private readonly IGlobalSettings _globalSettings; - private readonly ISystemDefaultCultureProvider _systemDefaultCultureProvider; + private readonly ISystemDefaultCultureAccessor _systemDefaultCultureAccessor; private readonly ISiteDomainHelper _siteDomainHelper; #region Constructors @@ -45,7 +45,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IEnumerable segmentProviders, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, - ISystemDefaultCultureProvider systemDefaultCultureProvider, + ISystemDefaultCultureAccessor systemDefaultCultureAccessor, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, @@ -54,7 +54,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, segmentProviders, publishedSnapshotAccessor, variationContextAccessor, documentRepository, mediaRepository, memberRepository, - systemDefaultCultureProvider, + systemDefaultCultureAccessor, logger, globalSettings, siteDomainHelper, null, mainDom, testing, enableRepositoryEvents) { } @@ -65,7 +65,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache ICacheProvider requestCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, - ISystemDefaultCultureProvider systemDefaultCultureProvider, + ISystemDefaultCultureAccessor systemDefaultCultureAccessor, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, @@ -75,7 +75,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, Enumerable.Empty(), publishedSnapshotAccessor, variationContextAccessor, documentRepository, mediaRepository, memberRepository, - systemDefaultCultureProvider, + systemDefaultCultureAccessor, logger, globalSettings, siteDomainHelper, contentTypeCache, mainDom, testing, enableRepositoryEvents) { } @@ -86,7 +86,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IEnumerable segmentProviders, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, - ISystemDefaultCultureProvider systemDefaultCultureProvider, + ISystemDefaultCultureAccessor systemDefaultCultureAccessor, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, @@ -108,7 +108,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _memberService = serviceContext.MemberService; _mediaService = serviceContext.MediaService; _userService = serviceContext.UserService; - _systemDefaultCultureProvider = systemDefaultCultureProvider; + _systemDefaultCultureAccessor = systemDefaultCultureAccessor; _requestCache = requestCache; _globalSettings = globalSettings; @@ -153,7 +153,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // the current caches, but that would mean creating an extra cache (StaticCache // probably) so better use RequestCache. - var domainCache = new DomainCache(_domainService, _systemDefaultCultureProvider); + var domainCache = new DomainCache(_domainService, _systemDefaultCultureAccessor); return new PublishedSnapshot( new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _siteDomainHelper, _contentTypeCache, _routesCache, previewToken), diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs index aea2fc60c6..a574575cbd 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs @@ -33,7 +33,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), - factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 54afd0ad75..17078826ff 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -70,7 +70,7 @@ namespace Umbraco.Web.Runtime composition.Container.RegisterFrom(); // register the system culture provider - composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton(); var typeLoader = composition.Container.GetInstance(); var logger = composition.Container.GetInstance(); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 2760229030..e0380c898e 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -343,7 +343,7 @@ - + @@ -389,7 +389,7 @@ - + From 27390afe86ea55c094125e5b4ce7faa43c8b685c Mon Sep 17 00:00:00 2001 From: Stephan Date: Sat, 28 Apr 2018 09:55:36 +0200 Subject: [PATCH 03/22] All tests green --- .../Models/PublishedContent/RawValueProperty.cs | 9 ++++++--- .../Repositories/Implement/ContentRepositoryBase.cs | 8 ++++++++ .../Implement/ContentTypeServiceBaseOfTItemTService.cs | 6 ++++++ src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 10 ++++++++++ src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs | 5 +++++ src/Umbraco.Web/PublishedContentExtensions.cs | 6 ++++-- 6 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs index 00a6b940bb..d7d71cea08 100644 --- a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs +++ b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs @@ -20,8 +20,11 @@ namespace Umbraco.Core.Models.PublishedContent private readonly Lazy _objectValue; private readonly Lazy _xpathValue; + // RawValueProperty does not (yet?) support variants, + // only manages the current "default" value + public override object GetSourceValue(string culture = ".", string segment = ".") - => culture == null & segment == null ? _sourceValue : null; + => culture == "." & segment == "." ? _sourceValue : null; public override bool HasValue(string culture = ".", string segment = ".") { @@ -30,10 +33,10 @@ namespace Umbraco.Core.Models.PublishedContent } public override object GetValue(string culture = ".", string segment = ".") - => culture == null & segment == null ? _objectValue.Value : null; + => culture == "." & segment == "." ? _objectValue.Value : null; public override object GetXPathValue(string culture = ".", string segment = ".") - => culture == null & segment == null ? _xpathValue.Value : null; + => culture == "." & segment == "." ? _xpathValue.Value : null; public RawValueProperty(PublishedPropertyType propertyType, IPublishedElement content, object sourceValue, bool isPreviewing = false) : base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs index e95e296e0a..83148d6bdb 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -565,6 +565,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement public static event TypedEventHandler ScopeEntityRemove; public static event TypedEventHandler ScopeVersionRemove; + // used by tests to clear events + internal static void ClearScopeEvents() + { + ScopedEntityRefresh = null; + ScopeEntityRemove = null; + ScopeVersionRemove = null; + } + protected void OnUowRefreshedEntity(ScopedEntityEventArgs args) { ScopedEntityRefresh.RaiseEvent(args, This); diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTItemTService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTItemTService.cs index a3db23e5ed..f02441eba0 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTItemTService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTItemTService.cs @@ -22,6 +22,12 @@ namespace Umbraco.Core.Services.Implement // that one is always immediate (transactional) public static event TypedEventHandler.EventArgs> UowRefreshedEntity; + // used by tests to clear events + internal static void ClearScopeEvents() + { + UowRefreshedEntity = null; + } + // these must be dispatched public static event TypedEventHandler> Saving; public static event TypedEventHandler> Saved; diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 6f803516cf..2f00d065cd 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -21,10 +21,12 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; @@ -386,6 +388,14 @@ namespace Umbraco.Tests.Testing SettingsForTests.Reset(); // fixme - should it be optional? Mapper.Reset(); + + // clear static events + DocumentRepository.ClearScopeEvents(); + MediaRepository.ClearScopeEvents(); + MemberRepository.ClearScopeEvents(); + ContentTypeService.ClearScopeEvents(); + MediaTypeService.ClearScopeEvents(); + MemberTypeService.ClearScopeEvents(); } #endregion diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index c7661a3f28..5f2df36bf1 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -178,9 +178,14 @@ namespace Umbraco.Web.PublishedCache.NuCache var part = parts[i++]; content = content.Children.FirstOrDefault(x => { + // fixme - should use ISystemDefaultCultureAccessor NOT ILocalizationService! var urlName = x.GetUrlName(_localizationService, culture); return urlName == part; }); + + // fixme - if content has a wildcard domain, switch culture! or, shall we? + // no - do NOT support wildcard domains, it makes no sense + // OTOH support '*/en' as a valid domain } return content; } diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 4ae03bfc22..fd2ddc51e5 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -1219,10 +1219,12 @@ namespace Umbraco.Web /// /// public static string GetUrlName(this IPublishedContent content, ILocalizationService localizationService, string culture = null) - { + { + // fixme publishedContent could get ISystemDefaultCultureAccessor injected! + if (content.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) { - var cultureCode = culture ?? localizationService.GetDefaultLanguageIsoCode(); + var cultureCode = culture ?? localizationService.GetDefaultLanguageIsoCode(); // fixme kill.kill.kill if (cultureCode != null && content.CultureNames.TryGetValue(cultureCode, out var cultureName)) { return cultureName.UrlName; From ce8775b3e7a75cd184161b13dc6e422e98fa50d0 Mon Sep 17 00:00:00 2001 From: Stephan Date: Sat, 28 Apr 2018 16:34:43 +0200 Subject: [PATCH 04/22] Cleanup IPublishedContent --- .../PublishedContent/IPublishedContent.cs | 133 ++++++++-- .../PublishedContent/IPublishedElement.cs | 2 +- .../PublishedContentWrapped.cs | 81 ++++-- .../PublishedContent/PublishedCultureName.cs | 51 ++++ src/Umbraco.Core/StringExtensions.cs | 11 +- .../PublishedMediaCacheTests.cs | 41 ++- .../Published/NestedContentTests.cs | 5 +- .../PublishedContentDataTableTests.cs | 22 +- .../PublishedContentExtensionTests.cs | 15 +- .../PublishedContentOtherTests.cs | 6 +- .../PublishedContent/PublishedContentTests.cs | 24 +- .../PublishedContent/PublishedRouterTests.cs | 5 +- .../SolidPublishedSnapshot.cs | 7 +- .../Routing/RenderRouteHandlerTests.cs | 5 + .../TestHelpers/Stubs/TestPublishedContent.cs | 22 +- .../TestPublishedVariationContextAccessor.cs | 6 +- src/Umbraco.Web/Editors/MacroController.cs | 10 +- .../Editors/TemplateQueryController.cs | 4 +- .../Models/PublishedContentBase.cs | 142 ++++++----- src/Umbraco.Web/Mvc/RenderRouteHandler.cs | 4 +- .../PublishedCache/NuCache/ContentCache.cs | 19 +- .../NuCache/PublishedContent.cs | 161 +++++++++--- .../PublishedCache/PublishedMember.cs | 8 +- .../DictionaryPublishedContent.cs | 237 ++++++++++++++++++ .../XmlPublishedCache/PublishedMediaCache.cs | 227 ----------------- .../XmlPublishedCache/XmlPublishedContent.cs | 20 +- src/Umbraco.Web/PublishedContentExtensions.cs | 64 ++--- .../Runtime/WebRuntimeComponent.cs | 5 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + src/Umbraco.Web/umbraco.presentation/page.cs | 57 +++-- 30 files changed, 855 insertions(+), 540 deletions(-) create mode 100644 src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index f75e59bee0..23f5bbff8d 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -6,36 +6,119 @@ namespace Umbraco.Core.Models.PublishedContent /// /// - /// Represents a cached content. + /// Represents a published content item. /// + /// + /// Can be a published document, media or member. + /// public interface IPublishedContent : IPublishedElement { #region Content - // fixme - all these are colliding with models => ? - // or could we force them to be 'new' in models? - - int Id { get; } - int TemplateId { get; } - int SortOrder { get; } - string Name { get; } - string UrlName { get; } // fixme rename - string DocumentTypeAlias { get; } // fixme obsolete - int DocumentTypeId { get; } // fixme obsolete - string WriterName { get; } - string CreatorName { get; } - int WriterId { get; } - int CreatorId { get; } - string Path { get; } - DateTime CreateDate { get; } - DateTime UpdateDate { get; } - int Level { get; } - string Url { get; } - - IReadOnlyDictionary CultureNames { get; } + // todo - IPublishedContent properties colliding with models + // we need to find a way to remove as much clutter as possible from IPublishedContent, + // since this is preventing someone from creating a property named 'Path' and have it + // in a model, for instance. we could move them all under one unique property eg + // Infos, so we would do .Infos.SortOrder - just an idea - not going to do it in v8 /// - /// Gets a value indicating whether the content is a content (aka a document) or a media. + /// Gets the unique identifier of the content item. + /// + int Id { get; } + + /// + /// Gets the name of the content item. + /// + /// + /// The value of this property is contextual. When the content type is multi-lingual, + /// this is the name for the 'current' culture. + /// + /// FIXME culture aware - returns the value for the 'current' culture whatever it is + see ?? for others + string Name { get; } + + /// + /// Gets the url segment of the content item. + /// + /// + /// The value of this property is contextual. When the content type is multi-lingual, + /// this is the name for the 'current' culture. + /// + /// FIXME rename UrlSegment + culture aware + string UrlName { get; } // fixme rename, segment! + + /// + /// Gets the sort order of the content item. + /// + int SortOrder { get; } + + /// + /// Gets the tree level of the content item. + /// + int Level { get; } + + /// + /// Gets the tree path of the content item. + /// + string Path { get; } + + /// + /// Gets the identifier of the template to use to render the content item. + /// + int TemplateId { get; } + + /// + /// Gets the identifier of the user who created the content item. + /// + int CreatorId { get; } + + /// + /// Gets the name of the user who created the content item. + /// + string CreatorName { get; } + + /// + /// Gets the date the content item was created. + /// + DateTime CreateDate { get; } + + /// + /// Gets the identifier of the user who last updated the content item. + /// + int WriterId { get; } + + /// + /// Gets the name of the user who last updated the content item. + /// + string WriterName { get; } + + /// + /// Gets the date the content item was last updated. + /// + /// + /// For published content items, this is also the date the item was published. + /// This date is global to the content item, see FIXME for per-culture dates + /// + DateTime UpdateDate { get; } + + /// + /// Gets the url of the content item. + /// + /// + /// The value of this property is contextual. It depends on the 'current' + /// In addition, when the content type is multi-lingual, this is the url for the + /// 'current' culture. + /// + /// FIXME explain what 'current' means here + string Url { get; } + + // fixme document + //PublishedCultureInfos Culture(string culture = "."); + //string GetName(string culture = "."); // best naming? GetName? CultureName? + PublishedCultureInfos GetCulture(string culture = "."); + IReadOnlyDictionary Cultures { get; } + + /// + /// Gets the type of the content item (document, media...). /// PublishedItemType ItemType { get; } @@ -51,13 +134,13 @@ namespace Umbraco.Core.Models.PublishedContent #region Tree /// - /// Gets the parent of the content. + /// Gets the parent of the content item. /// /// The parent of root content is null. IPublishedContent Parent { get; } /// - /// Gets the children of the content. + /// Gets the children of the content item. /// /// Children are sorted by their sortOrder. IEnumerable Children { get; } diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs index 6cd4818587..4b579d824b 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core.Models.PublishedContent #region PublishedElement /// - /// Gets the unique key of the published snapshot item. + /// Gets the unique key of the published element. /// Guid Key { get; } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 528a545f8f..f8a4e16bd2 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -39,72 +39,99 @@ namespace Umbraco.Core.Models.PublishedContent public IPublishedContent Unwrap() => _content; #region ContentType - + + /// public virtual PublishedContentType ContentType => _content.ContentType; #endregion + + #region PublishedElement - #region Content - - public virtual int Id => _content.Id; - + /// public Guid Key => _content.Key; - public virtual int TemplateId => _content.TemplateId; + #endregion - public virtual int SortOrder => _content.SortOrder; + #region PublishedContent - public virtual string Name => _content.Name; - - public virtual IReadOnlyDictionary CultureNames => _content.CultureNames; + /// + public virtual int Id => _content.Id; + /// + public virtual string Name => _content.Name; + + /// public virtual string UrlName => _content.UrlName; - public virtual string DocumentTypeAlias => _content.DocumentTypeAlias; - - public virtual int DocumentTypeId => _content.DocumentTypeId; - - public virtual string WriterName => _content.WriterName; - - public virtual string CreatorName => _content.CreatorName; - - public virtual int WriterId => _content.WriterId; - - public virtual int CreatorId => _content.CreatorId; - - public virtual string Path => _content.Path; - - public virtual DateTime CreateDate => _content.CreateDate; - - public virtual DateTime UpdateDate => _content.UpdateDate; + /// + public virtual int SortOrder => _content.SortOrder; + /// public virtual int Level => _content.Level; + /// + public virtual string Path => _content.Path; + + /// + public virtual int TemplateId => _content.TemplateId; + + /// + public virtual int CreatorId => _content.CreatorId; + + /// + public virtual string CreatorName => _content.CreatorName; + + /// + public virtual DateTime CreateDate => _content.CreateDate; + + /// + public virtual int WriterId => _content.WriterId; + + /// + public virtual string WriterName => _content.WriterName; + + /// + public virtual DateTime UpdateDate => _content.UpdateDate; + + /// public virtual string Url => _content.Url; + /// + public PublishedCultureInfos GetCulture(string culture = ".") => _content.GetCulture(culture); + + /// + public IReadOnlyDictionary Cultures => _content.Cultures; + + /// public virtual PublishedItemType ItemType => _content.ItemType; + /// public virtual bool IsDraft => _content.IsDraft; #endregion #region Tree + /// public virtual IPublishedContent Parent => _content.Parent; + /// public virtual IEnumerable Children => _content.Children; #endregion #region Properties + /// public virtual IEnumerable Properties => _content.Properties; + /// public virtual IPublishedProperty GetProperty(string alias) { return _content.GetProperty(alias); } + /// public virtual IPublishedProperty GetProperty(string alias, bool recurse) { return _content.GetProperty(alias, recurse); diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs index 59ac875aa4..898649f2e5 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs @@ -1,4 +1,5 @@ using System; +using Umbraco.Core.Exceptions; namespace Umbraco.Core.Models.PublishedContent { @@ -16,4 +17,54 @@ namespace Umbraco.Core.Models.PublishedContent public string Name { get; } public string UrlName { get; } } + + /// + /// Contains culture specific values for . + /// + public class PublishedCultureInfos + { + /// + /// Initializes a new instance of the class. + /// + public PublishedCultureInfos(string culture, string name, bool published, DateTime publishedDate) + { + if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentNullOrEmptyException(nameof(culture)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); + + Culture = culture; + Name = name; + UrlSegment = name.ToUrlSegment(culture); + Published = published; + PublishedDate = publishedDate; + } + + /// + /// Gets the culture. + /// + public string Culture { get; } + + /// + /// Gets the name of the item. + /// + public string Name { get; } + + /// + /// Gets the url segment of the item. + /// + public string UrlSegment { get; } + + /// + /// Gets a value indicating whether the culture is published. + /// + /// + /// A published content item will only have published cultures, and therefore this + /// value will always be true. On the other hand, fixme drafts? + /// + public bool Published { get; } + + /// + /// Gets the date when fixme? + /// + public DateTime PublishedDate { get; } // fixme - model? model.UpdateDate - here? + } } diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index afdb4c3646..0a41b3c09e 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -1157,7 +1157,16 @@ namespace Umbraco.Core /// The text to filter. /// The culture. /// The safe url segment. - public static string ToUrlSegment(this string text, CultureInfo culture) + public static string ToUrlSegment(this string text, string culture) + => text.ToUrlSegment(CultureInfo.GetCultureInfo(culture)); + + /// + /// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an url segment. + /// + /// The text to filter. + /// The culture. + /// The safe url segment. + public static string ToUrlSegment(this string text, CultureInfo culture) // fixme obsolete that one, use the string one? { return Current.ShortStringHelper.CleanStringForUrlSegment(text, culture); } diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index 34def77faa..78a75af835 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -16,6 +16,7 @@ using Current = Umbraco.Web.Composing.Current; using LightInject; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; +using Umbraco.Tests.PublishedContent; namespace Umbraco.Tests.Cache.PublishedCache { @@ -23,6 +24,8 @@ namespace Umbraco.Tests.Cache.PublishedCache [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] public class PublishMediaCacheTests : BaseWebTest { + private Dictionary _mediaTypes; + protected override void Compose() { base.Compose(); @@ -32,6 +35,21 @@ namespace Umbraco.Tests.Cache.PublishedCache .Append(); } + protected override void Initialize() + { + base.Initialize(); + var type = new AutoPublishedContentType(22, "myType", new PublishedPropertyType[] { }); + var image = new AutoPublishedContentType(23, "Image", new PublishedPropertyType[] { }); + var testMediaType = new AutoPublishedContentType(24, "TestMediaType", new PublishedPropertyType[] { }); + _mediaTypes = new Dictionary + { + { type.Alias, type }, + { image.Alias, image }, + { testMediaType.Alias, testMediaType } + }; + ContentTypesCache.GetPublishedContentTypeByAlias = alias => _mediaTypes[alias]; + } + private IMediaType MakeNewMediaType(IUser user, string text, int parentId = -1) { var mt = new MediaType(parentId) { Name = text, Alias = text, Thumbnail = "icon-folder", Icon = "icon-folder" }; @@ -69,6 +87,7 @@ namespace Umbraco.Tests.Cache.PublishedCache { var user = ServiceContext.UserService.GetUserById(0); var mType = MakeNewMediaType(user, "TestMediaType"); + _mediaTypes[mType.Alias] = new PublishedContentType(mType, null); var mRoot = MakeNewMedia("MediaRoot", mType, user, -1); var mChild1 = MakeNewMedia("Child1", mType, user, mRoot.Id); @@ -82,8 +101,8 @@ namespace Umbraco.Tests.Cache.PublishedCache Assert.AreEqual(mRoot.CreateDate.ToString("dd/MM/yyyy HH:mm:ss"), publishedMedia.CreateDate.ToString("dd/MM/yyyy HH:mm:ss")); Assert.AreEqual(mRoot.CreatorId, publishedMedia.CreatorId); //Assert.AreEqual(mRoot.User.Name, publishedMedia.CreatorName); - Assert.AreEqual(mRoot.ContentType.Alias, publishedMedia.DocumentTypeAlias); - Assert.AreEqual(mRoot.ContentType.Id, publishedMedia.DocumentTypeId); + Assert.AreEqual(mRoot.ContentType.Alias, publishedMedia.ContentType.Alias); + Assert.AreEqual(mRoot.ContentType.Id, publishedMedia.ContentType.Id); Assert.AreEqual(mRoot.Level, publishedMedia.Level); Assert.AreEqual(mRoot.Name, publishedMedia.Name); Assert.AreEqual(mRoot.Path, publishedMedia.Path); @@ -186,11 +205,11 @@ namespace Umbraco.Tests.Cache.PublishedCache }; var result = new SearchResult("1234", 1, 1, () => fields.ToDictionary(x => x.Key, x => new List { x.Value })); - + var store = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache); var doc = store.CreateFromCacheValues(store.ConvertFromSearchResult(result)); - DoAssert(doc, 1234, key, 0, 0, "/media/test.jpg", "Image", 0, "Shannon", "Shannon", 0, 0, "-1,1234", DateTime.Parse("2012-07-17T10:34:09"), DateTime.Parse("2012-07-16T10:34:09"), 2); + DoAssert(doc, 1234, key, 0, 0, "/media/test.jpg", "Image", 23, "Shannon", "Shannon", 0, 0, "-1,1234", DateTime.Parse("2012-07-17T10:34:09"), DateTime.Parse("2012-07-16T10:34:09"), 2); Assert.AreEqual(null, doc.Parent); } @@ -206,7 +225,7 @@ namespace Umbraco.Tests.Cache.PublishedCache var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache); var doc = cache.CreateFromCacheValues(cache.ConvertFromXPathNavigator(navigator, true)); - DoAssert(doc, 2000, key, 0, 2, "image1", "Image", 2044, "Shannon", "Shannon", 33, 33, "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1); + DoAssert(doc, 2000, key, 0, 2, "image1", "Image", 23, "Shannon", "Shannon", 33, 33, "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1); Assert.AreEqual(null, doc.Parent); Assert.AreEqual(2, doc.Children.Count()); Assert.AreEqual(2001, doc.Children.ElementAt(0).Id); @@ -271,7 +290,7 @@ namespace Umbraco.Tests.Cache.PublishedCache }; } - private PublishedMediaCache.DictionaryPublishedContent GetDictionaryDocument( + private DictionaryPublishedContent GetDictionaryDocument( string idKey = "id", string templateKey = "template", string nodeNameKey = "nodeName", @@ -284,11 +303,11 @@ namespace Umbraco.Tests.Cache.PublishedCache { if (children == null) children = new List(); - var dicDoc = new PublishedMediaCache.DictionaryPublishedContent( + var dicDoc = new DictionaryPublishedContent( //the dictionary GetDictionary(idVal, keyVal, parentIdVal, idKey, templateKey, nodeNameKey, nodeTypeAliasKey, pathKey), //callback to get the parent - d => new PublishedMediaCache.DictionaryPublishedContent( + d => new DictionaryPublishedContent( // the dictionary GetDictionary(parentIdVal, default(Guid), -1, idKey, templateKey, nodeNameKey, nodeTypeAliasKey, pathKey), // callback to get the parent: there is no parent @@ -317,7 +336,7 @@ namespace Umbraco.Tests.Cache.PublishedCache } private void DoAssert( - PublishedMediaCache.DictionaryPublishedContent dicDoc, + DictionaryPublishedContent dicDoc, int idVal = 1234, Guid keyVal = default(Guid), int templateIdVal = 0, @@ -375,8 +394,8 @@ namespace Umbraco.Tests.Cache.PublishedCache Assert.AreEqual(templateIdVal, doc.TemplateId); Assert.AreEqual(sortOrderVal, doc.SortOrder); Assert.AreEqual(urlNameVal, doc.UrlName); - Assert.AreEqual(nodeTypeAliasVal, doc.DocumentTypeAlias); - Assert.AreEqual(nodeTypeIdVal, doc.DocumentTypeId); + Assert.AreEqual(nodeTypeAliasVal, doc.ContentType.Alias); + Assert.AreEqual(nodeTypeIdVal, doc.ContentType.Id); Assert.AreEqual(writerNameVal, doc.WriterName); Assert.AreEqual(creatorNameVal, doc.CreatorName); Assert.AreEqual(writerIdVal, doc.WriterId); diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 2ad5f470ef..d86b7f0e6b 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -273,10 +273,9 @@ namespace Umbraco.Tests.Published public override int TemplateId { get; } public override int SortOrder { get; } public override string Name { get; } - public override IReadOnlyDictionary CultureNames => throw new NotSupportedException(); + public override PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); + public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); public override string UrlName { get; } - public override string DocumentTypeAlias { get; } - public override int DocumentTypeId { get; } public override string WriterName { get; } public override string CreatorName { get; } public override int WriterId { get; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 4668a86c78..56c60446ef 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -96,8 +96,9 @@ namespace Umbraco.Tests.PublishedContent public void To_DataTable_With_Filter() { var doc = GetContent(true, 1); - //change a doc type alias - ((TestPublishedContent) doc.Children.ElementAt(0)).DocumentTypeAlias = "DontMatch"; + //change a doc type alias + var c = (TestPublishedContent) doc.Children.ElementAt(0); + c.ContentType = new PublishedContentType(22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.InvariantNeutral); var dt = doc.ChildrenAsTable(Current.Services, "Child"); @@ -133,8 +134,9 @@ namespace Umbraco.Tests.PublishedContent CreateDate = DateTime.Now, CreatorId = 1, CreatorName = "Shannon", - DocumentTypeAlias = contentTypeAlias, - DocumentTypeId = 2, + // fixme what're we gonna do? + //DocumentTypeAlias = contentTypeAlias, + //DocumentTypeId = 2, Id = 3, SortOrder = 4, TemplateId = 5, @@ -175,6 +177,8 @@ namespace Umbraco.Tests.PublishedContent ((Collection) d.Properties).Add( new RawValueProperty(factory.CreatePropertyType("property3", 1), d, "value" + (indexVals + 2))); } + + d.ContentType = new PublishedContentType(22, contentTypeAlias, PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.InvariantNeutral); return d; } @@ -201,10 +205,9 @@ namespace Umbraco.Tests.PublishedContent public int TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } - public IReadOnlyDictionary CultureNames => throw new NotSupportedException(); + public PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); + public IReadOnlyDictionary Cultures => throw new NotSupportedException(); public string UrlName { get; set; } - public string DocumentTypeAlias { get; set; } - public int DocumentTypeId { get; set; } public string WriterName { get; set; } public string CreatorName { get; set; } public int WriterId { get; set; } @@ -240,10 +243,7 @@ namespace Umbraco.Tests.PublishedContent return property; } - public PublishedContentType ContentType - { - get { throw new NotImplementedException(); } - } + public PublishedContentType ContentType { get; set; } } } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs index ab4b1138c5..d6321af692 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs @@ -1,6 +1,8 @@ -using NUnit.Framework; +using System.Collections.Generic; +using NUnit.Framework; using Umbraco.Core.Composing; using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Tests.Testing; using Umbraco.Web; @@ -13,6 +15,7 @@ namespace Umbraco.Tests.PublishedContent private UmbracoContext ctx; private string xmlContent = ""; private bool createContentTypes = true; + private Dictionary _contentTypes; protected override string GetXmlContent(int templateId) { @@ -49,8 +52,8 @@ namespace Umbraco.Tests.PublishedContent [Test] public void IsDocumentType_Recursive_BaseType_ReturnsTrue() { - ContentTypesCache.GetPublishedContentTypeByAlias = null; // fixme this is not pretty InitializeInheritedContentTypes(); + ContentTypesCache.GetPublishedContentTypeByAlias = null; // fixme this is not pretty var publishedContent = ctx.ContentCache.GetById(1100); Assert.That(publishedContent.IsDocumentType("base", true)); @@ -76,9 +79,17 @@ namespace Umbraco.Tests.PublishedContent var inheritedType = new ContentType(baseType, contentTypeAlias) { Alias = contentTypeAlias, Name = "Inherited" }; contentTypeService.Save(baseType); contentTypeService.Save(inheritedType); + _contentTypes = new Dictionary + { + { baseType.Alias, new PublishedContentType(baseType, null) }, + { inheritedType.Alias, new PublishedContentType(inheritedType, null) } + }; + ContentTypesCache.GetPublishedContentTypeByAlias = alias => _contentTypes[alias]; createContentTypes = false; } + ContentTypesCache.GetPublishedContentTypeByAlias = alias => _contentTypes[alias]; + xmlContent = @" diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs index e1c2ed9d5a..e4fce2a3be 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs @@ -117,6 +117,9 @@ namespace Umbraco.Tests.PublishedContent var variationAccessor = new TestPublishedVariationContextAccessor(); + // invariant is the current default + variationAccessor.Context = new PublishedVariationContext(); + var options = new PublishedSnapshotService.Options { IgnoreLocalDb = true }; var snapshotService = new PublishedSnapshotService(options, null, @@ -139,9 +142,6 @@ namespace Umbraco.Tests.PublishedContent var snapshot = snapshotService.CreatePublishedSnapshot(previewToken: null); var publishedContent = snapshot.Content.GetById(1); - // invariant is the current default - variationAccessor.Context = null; - Assert.IsNotNull(publishedContent); Assert.AreEqual("It Works1!", publishedContent.Name); Assert.AreEqual("val1", publishedContent.Value("prop")); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 04ed54d81c..2cf555efda 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -364,9 +364,18 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Children_GroupBy_DocumentTypeAlias() { + var home = new AutoPublishedContentType(22, "Home", new PublishedPropertyType[] { }); + var custom = new AutoPublishedContentType(23, "CustomDocument", new PublishedPropertyType[] { }); + var contentTypes = new Dictionary + { + { home.Alias, home }, + { custom.Alias, custom } + }; + ContentTypesCache.GetPublishedContentTypeByAlias = alias => contentTypes[alias]; + var doc = GetNode(1046); - var found1 = doc.Children.GroupBy(x => x.DocumentTypeAlias).ToArray(); + var found1 = doc.Children.GroupBy(x => x.ContentType.Alias).ToArray(); Assert.AreEqual(2, found1.Length); Assert.AreEqual(2, found1.Single(x => x.Key.ToString() == "Home").Count()); @@ -376,10 +385,19 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Children_Where_DocumentTypeAlias() { + var home = new AutoPublishedContentType(22, "Home", new PublishedPropertyType[] { }); + var custom = new AutoPublishedContentType(23, "CustomDocument", new PublishedPropertyType[] { }); + var contentTypes = new Dictionary + { + { home.Alias, home }, + { custom.Alias, custom } + }; + ContentTypesCache.GetPublishedContentTypeByAlias = alias => contentTypes[alias]; + var doc = GetNode(1046); - var found1 = doc.Children.Where(x => x.DocumentTypeAlias == "CustomDocument"); - var found2 = doc.Children.Where(x => x.DocumentTypeAlias == "Home"); + var found1 = doc.Children.Where(x => x.ContentType.Alias == "CustomDocument"); + var found2 = doc.Children.Where(x => x.ContentType.Alias == "Home"); Assert.AreEqual(1, found1.Count()); Assert.AreEqual(2, found2.Count()); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs index cfc0df25b7..66f7871a4b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs @@ -2,8 +2,10 @@ using System.Collections; using System.Collections.ObjectModel; using System.Globalization; +using System.Linq; using Moq; using NUnit.Framework; +using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Tests.TestHelpers; @@ -72,8 +74,6 @@ namespace Umbraco.Tests.PublishedContent var pc = new Mock(); pc.Setup(content => content.Id).Returns(1); pc.Setup(content => content.Name).Returns("test"); - pc.Setup(content => content.DocumentTypeId).Returns(2); - pc.Setup(content => content.DocumentTypeAlias).Returns("testAlias"); pc.Setup(content => content.WriterName).Returns("admin"); pc.Setup(content => content.CreatorName).Returns("admin"); pc.Setup(content => content.CreateDate).Returns(DateTime.Now); @@ -81,6 +81,7 @@ namespace Umbraco.Tests.PublishedContent pc.Setup(content => content.Path).Returns("-1,1"); pc.Setup(content => content.Parent).Returns(() => null); pc.Setup(content => content.Properties).Returns(new Collection()); + pc.Setup(content => content.ContentType).Returns(new PublishedContentType(22, "anything", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.InvariantNeutral)); return pc; } } diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 2be86640d7..307ef459fb 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -165,8 +165,6 @@ namespace Umbraco.Tests.PublishedContent IsDraft = false; ContentType = contentType; - DocumentTypeAlias = contentType.Alias; - DocumentTypeId = contentType.Id; } #endregion @@ -178,10 +176,9 @@ namespace Umbraco.Tests.PublishedContent public int TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } - public IReadOnlyDictionary CultureNames => throw new NotSupportedException(); + public PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); + public IReadOnlyDictionary Cultures => throw new NotSupportedException(); public string UrlName { get; set; } - public string DocumentTypeAlias { get; private set; } - public int DocumentTypeId { get; private set; } public string WriterName { get; set; } public string CreatorName { get; set; } public int WriterId { get; set; } diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index f722906053..c64c70e65a 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -17,6 +17,8 @@ using Umbraco.Web.Routing; using Umbraco.Web.WebApi; using Umbraco.Core.Strings; using Umbraco.Core.Composing; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Tests.PublishedContent; using Umbraco.Tests.Testing; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Runtime; @@ -141,6 +143,9 @@ namespace Umbraco.Tests.Routing frequest.PublishedContent = umbracoContext.ContentCache.GetById(1172); frequest.TemplateModel = template; + var type = new AutoPublishedContentType(22, "CustomDocument", new PublishedPropertyType[] { }); + ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; + var handler = new RenderRouteHandler(umbracoContext, new TestControllerFactory(umbracoContext, Mock.Of())); handler.GetHandlerForRoute(umbracoContext.HttpContext.Request.RequestContext, frequest); diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 7d9bbec855..042794e573 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.PublishedCache; @@ -10,18 +7,31 @@ namespace Umbraco.Tests.TestHelpers.Stubs { internal class TestPublishedContent : PublishedElement, IPublishedContent { - public TestPublishedContent(PublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultureNames = null) + public TestPublishedContent(PublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultures = null) : base(contentType, key, values, previewing) { Id = id; - CultureNames = cultureNames; + Cultures = cultures; } public int Id { get; } public int TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } - public IReadOnlyDictionary CultureNames { get; set; } + public IPublishedVariationContextAccessor VariationContextAccessor { get; set; } + public PublishedCultureInfos GetCulture(string culture = ".") + { + // handle context culture + if (culture == ".") + culture = VariationContextAccessor?.Context.Culture; + + // no invariant culture infos + if (culture == null || Cultures == null) return null; + + // get + return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null; + } + public IReadOnlyDictionary Cultures { get; set; } public string UrlName { get; set; } public string DocumentTypeAlias => ContentType.Alias; public int DocumentTypeId { get; set; } diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs index 9327b462f1..455e5795a9 100644 --- a/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs +++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs @@ -8,6 +8,10 @@ namespace Umbraco.Tests.Testing.Objects.Accessors public class TestPublishedVariationContextAccessor : IPublishedVariationContextAccessor { /// - public PublishedVariationContext Context { get; set; } + public PublishedVariationContext Context + { + get; + set; + } } } diff --git a/src/Umbraco.Web/Editors/MacroController.cs b/src/Umbraco.Web/Editors/MacroController.cs index dc4d42a48c..ba23d4c8dd 100644 --- a/src/Umbraco.Web/Editors/MacroController.cs +++ b/src/Umbraco.Web/Editors/MacroController.cs @@ -12,6 +12,7 @@ using Umbraco.Web.Mvc; using Umbraco.Web.Macros; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Editors { @@ -26,6 +27,13 @@ namespace Umbraco.Web.Editors [PluginController("UmbracoApi")] public class MacroController : UmbracoAuthorizedJsonController, IRequiresSessionState { + private readonly IPublishedVariationContextAccessor _variationContextAccessor; + + public MacroController(IPublishedVariationContextAccessor variationContextAccessor) + { + _variationContextAccessor = variationContextAccessor; + } + /// /// Gets the macro parameters to be filled in for a particular macro /// @@ -113,7 +121,7 @@ namespace Umbraco.Web.Editors //the 'easiest' way might be to create an IPublishedContent manually and populate the legacy 'page' object with that //and then set the legacy parameters. - var legacyPage = new global::umbraco.page(doc); + var legacyPage = new global::umbraco.page(doc, _variationContextAccessor); UmbracoContext.HttpContext.Items["pageID"] = doc.Id; UmbracoContext.HttpContext.Items["pageElements"] = legacyPage.Elements; UmbracoContext.HttpContext.Items[global::Umbraco.Core.Constants.Conventions.Url.AltTemplate] = null; diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index d51a00b3a2..26ca518259 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -90,7 +90,7 @@ namespace Umbraco.Web.Editors { timer.Start(); - pointerNode = pointerNode.FirstChild(x => x.DocumentTypeAlias == contentTypeAlias); + pointerNode = pointerNode.FirstChild(x => x.ContentType.Alias == contentTypeAlias); if (pointerNode == null) break; @@ -277,7 +277,7 @@ namespace Umbraco.Web.Editors if (targetNode == null || targetNode.Id == current.Id) return aliases; if (targetNode.Id != current.Id) { - aliases.Add(targetNode.DocumentTypeAlias); + aliases.Add(targetNode.ContentType.Alias); } diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index a89cab2c65..084ca1fee2 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -19,24 +19,75 @@ namespace Umbraco.Web.Models [DebuggerDisplay("Content Id: {Id}, Name: {Name}")] public abstract class PublishedContentBase : IPublishedContent { - #region Content - private string _url; + + #region ContentType - /// - /// Gets the url of the content. - /// + public abstract PublishedContentType ContentType { get; } + + #endregion + + #region PublishedElement + + /// + public abstract Guid Key { get; } + + #endregion + + #region PublishedContent + + /// + public abstract int Id { get; } + + /// + public abstract string Name { get; } + + /// + public abstract string UrlName { get; } + + /// + public abstract int SortOrder { get; } + + /// + public abstract int Level { get; } + + /// + public abstract string Path { get; } + + /// + public abstract int TemplateId { get; } + + /// + public abstract int CreatorId { get; } + + /// + public abstract string CreatorName { get; } + + /// + public abstract DateTime CreateDate { get; } + + /// + public abstract int WriterId { get; } + + /// + public abstract string WriterName { get; } + + /// + public abstract DateTime UpdateDate { get; } + + /// /// - /// If this content is Content, the url that is returned is the one computed by the NiceUrlProvider, otherwise if - /// this content is Media, the url returned is the value found in the 'umbracoFile' property. + /// The url of documents are computed by the document url providers. The url of medias are, at the moment, + /// computed here from the 'umbracoFile' property -- but we should move to media url providers at some point. /// public virtual string Url { + // fixme contextual! get { // should be thread-safe although it won't prevent url from being resolved more than once if (_url != null) - return _url; + return _url; // fixme very bad idea with nucache? or? switch (ItemType) { @@ -86,86 +137,45 @@ namespace Umbraco.Web.Models return _url; } - } + } + + /// + public abstract PublishedCultureInfos GetCulture(string culture = "."); + /// + public abstract IReadOnlyDictionary Cultures { get; } + + /// public abstract PublishedItemType ItemType { get; } - public abstract int Id { get; } - public abstract Guid Key { get; } - public abstract int TemplateId { get; } - public abstract int SortOrder { get; } - public abstract string Name { get; } - //TODO: On the base ContentData instance this dictionary contains a CultureVariation, should we expose that model here or a different model? - public abstract IReadOnlyDictionary CultureNames { get; } - public abstract string UrlName { get; } - public abstract string DocumentTypeAlias { get; } - public abstract int DocumentTypeId { get; } - public abstract string WriterName { get; } - public abstract string CreatorName { get; } - public abstract int WriterId { get; } - public abstract int CreatorId { get; } - public abstract string Path { get; } - public abstract DateTime CreateDate { get; } - public abstract DateTime UpdateDate { get; } - public abstract int Level { get; } + /// public abstract bool IsDraft { get; } #endregion #region Tree - /// - /// Gets the parent of the content. - /// + /// public abstract IPublishedContent Parent { get; } - /// - /// Gets the children of the content. - /// - /// Children are sorted by their sortOrder. + /// public abstract IEnumerable Children { get; } #endregion - #region ContentType - - public abstract PublishedContentType ContentType { get; } - - #endregion - #region Properties - /// - /// Gets the properties of the content. - /// + /// public abstract IEnumerable Properties { get; } - /// - /// Gets a property identified by its alias. - /// - /// The property alias. - /// The property identified by the alias. - /// - /// If no property with the specified alias exists, returns null. - /// The returned property may have no value (ie HasValue is false). - /// The alias is case-insensitive. - /// + /// public abstract IPublishedProperty GetProperty(string alias); - /// - /// Gets a property identified by its alias. - /// - /// The property alias. - /// A value indicating whether to navigate the tree upwards until a property with a value is found. - /// The property identified by the alias. - /// - /// Navigate the tree upwards and look for a property with that alias and with a value (ie HasValue is true). - /// If found, return the property. If no property with that alias is found, having a value or not, return null. Otherwise - /// return the first property that was found with the alias but had no value (ie HasValue is false). - /// The alias is case-insensitive. - /// + /// public virtual IPublishedProperty GetProperty(string alias, bool recurse) - { + { + // fixme - but can this work with variants? + var property = GetProperty(alias); if (recurse == false) return property; diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index 3b0b6b35e2..446827f0a3 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -300,7 +300,7 @@ namespace Umbraco.Web.Mvc } //check if there's a custom controller assigned, base on the document type alias. - var controllerType = _controllerFactory.GetControllerTypeInternal(requestContext, request.PublishedContent.DocumentTypeAlias); + var controllerType = _controllerFactory.GetControllerTypeInternal(requestContext, request.PublishedContent.ContentType.Alias); //check if that controller exists if (controllerType != null) @@ -320,7 +320,7 @@ namespace Umbraco.Web.Mvc else { Current.Logger.Warn(() => - $"The current Document Type {request.PublishedContent.DocumentTypeAlias} matches a locally declared controller of type {controllerType.FullName}. Custom Controllers for Umbraco routing must implement '{typeof(IRenderController).FullName}' and inherit from '{typeof(ControllerBase).FullName}'."); + $"The current Document Type {request.PublishedContent.ContentType.Alias} matches a locally declared controller of type {controllerType.FullName}. Custom Controllers for Umbraco routing must implement '{typeof(IRenderController).FullName}' and inherit from '{typeof(ControllerBase).FullName}'."); //we cannot route to this custom controller since it is not of the correct type so we'll continue with the defaults // that have already been set above. diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 5f2df36bf1..4b235e42b9 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -104,8 +104,8 @@ namespace Umbraco.Web.PublishedCache.NuCache // hideTopLevelNode = support legacy stuff, look for /*/path/to/node // else normal, look for /path/to/node content = hideTopLevelNode.Value - ? GetAtRoot(preview).SelectMany(x => x.Children).FirstOrDefault(x => x.GetUrlName(_localizationService, culture) == parts[0]) - : GetAtRoot(preview).FirstOrDefault(x => x.GetUrlName(_localizationService, culture) == parts[0]); + ? GetAtRoot(preview).SelectMany(x => x.Children).FirstOrDefault(x => x.GetCulture(culture).UrlSegment == parts[0]) + : GetAtRoot(preview).FirstOrDefault(x => x.GetCulture(culture).UrlSegment == parts[0]); content = FollowRoute(content, parts, 1, culture); } @@ -114,7 +114,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // have to look for /foo (see note in ApplyHideTopLevelNodeFromPath). if (content == null && hideTopLevelNode.Value && parts.Length == 1) { - content = GetAtRoot(preview).FirstOrDefault(x => x.GetUrlName(_localizationService, culture) == parts[0]); + content = GetAtRoot(preview).FirstOrDefault(x => x.GetCulture(culture).UrlSegment == parts[0]); } return content; @@ -147,9 +147,9 @@ namespace Umbraco.Web.PublishedCache.NuCache var hasDomains = _domainHelper.NodeHasDomains(n.Id); while (hasDomains == false && n != null) // n is null at root { - var urlName = n.GetUrlName(_localizationService, culture); + var urlSegment = n.GetCulture(culture).UrlSegment; - pathParts.Add(urlName); + pathParts.Add(urlSegment); // move to parent node n = n.Parent; @@ -178,14 +178,9 @@ namespace Umbraco.Web.PublishedCache.NuCache var part = parts[i++]; content = content.Children.FirstOrDefault(x => { - // fixme - should use ISystemDefaultCultureAccessor NOT ILocalizationService! - var urlName = x.GetUrlName(_localizationService, culture); - return urlName == part; + var urlSegment = x.GetCulture(culture).UrlSegment; + return urlSegment == part; }); - - // fixme - if content has a wildcard domain, switch culture! or, shall we? - // no - do NOT support wildcard domains, it makes no sense - // OTOH support '*/en' as a valid domain } return content; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index a0ec6e4687..d2a4e2f539 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // ReSharper disable once InconsistentNaming internal readonly ContentData _contentData; // internal for ContentNode cloning - private readonly string _urlName; + private readonly string _urlSegment; private IReadOnlyDictionary _cultureNames; #region Constructors @@ -28,9 +28,9 @@ namespace Umbraco.Web.PublishedCache.NuCache _contentNode = contentNode; _contentData = contentData; _publishedSnapshotAccessor = publishedSnapshotAccessor; - VariationContextAccessor = variationContextAccessor; + VariationContextAccessor = variationContextAccessor; // fixme why is this a property? should be be on the base class? - _urlName = _contentData.Name.ToUrlSegment(); + _urlSegment = _contentData.Name.ToUrlSegment(); IsPreviewing = _contentData.Published == false; var properties = new List(); @@ -74,7 +74,7 @@ namespace Umbraco.Web.PublishedCache.NuCache VariationContextAccessor = origin.VariationContextAccessor; _contentData = origin._contentData; - _urlName = origin._urlName; + _urlSegment = origin._urlSegment; IsPreviewing = origin.IsPreviewing; // here is the main benefit: we do not re-create properties so if anything @@ -91,7 +91,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _contentNode = origin._contentNode; _contentData = origin._contentData; - _urlName = origin._urlName; + _urlSegment = origin._urlSegment; IsPreviewing = true; // clone properties so _isPreviewing is true @@ -155,51 +155,131 @@ namespace Umbraco.Web.PublishedCache.NuCache #endregion - #region IPublishedContent + #region Content Type - public override int Id => _contentNode.Id; + /// + public override PublishedContentType ContentType => _contentNode.ContentType; + + #endregion + + #region PublishedElement + + /// public override Guid Key => _contentNode.Uid; - public override int DocumentTypeId => _contentNode.ContentType.Id; - public override string DocumentTypeAlias => _contentNode.ContentType.Alias; - public override PublishedItemType ItemType => _contentNode.ContentType.ItemType; - public override string Name => _contentData.Name; - public override IReadOnlyDictionary CultureNames + #endregion + + #region PublishedContent + + /// + public override int Id => _contentNode.Id; + + /// + public override string Name { get { - if (!ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) - return null; + if (!ContentType.Variations.Has(ContentVariation.CultureNeutral)) // fixme CultureSegment? + return _contentData.Name; - if (_cultureNames == null) - { - var d = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - foreach(var c in _contentData.CultureInfos) - { - d[c.Key] = new PublishedCultureName(c.Value.Name, c.Value.Name.ToUrlSegment()); - } - _cultureNames = d; - } - return _cultureNames; + var culture = VariationContextAccessor.Context.Culture; + if (culture == null) + return _contentData.Name; + + return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos.Name : null; } } - public override int Level => _contentNode.Level; - public override string Path => _contentNode.Path; + + /// + public override string UrlName + { + get + { + if (!ContentType.Variations.Has(ContentVariation.CultureNeutral)) // fixme CultureSegment? + return _urlSegment; + + var culture = VariationContextAccessor.Context.Culture; + if (culture == null) + return _urlSegment; + + return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos.UrlSegment : null; + } + } + + /// public override int SortOrder => _contentNode.SortOrder; + + /// + public override int Level => _contentNode.Level; + + /// + public override string Path => _contentNode.Path; + + /// public override int TemplateId => _contentData.TemplateId; - public override string UrlName => _urlName; - - public override DateTime CreateDate => _contentNode.CreateDate; - public override DateTime UpdateDate => _contentData.VersionDate; - + /// public override int CreatorId => _contentNode.CreatorId; + + /// public override string CreatorName => GetProfileNameById(_contentNode.CreatorId); + + /// + public override DateTime CreateDate => _contentNode.CreateDate; + + /// public override int WriterId => _contentData.WriterId; + + /// public override string WriterName => GetProfileNameById(_contentData.WriterId); + /// + public override DateTime UpdateDate => _contentData.VersionDate; + + private IReadOnlyDictionary _cultureInfos; + + private static readonly IReadOnlyDictionary NoCultureInfos = new Dictionary(); + + /// + public override PublishedCultureInfos GetCulture(string culture = ".") + { + // handle context culture + if (culture == ".") + culture = VariationContextAccessor.Context.Culture; + + // no invariant culture infos + if (culture == null) return null; + + // get + return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null; + } + + /// + public override IReadOnlyDictionary Cultures + { + get + { + if (!ContentType.Variations.Has(ContentVariation.CultureNeutral)) // fixme CultureSegment? + return NoCultureInfos; + + if (_cultureInfos != null) return _cultureInfos; + + return _cultureInfos = _contentData.CultureInfos // fixme can it be null? + .ToDictionary(x => x.Key, x => new PublishedCultureInfos(x.Key, x.Value.Name, false, DateTime.MinValue)); // fixme values! + } + } + + /// + public override PublishedItemType ItemType => _contentNode.ContentType.ItemType; + + /// public override bool IsDraft => _contentData.Published == false; + #endregion + + #region Tree + + /// public override IPublishedContent Parent { get @@ -218,10 +298,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - private string _childrenCacheKey; - - private string ChildrenCacheKey => _childrenCacheKey ?? (_childrenCacheKey = CacheKeys.PublishedContentChildren(Key, IsPreviewing)); - + /// public override IEnumerable Children { get @@ -235,6 +312,10 @@ namespace Umbraco.Web.PublishedCache.NuCache } } + private string _childrenCacheKey; + + private string ChildrenCacheKey => _childrenCacheKey ?? (_childrenCacheKey = CacheKeys.PublishedContentChildren(Key, IsPreviewing)); + private IEnumerable GetChildren() { IEnumerable c; @@ -258,8 +339,15 @@ namespace Umbraco.Web.PublishedCache.NuCache // Q: perfs-wise, is it better than having the store managed an ordered list } + #endregion + + #region Properties + + + /// public override IEnumerable Properties => PropertiesArray; + /// public override IPublishedProperty GetProperty(string alias) { var index = _contentNode.ContentType.GetPropertyIndex(alias); @@ -270,6 +358,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return property; } + /// public override IPublishedProperty GetProperty(string alias, bool recurse) { var property = GetProperty(alias); @@ -283,8 +372,6 @@ namespace Umbraco.Web.PublishedCache.NuCache return (Property)cache.GetCacheItem(key, () => base.GetProperty(alias, true)); } - public override PublishedContentType ContentType => _contentNode.ContentType; - #endregion #region Caching diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index bf764b0ff0..084daf6457 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -144,14 +144,12 @@ namespace Umbraco.Web.PublishedCache public override string Name => _member.Name; - public override IReadOnlyDictionary CultureNames => throw new NotSupportedException(); + public override PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); + + public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); public override string UrlName => throw new NotSupportedException(); - public override string DocumentTypeAlias => _member.ContentTypeAlias; - - public override int DocumentTypeId => _member.ContentType.Id; - //TODO: ARGH! need to fix this - this is not good because it uses ApplicationContext.Current public override string WriterName => _member.GetCreatorProfile().Name; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs new file mode 100644 index 0000000000..5ef600ba76 --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Xml.XPath; +using Examine.LuceneEngine.Providers; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.Composing; +using Umbraco.Web.Models; + +namespace Umbraco.Web.PublishedCache.XmlPublishedCache +{ + /// + /// An IPublishedContent that is represented all by a dictionary. + /// + /// + /// This is a helper class and definitely not intended for public use, it expects that all of the values required + /// to create an IPublishedContent exist in the dictionary by specific aliases. + /// + internal class DictionaryPublishedContent : PublishedContentBase + { + // note: I'm not sure this class fully complies with IPublishedContent rules especially + // I'm not sure that _properties contains all properties including those without a value, + // neither that GetProperty will return a property without a value vs. null... @zpqrtbnk + + // List of properties that will appear in the XML and do not match + // anything in the ContentType, so they must be ignored. + private static readonly string[] IgnoredKeys = { "version", "isDoc" }; + + public DictionaryPublishedContent( + IReadOnlyDictionary valueDictionary, + Func getParent, + Func> getChildren, + Func getProperty, + ICacheProvider cacheProvider, + PublishedContentTypeCache contentTypeCache, + XPathNavigator nav, + bool fromExamine) + { + if (valueDictionary == null) throw new ArgumentNullException(nameof(valueDictionary)); + if (getParent == null) throw new ArgumentNullException(nameof(getParent)); + if (getProperty == null) throw new ArgumentNullException(nameof(getProperty)); + + _getParent = new Lazy(() => getParent(ParentId)); + _getChildren = new Lazy>(() => getChildren(Id, nav)); + _getProperty = getProperty; + _cacheProvider = cacheProvider; + + LoadedFromExamine = fromExamine; + + ValidateAndSetProperty(valueDictionary, val => _id = Int32.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int! + ValidateAndSetProperty(valueDictionary, val => _key = Guid.Parse(val), "key"); + //ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId"); + ValidateAndSetProperty(valueDictionary, val => _sortOrder = Int32.Parse(val), "sortOrder"); + ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName", "__nodeName"); + ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName"); + ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", LuceneIndexer.ItemTypeFieldName); + ValidateAndSetProperty(valueDictionary, val => _documentTypeId = Int32.Parse(val), "nodeType"); + //ValidateAndSetProperty(valueDictionary, val => _writerName = val, "writerName"); + ValidateAndSetProperty(valueDictionary, val => _creatorName = val, "creatorName", "writerName"); //this is a bit of a hack fix for: U4-1132 + //ValidateAndSetProperty(valueDictionary, val => _writerId = int.Parse(val), "writerID"); + ValidateAndSetProperty(valueDictionary, val => _creatorId = Int32.Parse(val), "creatorID", "writerID"); //this is a bit of a hack fix for: U4-1132 + ValidateAndSetProperty(valueDictionary, val => _path = val, "path", "__Path"); + ValidateAndSetProperty(valueDictionary, val => _createDate = ParseDateTimeValue(val), "createDate"); + ValidateAndSetProperty(valueDictionary, val => _updateDate = ParseDateTimeValue(val), "updateDate"); + ValidateAndSetProperty(valueDictionary, val => _level = Int32.Parse(val), "level"); + ValidateAndSetProperty(valueDictionary, val => + { + int pId; + ParentId = -1; + if (Int32.TryParse(val, out pId)) + { + ParentId = pId; + } + }, "parentID"); + + _contentType = contentTypeCache.Get(PublishedItemType.Media, _documentTypeAlias); + _properties = new Collection(); + + //handle content type properties + //make sure we create them even if there's no value + foreach (var propertyType in _contentType.PropertyTypes) + { + var alias = propertyType.Alias; + _keysAdded.Add(alias); + string value; + const bool isPreviewing = false; // false :: never preview a media + var property = valueDictionary.TryGetValue(alias, out value) == false || value == null + ? new XmlPublishedProperty(propertyType, this, isPreviewing) + : new XmlPublishedProperty(propertyType, this, isPreviewing, value); + _properties.Add(property); + } + + //loop through remaining values that haven't been applied + foreach (var i in valueDictionary.Where(x => + _keysAdded.Contains(x.Key) == false // not already processed + && IgnoredKeys.Contains(x.Key) == false)) // not ignorable + { + if (i.Key.InvariantStartsWith("__")) + { + // no type for that one, dunno how to convert, drop it + //IPublishedProperty property = new PropertyResult(i.Key, i.Value, PropertyResultType.CustomProperty); + //_properties.Add(property); + } + else + { + // this is a property that does not correspond to anything, ignore and log + Current.Logger.Warn("Dropping property \"" + i.Key + "\" because it does not belong to the content type."); + } + } + } + + private DateTime ParseDateTimeValue(string val) + { + if (LoadedFromExamine == false) + return DateTime.Parse(val); + + //we need to parse the date time using Lucene converters + var ticks = Int64.Parse(val); + return new DateTime(ticks); + } + + /// + /// Flag to get/set if this was laoded from examine cache + /// + internal bool LoadedFromExamine { get; } + + //private readonly Func _getParent; + private readonly Lazy _getParent; + //private readonly Func> _getChildren; + private readonly Lazy> _getChildren; + private readonly Func _getProperty; + private readonly ICacheProvider _cacheProvider; + + /// + /// Returns 'Media' as the item type + /// + public override PublishedItemType ItemType => PublishedItemType.Media; + + public override IPublishedContent Parent => _getParent.Value; + + public int ParentId { get; private set; } + + public override int Id => _id; + + public override Guid Key => _key; + + public override int TemplateId => 0; + + public override int SortOrder => _sortOrder; + + public override string Name => _name; + + public override PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); + + public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); + + public override string UrlName => _urlName; + + public override string WriterName => _creatorName; + + public override string CreatorName => _creatorName; + + public override int WriterId => _creatorId; + + public override int CreatorId => _creatorId; + + public override string Path => _path; + + public override DateTime CreateDate => _createDate; + + public override DateTime UpdateDate => _updateDate; + + public override int Level => _level; + + public override bool IsDraft => false; + + public override IEnumerable Properties => _properties; + + public override IEnumerable Children => _getChildren.Value; + + public override IPublishedProperty GetProperty(string alias) + { + return _getProperty(this, alias); + } + + public override PublishedContentType ContentType => _contentType; + + // override to implement cache + // cache at context level, ie once for the whole request + // but cache is not shared by requests because we wouldn't know how to clear it + public override IPublishedProperty GetProperty(string alias, bool recurse) + { + if (recurse == false) return GetProperty(alias); + + var key = $"XmlPublishedCache.PublishedMediaCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}"; + var cacheProvider = _cacheProvider; + return cacheProvider.GetCacheItem(key, () => base.GetProperty(alias, true)); + } + + private readonly List _keysAdded = new List(); + private int _id; + private Guid _key; + //private int _templateId; + private int _sortOrder; + private string _name; + private string _urlName; + private string _documentTypeAlias; + private int _documentTypeId; + //private string _writerName; + private string _creatorName; + //private int _writerId; + private int _creatorId; + private string _path; + private DateTime _createDate; + private DateTime _updateDate; + //private Guid _version; + private int _level; + private readonly ICollection _properties; + private readonly PublishedContentType _contentType; + + private void ValidateAndSetProperty(IReadOnlyDictionary valueDictionary, Action setProperty, params string[] potentialKeys) + { + var key = potentialKeys.FirstOrDefault(x => valueDictionary.ContainsKey(x) && valueDictionary[x] != null); + if (key == null) + { + throw new FormatException("The valueDictionary is not formatted correctly and is missing any of the '" + String.Join(",", potentialKeys) + "' elements"); + } + + setProperty(valueDictionary[key]); + _keysAdded.Add(key); + } + } +} diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 48a63aa8d3..8f354240e0 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -1,13 +1,11 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Configuration; using System.IO; using System.Linq; using System.Threading; using System.Xml.XPath; using Examine; -using Examine.LuceneEngine.Providers; using Examine.LuceneEngine.SearchCriteria; using Examine.Providers; using Lucene.Net.Store; @@ -16,7 +14,6 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Xml; -using Umbraco.Web.Models; using Umbraco.Examine; using umbraco; using Umbraco.Core.Cache; @@ -607,230 +604,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return mediaList; } - /// - /// An IPublishedContent that is represented all by a dictionary. - /// - /// - /// This is a helper class and definitely not intended for public use, it expects that all of the values required - /// to create an IPublishedContent exist in the dictionary by specific aliases. - /// - internal class DictionaryPublishedContent : PublishedContentBase - { - // note: I'm not sure this class fully complies with IPublishedContent rules especially - // I'm not sure that _properties contains all properties including those without a value, - // neither that GetProperty will return a property without a value vs. null... @zpqrtbnk - - // List of properties that will appear in the XML and do not match - // anything in the ContentType, so they must be ignored. - private static readonly string[] IgnoredKeys = { "version", "isDoc" }; - - public DictionaryPublishedContent( - IReadOnlyDictionary valueDictionary, - Func getParent, - Func> getChildren, - Func getProperty, - ICacheProvider cacheProvider, - PublishedContentTypeCache contentTypeCache, - XPathNavigator nav, - bool fromExamine) - { - if (valueDictionary == null) throw new ArgumentNullException(nameof(valueDictionary)); - if (getParent == null) throw new ArgumentNullException(nameof(getParent)); - if (getProperty == null) throw new ArgumentNullException(nameof(getProperty)); - - _getParent = new Lazy(() => getParent(ParentId)); - _getChildren = new Lazy>(() => getChildren(Id, nav)); - _getProperty = getProperty; - _cacheProvider = cacheProvider; - - LoadedFromExamine = fromExamine; - - ValidateAndSetProperty(valueDictionary, val => _id = int.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int! - ValidateAndSetProperty(valueDictionary, val => _key = Guid.Parse(val), "key"); - //ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId"); - ValidateAndSetProperty(valueDictionary, val => _sortOrder = int.Parse(val), "sortOrder"); - ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName", "__nodeName"); - ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName"); - ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", LuceneIndexer.ItemTypeFieldName); - ValidateAndSetProperty(valueDictionary, val => _documentTypeId = int.Parse(val), "nodeType"); - //ValidateAndSetProperty(valueDictionary, val => _writerName = val, "writerName"); - ValidateAndSetProperty(valueDictionary, val => _creatorName = val, "creatorName", "writerName"); //this is a bit of a hack fix for: U4-1132 - //ValidateAndSetProperty(valueDictionary, val => _writerId = int.Parse(val), "writerID"); - ValidateAndSetProperty(valueDictionary, val => _creatorId = int.Parse(val), "creatorID", "writerID"); //this is a bit of a hack fix for: U4-1132 - ValidateAndSetProperty(valueDictionary, val => _path = val, "path", "__Path"); - ValidateAndSetProperty(valueDictionary, val => _createDate = ParseDateTimeValue(val), "createDate"); - ValidateAndSetProperty(valueDictionary, val => _updateDate = ParseDateTimeValue(val), "updateDate"); - ValidateAndSetProperty(valueDictionary, val => _level = int.Parse(val), "level"); - ValidateAndSetProperty(valueDictionary, val => - { - int pId; - ParentId = -1; - if (int.TryParse(val, out pId)) - { - ParentId = pId; - } - }, "parentID"); - - _contentType = contentTypeCache.Get(PublishedItemType.Media, _documentTypeAlias); - _properties = new Collection(); - - //handle content type properties - //make sure we create them even if there's no value - foreach (var propertyType in _contentType.PropertyTypes) - { - var alias = propertyType.Alias; - _keysAdded.Add(alias); - string value; - const bool isPreviewing = false; // false :: never preview a media - var property = valueDictionary.TryGetValue(alias, out value) == false || value == null - ? new XmlPublishedProperty(propertyType, this, isPreviewing) - : new XmlPublishedProperty(propertyType, this, isPreviewing, value); - _properties.Add(property); - } - - //loop through remaining values that haven't been applied - foreach (var i in valueDictionary.Where(x => - _keysAdded.Contains(x.Key) == false // not already processed - && IgnoredKeys.Contains(x.Key) == false)) // not ignorable - { - if (i.Key.InvariantStartsWith("__")) - { - // no type for that one, dunno how to convert, drop it - //IPublishedProperty property = new PropertyResult(i.Key, i.Value, PropertyResultType.CustomProperty); - //_properties.Add(property); - } - else - { - // this is a property that does not correspond to anything, ignore and log - Current.Logger.Warn("Dropping property \"" + i.Key + "\" because it does not belong to the content type."); - } - } - } - - private DateTime ParseDateTimeValue(string val) - { - if (LoadedFromExamine == false) - return DateTime.Parse(val); - - //we need to parse the date time using Lucene converters - var ticks = long.Parse(val); - return new DateTime(ticks); - } - - /// - /// Flag to get/set if this was laoded from examine cache - /// - internal bool LoadedFromExamine { get; } - - //private readonly Func _getParent; - private readonly Lazy _getParent; - //private readonly Func> _getChildren; - private readonly Lazy> _getChildren; - private readonly Func _getProperty; - private readonly ICacheProvider _cacheProvider; - - /// - /// Returns 'Media' as the item type - /// - public override PublishedItemType ItemType => PublishedItemType.Media; - - public override IPublishedContent Parent => _getParent.Value; - - public int ParentId { get; private set; } - - public override int Id => _id; - - public override Guid Key => _key; - - public override int TemplateId => 0; - - public override int SortOrder => _sortOrder; - - public override string Name => _name; - - public override IReadOnlyDictionary CultureNames => throw new NotSupportedException(); - - public override string UrlName => _urlName; - - public override string DocumentTypeAlias => _documentTypeAlias; - - public override int DocumentTypeId => _documentTypeId; - - public override string WriterName => _creatorName; - - public override string CreatorName => _creatorName; - - public override int WriterId => _creatorId; - - public override int CreatorId => _creatorId; - - public override string Path => _path; - - public override DateTime CreateDate => _createDate; - - public override DateTime UpdateDate => _updateDate; - - public override int Level => _level; - - public override bool IsDraft => false; - - public override IEnumerable Properties => _properties; - - public override IEnumerable Children => _getChildren.Value; - - public override IPublishedProperty GetProperty(string alias) - { - return _getProperty(this, alias); - } - - public override PublishedContentType ContentType => _contentType; - - // override to implement cache - // cache at context level, ie once for the whole request - // but cache is not shared by requests because we wouldn't know how to clear it - public override IPublishedProperty GetProperty(string alias, bool recurse) - { - if (recurse == false) return GetProperty(alias); - - var key = $"XmlPublishedCache.PublishedMediaCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}"; - var cacheProvider = _cacheProvider; - return cacheProvider.GetCacheItem(key, () => base.GetProperty(alias, true)); - } - - private readonly List _keysAdded = new List(); - private int _id; - private Guid _key; - //private int _templateId; - private int _sortOrder; - private string _name; - private string _urlName; - private string _documentTypeAlias; - private int _documentTypeId; - //private string _writerName; - private string _creatorName; - //private int _writerId; - private int _creatorId; - private string _path; - private DateTime _createDate; - private DateTime _updateDate; - //private Guid _version; - private int _level; - private readonly ICollection _properties; - private readonly PublishedContentType _contentType; - - private void ValidateAndSetProperty(IReadOnlyDictionary valueDictionary, Action setProperty, params string[] potentialKeys) - { - var key = potentialKeys.FirstOrDefault(x => valueDictionary.ContainsKey(x) && valueDictionary[x] != null); - if (key == null) - { - throw new FormatException("The valueDictionary is not formatted correctly and is missing any of the '" + string.Join(",", potentialKeys) + "' elements"); - } - - setProperty(valueDictionary[key]); - _keysAdded.Add(key); - } - } - internal void Resync() { // clear recursive properties cached by XmlPublishedContent.GetProperty diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs index 6f9746c57d..eba0312a46 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs @@ -150,25 +150,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache } } - public override IReadOnlyDictionary CultureNames => throw new NotSupportedException(); + public override PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); - public override string DocumentTypeAlias - { - get - { - if (_nodeInitialized == false) InitializeNode(); - return _docTypeAlias; - } - } - - public override int DocumentTypeId - { - get - { - if (_nodeInitialized == false) InitializeNode(); - return _docTypeId; - } - } + public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); public override string WriterName { diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index fd2ddc51e5..c6d39aa0b0 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -122,8 +122,8 @@ namespace Umbraco.Web #endregion #region Value - - // fixme missing variations, but recurse/variations/fallback = ? + + // fixme missing variations, but recurse/variations/fallback = ? /// /// Recursively gets the value of a content's property identified by its alias. @@ -163,7 +163,7 @@ namespace Umbraco.Web /// The alias is case-insensitive. /// public static object Value(this IPublishedContent content, string alias, string culture = ".", string segment = ".", object defaultValue = default, bool recurse = false) - { + { // fixme - variations+recurse not implemented here var property = content.GetProperty(alias, recurse); return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(); @@ -172,7 +172,7 @@ namespace Umbraco.Web #endregion #region Value - + /// /// Recursively gets the value of a content's property identified by its alias, converted to a specified type. /// @@ -290,7 +290,7 @@ namespace Umbraco.Web /// True if the content is of the specified content type; otherwise false. public static bool IsDocumentType(this IPublishedContent content, string docTypeAlias) { - return content.DocumentTypeAlias.InvariantEquals(docTypeAlias); + return content.ContentType.Alias.InvariantEquals(docTypeAlias); } /// @@ -477,7 +477,7 @@ namespace Umbraco.Web /// Does not consider the content itself. Returns all ancestors, of the specified content type. public static IEnumerable Ancestors(this IPublishedContent content, string contentTypeAlias) { - return content.AncestorsOrSelf(false, n => n.DocumentTypeAlias == contentTypeAlias); + return content.AncestorsOrSelf(false, n => n.ContentType.Alias == contentTypeAlias); } /// @@ -542,7 +542,7 @@ namespace Umbraco.Web /// May or may not begin with the content itself, depending on its content type. public static IEnumerable AncestorsOrSelf(this IPublishedContent content, string contentTypeAlias) { - return content.AncestorsOrSelf(true, n => n.DocumentTypeAlias == contentTypeAlias); + return content.AncestorsOrSelf(true, n => n.ContentType.Alias == contentTypeAlias); } /// @@ -605,7 +605,7 @@ namespace Umbraco.Web /// Does not consider the content itself. May return null. public static IPublishedContent Ancestor(this IPublishedContent content, string contentTypeAlias) { - return content.EnumerateAncestors(false).FirstOrDefault(x => x.DocumentTypeAlias == contentTypeAlias); + return content.EnumerateAncestors(false).FirstOrDefault(x => x.ContentType.Alias == contentTypeAlias); } /// @@ -668,7 +668,7 @@ namespace Umbraco.Web /// May or may not return the content itself depending on its content type. May return null. public static IPublishedContent AncestorOrSelf(this IPublishedContent content, string contentTypeAlias) { - return content.EnumerateAncestors(true).FirstOrDefault(x => x.DocumentTypeAlias == contentTypeAlias); + return content.EnumerateAncestors(true).FirstOrDefault(x => x.ContentType.Alias == contentTypeAlias); } /// @@ -781,7 +781,7 @@ namespace Umbraco.Web public static IEnumerable Descendants(this IPublishedContent content, string contentTypeAlias) { - return content.DescendantsOrSelf(false, p => p.DocumentTypeAlias == contentTypeAlias); + return content.DescendantsOrSelf(false, p => p.ContentType.Alias == contentTypeAlias); } public static IEnumerable Descendants(this IPublishedContent content) @@ -808,7 +808,7 @@ namespace Umbraco.Web public static IEnumerable DescendantsOrSelf(this IPublishedContent content, string contentTypeAlias) { - return content.DescendantsOrSelf(true, p => p.DocumentTypeAlias == contentTypeAlias); + return content.DescendantsOrSelf(true, p => p.ContentType.Alias == contentTypeAlias); } public static IEnumerable DescendantsOrSelf(this IPublishedContent content) @@ -835,7 +835,7 @@ namespace Umbraco.Web public static IPublishedContent Descendant(this IPublishedContent content, string contentTypeAlias) { - return content.EnumerateDescendants(false).FirstOrDefault(x => x.DocumentTypeAlias == contentTypeAlias); + return content.EnumerateDescendants(false).FirstOrDefault(x => x.ContentType.Alias == contentTypeAlias); } public static T Descendant(this IPublishedContent content) @@ -862,7 +862,7 @@ namespace Umbraco.Web public static IPublishedContent DescendantOrSelf(this IPublishedContent content, string contentTypeAlias) { - return content.EnumerateDescendants(true).FirstOrDefault(x => x.DocumentTypeAlias == contentTypeAlias); + return content.EnumerateDescendants(true).FirstOrDefault(x => x.ContentType.Alias == contentTypeAlias); } public static T DescendantOrSelf(this IPublishedContent content) @@ -1018,7 +1018,7 @@ namespace Umbraco.Web /// The children of the content, of any of the specified types. public static IEnumerable Children(this IPublishedContent content, params string[] alias) { - return content.Children(x => alias.InvariantContains(x.DocumentTypeAlias)); + return content.Children(x => alias.InvariantContains(x.ContentType.Alias)); } /// @@ -1094,14 +1094,14 @@ namespace Umbraco.Web ? content.Children.Any() ? content.Children.ElementAt(0) : null - : content.Children.FirstOrDefault(x => x.DocumentTypeAlias == contentTypeAliasFilter); + : content.Children.FirstOrDefault(x => x.ContentType.Alias == contentTypeAliasFilter); if (firstNode == null) return new DataTable(); //no children found //use new utility class to create table so that we don't have to maintain code in many places, just one var dt = Core.DataTableExtensions.GenerateDataTable( //pass in the alias of the first child node since this is the node type we're rendering headers for - firstNode.DocumentTypeAlias, + firstNode.ContentType.Alias, //pass in the callback to extract the Dictionary of all defined aliases to their names alias => GetPropertyAliasesAndNames(services, alias), //pass in a callback to populate the datatable, yup its a bit ugly but it's already legacy and we just want to maintain code in one place. @@ -1114,7 +1114,7 @@ namespace Umbraco.Web { if (contentTypeAliasFilter.IsNullOrWhiteSpace() == false) { - if (n.DocumentTypeAlias != contentTypeAliasFilter) + if (n.ContentType.Alias != contentTypeAliasFilter) continue; //skip this one, it doesn't match the filter } @@ -1122,7 +1122,7 @@ namespace Umbraco.Web { { "Id", n.Id }, { "NodeName", n.Name }, - { "NodeTypeAlias", n.DocumentTypeAlias }, + { "NodeTypeAlias", n.ContentType.Alias }, { "CreateDate", n.CreateDate }, { "UpdateDate", n.UpdateDate }, { "CreatorName", n.CreatorName }, @@ -1208,33 +1208,5 @@ namespace Umbraco.Web } #endregion - - #region Culture - - /// - /// Return the URL name for the based on the culture specified or default culture defined - /// - /// - /// - /// - /// - public static string GetUrlName(this IPublishedContent content, ILocalizationService localizationService, string culture = null) - { - // fixme publishedContent could get ISystemDefaultCultureAccessor injected! - - if (content.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) - { - var cultureCode = culture ?? localizationService.GetDefaultLanguageIsoCode(); // fixme kill.kill.kill - if (cultureCode != null && content.CultureNames.TryGetValue(cultureCode, out var cultureName)) - { - return cultureName.UrlName; - } - } - - //if we get here, the content type is invariant or we don't have access to a usable culture code - return content.UrlName; - } - - #endregion } } diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 17078826ff..41d4d4883c 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -24,6 +24,7 @@ using Umbraco.Core.Dictionary; using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Macros; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Profiling; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; @@ -69,8 +70,10 @@ namespace Umbraco.Web.Runtime //it still needs to use the install controller so we can't do that composition.Container.RegisterFrom(); - // register the system culture provider + // register accessors for cultures + // fixme merge the two accessors? composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton(); var typeLoader = composition.Container.GetInstance(); var logger = composition.Container.GetInstance(); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e0380c898e..e730c0b29c 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -391,6 +391,7 @@ + diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index 61f263f02c..e49bdff9ec 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -65,7 +65,7 @@ namespace umbraco throw new ArgumentException("Document request has no node.", "frequest"); populatePageData(frequest.PublishedContent.Id, - frequest.PublishedContent.Name, frequest.PublishedContent.DocumentTypeId, frequest.PublishedContent.DocumentTypeAlias, + frequest.PublishedContent.Name, frequest.PublishedContent.ContentType.Id, frequest.PublishedContent.ContentType.Alias, frequest.PublishedContent.WriterName, frequest.PublishedContent.CreatorName, frequest.PublishedContent.CreateDate, frequest.PublishedContent.UpdateDate, frequest.PublishedContent.Path, frequest.PublishedContent.Parent == null ? -1 : frequest.PublishedContent.Parent.Id); @@ -89,7 +89,7 @@ namespace umbraco if (doc == null) throw new ArgumentNullException("doc"); populatePageData(doc.Id, - doc.Name, doc.DocumentTypeId, doc.DocumentTypeAlias, + doc.Name, doc.ContentType.Id, doc.ContentType.Alias, doc.WriterName, doc.CreatorName, doc.CreateDate, doc.UpdateDate, doc.Path, doc.Parent == null ? -1 : doc.Parent.Id); @@ -108,8 +108,8 @@ namespace umbraco /// /// The content. /// This is for usage only. - internal page(IContent content) - : this(new PagePublishedContent(content)) + internal page(IContent content, IPublishedVariationContextAccessor variationContextAccessor) + : this(new PagePublishedContent(content, variationContextAccessor)) { } #endregion @@ -408,19 +408,24 @@ namespace umbraco private readonly PublishedContentType _contentType; private readonly IPublishedProperty[] _properties; private readonly IPublishedContent _parent; - private IReadOnlyDictionary _cultureNames; + private IReadOnlyDictionary _cultureNames; + private IReadOnlyDictionary _cultureInfos; + private readonly IPublishedVariationContextAccessor _variationContextAccessor; + + private static readonly IReadOnlyDictionary NoCultureInfos = new Dictionary(); private PagePublishedContent(int id) { _id = id; } - public PagePublishedContent(IContent inner) + public PagePublishedContent(IContent inner, IPublishedVariationContextAccessor variationContextAccessor) { if (inner == null) throw new NullReferenceException("content"); _inner = inner; + _variationContextAccessor = variationContextAccessor; _id = _inner.Id; _key = _inner.Key; @@ -472,23 +477,31 @@ namespace umbraco get { return _inner.Name; } } - public IReadOnlyDictionary CultureNames + public PublishedCultureInfos GetCulture(string culture = ".") { - get - { - if (!_inner.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) - return null; - - if (_cultureNames == null) - { - var d = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - foreach (var c in _inner.Names) - { - d[c.Key] = new PublishedCultureName(c.Value, c.Value.ToUrlSegment()); - } - _cultureNames = d; - } - return _cultureNames; + // handle context culture + if (culture == ".") + culture = _variationContextAccessor.Context.Culture; + + // no invariant culture infos + if (culture == null) return null; + + // get + return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null; + } + + public IReadOnlyDictionary Cultures + { + get + { + if (!_inner.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) // fixme CultureSegment? + return NoCultureInfos; + + if (_cultureInfos != null) + return _cultureInfos; + + return _cultureInfos = _inner.Names + .ToDictionary(x => x.Key, x => new PublishedCultureInfos(x.Key, x.Value, false, DateTime.MinValue)); // fixme values! } } From 6959e595f56be770bab8b41b6c8932fe712057ce Mon Sep 17 00:00:00 2001 From: Stephan Date: Sat, 28 Apr 2018 16:35:33 +0200 Subject: [PATCH 05/22] IPublishedContent.UrlSegment --- .../Models/PublishedContent/IPublishedContent.cs | 2 +- .../Models/PublishedContent/PublishedContentWrapped.cs | 2 +- .../Cache/PublishedCache/PublishedMediaCacheTests.cs | 2 +- src/Umbraco.Tests/Published/NestedContentTests.cs | 2 +- .../PublishedContent/PublishedContentDataTableTests.cs | 4 ++-- .../PublishedContent/PublishedContentMoreTests.cs | 6 +++--- .../PublishedContent/SolidPublishedSnapshot.cs | 2 +- src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs | 2 +- src/Umbraco.Web/Models/PublishedContentBase.cs | 2 +- .../PublishedCache/NuCache/Navigable/NavigableContent.cs | 2 +- src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs | 2 +- src/Umbraco.Web/PublishedCache/PublishedMember.cs | 2 +- .../XmlPublishedCache/DictionaryPublishedContent.cs | 2 +- .../XmlPublishedCache/PublishedContentCache.cs | 2 +- .../PublishedCache/XmlPublishedCache/XmlPublishedContent.cs | 2 +- src/Umbraco.Web/umbraco.presentation/page.cs | 2 +- 16 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 23f5bbff8d..e8de7efeda 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -44,7 +44,7 @@ namespace Umbraco.Core.Models.PublishedContent /// this is the name for the 'current' culture. /// /// FIXME rename UrlSegment + culture aware - string UrlName { get; } // fixme rename, segment! + string UrlSegment { get; } /// /// Gets the sort order of the content item. diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index f8a4e16bd2..8d4888cf25 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -61,7 +61,7 @@ namespace Umbraco.Core.Models.PublishedContent public virtual string Name => _content.Name; /// - public virtual string UrlName => _content.UrlName; + public virtual string UrlSegment => _content.UrlSegment; /// public virtual int SortOrder => _content.SortOrder; diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index 78a75af835..64194ebb47 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -393,7 +393,7 @@ namespace Umbraco.Tests.Cache.PublishedCache Assert.AreEqual(keyVal, doc.Key); Assert.AreEqual(templateIdVal, doc.TemplateId); Assert.AreEqual(sortOrderVal, doc.SortOrder); - Assert.AreEqual(urlNameVal, doc.UrlName); + Assert.AreEqual(urlNameVal, doc.UrlSegment); Assert.AreEqual(nodeTypeAliasVal, doc.ContentType.Alias); Assert.AreEqual(nodeTypeIdVal, doc.ContentType.Id); Assert.AreEqual(writerNameVal, doc.WriterName); diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index d86b7f0e6b..ea5fa6f7e6 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -275,7 +275,7 @@ namespace Umbraco.Tests.Published public override string Name { get; } public override PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public override string UrlName { get; } + public override string UrlSegment { get; } public override string WriterName { get; } public override string CreatorName { get; } public override int WriterId { get; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 56c60446ef..af63d2244b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -142,7 +142,7 @@ namespace Umbraco.Tests.PublishedContent TemplateId = 5, UpdateDate = DateTime.Now, Path = "-1,3", - UrlName = "home-page", + UrlSegment = "home-page", Name = "Page" + Guid.NewGuid().ToString(), Version = Guid.NewGuid(), WriterId = 1, @@ -207,7 +207,7 @@ namespace Umbraco.Tests.PublishedContent public string Name { get; set; } public PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); public IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public string UrlName { get; set; } + public string UrlSegment { get; set; } public string WriterName { get; set; } public string CreatorName { get; set; } public int WriterId { get; set; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 61c37d6a51..465034db05 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -220,7 +220,7 @@ namespace Umbraco.Tests.PublishedContent Id = 1, SortOrder = 0, Name = "Content 1", - UrlName = "content-1", + UrlSegment = "content-1", Path = "/1", Level = 1, Url = "/content-1", @@ -243,7 +243,7 @@ namespace Umbraco.Tests.PublishedContent Id = 2, SortOrder = 1, Name = "Content 2", - UrlName = "content-2", + UrlSegment = "content-2", Path = "/2", Level = 1, Url = "/content-2", @@ -266,7 +266,7 @@ namespace Umbraco.Tests.PublishedContent Id = 3, SortOrder = 2, Name = "Content 2Sub", - UrlName = "content-2sub", + UrlSegment = "content-2sub", Path = "/3", Level = 1, Url = "/content-2sub", diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 307ef459fb..38ba7e771b 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -178,7 +178,7 @@ namespace Umbraco.Tests.PublishedContent public string Name { get; set; } public PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); public IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public string UrlName { get; set; } + public string UrlSegment { get; set; } public string WriterName { get; set; } public string CreatorName { get; set; } public int WriterId { get; set; } diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 042794e573..14a0979091 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null; } public IReadOnlyDictionary Cultures { get; set; } - public string UrlName { get; set; } + public string UrlSegment { get; set; } public string DocumentTypeAlias => ContentType.Alias; public int DocumentTypeId { get; set; } public string WriterName { get; set; } diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 084ca1fee2..0fe7bc0fc7 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -43,7 +43,7 @@ namespace Umbraco.Web.Models public abstract string Name { get; } /// - public abstract string UrlName { get; } + public abstract string UrlSegment { get; } /// public abstract int SortOrder { get; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs index 2357d17273..2fe7a26d45 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Navigable/NavigableContent.cs @@ -28,7 +28,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.Navigable XmlString(i++, _content.TemplateId), XmlString(i++, _content.WriterId), XmlString(i++, _content.CreatorId), - XmlString(i++, _content.UrlName), + XmlString(i++, _content.UrlSegment), XmlString(i, _content.IsDraft) }; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index d2a4e2f539..5b6c8ca783 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -191,7 +191,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } /// - public override string UrlName + public override string UrlSegment { get { diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index 084daf6457..8983f1e152 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -148,7 +148,7 @@ namespace Umbraco.Web.PublishedCache public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public override string UrlName => throw new NotSupportedException(); + public override string UrlSegment => throw new NotSupportedException(); //TODO: ARGH! need to fix this - this is not good because it uses ApplicationContext.Current public override string WriterName => _member.GetCreatorProfile().Name; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs index 5ef600ba76..b72425085e 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs @@ -158,7 +158,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public override string UrlName => _urlName; + public override string UrlSegment => _urlName; public override string WriterName => _creatorName; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index 57d9e0a980..7aac1ed017 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -266,7 +266,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache while (hasDomains == false && n != null) // n is null at root { // get the url - var urlName = n.UrlName; + var urlName = n.UrlSegment; pathParts.Add(urlName); // move to parent node diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs index eba0312a46..3ad30b4d3f 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs @@ -217,7 +217,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache } } - public override string UrlName + public override string UrlSegment { get { diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index e49bdff9ec..b37a5d42e0 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -505,7 +505,7 @@ namespace umbraco } } - public string UrlName + public string UrlSegment { get { throw new NotImplementedException(); } } From 7dd1efb29f16477a341f16198fc46016dfddeed2 Mon Sep 17 00:00:00 2001 From: Stephan Date: Sat, 28 Apr 2018 21:57:07 +0200 Subject: [PATCH 06/22] Refactor content culture & variations --- src/Umbraco.Core/Models/Content.cs | 3 +- src/Umbraco.Core/Models/ContentBase.cs | 65 +++++++++++++------ src/Umbraco.Core/Models/IContent.cs | 2 +- src/Umbraco.Core/Models/IContentBase.cs | 14 ++-- ...ultureName.cs => PublishedCultureInfos.cs} | 15 ----- .../Implement/DocumentRepository.cs | 6 +- src/Umbraco.Core/Umbraco.Core.csproj | 2 +- src/Umbraco.Tests/Models/VariationTests.cs | 6 +- .../PublishedContentOtherTests.cs | 63 ++++++++++++++---- .../Services/ContentServiceTests.cs | 24 +++---- ....DictionaryOfCultureVariationSerializer.cs | 3 +- .../NuCache/DataSource/ContentData.cs | 16 ++--- .../NuCache/DataSource/CultureVariation.cs | 6 +- .../PublishedCache/NuCache/Property.cs | 10 ++- .../NuCache/PublishedContent.cs | 3 +- .../NuCache/PublishedSnapshotService.cs | 12 ++-- src/Umbraco.Web/umbraco.presentation/page.cs | 5 +- 17 files changed, 156 insertions(+), 99 deletions(-) rename src/Umbraco.Core/Models/PublishedContent/{PublishedCultureName.cs => PublishedCultureInfos.cs} (78%) diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 8bfb5718a2..73f462b65d 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -225,7 +225,6 @@ namespace Umbraco.Core.Models /// [IgnoreDataMember] - //public IReadOnlyDictionary PublishNames => _publishNames ?? NoNames; public IReadOnlyDictionary PublishNames => _publishInfos?.ToDictionary(x => x.Key, x => x.Value.Name) ?? NoNames; /// @@ -263,7 +262,7 @@ namespace Umbraco.Core.Models => !string.IsNullOrWhiteSpace(GetPublishName(culture)); /// - public DateTime GetDateCulturePublished(string culture) + public DateTime GetCulturePublishDate(string culture) { if (_publishInfos != null && _publishInfos.TryGetValue(culture, out var infos)) return infos.Date; diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 7bc327c2d0..b40e11e25d 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Web; +using Umbraco.Core.Exceptions; using Umbraco.Core.Models.Entities; namespace Umbraco.Core.Models @@ -25,7 +26,7 @@ namespace Umbraco.Core.Models protected IContentTypeComposition ContentTypeBase; private int _writerId; private PropertyCollection _properties; - private Dictionary _names; + private Dictionary _cultureInfos; /// /// Initializes a new instance of the class. @@ -139,16 +140,30 @@ namespace Umbraco.Core.Models /// [DataMember] - public virtual IReadOnlyDictionary Names - { - get => _names ?? NoNames; - set - { - foreach (var (culture, name) in value) - SetName(culture, name); - } - } + public virtual IReadOnlyDictionary Names => _cultureInfos?.ToDictionary(x => x.Key, x => x.Value.Name) ?? NoNames; + // sets culture infos + // internal for repositories + // clear by clearing name + internal void SetCultureInfos(string culture, string name, DateTime date) + { + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentNullOrEmptyException(nameof(name)); + + if (culture == null) + { + Name = name; + return; + } + + // private method, assume that culture is valid + + if (_cultureInfos == null) + _cultureInfos = new Dictionary(StringComparer.OrdinalIgnoreCase); + + _cultureInfos[culture] = (name, date); + } + /// public virtual void SetName(string culture, string name) { @@ -166,11 +181,11 @@ namespace Umbraco.Core.Models if (!ContentTypeBase.Variations.HasAny(ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) throw new NotSupportedException("Content type does not support varying name by culture."); + + if (_cultureInfos == null) + _cultureInfos = new Dictionary(StringComparer.OrdinalIgnoreCase); - if (_names == null) - _names = new Dictionary(StringComparer.OrdinalIgnoreCase); - - _names[culture] = name; + _cultureInfos[culture] = (name, DateTime.Now) ; OnPropertyChanged(Ps.Value.NamesSelector); } @@ -178,8 +193,8 @@ namespace Umbraco.Core.Models public virtual string GetName(string culture) { if (culture == null) return Name; - if (_names == null) return null; - return _names.TryGetValue(culture, out var name) ? name : null; + if (_cultureInfos == null) return null; + return _cultureInfos.TryGetValue(culture, out var infos) ? infos.Name : null; } /// @@ -194,17 +209,25 @@ namespace Umbraco.Core.Models return; } - if (_names == null) return; - _names.Remove(culture); - if (_names.Count == 0) - _names = null; + if (_cultureInfos == null) return; + _cultureInfos.Remove(culture); + if (_cultureInfos.Count == 0) + _cultureInfos = null; } protected virtual void ClearNames() { - _names = null; + _cultureInfos = null; OnPropertyChanged(Ps.Value.NamesSelector); } + + /// + public DateTime GetCultureDate(string culture) + { + if (_cultureInfos != null && _cultureInfos.TryGetValue(culture, out var infos)) + return infos.Date; + throw new InvalidOperationException($"Culture \"{culture}\" is not available."); + } #endregion diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index 2f3fd06f5f..59ff548e16 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -93,7 +93,7 @@ namespace Umbraco.Core.Models /// /// Gets the date a culture was published. /// - DateTime GetDateCulturePublished(string culture); + DateTime GetCulturePublishDate(string culture); /// /// Gets a value indicated whether a given culture is edited. diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs index 93a6e82ada..b6e05fa7dd 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Core/Models/IContentBase.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Core.Models.Entities; namespace Umbraco.Core.Models @@ -46,13 +47,13 @@ namespace Umbraco.Core.Models string GetName(string culture); /// - /// Gets or sets the names of the content item. + /// Gets the names of the content item. /// /// - /// Because a dictionary key cannot be null this cannot get nor set the invariant + /// Because a dictionary key cannot be null this cannot get the invariant /// name, which must be get or set via the property. /// - IReadOnlyDictionary Names { get; set; } + IReadOnlyDictionary Names { get; } /// /// Gets a value indicating whether a given culture is available. @@ -63,6 +64,11 @@ namespace Umbraco.Core.Models /// bool IsCultureAvailable(string culture); + /// + /// Gets the date a culture was created. + /// + DateTime GetCultureDate(string culture); + /// /// List of properties, which make up all the data available for this Content object /// diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs similarity index 78% rename from src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs rename to src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs index 898649f2e5..7a563544a4 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs @@ -3,21 +3,6 @@ using Umbraco.Core.Exceptions; namespace Umbraco.Core.Models.PublishedContent { - /// - /// Contains the culture specific data for a item - /// - public struct PublishedCultureName - { - public PublishedCultureName(string name, string urlName) : this() - { - Name = name ?? throw new ArgumentNullException(nameof(name)); - UrlName = urlName ?? throw new ArgumentNullException(nameof(urlName)); - } - - public string Name { get; } - public string UrlName { get; } - } - /// /// Contains culture specific values for . /// diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index 76321abe6a..a9eed7edb1 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -945,7 +945,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { if (contentVariations.TryGetValue(content.VersionId, out var contentVariation)) foreach (var v in contentVariation) - content.SetName(v.Culture, v.Name); + content.SetCultureInfos(v.Culture, v.Name, v.Date); if (content.PublishedVersionId > 0 && contentVariations.TryGetValue(content.PublishedVersionId, out contentVariation)) foreach (var v in contentVariation) content.SetPublishInfos(v.Culture, v.Name, v.Date); @@ -1028,7 +1028,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), Culture = culture, Name = name, - Date = content.UpdateDate + Date = content.GetCultureDate(culture) }; // if not publishing, we're just updating the 'current' (non-published) version, @@ -1043,7 +1043,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), Culture = culture, Name = name, - Date = content.GetDateCulturePublished(culture) + Date = content.GetCulturePublishDate(culture) }; } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 7d6f27083b..954391c8a6 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -368,7 +368,7 @@ - + diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index bc0891e397..8502af0e68 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -336,19 +336,19 @@ namespace Umbraco.Tests.Models Assert.IsTrue(content.IsCultureAvailable(langFr)); Assert.IsTrue(content.IsCulturePublished(langFr)); Assert.AreEqual("name-fr", content.GetPublishName(langFr)); - Assert.AreNotEqual(DateTime.MinValue, content.GetDateCulturePublished(langFr)); + Assert.AreNotEqual(DateTime.MinValue, content.GetCulturePublishDate(langFr)); Assert.IsFalse(content.IsCultureEdited(langFr)); // once published, edited is *wrong* until saved Assert.IsTrue(content.IsCultureAvailable(langUk)); Assert.IsFalse(content.IsCulturePublished(langUk)); Assert.IsNull(content.GetPublishName(langUk)); - Assert.Throws(() => content.GetDateCulturePublished(langUk)); // not published! + Assert.Throws(() => content.GetCulturePublishDate(langUk)); // not published! Assert.IsTrue(content.IsCultureEdited(langEs)); // not published, so... edited Assert.IsFalse(content.IsCultureAvailable(langEs)); Assert.IsFalse(content.IsCulturePublished(langEs)); Assert.IsNull(content.GetPublishName(langEs)); - Assert.Throws(() => content.GetDateCulturePublished(langEs)); // not published! + Assert.Throws(() => content.GetCulturePublishDate(langEs)); // not published! Assert.IsTrue(content.IsCultureEdited(langEs)); // not published, so... edited // cannot test IsCultureEdited here - as that requires the content service and repository diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs index e4fce2a3be..3aa1fbf41f 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs @@ -14,11 +14,11 @@ using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; +using Umbraco.Core.Services.Changes; using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; -using Umbraco.Web.PublishedCache; +using Umbraco.Web.Cache; using Umbraco.Web.PublishedCache.NuCache; using Umbraco.Web.PublishedCache.NuCache.DataSource; using Umbraco.Web.Routing; @@ -34,7 +34,6 @@ namespace Umbraco.Tests.PublishedContent SettingsForTests.ConfigureSettings(SettingsForTests.GenerateMockUmbracoSettings()); var globalSettings = UmbracoConfig.For.GlobalSettings(); - // fixme - missing variant names here, and what else? var kit = new ContentNodeKit { ContentTypeId = 2, @@ -45,14 +44,26 @@ namespace Umbraco.Tests.PublishedContent new PropertyData { Value = "val2" }, new PropertyData { Culture = "fr-FR", Value = "val-fr2" }, new PropertyData { Culture = "en-UK", Value = "val-uk2" } - } } } }, + } } }, + CultureInfos = new Dictionary + { + { "fr-FR", new CultureVariation { Name = "name-fr2", Date = new DateTime(2018, 01, 03, 01, 00, 00) } }, + { "en-UK", new CultureVariation { Name = "name-uk2", Date = new DateTime(2018, 01, 04, 01, 00, 00) } } + } + }, PublishedData = new ContentData { Name="It Works1!", Published = true, TemplateId = 0, VersionId = 1, VersionDate = DateTime.Now, WriterId = 0, Properties = new Dictionary { { "prop", new[] { new PropertyData { Value = "val1" }, new PropertyData { Culture = "fr-FR", Value = "val-fr1" }, new PropertyData { Culture = "en-UK", Value = "val-uk1" } - } } } } + } } }, + CultureInfos = new Dictionary + { + { "fr-FR", new CultureVariation { Name = "name-fr1", Date = new DateTime(2018, 01, 01, 01, 00, 00) } }, + { "en-UK", new CultureVariation { Name = "name-uk1", Date = new DateTime(2018, 01, 02, 01, 00, 00) } } + } + } }; var dataSource = new TestDataSource(kit); @@ -78,18 +89,16 @@ namespace Umbraco.Tests.PublishedContent var contentTypeService = Mock.Of(); Mock.Get(contentTypeService).Setup(x => x.GetAll()).Returns(contentTypes); + Mock.Get(contentTypeService).Setup(x => x.GetAll(It.IsAny())).Returns(contentTypes); var dataTypeService = Mock.Of(); Mock.Get(dataTypeService).Setup(x => x.GetAll()).Returns(dataTypes); var serviceContext = new ServiceContext( dataTypeService : dataTypeService, - memberTypeService: Mock.Of(), memberService: Mock.Of(), - contentTypeService : contentTypeService, - localizationService: Mock.Of() ); @@ -117,9 +126,6 @@ namespace Umbraco.Tests.PublishedContent var variationAccessor = new TestPublishedVariationContextAccessor(); - // invariant is the current default - variationAccessor.Context = new PublishedVariationContext(); - var options = new PublishedSnapshotService.Options { IgnoreLocalDb = true }; var snapshotService = new PublishedSnapshotService(options, null, @@ -134,7 +140,7 @@ namespace Umbraco.Tests.PublishedContent documentRepository, mediaRepository, memberRepository, - new TestSystemDefaultCultureAccessor(), + new TestSystemDefaultCultureAccessor(), dataSource, globalSettings, new SiteDomainHelper()); @@ -142,26 +148,59 @@ namespace Umbraco.Tests.PublishedContent var snapshot = snapshotService.CreatePublishedSnapshot(previewToken: null); var publishedContent = snapshot.Content.GetById(1); + // invariant is the current default + variationAccessor.Context = new PublishedVariationContext(); + Assert.IsNotNull(publishedContent); Assert.AreEqual("It Works1!", publishedContent.Name); Assert.AreEqual("val1", publishedContent.Value("prop")); Assert.AreEqual("val-fr1", publishedContent.Value("prop", "fr-FR")); Assert.AreEqual("val-uk1", publishedContent.Value("prop", "en-UK")); + Assert.AreEqual("name-fr1", publishedContent.GetCulture("fr-FR").Name); + Assert.AreEqual("name-uk1", publishedContent.GetCulture("en-UK").Name); + var draftContent = snapshot.Content.GetById(true, 1); Assert.AreEqual("It Works2!", draftContent.Name); Assert.AreEqual("val2", draftContent.Value("prop")); Assert.AreEqual("val-fr2", draftContent.Value("prop", "fr-FR")); Assert.AreEqual("val-uk2", draftContent.Value("prop", "en-UK")); + Assert.AreEqual("name-fr2", draftContent.GetCulture("fr-FR").Name); + Assert.AreEqual("name-uk2", draftContent.GetCulture("en-UK").Name); + + // now french is default variationAccessor.Context = new PublishedVariationContext("fr-FR"); Assert.AreEqual("val-fr1", publishedContent.Value("prop")); + Assert.AreEqual("name-fr1", publishedContent.GetCulture().Name); + Assert.AreEqual("name-fr1", publishedContent.Name); + Assert.AreEqual(new DateTime(2018, 01, 01, 01, 00, 00), publishedContent.GetCulture().PublishedDate); + + // now uk is default variationAccessor.Context = new PublishedVariationContext("en-UK"); Assert.AreEqual("val-uk1", publishedContent.Value("prop")); + Assert.AreEqual("name-uk1", publishedContent.GetCulture().Name); + Assert.AreEqual("name-uk1", publishedContent.Name); + Assert.AreEqual(new DateTime(2018, 01, 02, 01, 00, 00), publishedContent.GetCulture().PublishedDate); // invariant needs to be retrieved explicitely, when it's not default Assert.AreEqual("val1", publishedContent.Value("prop", culture: null)); + // but, + // if the content type / property type does not vary, then it's all invariant again + contentType.Variations = ContentVariation.InvariantNeutral; + propertyType.Variations = ContentVariation.InvariantNeutral; + snapshotService.Notify(new[] { new ContentTypeCacheRefresher.JsonPayload("IContentType", publishedContent.ContentType.Id, ContentTypeChangeTypes.RefreshMain) }); + + var anotherSnapshot = snapshotService.CreatePublishedSnapshot(previewToken: null); + var againContent = anotherSnapshot.Content.GetById(1); + + Assert.AreEqual(ContentVariation.InvariantNeutral, againContent.ContentType.Variations); + Assert.AreEqual(ContentVariation.InvariantNeutral, againContent.ContentType.GetPropertyType("prop").Variations); + + Assert.AreEqual("It Works1!", againContent.Name); + Assert.AreEqual("val1", againContent.Value("prop")); + // then, test fallback } diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 8bb5f1bc9e..47f1d49c2a 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -2583,8 +2583,8 @@ namespace Umbraco.Tests.Services AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false), (langDe, true)); AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false), (langDe, true)); - AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw - AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw + AssertPerCulture(content, (x, c) => x.GetCulturePublishDate(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw + AssertPerCulture(content2, (x, c) => x.GetCulturePublishDate(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw // note that content and content2 culture published dates might be slightly different due to roundtrip to database @@ -2644,8 +2644,8 @@ namespace Umbraco.Tests.Services AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); - AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw - AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw + AssertPerCulture(content, (x, c) => x.GetCulturePublishDate(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw + AssertPerCulture(content2, (x, c) => x.GetCulturePublishDate(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw // act @@ -2687,8 +2687,8 @@ namespace Umbraco.Tests.Services AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); - AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw - AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content, (x, c) => x.GetCulturePublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content2, (x, c) => x.GetCulturePublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw // act @@ -2734,8 +2734,8 @@ namespace Umbraco.Tests.Services AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); - AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw - AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content, (x, c) => x.GetCulturePublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content2, (x, c) => x.GetCulturePublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw // act @@ -2774,8 +2774,8 @@ namespace Umbraco.Tests.Services AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); - AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw - AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content, (x, c) => x.GetCulturePublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content2, (x, c) => x.GetCulturePublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw // act @@ -2797,8 +2797,8 @@ namespace Umbraco.Tests.Services AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, true)); AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, true)); - AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw - AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content, (x, c) => x.GetCulturePublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content2, (x, c) => x.GetCulturePublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw // act diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs index a835886254..c9d809714d 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource for (var i = 0; i < pcount; i++) { var languageId = PrimitiveSerializer.String.ReadFrom(stream); - var cultureVariation = new CultureVariation { Name = ReadStringObject(stream) }; + var cultureVariation = new CultureVariation { Name = ReadStringObject(stream), Date = ReadDateTime(stream) }; dict[languageId] = cultureVariation; } return dict; @@ -40,6 +40,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource PrimitiveSerializer.String.WriteTo(culture, stream); // should never be null WriteObject(variation.Name, stream); // write an object in case it's null (though... should not happen) + PrimitiveSerializer.DateTime.WriteTo(variation.Date, stream); } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs index 6841937c14..4721a1f4ca 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs @@ -6,20 +6,18 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource // represents everything that is specific to edited or published version internal class ContentData { + public string Name { get; set; } + public int VersionId { get; set; } + public DateTime VersionDate { get; set; } + public int WriterId { get; set; } + public int TemplateId { get; set; } public bool Published { get; set; } + public IDictionary Properties { get; set; } + /// /// The collection of language Id to name for the content item /// public IReadOnlyDictionary CultureInfos { get; set; } - - public string Name { get; set; } - public int VersionId { get; set; } - //TODO: This will not make a lot of sense since we'll have dates for each variant publishing, need to wait on Stephane - public DateTime VersionDate { get; set; } - public int WriterId { get; set; } - public int TemplateId { get; set; } - - public IDictionary Properties { get; set; } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs index aad416a925..50a2adaeb8 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/CultureVariation.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; namespace Umbraco.Web.PublishedCache.NuCache.DataSource { @@ -10,6 +11,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource [JsonProperty("name")] public string Name { get; set; } - //TODO: We may want some date stamps here + [JsonProperty("date")] + public DateTime Date { get; set; } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index d2f37a1488..02dfc4e934 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -2,8 +2,10 @@ using System.Collections.Generic; using System.Linq; using System.Xml.Serialization; +using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Collections; +using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Web.PublishedCache.NuCache.DataSource; @@ -19,6 +21,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly bool _isPreviewing; private readonly bool _isMember; private readonly PublishedContent _content; + private readonly ContentVariation _variations; private readonly object _locko = new object(); @@ -68,6 +71,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _isPreviewing = content.IsPreviewing; _isMember = content.ContentType.ItemType == PublishedItemType.Member; _publishedSnapshotAccessor = publishedSnapshotAccessor; + _variations = propertyType.Variations; } // clone for previewing as draft a published content that is published and has no draft @@ -82,6 +86,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _isPreviewing = true; _isMember = origin._isMember; _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; + _variations = origin._variations; } public override bool HasValue(string culture = ".", string segment = ".") => _sourceValue != null @@ -185,9 +190,10 @@ namespace Umbraco.Web.PublishedCache.NuCache if (culture != "." && segment != ".") return; // use context values + // fixme CultureSegment? var publishedVariationContext = _content.VariationContextAccessor?.Context; - if (culture == ".") culture = publishedVariationContext?.Culture; - if (segment == ".") segment = publishedVariationContext?.Segment; + if (culture == ".") culture = _variations.Has(ContentVariation.CultureNeutral) ? publishedVariationContext?.Culture : null; + if (segment == ".") segment = _variations.Has(ContentVariation.CultureNeutral) ? publishedVariationContext?.Segment : null; } public override object GetValue(string culture = ".", string segment = ".") diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 5b6c8ca783..0ca20fd625 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -19,7 +19,6 @@ namespace Umbraco.Web.PublishedCache.NuCache internal readonly ContentData _contentData; // internal for ContentNode cloning private readonly string _urlSegment; - private IReadOnlyDictionary _cultureNames; #region Constructors @@ -265,7 +264,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (_cultureInfos != null) return _cultureInfos; return _cultureInfos = _contentData.CultureInfos // fixme can it be null? - .ToDictionary(x => x.Key, x => new PublishedCultureInfos(x.Key, x.Value.Name, false, DateTime.MinValue)); // fixme values! + .ToDictionary(x => x.Key, x => new PublishedCultureInfos(x.Key, x.Value.Name, false, x.Value.Date)); // fixme values! } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index af84f54c11..b2cb8bbcf8 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -722,7 +722,7 @@ namespace Umbraco.Web.PublishedCache.NuCache Notify(_contentStore, payloads, RefreshContentTypesLocked); Notify(_mediaStore, payloads, RefreshMediaTypesLocked); - ((PublishedSnapshot)CurrentPublishedSnapshot).Resync(); + ((PublishedSnapshot)CurrentPublishedSnapshot)?.Resync(); // fixme all } private void Notify(ContentStore store, ContentTypeCacheRefresher.JsonPayload[] payloads, Action, IEnumerable, IEnumerable, IEnumerable> action) @@ -799,7 +799,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - ((PublishedSnapshot)CurrentPublishedSnapshot).Resync(); + ((PublishedSnapshot)CurrentPublishedSnapshot)?.Resync(); // fixme elsewhere! } public override void Notify(DomainCacheRefresher.JsonPayload[] payloads) @@ -900,8 +900,9 @@ namespace Umbraco.Web.PublishedCache.NuCache // contentStore is wlocked (so readable, only no new views) // and it can be wlocked by 1 thread only at a time - if (!(_serviceContext.ContentService is ContentService)) - throw new Exception("oops"); + // fixme wtf? + //if (!(_serviceContext.ContentService is ContentService)) + // throw new Exception("oops"); var refreshedIdsA = refreshedIds.ToArray(); @@ -1219,7 +1220,6 @@ namespace Umbraco.Web.PublishedCache.NuCache var cultureData = new Dictionary(); - // fixme refactor!!! var names = content is IContent document ? (published ? document.PublishNames @@ -1228,7 +1228,7 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var (culture, name) in names) { - cultureData[culture] = new CultureVariation { Name = name }; + cultureData[culture] = new CultureVariation { Name = name, Date = content.GetCultureDate(culture) }; } //the dictionary that will be serialized diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index b37a5d42e0..f65f04fb76 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -408,7 +408,6 @@ namespace umbraco private readonly PublishedContentType _contentType; private readonly IPublishedProperty[] _properties; private readonly IPublishedContent _parent; - private IReadOnlyDictionary _cultureNames; private IReadOnlyDictionary _cultureInfos; private readonly IPublishedVariationContextAccessor _variationContextAccessor; @@ -500,8 +499,8 @@ namespace umbraco if (_cultureInfos != null) return _cultureInfos; - return _cultureInfos = _inner.Names - .ToDictionary(x => x.Key, x => new PublishedCultureInfos(x.Key, x.Value, false, DateTime.MinValue)); // fixme values! + return _cultureInfos = _inner.Names // fixme names, or PublishNames? + .ToDictionary(x => x.Key, x => new PublishedCultureInfos(x.Key, x.Value, false, _inner.GetCulturePublishDate(x.Key))); // fixme values! } } From bcd0c95ec190aab51f93532164f57648ace0b213 Mon Sep 17 00:00:00 2001 From: Stephan Date: Sun, 29 Apr 2018 20:02:38 +0200 Subject: [PATCH 07/22] Refactor getting urls --- ...ariationContext.cs => CurrentVariation.cs} | 13 +- .../ICurrentVariationAccessor.cs | 13 ++ .../PublishedContent/IPublishedContent.cs | 41 ++-- .../IPublishedVariationContextAccessor.cs | 13 -- .../PublishedContentWrapped.cs | 3 + .../PublishedContent/PublishedCultureInfos.cs | 13 +- ... ThreadCultureCurrentVariationAccessor.cs} | 10 +- ...> ThreadStaticCurrentVariationAccessor.cs} | 8 +- src/Umbraco.Core/StringExtensions.cs | 3 +- .../Strings/IUrlSegmentProvider.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 8 +- ...edContentOtherTests.cs => NuCacheTests.cs} | 123 ++++------- .../PublishedContentDataTableTests.cs | 7 +- .../PublishedContentExtensionTests.cs | 28 +-- .../SolidPublishedSnapshot.cs | 1 + src/Umbraco.Tests/Routing/UrlProviderTests.cs | 20 +- .../Routing/UrlsProviderWithDomainsTests.cs | 46 ++-- .../Scoping/ScopedNuCacheTests.cs | 2 +- .../TestControllerActivatorBase.cs | 2 +- .../TestHelpers/Stubs/TestPublishedContent.cs | 5 +- .../TestHelpers/TestWithDatabaseBase.cs | 2 +- .../Accessors/TestCurrentVariationAccessor.cs | 17 ++ .../TestPublishedVariationContextAccessor.cs | 17 -- .../Testing/Objects/TestDataSource.cs | 57 +++++ .../Testing/TestingTests/MockTests.cs | 11 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 5 +- .../Web/TemplateUtilitiesTests.cs | 36 ++-- src/Umbraco.Web/Editors/MacroController.cs | 8 +- .../Models/PublishedContentBase.cs | 46 ++-- .../PublishedCache/NuCache/ContentNode.cs | 16 +- .../PublishedCache/NuCache/ContentNodeKit.cs | 4 +- .../PublishedCache/NuCache/ContentStore.cs | 12 +- .../PublishedCache/NuCache/MemberCache.cs | 14 +- .../PublishedCache/NuCache/Property.cs | 2 +- .../NuCache/PublishedContent.cs | 16 +- .../PublishedCache/NuCache/PublishedMember.cs | 8 +- .../NuCache/PublishedSnapshotService.cs | 63 ++---- .../PublishedSnapshotServiceBase.cs | 6 +- .../SystemDefaultCultureAccessor.cs | 4 +- .../PublishedSnapshotService.cs | 12 +- .../XmlPublishedCache/XmlCacheComponent.cs | 2 +- src/Umbraco.Web/Routing/AliasUrlProvider.cs | 29 +-- .../Routing/CustomRouteUrlProvider.cs | 26 +-- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 29 +-- src/Umbraco.Web/Routing/IUrlProvider.cs | 13 +- src/Umbraco.Web/Routing/UrlProvider.cs | 201 +++++++++--------- .../Runtime/WebRuntimeComponent.cs | 3 +- src/Umbraco.Web/umbraco.presentation/page.cs | 14 +- 48 files changed, 512 insertions(+), 522 deletions(-) rename src/Umbraco.Core/Models/PublishedContent/{PublishedVariationContext.cs => CurrentVariation.cs} (52%) create mode 100644 src/Umbraco.Core/Models/PublishedContent/ICurrentVariationAccessor.cs delete mode 100644 src/Umbraco.Core/Models/PublishedContent/IPublishedVariationContextAccessor.cs rename src/Umbraco.Core/Models/PublishedContent/{ThreadCulturePublishedVariationContextAccessor.cs => ThreadCultureCurrentVariationAccessor.cs} (54%) rename src/Umbraco.Core/Models/PublishedContent/{ThreadStaticPublishedVariationContextAccessor.cs => ThreadStaticCurrentVariationAccessor.cs} (62%) rename src/Umbraco.Tests/PublishedContent/{PublishedContentOtherTests.cs => NuCacheTests.cs} (76%) create mode 100644 src/Umbraco.Tests/Testing/Objects/Accessors/TestCurrentVariationAccessor.cs delete mode 100644 src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs create mode 100644 src/Umbraco.Tests/Testing/Objects/TestDataSource.cs diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedVariationContext.cs b/src/Umbraco.Core/Models/PublishedContent/CurrentVariation.cs similarity index 52% rename from src/Umbraco.Core/Models/PublishedContent/PublishedVariationContext.cs rename to src/Umbraco.Core/Models/PublishedContent/CurrentVariation.cs index 2440b1dc32..f52763ecf0 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedVariationContext.cs +++ b/src/Umbraco.Core/Models/PublishedContent/CurrentVariation.cs @@ -1,17 +1,14 @@ namespace Umbraco.Core.Models.PublishedContent { /// - /// Represents the published variation context. + /// Represents the current variation. /// - /// - /// The published variation context indicates which variation is the current default variation. - /// - public class PublishedVariationContext + public class CurrentVariation { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public PublishedVariationContext(string culture = null, string segment = null) + public CurrentVariation(string culture = null, string segment = null) { Culture = culture; Segment = segment; @@ -27,4 +24,4 @@ /// public string Segment { get; set; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Models/PublishedContent/ICurrentVariationAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/ICurrentVariationAccessor.cs new file mode 100644 index 0000000000..969601f080 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/ICurrentVariationAccessor.cs @@ -0,0 +1,13 @@ +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Gives access to the current . + /// + public interface ICurrentVariationAccessor + { + /// + /// Gets or sets the current . + /// + CurrentVariation CurrentVariation { get; set; } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index e8de7efeda..b3358c4676 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -31,9 +31,8 @@ namespace Umbraco.Core.Models.PublishedContent /// /// /// The value of this property is contextual. When the content type is multi-lingual, - /// this is the name for the 'current' culture. + /// this is the name for the 'current' culture. Otherwise, it is the invariant name. /// - /// FIXME culture aware - returns the value for the 'current' culture whatever it is + see ?? for others string Name { get; } /// @@ -41,9 +40,8 @@ namespace Umbraco.Core.Models.PublishedContent /// /// /// The value of this property is contextual. When the content type is multi-lingual, - /// this is the name for the 'current' culture. + /// this is the name for the 'current' culture. Otherwise, it is the invariant url segment. /// - /// FIXME rename UrlSegment + culture aware string UrlSegment { get; } /// @@ -96,7 +94,8 @@ namespace Umbraco.Core.Models.PublishedContent /// /// /// For published content items, this is also the date the item was published. - /// This date is global to the content item, see FIXME for per-culture dates + /// This date is always global to the content item, see GetCulture().Date for the + /// date each culture was published. /// DateTime UpdateDate { get; } @@ -104,17 +103,35 @@ namespace Umbraco.Core.Models.PublishedContent /// Gets the url of the content item. /// /// - /// The value of this property is contextual. It depends on the 'current' - /// In addition, when the content type is multi-lingual, this is the url for the - /// 'current' culture. + /// The value of this property is contextual. It depends on the 'current' request uri, + /// if any. In addition, when the content type is multi-lingual, this is the url for the + /// 'current' culture. Otherwise, it is the invariant url. /// - /// FIXME explain what 'current' means here string Url { get; } - // fixme document - //PublishedCultureInfos Culture(string culture = "."); - //string GetName(string culture = "."); // best naming? GetName? CultureName? + /// + /// Gets the url of the content item. + /// + /// + /// The value of this property is contextual. It depends on the 'current' request uri, + /// if any. In addition, when the content type is multi-lingual, this is the url for the + /// specified culture. Otherwise, it is the invariant url. + /// + string GetUrl(string culture = "."); + + /// + /// Gets culture infos for a culture. + /// PublishedCultureInfos GetCulture(string culture = "."); + + /// + /// Gets culture infos. + /// + /// + /// Contains only those culture that are available. For a published content, these are + /// the cultures that are published. For a draft content, those that are 'available' ie + /// have a non-empty content name. + /// IReadOnlyDictionary Cultures { get; } /// diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedVariationContextAccessor.cs deleted file mode 100644 index 2af4230665..0000000000 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedVariationContextAccessor.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Gives access to the current . - /// - public interface IPublishedVariationContextAccessor - { - /// - /// Gets or sets the current . - /// - PublishedVariationContext Context { get; set; } - } -} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 8d4888cf25..51fe3045f7 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -96,6 +96,9 @@ namespace Umbraco.Core.Models.PublishedContent /// public virtual string Url => _content.Url; + /// + public virtual string GetUrl(string culture = ".") => _content.GetUrl(culture); + /// public PublishedCultureInfos GetCulture(string culture = ".") => _content.GetCulture(culture); diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs index 7a563544a4..2522f0366f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Initializes a new instance of the class. /// - public PublishedCultureInfos(string culture, string name, bool published, DateTime publishedDate) + public PublishedCultureInfos(string culture, string name, bool published, DateTime date) { if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentNullOrEmptyException(nameof(culture)); if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); @@ -20,7 +20,7 @@ namespace Umbraco.Core.Models.PublishedContent Name = name; UrlSegment = name.ToUrlSegment(culture); Published = published; - PublishedDate = publishedDate; + Date = date; } /// @@ -48,8 +48,13 @@ namespace Umbraco.Core.Models.PublishedContent public bool Published { get; } /// - /// Gets the date when fixme? + /// Gets the date associated with the culture. /// - public DateTime PublishedDate { get; } // fixme - model? model.UpdateDate - here? + /// + /// For published culture, this is the date the culture was published. For draft + /// cultures, this is the date the culture was made available, ie the last time its + /// name changed. + /// + public DateTime Date { get; } } } diff --git a/src/Umbraco.Core/Models/PublishedContent/ThreadCulturePublishedVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/ThreadCultureCurrentVariationAccessor.cs similarity index 54% rename from src/Umbraco.Core/Models/PublishedContent/ThreadCulturePublishedVariationContextAccessor.cs rename to src/Umbraco.Core/Models/PublishedContent/ThreadCultureCurrentVariationAccessor.cs index 8bf02e3f9b..9883cf9e3f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/ThreadCulturePublishedVariationContextAccessor.cs +++ b/src/Umbraco.Core/Models/PublishedContent/ThreadCultureCurrentVariationAccessor.cs @@ -5,18 +5,18 @@ using System.Threading; namespace Umbraco.Core.Models.PublishedContent { /// - /// Provides a CurrentUICulture-based implementation of . + /// Provides a CurrentUICulture-based implementation of . /// /// /// This accessor does not support segments. There is no need to set the current context. /// - public class ThreadCulturePublishedVariationContextAccessor : IPublishedVariationContextAccessor + public class ThreadCultureCurrentVariationAccessor : ICurrentVariationAccessor { - private readonly ConcurrentDictionary _contexts = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _contexts = new ConcurrentDictionary(); - public PublishedVariationContext Context + public CurrentVariation CurrentVariation { - get => _contexts.GetOrAdd(Thread.CurrentThread.CurrentUICulture.Name, culture => new PublishedVariationContext { Culture = culture }); + get => _contexts.GetOrAdd(Thread.CurrentThread.CurrentUICulture.Name, culture => new CurrentVariation { Culture = culture }); set => throw new NotSupportedException(); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/ThreadStaticPublishedVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/ThreadStaticCurrentVariationAccessor.cs similarity index 62% rename from src/Umbraco.Core/Models/PublishedContent/ThreadStaticPublishedVariationContextAccessor.cs rename to src/Umbraco.Core/Models/PublishedContent/ThreadStaticCurrentVariationAccessor.cs index b7391e8b0d..19ec89ff97 100644 --- a/src/Umbraco.Core/Models/PublishedContent/ThreadStaticPublishedVariationContextAccessor.cs +++ b/src/Umbraco.Core/Models/PublishedContent/ThreadStaticCurrentVariationAccessor.cs @@ -3,18 +3,18 @@ namespace Umbraco.Core.Models.PublishedContent { /// - /// Provides a ThreadStatic-based implementation of . + /// Provides a ThreadStatic-based implementation of . /// /// /// Something must set the current context. /// - public class ThreadStaticPublishedVariationContextAccessor : IPublishedVariationContextAccessor + public class ThreadStaticCurrentVariationAccessor : ICurrentVariationAccessor { [ThreadStatic] - private static PublishedVariationContext _context; + private static CurrentVariation _context; /// - public PublishedVariationContext Context + public CurrentVariation CurrentVariation { get => _context; set => _context = value; diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 0a41b3c09e..cded44a7e9 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -1166,7 +1166,8 @@ namespace Umbraco.Core /// The text to filter. /// The culture. /// The safe url segment. - public static string ToUrlSegment(this string text, CultureInfo culture) // fixme obsolete that one, use the string one? + // todo: obsolete that one and use the string one (requires changes to IShortStringHelper) + public static string ToUrlSegment(this string text, CultureInfo culture) { return Current.ShortStringHelper.CleanStringForUrlSegment(text, culture); } diff --git a/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs b/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs index 824ae50aa4..4674361b95 100644 --- a/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs +++ b/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Strings /// /// The content. /// The url segment. - string GetUrlSegment(IContentBase content); + string GetUrlSegment(IContentBase content); // fixme do we need to have both? /// /// Gets the url segment for a specified content and culture. diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 954391c8a6..9284a12cbd 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -369,11 +369,11 @@ - + - - - + + + diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs similarity index 76% rename from src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs rename to src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 3aa1fbf41f..ffdaf71711 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Data; -using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -16,6 +15,7 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing.Objects; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Cache; @@ -26,14 +26,18 @@ using Umbraco.Web.Routing; namespace Umbraco.Tests.PublishedContent { [TestFixture] - public class PublishedContentOtherTests // FIXME rename! + public class NuCacheTests { [Test] - public void Test() + public void StandaloneVariations() { + // this test implements a full standalone NuCache (based upon a test IDataSource, does not + // use any local db files, does not rely on any database) - and tests variations + SettingsForTests.ConfigureSettings(SettingsForTests.GenerateMockUmbracoSettings()); var globalSettings = UmbracoConfig.For.GlobalSettings(); + // create a content node kit var kit = new ContentNodeKit { ContentTypeId = 2, @@ -66,11 +70,20 @@ namespace Umbraco.Tests.PublishedContent } }; + // create a data source for NuCache var dataSource = new TestDataSource(kit); var runtime = Mock.Of(); Mock.Get(runtime).Setup(x => x.Level).Returns(RuntimeLevel.Run); + // create data types, property types and content types + var dataType = new DataType(new VoidEditor("Editor", Mock.Of())) { Id = 3 }; + + var dataTypes = new[] + { + dataType + }; + var propertyType = new PropertyType("Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.InvariantNeutral | ContentVariation.CultureNeutral }; var contentType = new ContentType(-1) { Id = 2, Alias = "alias-ct", Variations = ContentVariation.InvariantNeutral | ContentVariation.CultureNeutral }; contentType.AddPropertyType(propertyType); @@ -80,13 +93,6 @@ namespace Umbraco.Tests.PublishedContent contentType }; - var dataType = new DataType(new VoidEditor("Editor", Mock.Of())) { Id = 3 }; - - var dataTypes = new[] - { - dataType - }; - var contentTypeService = Mock.Of(); Mock.Get(contentTypeService).Setup(x => x.GetAll()).Returns(contentTypes); Mock.Get(contentTypeService).Setup(x => x.GetAll(It.IsAny())).Returns(contentTypes); @@ -94,6 +100,7 @@ namespace Umbraco.Tests.PublishedContent var dataTypeService = Mock.Of(); Mock.Get(dataTypeService).Setup(x => x.GetAll()).Returns(dataTypes); + // create a service context var serviceContext = new ServiceContext( dataTypeService : dataTypeService, memberTypeService: Mock.Of(), @@ -102,17 +109,7 @@ namespace Umbraco.Tests.PublishedContent localizationService: Mock.Of() ); - var contentTypeFactory = new PublishedContentTypeFactory( - Mock.Of(), - new PropertyValueConverterCollection(Array.Empty()), - dataTypeService); - - var documentRepository = Mock.Of(); - var mediaRepository = Mock.Of(); - var memberRepository = Mock.Of(); - - var snapshotAccessor = new TestPublishedSnapshotAccessor(); - + // create a scope provider var scopeProvider = Mock.Of(); Mock.Get(scopeProvider) .Setup(x => x.CreateScope( @@ -122,10 +119,18 @@ namespace Umbraco.Tests.PublishedContent It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(() => Mock.Of()); + .Returns(Mock.Of); - var variationAccessor = new TestPublishedVariationContextAccessor(); + // create a published content type factory + var contentTypeFactory = new PublishedContentTypeFactory( + Mock.Of(), + new PropertyValueConverterCollection(Array.Empty()), + dataTypeService); + // create a variation accessor + var variationAccessor = new TestCurrentVariationAccessor(); + + // at last, create the complete NuCache snapshot service! var options = new PublishedSnapshotService.Options { IgnoreLocalDb = true }; var snapshotService = new PublishedSnapshotService(options, null, @@ -133,23 +138,24 @@ namespace Umbraco.Tests.PublishedContent serviceContext, contentTypeFactory, null, - snapshotAccessor, + new TestPublishedSnapshotAccessor(), variationAccessor, Mock.Of(), scopeProvider, - documentRepository, - mediaRepository, - memberRepository, + Mock.Of(), + Mock.Of(), + Mock.Of(), new TestSystemDefaultCultureAccessor(), dataSource, globalSettings, new SiteDomainHelper()); + // get a snapshot, get a published content var snapshot = snapshotService.CreatePublishedSnapshot(previewToken: null); var publishedContent = snapshot.Content.GetById(1); // invariant is the current default - variationAccessor.Context = new PublishedVariationContext(); + variationAccessor.CurrentVariation = new CurrentVariation(); Assert.IsNotNull(publishedContent); Assert.AreEqual("It Works1!", publishedContent.Name); @@ -170,86 +176,39 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual("name-uk2", draftContent.GetCulture("en-UK").Name); // now french is default - variationAccessor.Context = new PublishedVariationContext("fr-FR"); + variationAccessor.CurrentVariation = new CurrentVariation("fr-FR"); Assert.AreEqual("val-fr1", publishedContent.Value("prop")); Assert.AreEqual("name-fr1", publishedContent.GetCulture().Name); Assert.AreEqual("name-fr1", publishedContent.Name); - Assert.AreEqual(new DateTime(2018, 01, 01, 01, 00, 00), publishedContent.GetCulture().PublishedDate); + Assert.AreEqual(new DateTime(2018, 01, 01, 01, 00, 00), publishedContent.GetCulture().Date); // now uk is default - variationAccessor.Context = new PublishedVariationContext("en-UK"); + variationAccessor.CurrentVariation = new CurrentVariation("en-UK"); Assert.AreEqual("val-uk1", publishedContent.Value("prop")); Assert.AreEqual("name-uk1", publishedContent.GetCulture().Name); Assert.AreEqual("name-uk1", publishedContent.Name); - Assert.AreEqual(new DateTime(2018, 01, 02, 01, 00, 00), publishedContent.GetCulture().PublishedDate); + Assert.AreEqual(new DateTime(2018, 01, 02, 01, 00, 00), publishedContent.GetCulture().Date); // invariant needs to be retrieved explicitely, when it's not default Assert.AreEqual("val1", publishedContent.Value("prop", culture: null)); // but, // if the content type / property type does not vary, then it's all invariant again + // modify the content type and property type, notify the snapshot service contentType.Variations = ContentVariation.InvariantNeutral; propertyType.Variations = ContentVariation.InvariantNeutral; snapshotService.Notify(new[] { new ContentTypeCacheRefresher.JsonPayload("IContentType", publishedContent.ContentType.Id, ContentTypeChangeTypes.RefreshMain) }); + // get a new snapshot (nothing changed in the old one), get the published content again var anotherSnapshot = snapshotService.CreatePublishedSnapshot(previewToken: null); var againContent = anotherSnapshot.Content.GetById(1); Assert.AreEqual(ContentVariation.InvariantNeutral, againContent.ContentType.Variations); Assert.AreEqual(ContentVariation.InvariantNeutral, againContent.ContentType.GetPropertyType("prop").Variations); + // now, "no culture" means "invariant" Assert.AreEqual("It Works1!", againContent.Name); Assert.AreEqual("val1", againContent.Value("prop")); - - // then, test fallback - } - - internal class TestDataSource : IDataSource - { - private readonly Dictionary _kits; - - public TestDataSource(params ContentNodeKit[] kits) - : this((IEnumerable) kits) - { } - - public TestDataSource(IEnumerable kits) - { - _kits = kits.ToDictionary(x => x.Node.Id, x => x); - } - - public ContentNodeKit GetContentSource(IScope scope, int id) - => _kits.TryGetValue(id, out var kit) ? kit : default; - - public IEnumerable GetAllContentSources(IScope scope) - => _kits.Values; - - public IEnumerable GetBranchContentSources(IScope scope, int id) - { - throw new NotImplementedException(); - } - - public IEnumerable GetTypeContentSources(IScope scope, IEnumerable ids) - => _kits.Values.Where(x => ids.Contains(x.ContentTypeId)); - - public ContentNodeKit GetMediaSource(IScope scope, int id) - { - throw new NotImplementedException(); - } - - public IEnumerable GetAllMediaSources(IScope scope) - { - throw new NotImplementedException(); - } - - public IEnumerable GetBranchMediaSources(IScope scope, int id) - { - throw new NotImplementedException(); - } - - public IEnumerable GetTypeMediaSources(IScope scope, IEnumerable ids) - { - throw new NotImplementedException(); - } } } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index af63d2244b..70a349e0cb 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -134,9 +134,6 @@ namespace Umbraco.Tests.PublishedContent CreateDate = DateTime.Now, CreatorId = 1, CreatorName = "Shannon", - // fixme what're we gonna do? - //DocumentTypeAlias = contentTypeAlias, - //DocumentTypeId = 2, Id = 3, SortOrder = 4, TemplateId = 5, @@ -186,7 +183,9 @@ namespace Umbraco.Tests.PublishedContent // l8tr... private class TestPublishedContent : IPublishedContent { - public string Url { get; set; } + public string Url { get; set; } + public string GetUrl(string culture = ".") => throw new NotSupportedException(); + public PublishedItemType ItemType { get; set; } IPublishedContent IPublishedContent.Parent diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs index d6321af692..acff953503 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs @@ -12,14 +12,14 @@ namespace Umbraco.Tests.PublishedContent [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] public class PublishedContentExtensionTests : PublishedContentTestBase { - private UmbracoContext ctx; - private string xmlContent = ""; - private bool createContentTypes = true; + private UmbracoContext _ctx; + private string _xmlContent = ""; + private bool _createContentTypes = true; private Dictionary _contentTypes; protected override string GetXmlContent(int templateId) { - return xmlContent; + return _xmlContent; } [Test] @@ -27,7 +27,7 @@ namespace Umbraco.Tests.PublishedContent { InitializeInheritedContentTypes(); - var publishedContent = ctx.ContentCache.GetById(1100); + var publishedContent = _ctx.ContentCache.GetById(1100); Assert.That(publishedContent.IsDocumentType("inherited", false)); } @@ -36,7 +36,7 @@ namespace Umbraco.Tests.PublishedContent { InitializeInheritedContentTypes(); - var publishedContent = ctx.ContentCache.GetById(1100); + var publishedContent = _ctx.ContentCache.GetById(1100); Assert.That(publishedContent.IsDocumentType("base", false), Is.False); } @@ -45,7 +45,7 @@ namespace Umbraco.Tests.PublishedContent { InitializeInheritedContentTypes(); - var publishedContent = ctx.ContentCache.GetById(1100); + var publishedContent = _ctx.ContentCache.GetById(1100); Assert.That(publishedContent.IsDocumentType("inherited", true)); } @@ -53,9 +53,9 @@ namespace Umbraco.Tests.PublishedContent public void IsDocumentType_Recursive_BaseType_ReturnsTrue() { InitializeInheritedContentTypes(); - ContentTypesCache.GetPublishedContentTypeByAlias = null; // fixme this is not pretty + ContentTypesCache.GetPublishedContentTypeByAlias = null; - var publishedContent = ctx.ContentCache.GetById(1100); + var publishedContent = _ctx.ContentCache.GetById(1100); Assert.That(publishedContent.IsDocumentType("base", true)); } @@ -64,14 +64,14 @@ namespace Umbraco.Tests.PublishedContent { InitializeInheritedContentTypes(); - var publishedContent = ctx.ContentCache.GetById(1100); + var publishedContent = _ctx.ContentCache.GetById(1100); Assert.That(publishedContent.IsDocumentType("invalidbase", true), Is.False); } private void InitializeInheritedContentTypes() { - ctx = GetUmbracoContext("/", 1, null, true); - if (createContentTypes) + _ctx = GetUmbracoContext("/", 1, null, true); + if (_createContentTypes) { var contentTypeService = Current.Services.ContentTypeService; var baseType = new ContentType(-1) { Alias = "base", Name = "Base" }; @@ -85,12 +85,12 @@ namespace Umbraco.Tests.PublishedContent { inheritedType.Alias, new PublishedContentType(inheritedType, null) } }; ContentTypesCache.GetPublishedContentTypeByAlias = alias => _contentTypes[alias]; - createContentTypes = false; + _createContentTypes = false; } ContentTypesCache.GetPublishedContentTypeByAlias = alias => _contentTypes[alias]; - xmlContent = @" + _xmlContent = @" diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 38ba7e771b..7cdea14008 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -189,6 +189,7 @@ namespace Umbraco.Tests.PublishedContent public Guid Version { get; set; } public int Level { get; set; } public string Url { get; set; } + public string GetUrl(string culture = ".") => throw new NotSupportedException(); public PublishedItemType ItemType { get { return PublishedItemType.Content; } } public bool IsDraft { get; set; } diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 185812002d..d3cd25ae92 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -5,6 +5,7 @@ using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; @@ -166,9 +167,15 @@ namespace Umbraco.Tests.Routing var requestMock = Mock.Get(_umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), + ContentVariation.CultureNeutral); + var publishedContent = new TestPublishedContent(contentType, 1234, Guid.NewGuid(), new Dictionary(), false); + var publishedContentCache = new Mock(); publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) .Returns("9876/home/test-fr"); //prefix with the root id node with the domain assigned as per the umbraco standard + publishedContentCache.Setup(x => x.GetById(It.IsAny())) + .Returns(id => id == 1234 ? publishedContent : null); var domainCache = new Mock(); domainCache.Setup(x => x.GetAssigned(It.IsAny(), false)) @@ -209,9 +216,15 @@ namespace Umbraco.Tests.Routing var requestMock = Mock.Get(_umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), + ContentVariation.CultureNeutral); + var publishedContent = new TestPublishedContent(contentType, 1234, Guid.NewGuid(), new Dictionary(), false); + var publishedContentCache = new Mock(); publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) .Returns("9876/home/test-fr"); //prefix with the root id node with the domain assigned as per the umbraco standard + publishedContentCache.Setup(x => x.GetById(It.IsAny())) + .Returns(id => id == 1234 ? publishedContent : null); var domainCache = new Mock(); domainCache.Setup(x => x.GetAssigned(It.IsAny(), false)) @@ -261,9 +274,15 @@ namespace Umbraco.Tests.Routing var requestMock = Mock.Get(_umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), + ContentVariation.CultureNeutral); + var publishedContent = new TestPublishedContent(contentType, 1234, Guid.NewGuid(), new Dictionary(), false); + var publishedContentCache = new Mock(); publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) .Returns("9876/home/test-fr"); //prefix with the root id node with the domain assigned as per the umbraco standard + publishedContentCache.Setup(x => x.GetById(It.IsAny())) + .Returns(id => id == 1234 ? publishedContent : null); var domainCache = new Mock(); domainCache.Setup(x => x.GetAssigned(It.IsAny(), false)) @@ -306,7 +325,6 @@ namespace Umbraco.Tests.Routing globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettings.Object); - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); diff --git a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs index b064e9685c..627d95ea29 100644 --- a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs @@ -190,7 +190,7 @@ namespace Umbraco.Tests.Routing SetDomains1(); var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); + var result = umbracoContext.UrlProvider.GetUrl(nodeId, absolute, current: currentUri); Assert.AreEqual(expected, result); } @@ -226,7 +226,7 @@ namespace Umbraco.Tests.Routing SetDomains2(); var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); + var result = umbracoContext.UrlProvider.GetUrl(nodeId, absolute, current : currentUri); Assert.AreEqual(expected, result); } @@ -254,7 +254,7 @@ namespace Umbraco.Tests.Routing SetDomains3(); var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); + var result = umbracoContext.UrlProvider.GetUrl(nodeId, absolute, current : currentUri); Assert.AreEqual(expected, result); } @@ -288,7 +288,7 @@ namespace Umbraco.Tests.Routing SetDomains4(); var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); + var result = umbracoContext.UrlProvider.GetUrl(nodeId, absolute, current : currentUri); Assert.AreEqual(expected, result); } @@ -312,17 +312,17 @@ namespace Umbraco.Tests.Routing SetDomains4(); string ignore; - ignore = umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(10012, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(10013, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain2.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain2.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain2.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain2.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(1001, false, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(10011, false, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(100111, false, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(10012, false, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(100121, false, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(10013, false, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(1002, false, current: new Uri("http://domain1.com")); + ignore = umbracoContext.UrlProvider.GetUrl(1001, false, current: new Uri("http://domain2.com")); + ignore = umbracoContext.UrlProvider.GetUrl(10011, false, current: new Uri("http://domain2.com")); + ignore = umbracoContext.UrlProvider.GetUrl(100111, false, current: new Uri("http://domain2.com")); + ignore = umbracoContext.UrlProvider.GetUrl(1002, false, current: new Uri("http://domain2.com")); var cache = umbracoContext.ContentCache as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); @@ -341,15 +341,15 @@ namespace Umbraco.Tests.Routing CheckRoute(cachedRoutes, cachedIds, 1002, "/1002"); // use the cache - Assert.AreEqual("/", umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/en/", umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/fr/", umbracoContext.UrlProvider.GetUrl(10012, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/1001-3/", umbracoContext.UrlProvider.GetUrl(10013, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/1002/", umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/", umbracoContext.UrlProvider.GetUrl(1001, false, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/en/", umbracoContext.UrlProvider.GetUrl(10011, false, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, false, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/fr/", umbracoContext.UrlProvider.GetUrl(10012, false, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, false, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/1001-3/", umbracoContext.UrlProvider.GetUrl(10013, false, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/1002/", umbracoContext.UrlProvider.GetUrl(1002, false, current: new Uri("http://domain1.com"))); - Assert.AreEqual("http://domain1.com/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain2.com"), false)); + Assert.AreEqual("http://domain1.com/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, false, current: new Uri("http://domain2.com"))); } private static void CheckRoute(IDictionary routes, IDictionary ids, int id, string route) diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index fb9b3ee0e4..b6eff1e0ef 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -89,7 +89,7 @@ namespace Umbraco.Tests.Scoping contentTypeFactory, null, publishedSnapshotAccessor, - Mock.Of(), + Mock.Of(), Logger, ScopeProvider, documentRepository, mediaRepository, memberRepository, diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 97d88a680b..d5f10cc324 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -153,7 +153,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting true); //replace it var urlHelper = new Mock(); - urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("/hello/world/1234"); var membershipHelper = new MembershipHelper(umbCtx, Mock.Of(), Mock.Of()); diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 14a0979091..3f2a05dfda 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -18,12 +18,12 @@ namespace Umbraco.Tests.TestHelpers.Stubs public int TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } - public IPublishedVariationContextAccessor VariationContextAccessor { get; set; } + public ICurrentVariationAccessor VariationAccessor { get; set; } public PublishedCultureInfos GetCulture(string culture = ".") { // handle context culture if (culture == ".") - culture = VariationContextAccessor?.Context.Culture; + culture = VariationAccessor?.CurrentVariation.Culture; // no invariant culture infos if (culture == null || Cultures == null) return null; @@ -45,6 +45,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs public Guid Version { get; set; } public int Level { get; set; } public string Url { get; set; } + public string GetUrl(string culture = ".") => throw new NotSupportedException(); public PublishedItemType ItemType => ContentType.ItemType; public bool IsDraft { get; set; } public IPublishedContent Parent { get; set; } diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 75b9d6b0fd..2c2b275e7f 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -264,7 +264,7 @@ namespace Umbraco.Tests.TestHelpers // testing=true so XmlStore will not use the file nor the database var publishedSnapshotAccessor = new UmbracoContextPublishedSnapshotAccessor(Umbraco.Web.Composing.Current.UmbracoContextAccessor); - var variationContextAccessor = new TestPublishedVariationContextAccessor(); + var variationContextAccessor = new TestCurrentVariationAccessor(); var service = new PublishedSnapshotService( ServiceContext, Container.GetInstance(), diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestCurrentVariationAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestCurrentVariationAccessor.cs new file mode 100644 index 0000000000..3b2ad36f00 --- /dev/null +++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestCurrentVariationAccessor.cs @@ -0,0 +1,17 @@ +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Tests.Testing.Objects.Accessors +{ + /// + /// Provides an implementation of for tests. + /// + public class TestCurrentVariationAccessor : ICurrentVariationAccessor + { + /// + public CurrentVariation CurrentVariation + { + get; + set; + } + } +} diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs deleted file mode 100644 index 455e5795a9..0000000000 --- a/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Tests.Testing.Objects.Accessors -{ - /// - /// Provides an implementation of for tests. - /// - public class TestPublishedVariationContextAccessor : IPublishedVariationContextAccessor - { - /// - public PublishedVariationContext Context - { - get; - set; - } - } -} diff --git a/src/Umbraco.Tests/Testing/Objects/TestDataSource.cs b/src/Umbraco.Tests/Testing/Objects/TestDataSource.cs new file mode 100644 index 0000000000..26bfff0e1a --- /dev/null +++ b/src/Umbraco.Tests/Testing/Objects/TestDataSource.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Scoping; +using Umbraco.Web.PublishedCache.NuCache; +using Umbraco.Web.PublishedCache.NuCache.DataSource; + +namespace Umbraco.Tests.Testing.Objects +{ + internal class TestDataSource : IDataSource + { + private readonly Dictionary _kits; + + public TestDataSource(params ContentNodeKit[] kits) + : this((IEnumerable) kits) + { } + + public TestDataSource(IEnumerable kits) + { + _kits = kits.ToDictionary(x => x.Node.Id, x => x); + } + + public ContentNodeKit GetContentSource(IScope scope, int id) + => _kits.TryGetValue(id, out var kit) ? kit : default; + + public IEnumerable GetAllContentSources(IScope scope) + => _kits.Values; + + public IEnumerable GetBranchContentSources(IScope scope, int id) + { + throw new NotImplementedException(); + } + + public IEnumerable GetTypeContentSources(IScope scope, IEnumerable ids) + => _kits.Values.Where(x => ids.Contains(x.ContentTypeId)); + + public ContentNodeKit GetMediaSource(IScope scope, int id) + { + throw new NotImplementedException(); + } + + public IEnumerable GetAllMediaSources(IScope scope) + { + throw new NotImplementedException(); + } + + public IEnumerable GetBranchMediaSources(IScope scope, int id) + { + throw new NotImplementedException(); + } + + public IEnumerable GetTypeMediaSources(IScope scope, IEnumerable ids) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 9f06d0de17..f44cba624f 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -1,11 +1,13 @@ using System; using System.Globalization; +using System.Linq; using System.Web.Security; using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Dictionary; +using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; @@ -75,13 +77,18 @@ namespace Umbraco.Tests.Testing.TestingTests var umbracoContext = TestObjects.GetUmbracoContextMock(); var urlProviderMock = new Mock(); - urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("/hello/world/1234"); var urlProvider = urlProviderMock.Object; var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }); - Assert.AreEqual("/hello/world/1234", theUrlProvider.GetUrl(1234)); + var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), + ContentVariation.InvariantNeutral); + var publishedContent = Mock.Of(); + Mock.Get(publishedContent).Setup(x => x.ContentType).Returns(contentType); + + Assert.AreEqual("/hello/world/1234", theUrlProvider.GetUrl(publishedContent)); } } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index c1d1da0b54..bf8dc2285a 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -129,7 +129,8 @@ - + + @@ -180,7 +181,7 @@ - + diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index 165f103d43..27a4541733 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.Linq; using System.Web; using LightInject; using Moq; @@ -10,6 +11,7 @@ using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; @@ -44,7 +46,7 @@ namespace Umbraco.Tests.Web Current.Container = container.Object; Umbraco.Web.Composing.Current.UmbracoContextAccessor = new TestUmbracoContextAccessor(); - + Udi.ResetUdiTypes(); } @@ -66,26 +68,36 @@ namespace Umbraco.Tests.Web //setup a mock entity service from the service context to return an integer for a GUID var entityService = Mock.Get(serviceCtxMock.EntityService); - entityService.Setup(x => x.GetId(It.IsAny(), It.IsAny())) - .Returns((Guid id, UmbracoObjectTypes objType) => - { - return Attempt.Succeed(1234); - }); + //entityService.Setup(x => x.GetId(It.IsAny(), It.IsAny())) + // .Returns((Guid id, UmbracoObjectTypes objType) => + // { + // return Attempt.Succeed(1234); + // }); //setup a mock url provider which we'll use fo rtesting var testUrlProvider = new Mock(); - testUrlProvider.Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((UmbracoContext umbCtx, int id, Uri url, UrlProviderMode mode, string culture) => - { - return "/my-test-url"; - }); + testUrlProvider + .Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((UmbracoContext umbCtx, IPublishedContent content, UrlProviderMode mode, string culture, Uri url) => "/my-test-url"); var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); + var contentType = new PublishedContentType(666, "alia", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.InvariantNeutral); + var publishedContent = Mock.Of(); + Mock.Get(publishedContent).Setup(x => x.Id).Returns(1234); + Mock.Get(publishedContent).Setup(x => x.ContentType).Returns(contentType); + var contentCache = Mock.Of(); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny())).Returns(publishedContent); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny())).Returns(publishedContent); + var snapshot = Mock.Of(); + Mock.Get(snapshot).Setup(x => x.Content).Returns(contentCache); + var snapshotService = Mock.Of(); + Mock.Get(snapshotService).Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(snapshot); + using (var umbCtx = UmbracoContext.EnsureContext( Umbraco.Web.Composing.Current.UmbracoContextAccessor, Mock.Of(), - Mock.Of(), + snapshotService, new Mock(null, null, globalSettings).Object, //setup a quick mock of the WebRouting section Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "AutoLegacy")), diff --git a/src/Umbraco.Web/Editors/MacroController.cs b/src/Umbraco.Web/Editors/MacroController.cs index ba23d4c8dd..bc0d1f4a0c 100644 --- a/src/Umbraco.Web/Editors/MacroController.cs +++ b/src/Umbraco.Web/Editors/MacroController.cs @@ -27,11 +27,11 @@ namespace Umbraco.Web.Editors [PluginController("UmbracoApi")] public class MacroController : UmbracoAuthorizedJsonController, IRequiresSessionState { - private readonly IPublishedVariationContextAccessor _variationContextAccessor; + private readonly ICurrentVariationAccessor _variationAccessor; - public MacroController(IPublishedVariationContextAccessor variationContextAccessor) + public MacroController(ICurrentVariationAccessor variationAccessor) { - _variationContextAccessor = variationContextAccessor; + _variationAccessor = variationAccessor; } /// @@ -121,7 +121,7 @@ namespace Umbraco.Web.Editors //the 'easiest' way might be to create an IPublishedContent manually and populate the legacy 'page' object with that //and then set the legacy parameters. - var legacyPage = new global::umbraco.page(doc, _variationContextAccessor); + var legacyPage = new global::umbraco.page(doc, _variationAccessor); UmbracoContext.HttpContext.Items["pageID"] = doc.Id; UmbracoContext.HttpContext.Items["pageElements"] = legacyPage.Elements; UmbracoContext.HttpContext.Items[global::Umbraco.Core.Constants.Conventions.Url.AltTemplate] = null; diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 0fe7bc0fc7..1c91b41bff 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -1,11 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Umbraco.Core; -using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors.ValueConverters; @@ -19,7 +15,7 @@ namespace Umbraco.Web.Models [DebuggerDisplay("Content Id: {Id}, Name: {Name}")] public abstract class PublishedContentBase : IPublishedContent { - private string _url; + private string _url; // fixme meaning? #region ContentType @@ -75,34 +71,31 @@ namespace Umbraco.Web.Models /// public abstract DateTime UpdateDate { get; } + /// + public virtual string Url => GetUrl(); + /// /// /// The url of documents are computed by the document url providers. The url of medias are, at the moment, /// computed here from the 'umbracoFile' property -- but we should move to media url providers at some point. /// - public virtual string Url + public virtual string GetUrl(string culture = ".") // fixme - consider .GetCulture("fr-FR").Url { - // fixme contextual! - get - { - // should be thread-safe although it won't prevent url from being resolved more than once - if (_url != null) - return _url; // fixme very bad idea with nucache? or? - switch (ItemType) { - case PublishedItemType.Content: + case PublishedItemType.Content: + // fixme inject an umbraco context accessor! if (UmbracoContext.Current == null) - throw new InvalidOperationException( - "Cannot resolve a Url for a content item when UmbracoContext.Current is null."); + throw new InvalidOperationException("Cannot compute 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."); - _url = UmbracoContext.Current.UrlProvider.GetUrl(Id); - break; + throw new InvalidOperationException("Cannot compute Url for a content item when UmbracoContext.Current.UrlProvider is null."); + return UmbracoContext.Current.UrlProvider.GetUrl(this); + case PublishedItemType.Media: + if (_url != null) return _url; // assume it will not depend on current uri/culture + var prop = GetProperty(Constants.Conventions.Media.File); - if (prop == null || prop.GetValue() == null) + if (prop?.GetValue() == null) { _url = string.Empty; return _url; @@ -110,7 +103,7 @@ namespace Umbraco.Web.Models var propType = ContentType.GetPropertyType(Constants.Conventions.Media.File); - // fixme this is horrible we need url providers for media too + // fixme this is horrible we need url providers for media too + this does NOT support variations //This is a hack - since we now have 2 properties that support a URL: upload and cropper, we need to detect this since we always // want to return the normal URL and the cropper stores data as json switch (propType.EditorAlias) @@ -130,13 +123,12 @@ namespace Umbraco.Web.Models _url = prop.GetValue()?.ToString(); break; } - break; + + return _url; + default: throw new NotSupportedException(); } - - return _url; - } } /// @@ -174,7 +166,7 @@ namespace Umbraco.Web.Models /// public virtual IPublishedProperty GetProperty(string alias, bool recurse) { - // fixme - but can this work with variants? + // fixme - but can recurse work with variants? var property = GetProperty(alias); if (recurse == false) return property; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs index de658daeec..1a562dad79 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs @@ -34,10 +34,10 @@ namespace Umbraco.Web.PublishedCache.NuCache DateTime createDate, int creatorId, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, - IPublishedVariationContextAccessor variationContextAccessor) + ICurrentVariationAccessor variationAccessor) : this(id, uid, level, path, sortOrder, parentContentId, createDate, creatorId) { - SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor, variationContextAccessor); + SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor, variationAccessor); } // 2-phases ctor, phase 1 @@ -59,7 +59,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // two-phase ctor, phase 2 - public void SetContentTypeAndData(PublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) + public void SetContentTypeAndData(PublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) { ContentType = contentType; @@ -67,9 +67,9 @@ namespace Umbraco.Web.PublishedCache.NuCache throw new ArgumentException("Both draftData and publishedData cannot be null at the same time."); if (draftData != null) - Draft = new PublishedContent(this, draftData, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); + Draft = new PublishedContent(this, draftData, publishedSnapshotAccessor, variationAccessor).CreateModel(); if (publishedData != null) - Published = new PublishedContent(this, publishedData, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); + Published = new PublishedContent(this, publishedData, publishedSnapshotAccessor, variationAccessor).CreateModel(); } // clone parent @@ -98,7 +98,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // clone with new content type - public ContentNode(ContentNode origin, PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) + public ContentNode(ContentNode origin, PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) { Id = origin.Id; Uid = origin.Uid; @@ -113,8 +113,8 @@ namespace Umbraco.Web.PublishedCache.NuCache var originDraft = origin.Draft == null ? null : PublishedContent.UnwrapIPublishedContent(origin.Draft); var originPublished = origin.Published == null ? null : PublishedContent.UnwrapIPublishedContent(origin.Published); - Draft = originDraft == null ? null : new PublishedContent(this, originDraft._contentData, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); - Published = originPublished == null ? null : new PublishedContent(this, originPublished._contentData, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); + Draft = originDraft == null ? null : new PublishedContent(this, originDraft._contentData, publishedSnapshotAccessor, variationAccessor).CreateModel(); + Published = originPublished == null ? null : new PublishedContent(this, originPublished._contentData, publishedSnapshotAccessor, variationAccessor).CreateModel(); ChildContentIds = origin.ChildContentIds; // can be the *same* list FIXME oh really? } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs index 19163d5e8d..d4d4221bf0 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs @@ -17,9 +17,9 @@ namespace Umbraco.Web.PublishedCache.NuCache public static ContentNodeKit Null { get; } = new ContentNodeKit { ContentTypeId = -1 }; - public void Build(PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) + public void Build(PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) { - Node.SetContentTypeAndData(contentType, DraftData, PublishedData, publishedSnapshotAccessor, variationContextAccessor); + Node.SetContentTypeAndData(contentType, DraftData, PublishedData, publishedSnapshotAccessor, variationAccessor); } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 95482bb6b1..b19e9f5ddf 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // SnapDictionary has unit tests to ensure it all works correctly private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; - private readonly IPublishedVariationContextAccessor _variationContextAccessor; + private readonly ICurrentVariationAccessor _variationAccessor; private readonly ConcurrentDictionary> _contentNodes; private readonly ConcurrentDictionary> _contentRootNodes; private readonly ConcurrentDictionary> _contentTypesById; @@ -44,10 +44,10 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Ctor - public ContentStore(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, ILogger logger, BPlusTree localDb = null) + public ContentStore(IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor, ILogger logger, BPlusTree localDb = null) { _publishedSnapshotAccessor = publishedSnapshotAccessor; - _variationContextAccessor = variationContextAccessor; + _variationAccessor = variationAccessor; _logger = logger; _localDb = localDb; @@ -279,7 +279,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (node == null) continue; var contentTypeId = node.ContentType.Id; if (index.TryGetValue(contentTypeId, out PublishedContentType contentType) == false) continue; - SetValueLocked(_contentNodes, node.Id, new ContentNode(node, contentType, _publishedSnapshotAccessor, _variationContextAccessor)); + SetValueLocked(_contentNodes, node.Id, new ContentNode(node, contentType, _publishedSnapshotAccessor, _variationAccessor)); } } finally @@ -393,7 +393,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _contentNodes.TryGetValue(id, out LinkedNode link); if (link?.Value == null) continue; - var node = new ContentNode(link.Value, contentType, _publishedSnapshotAccessor, _variationContextAccessor); + var node = new ContentNode(link.Value, contentType, _publishedSnapshotAccessor, _variationAccessor); SetValueLocked(_contentNodes, id, node); if (_localDb != null) RegisterChange(id, node.ToKit()); } @@ -416,7 +416,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return false; // and use - kit.Build(link.Value, _publishedSnapshotAccessor, _variationContextAccessor); + kit.Build(link.Value, _publishedSnapshotAccessor, _variationAccessor); return true; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs index 899fbd6eed..61f3d9862d 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs @@ -16,7 +16,7 @@ namespace Umbraco.Web.PublishedCache.NuCache class MemberCache : IPublishedMemberCache, INavigableData { private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; - public readonly IPublishedVariationContextAccessor _variationContextAccessor; + public readonly ICurrentVariationAccessor VariationAccessor; private readonly ICacheProvider _snapshotCache; private readonly IMemberService _memberService; private readonly IDataTypeService _dataTypeService; @@ -24,11 +24,11 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly PublishedContentTypeCache _contentTypeCache; private readonly bool _previewDefault; - public MemberCache(bool previewDefault, ICacheProvider snapshotCache, IMemberService memberService, IDataTypeService dataTypeService, ILocalizationService localizationService, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) + public MemberCache(bool previewDefault, ICacheProvider snapshotCache, IMemberService memberService, IDataTypeService dataTypeService, ILocalizationService localizationService, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) { _snapshotCache = snapshotCache; _publishedSnapshotAccessor = publishedSnapshotAccessor; - _variationContextAccessor = variationContextAccessor; + VariationAccessor = variationAccessor; _memberService = memberService; _dataTypeService = dataTypeService; _localizationService = localizationService; @@ -65,14 +65,14 @@ namespace Umbraco.Web.PublishedCache.NuCache var member = _memberService.GetById(memberId); return member == null ? null - : PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, _variationContextAccessor); + : PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, VariationAccessor); }); } private IPublishedContent /*IPublishedMember*/ GetById(IMember member, bool previewing) { return GetCacheItem(CacheKeys.MemberCacheMember("ById", _previewDefault, member.Id), () => - PublishedMember.Create(member, GetContentType(member.ContentTypeId), previewing, _publishedSnapshotAccessor, _variationContextAccessor)); + PublishedMember.Create(member, GetContentType(member.ContentTypeId), previewing, _publishedSnapshotAccessor, VariationAccessor)); } public IPublishedContent /*IPublishedMember*/ GetByProviderKey(object key) @@ -107,7 +107,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public IPublishedContent /*IPublishedMember*/ GetByMember(IMember member) { - return PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, _variationContextAccessor); + return PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, VariationAccessor); } public IEnumerable GetAtRoot(bool preview) @@ -115,7 +115,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // because members are flat (not a tree) everything is at root // because we're loading everything... let's just not cache? var members = _memberService.GetAllMembers(); - return members.Select(m => PublishedMember.Create(m, GetContentType(m.ContentTypeId), preview, _publishedSnapshotAccessor, _variationContextAccessor)); + return members.Select(m => PublishedMember.Create(m, GetContentType(m.ContentTypeId), preview, _publishedSnapshotAccessor, VariationAccessor)); } public XPathNavigator CreateNavigator() diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index 02dfc4e934..6231425e50 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -191,7 +191,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // use context values // fixme CultureSegment? - var publishedVariationContext = _content.VariationContextAccessor?.Context; + var publishedVariationContext = _content.VariationAccessor?.CurrentVariation; if (culture == ".") culture = _variations.Has(ContentVariation.CultureNeutral) ? publishedVariationContext?.Culture : null; if (segment == ".") segment = _variations.Has(ContentVariation.CultureNeutral) ? publishedVariationContext?.Segment : null; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 0ca20fd625..ea2480ec7a 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -22,12 +22,12 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Constructors - public PublishedContent(ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) + public PublishedContent(ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) { _contentNode = contentNode; _contentData = contentData; _publishedSnapshotAccessor = publishedSnapshotAccessor; - VariationContextAccessor = variationContextAccessor; // fixme why is this a property? should be be on the base class? + VariationAccessor = variationAccessor; // fixme why is this a property? should be be on the base class? _urlSegment = _contentData.Name.ToUrlSegment(); IsPreviewing = _contentData.Published == false; @@ -70,7 +70,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { _contentNode = contentNode; _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; - VariationContextAccessor = origin.VariationContextAccessor; + VariationAccessor = origin.VariationAccessor; _contentData = origin._contentData; _urlSegment = origin._urlSegment; @@ -86,7 +86,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private PublishedContent(PublishedContent origin) { _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; - VariationContextAccessor = origin.VariationContextAccessor; + VariationAccessor = origin.VariationAccessor; _contentNode = origin._contentNode; _contentData = origin._contentData; @@ -181,7 +181,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (!ContentType.Variations.Has(ContentVariation.CultureNeutral)) // fixme CultureSegment? return _contentData.Name; - var culture = VariationContextAccessor.Context.Culture; + var culture = VariationAccessor.CurrentVariation.Culture; if (culture == null) return _contentData.Name; @@ -197,7 +197,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (!ContentType.Variations.Has(ContentVariation.CultureNeutral)) // fixme CultureSegment? return _urlSegment; - var culture = VariationContextAccessor.Context.Culture; + var culture = VariationAccessor.CurrentVariation.Culture; if (culture == null) return _urlSegment; @@ -244,7 +244,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { // handle context culture if (culture == ".") - culture = VariationContextAccessor.Context.Culture; + culture = VariationAccessor.CurrentVariation.Culture; // no invariant culture infos if (culture == null) return null; @@ -397,7 +397,7 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Internal - internal IPublishedVariationContextAccessor VariationContextAccessor { get; } + internal ICurrentVariationAccessor VariationAccessor { get; } // used by navigable content internal IPublishedProperty[] PropertiesArray { get; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs index 63ef1ae5aa..4e01df0cd9 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs @@ -15,13 +15,13 @@ namespace Umbraco.Web.PublishedCache.NuCache { private readonly IMember _member; - private PublishedMember(IMember member, ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) - : base(contentNode, contentData, publishedSnapshotAccessor, variationContextAccessor) + private PublishedMember(IMember member, ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) + : base(contentNode, contentData, publishedSnapshotAccessor, variationAccessor) { _member = member; } - public static IPublishedContent Create(IMember member, PublishedContentType contentType, bool previewing, IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) + public static IPublishedContent Create(IMember member, PublishedContentType contentType, bool previewing, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) { var d = new ContentData { @@ -37,7 +37,7 @@ namespace Umbraco.Web.PublishedCache.NuCache member.Level, member.Path, member.SortOrder, member.ParentId, member.CreateDate, member.CreatorId); - return new PublishedMember(member, n, d, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); + return new PublishedMember(member, n, d, publishedSnapshotAccessor, variationAccessor).CreateModel(); } private static Dictionary GetPropertyValues(PublishedContentType contentType, IMember member) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index b2cb8bbcf8..3108f24090 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -81,12 +81,12 @@ namespace Umbraco.Web.PublishedCache.NuCache public PublishedSnapshotService(Options options, MainDom mainDom, IRuntimeState runtime, ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IdkMap idkMap, - IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, + IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor, ILogger logger, IScopeProvider scopeProvider, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, ISystemDefaultCultureAccessor systemDefaultCultureAccessor, IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) - : base(publishedSnapshotAccessor, variationContextAccessor) + : base(publishedSnapshotAccessor, variationAccessor) { //if (Interlocked.Increment(ref _singletonCheck) > 1) // throw new Exception("Singleton must be instancianted only once!"); @@ -145,13 +145,13 @@ namespace Umbraco.Web.PublishedCache.NuCache // stores are created with a db so they can write to it, but they do not read from it, // stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to // figure out whether it can read the dbs or it should populate them from sql - _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localContentDb); - _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localMediaDb); + _contentStore = new ContentStore(publishedSnapshotAccessor, variationAccessor, logger, _localContentDb); + _mediaStore = new ContentStore(publishedSnapshotAccessor, variationAccessor, logger, _localMediaDb); } else { - _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger); - _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger); + _contentStore = new ContentStore(publishedSnapshotAccessor, variationAccessor, logger); + _mediaStore = new ContentStore(publishedSnapshotAccessor, variationAccessor, logger); } _domainStore = new SnapDictionary(); @@ -173,7 +173,7 @@ namespace Umbraco.Web.PublishedCache.NuCache try { - if (_localDbExists) // fixme? + if (_localDbExists) { LockAndLoadContent(LoadContentFromLocalDbLocked); LockAndLoadMedia(LoadMediaFromLocalDbLocked); @@ -333,7 +333,6 @@ namespace Umbraco.Web.PublishedCache.NuCache //private void LoadContent(IContent content) //{ // var contentService = _serviceContext.ContentService as ContentService; - // if (contentService == null) throw new Exception("oops"); // var newest = content; // var published = newest.Published // ? newest @@ -536,7 +535,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } if (draftChanged || publishedChanged) - ((PublishedSnapshot)CurrentPublishedSnapshot).Resync(); + ((PublishedSnapshot)CurrentPublishedSnapshot)?.Resync(); } private void NotifyLocked(IEnumerable payloads, out bool draftChanged, out bool publishedChanged) @@ -544,9 +543,6 @@ namespace Umbraco.Web.PublishedCache.NuCache publishedChanged = false; draftChanged = false; - if (!(_serviceContext.ContentService is ContentService)) - throw new Exception("oops"); - // locks: // content (and content types) are read-locked while reading content // contentStore is wlocked (so readable, only no new views) @@ -633,16 +629,13 @@ namespace Umbraco.Web.PublishedCache.NuCache } if (anythingChanged) - ((PublishedSnapshot)CurrentPublishedSnapshot).Resync(); + ((PublishedSnapshot)CurrentPublishedSnapshot)?.Resync(); } private void NotifyLocked(IEnumerable payloads, out bool anythingChanged) { anythingChanged = false; - if (!(_serviceContext.MediaService is MediaService)) - throw new Exception("oops"); - // locks: // see notes for content cache refresher @@ -722,7 +715,7 @@ namespace Umbraco.Web.PublishedCache.NuCache Notify(_contentStore, payloads, RefreshContentTypesLocked); Notify(_mediaStore, payloads, RefreshMediaTypesLocked); - ((PublishedSnapshot)CurrentPublishedSnapshot)?.Resync(); // fixme all + ((PublishedSnapshot)CurrentPublishedSnapshot)?.Resync(); } private void Notify(ContentStore store, ContentTypeCacheRefresher.JsonPayload[] payloads, Action, IEnumerable, IEnumerable, IEnumerable> action) @@ -778,9 +771,6 @@ namespace Umbraco.Web.PublishedCache.NuCache // some locking on datatypes _publishedContentTypeFactory.NotifyDataTypeChanges(idsA); - if (!(_serviceContext.ContentService is ContentService)) - throw new Exception("oops"); - using (var scope = _scopeProvider.CreateScope()) { scope.ReadLock(Constants.Locks.ContentTree); @@ -788,9 +778,6 @@ namespace Umbraco.Web.PublishedCache.NuCache scope.Complete(); } - if (!(_serviceContext.MediaService is MediaService)) - throw new Exception("oops"); - using (var scope = _scopeProvider.CreateScope()) { scope.ReadLock(Constants.Locks.MediaTree); @@ -799,7 +786,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } } - ((PublishedSnapshot)CurrentPublishedSnapshot)?.Resync(); // fixme elsewhere! + ((PublishedSnapshot)CurrentPublishedSnapshot)?.Resync(); } public override void Notify(DomainCacheRefresher.JsonPayload[] payloads) @@ -815,8 +802,6 @@ namespace Umbraco.Web.PublishedCache.NuCache switch (payload.ChangeType) { case DomainChangeTypes.RefreshAll: - if (!(_serviceContext.DomainService is DomainService)) - throw new Exception("oops"); using (var scope = _scopeProvider.CreateScope()) { @@ -900,10 +885,6 @@ namespace Umbraco.Web.PublishedCache.NuCache // contentStore is wlocked (so readable, only no new views) // and it can be wlocked by 1 thread only at a time - // fixme wtf? - //if (!(_serviceContext.ContentService is ContentService)) - // throw new Exception("oops"); - var refreshedIdsA = refreshedIds.ToArray(); using (var scope = _scopeProvider.CreateScope()) @@ -925,9 +906,6 @@ namespace Umbraco.Web.PublishedCache.NuCache // mediaStore is wlocked (so readable, only no new views) // and it can be wlocked by 1 thread only at a time - if (!(_serviceContext.MediaService is MediaService)) - throw new Exception("oops"); - var refreshedIdsA = refreshedIds.ToArray(); using (var scope = _scopeProvider.CreateScope()) @@ -1000,7 +978,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // a MaxValue to make sure this one runs last, and it should be ok scopeContext.Enlist("Umbraco.Web.PublishedCache.NuCache.PublishedSnapshotService.Resync", () => this, (completed, svc) => { - ((PublishedSnapshot)svc.CurrentPublishedSnapshot).Resync(); + ((PublishedSnapshot)svc.CurrentPublishedSnapshot)?.Resync(); }, int.MaxValue); } @@ -1026,7 +1004,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainHelper, _globalSettings, _serviceContext.LocalizationService), MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache), - MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, _serviceContext.DataTypeService, _serviceContext.LocalizationService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor), + MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, _serviceContext.DataTypeService, _serviceContext.LocalizationService, memberTypeCache, PublishedSnapshotAccessor, VariationAccessor), DomainCache = domainCache, SnapshotCache = snapshotCache, ElementsCache = elementsCache @@ -1084,21 +1062,6 @@ namespace Umbraco.Web.PublishedCache.NuCache db.Execute("DELETE FROM cmsContentNu WHERE nodeId=@id", new { id = item.Id }); } - private static readonly string[] PropertiesImpactingAllVersions = { "SortOrder", "ParentId", "Level", "Path", "Trashed" }; - - private static bool HasChangesImpactingAllVersions(IContent icontent) - { - var content = (Content)icontent; - - // UpdateDate will be dirty - // Published may be dirty if saving a Published entity - // so cannot do this (would always be true): - //return content.IsEntityDirty(); - - // have to be more precise & specify properties - return PropertiesImpactingAllVersions.Any(content.IsPropertyDirty); - } - private void OnContentRefreshedEntity(DocumentRepository sender, DocumentRepository.ScopedEntityEventArgs args) { var db = args.Scope.Database; diff --git a/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs b/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs index 64dda9f20b..b5d721e03c 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs @@ -7,14 +7,14 @@ namespace Umbraco.Web.PublishedCache { abstract class PublishedSnapshotServiceBase : IPublishedSnapshotService { - protected PublishedSnapshotServiceBase(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor) + protected PublishedSnapshotServiceBase(IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) { PublishedSnapshotAccessor = publishedSnapshotAccessor; - VariationContextAccessor = variationContextAccessor; + VariationAccessor = variationAccessor; } public IPublishedSnapshotAccessor PublishedSnapshotAccessor { get; } - public IPublishedVariationContextAccessor VariationContextAccessor { get; } + public ICurrentVariationAccessor VariationAccessor { get; } // note: NOT setting _publishedSnapshotAccessor.PublishedSnapshot here because it is the // responsibility of the caller to manage what the 'current' facade is diff --git a/src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs b/src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs index 34910bfe1b..42e1c4dbca 100644 --- a/src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs +++ b/src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs @@ -19,6 +19,6 @@ namespace Umbraco.Web.PublishedCache } /// - public string DefaultCulture => _localizationService.GetDefaultLanguageIsoCode(); // capture - fast + public string DefaultCulture => _localizationService.GetDefaultLanguageIsoCode(); // fast } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs index 259229a600..ab59fc6aca 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs @@ -43,7 +43,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IScopeProvider scopeProvider, ICacheProvider requestCache, IEnumerable segmentProviders, - IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, + IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, ISystemDefaultCultureAccessor systemDefaultCultureAccessor, ILogger logger, @@ -52,7 +52,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache MainDom mainDom, bool testing = false, bool enableRepositoryEvents = true) : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, segmentProviders, - publishedSnapshotAccessor, variationContextAccessor, + publishedSnapshotAccessor, variationAccessor, documentRepository, mediaRepository, memberRepository, systemDefaultCultureAccessor, logger, globalSettings, siteDomainHelper, null, mainDom, testing, enableRepositoryEvents) @@ -63,7 +63,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IPublishedContentTypeFactory publishedContentTypeFactory, IScopeProvider scopeProvider, ICacheProvider requestCache, - IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, + IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, ISystemDefaultCultureAccessor systemDefaultCultureAccessor, ILogger logger, @@ -73,7 +73,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache MainDom mainDom, bool testing, bool enableRepositoryEvents) : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, Enumerable.Empty(), - publishedSnapshotAccessor, variationContextAccessor, + publishedSnapshotAccessor, variationAccessor, documentRepository, mediaRepository, memberRepository, systemDefaultCultureAccessor, logger, globalSettings, siteDomainHelper, contentTypeCache, mainDom, testing, enableRepositoryEvents) @@ -84,7 +84,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IScopeProvider scopeProvider, ICacheProvider requestCache, IEnumerable segmentProviders, - IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedVariationContextAccessor variationContextAccessor, + IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, ISystemDefaultCultureAccessor systemDefaultCultureAccessor, ILogger logger, @@ -93,7 +93,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache PublishedContentTypeCache contentTypeCache, MainDom mainDom, bool testing, bool enableRepositoryEvents) - : base(publishedSnapshotAccessor, variationContextAccessor) + : base(publishedSnapshotAccessor, variationAccessor) { _routesCache = new RoutesCache(); _publishedContentTypeFactory = publishedContentTypeFactory; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs index a574575cbd..76af7102f5 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache factory.GetInstance().RequestCache, factory.GetInstance(), factory.GetInstance(), - factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index 4b7cb48add..7b7a70cc2a 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -1,13 +1,10 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; -using Umbraco.Web.PublishedCache; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Routing { @@ -17,15 +14,13 @@ namespace Umbraco.Web.Routing public class AliasUrlProvider : IUrlProvider { private readonly IGlobalSettings _globalSettings; - private readonly IRequestHandlerSection _requestConfig; - private readonly ILocalizationService _localizationService; + private readonly IRequestHandlerSection _requestConfig; private readonly ISiteDomainHelper _siteDomainHelper; - public AliasUrlProvider(IGlobalSettings globalSettings, IRequestHandlerSection requestConfig, ILocalizationService localizationService, ISiteDomainHelper siteDomainHelper) + public AliasUrlProvider(IGlobalSettings globalSettings, IRequestHandlerSection requestConfig, ISiteDomainHelper siteDomainHelper) { _globalSettings = globalSettings; - _requestConfig = requestConfig; - _localizationService = localizationService; + _requestConfig = requestConfig; _siteDomainHelper = siteDomainHelper; } @@ -35,20 +30,8 @@ namespace Umbraco.Web.Routing #region GetUrl - /// - /// Gets the nice url of a published content. - /// - /// The Umbraco context. - /// The published content id. - /// The current absolute url. - /// The url mode. - /// The url for the published content. - /// - /// The url is absolute or relative depending on url indicated by current and settings, unless - /// absolute is true, in which case the url is always absolute. - /// If the provider is unable to provide a url, it should return null. - /// - public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture = null) + /// + public string GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current) { return null; // we have nothing to say } diff --git a/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs b/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs index 926db253c8..893445add6 100644 --- a/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs +++ b/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Globalization; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Routing { @@ -14,25 +14,15 @@ namespace Umbraco.Web.Routing /// /// This will simply return the URL that is returned by the assigned IPublishedContent if this is a custom route /// - /// - /// - /// - /// - /// - public string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture = null) + public string GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current) { - if (umbracoContext == null) return null; - if (umbracoContext.PublishedRequest == null) return null; - if (umbracoContext.PublishedRequest.PublishedContent == null) return null; - if (umbracoContext.HttpContext == null) return null; - if (umbracoContext.HttpContext.Request == null) return null; - if (umbracoContext.HttpContext.Request.RequestContext == null) return null; - if (umbracoContext.HttpContext.Request.RequestContext.RouteData == null) return null; - if (umbracoContext.HttpContext.Request.RequestContext.RouteData.DataTokens == null) return null; - if (umbracoContext.HttpContext.Request.RequestContext.RouteData.DataTokens.ContainsKey(Umbraco.Core.Constants.Web.CustomRouteDataToken) == false) return null; + if (umbracoContext?.PublishedRequest?.PublishedContent == null) return null; + if (umbracoContext.HttpContext?.Request?.RequestContext?.RouteData?.DataTokens == null) return null; + if (umbracoContext.HttpContext.Request.RequestContext.RouteData.DataTokens.ContainsKey(Core.Constants.Web.CustomRouteDataToken) == false) return null; + //ok so it's a custom route with published content assigned, check if the id being requested for is the same id as the assigned published content - return id == umbracoContext.PublishedRequest.PublishedContent.Id - ? umbracoContext.PublishedRequest.PublishedContent.Url + return content.Id == umbracoContext.PublishedRequest.PublishedContent.Id + ? umbracoContext.PublishedRequest.PublishedContent.GetUrl(culture) // fixme ∞ loop. : null; } diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 37d38d521e..366bcd865f 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -1,14 +1,11 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; -using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; - +using Umbraco.Core.Models.PublishedContent; + namespace Umbraco.Web.Routing { /// @@ -31,27 +28,15 @@ namespace Umbraco.Web.Routing #region GetUrl - /// - /// Gets the url of a published content. - /// - /// The Umbraco context. - /// The published content id. - /// The current absolute url. - /// The url mode. - /// The culture. - /// The url for the published content. - /// - /// The url is absolute or relative depending on mode and on current. - /// If the provider is unable to provide a url, it should return null. - /// - public virtual string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture = null) + /// + public virtual string GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current) { if (!current.IsAbsoluteUri) throw new ArgumentException("Current url must be absolute.", nameof(current)); - + // will not use cache if previewing - var route = umbracoContext.ContentCache.GetRouteById(id, culture); + var route = umbracoContext.ContentCache.GetRouteById(content.Id, culture); - return GetUrlFromRoute(route, umbracoContext, id, current, mode, culture); + return GetUrlFromRoute(route, umbracoContext, content.Id, current, mode, culture); } internal string GetUrlFromRoute(string route, UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture) diff --git a/src/Umbraco.Web/Routing/IUrlProvider.cs b/src/Umbraco.Web/Routing/IUrlProvider.cs index 031f4670b2..53a7e9886c 100644 --- a/src/Umbraco.Web/Routing/IUrlProvider.cs +++ b/src/Umbraco.Web/Routing/IUrlProvider.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; -using System.Globalization; -using Umbraco.Web.PublishedCache; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Routing { @@ -14,16 +13,18 @@ namespace Umbraco.Web.Routing /// Gets the nice url of a published content. /// /// The Umbraco context. - /// The published content id. - /// The current absolute url. + /// The published content. /// The url mode. + /// A culture. + /// The current absolute url. /// The url for the published content. /// /// The url is absolute or relative depending on mode and on current. + /// If the published content is multi-lingual, gets the url for the specified culture or, + /// when no culture is specified, the current culture. /// If the provider is unable to provide a url, it should return null. /// - string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture = null); - // FIXME WE HAVE TO DOCUMENT CULTURE FFS + string GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current); /// /// Gets the other urls of a published content. diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index c23ff5fb86..b8c28814b5 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -1,15 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Web.PublishedCache; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Web.Composing; -using Umbraco.Core.Services; -using System.Globalization; - +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; + namespace Umbraco.Web.Routing { /// @@ -57,7 +54,8 @@ namespace Umbraco.Web.Routing private readonly UmbracoContext _umbracoContext; private readonly IEnumerable _urlProviders; - private readonly IEntityService _entityService; + private readonly IEntityService _entityService; + private readonly ICurrentVariationAccessor _variationAccessor; // fixme set! /// /// Gets or sets the provider url mode. @@ -68,150 +66,149 @@ namespace Umbraco.Web.Routing #region GetUrl + private UrlProviderMode GetMode(bool absolute) => absolute ? UrlProviderMode.Absolute : Mode; + private IPublishedContent GetDocument(int id) => _umbracoContext.ContentCache.GetById(id); + private IPublishedContent GetDocument(Guid id) => _umbracoContext.ContentCache.GetById(id); + /// /// Gets the url of a published content. /// - /// The published content identifier. - /// The url for the published content. - /// - /// The url is absolute or relative depending on Mode and on the current url. - /// If the provider is unable to provide a url, it returns "#". - /// - public string GetUrl(Guid id, string culture = null) - { - var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); - return GetUrl(intId.Success ? intId.Result : -1, culture); - } - - /// - /// Gets the nice url of a published content. - /// - /// The published content identifier. - /// A value indicating whether the url should be absolute in any case. - /// The url for the published content. - /// - /// The url is absolute or relative depending on Mode and on current, unless - /// absolute is true, in which case the url is always absolute. - /// If the provider is unable to provide a url, it returns "#". - /// - public string GetUrl(Guid id, bool absolute, string culture = null) - { - var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); - return GetUrl(intId.Success ? intId.Result : -1, absolute, culture); - } - - /// - /// Gets the nice url of a published content. - /// - /// The published content id. + /// The published content. + /// A culture. /// The current absolute url. - /// A value indicating whether the url should be absolute in any case. /// The url for the published content. - /// - /// The url is absolute or relative depending on Mode and on current, unless - /// absolute is true, in which case the url is always absolute. - /// If the provider is unable to provide a url, it returns "#". - /// - public string GetUrl(Guid id, Uri current, bool absolute, string culture = null) - { - var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); - return GetUrl(intId.Success ? intId.Result : -1, current, absolute, culture); - } + public string GetUrl(IPublishedContent content, string culture = ".", Uri current = null) + => GetUrl(content, Mode, culture, current); /// /// Gets the nice url of a published content. /// - /// The published content identifier. - /// The url mode. + /// The published content. + /// A value indicating whether the url should be absolute in any case. + /// A culture. + /// The current absolute url. /// The url for the published content. /// - /// The url is absolute or relative depending on mode and on the current url. - /// If the provider is unable to provide a url, it returns "#". + /// The url is absolute or relative depending on Mode and on current, unless + /// absolute is true, in which case the url is always absolute. /// - public string GetUrl(Guid id, UrlProviderMode mode, string culture = null) - { - var intId = _entityService.GetId(id, UmbracoObjectTypes.Document); - return GetUrl(intId.Success ? intId.Result : -1, mode, culture); - } + public string GetUrl(IPublishedContent content, bool absolute, Uri current = null, string culture = ".") + => GetUrl(content, GetMode(absolute), culture, current); + + /// + /// Gets the nice url of a published content. + /// + /// The published content. + /// The url mode. + /// A culture. + /// The current absolute url. + /// The url for the published content. + public string GetUrl(IPublishedContent content, UrlProviderMode mode, Uri current = null, string culture = ".") + => GetUrl(content, mode, culture, current); /// /// Gets the url of a published content. /// /// The published content identifier. + /// A culture. + /// The current absolute url. /// The url for the published content. - /// - /// The url is absolute or relative depending on Mode and on the current url. - /// If the provider is unable to provide a url, it returns "#". - /// - public string GetUrl(int id, string culture = null) - { - return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, Mode, culture); - } + public string GetUrl(Guid id, string culture = ".", Uri current = null) + => GetUrl(GetDocument(id), Mode, culture, current); /// /// Gets the nice url of a published content. /// /// The published content identifier. /// A value indicating whether the url should be absolute in any case. - /// The url for the published content. - /// - /// The url is absolute or relative depending on Mode and on current, unless - /// absolute is true, in which case the url is always absolute. - /// If the provider is unable to provide a url, it returns "#". - /// - public string GetUrl(int id, bool absolute, string culture = null) - { - var mode = absolute ? UrlProviderMode.Absolute : Mode; - return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, mode, culture); - } - - /// - /// Gets the nice url of a published content. - /// - /// The published content id. + /// A culture. /// The current absolute url. - /// A value indicating whether the url should be absolute in any case. /// The url for the published content. /// /// The url is absolute or relative depending on Mode and on current, unless /// absolute is true, in which case the url is always absolute. - /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id, Uri current, bool absolute, string culture = null) - { - var mode = absolute ? UrlProviderMode.Absolute : Mode; - return GetUrl(id, current, mode, culture); - } + public string GetUrl(Guid id, bool absolute, string culture = ".", Uri current = null) + => GetUrl(GetDocument(id), GetMode(absolute), culture, current); /// /// Gets the nice url of a published content. /// /// The published content identifier. /// The url mode. + /// A culture. + /// The current absolute url. /// The url for the published content. - /// - /// The url is absolute or relative depending on mode and on the current url. - /// If the provider is unable to provide a url, it returns "#". - /// - public string GetUrl(int id, UrlProviderMode mode, string culture = null) - { - return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, mode, culture); - } + public string GetUrl(Guid id, UrlProviderMode mode, string culture = ".", Uri current = null) + => GetUrl(GetDocument(id), mode, culture, current); + + /// + /// Gets the url of a published content. + /// + /// The published content identifier. + /// A culture. + /// The current absolute url. + /// The url for the published content. + public string GetUrl(int id, string culture = ".", Uri current = null) + => GetUrl(GetDocument(id), Mode, culture, current); /// /// Gets the nice url of a published content. /// - /// The published content id. + /// The published content identifier. + /// A value indicating whether the url should be absolute in any case. + /// A culture. /// The current absolute url. + /// The url for the published content. + /// + /// The url is absolute or relative depending on Mode and on current, unless + /// absolute is true, in which case the url is always absolute. + /// + public string GetUrl(int id, bool absolute, string culture = ".", Uri current = null) + => GetUrl(GetDocument(id), GetMode(absolute), culture, current); + + /// + /// Gets the nice url of a published content. + /// + /// The published content identifier. /// The url mode. + /// A culture. + /// The current absolute url. + /// The url for the published content. + public string GetUrl(int id, UrlProviderMode mode, string culture = ".", Uri current = null) + => GetUrl(GetDocument(id), mode, culture, current); + + /// + /// Gets the nice url of a published content. + /// + /// The published content. + /// The url mode. + /// A culture. + /// The current absolute url. /// The url for the published content. /// /// The url is absolute or relative depending on mode and on current. + /// If the published content is multi-lingual, gets the url for the specified culture or, + /// when no culture is specified, the current culture. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(int id, Uri current, UrlProviderMode mode, string culture = null) // FIXME DOCUMENT + public string GetUrl(IPublishedContent content, UrlProviderMode mode, string culture = ".", Uri current = null) { - var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, id, current, mode, culture)) + if (content == null) + return "#"; + + // this the ONLY place where we deal with default culture - IUrlProvider always receive a culture + if (culture == ".") + { + culture = content.ContentType.Variations.Has(ContentVariation.CultureNeutral) // fixme CultureSegment + ? _variationAccessor.CurrentVariation.Culture + : null; + } + + if (current == null) + current = _umbracoContext.CleanedUmbracoUrl; + + var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, content, mode, culture, current)) .FirstOrDefault(u => u != null); return url ?? "#"; // legacy wants this } diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 41d4d4883c..bd5acab5de 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -71,9 +71,8 @@ namespace Umbraco.Web.Runtime composition.Container.RegisterFrom(); // register accessors for cultures - // fixme merge the two accessors? composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton(); var typeLoader = composition.Container.GetInstance(); var logger = composition.Container.GetInstance(); diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index f65f04fb76..f7660987ea 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -108,8 +108,8 @@ namespace umbraco /// /// The content. /// This is for usage only. - internal page(IContent content, IPublishedVariationContextAccessor variationContextAccessor) - : this(new PagePublishedContent(content, variationContextAccessor)) + internal page(IContent content, ICurrentVariationAccessor variationAccessor) + : this(new PagePublishedContent(content, variationAccessor)) { } #endregion @@ -409,7 +409,7 @@ namespace umbraco private readonly IPublishedProperty[] _properties; private readonly IPublishedContent _parent; private IReadOnlyDictionary _cultureInfos; - private readonly IPublishedVariationContextAccessor _variationContextAccessor; + private readonly ICurrentVariationAccessor _variationAccessor; private static readonly IReadOnlyDictionary NoCultureInfos = new Dictionary(); @@ -418,13 +418,13 @@ namespace umbraco _id = id; } - public PagePublishedContent(IContent inner, IPublishedVariationContextAccessor variationContextAccessor) + public PagePublishedContent(IContent inner, ICurrentVariationAccessor variationAccessor) { if (inner == null) throw new NullReferenceException("content"); _inner = inner; - _variationContextAccessor = variationContextAccessor; + _variationAccessor = variationAccessor; _id = _inner.Id; _key = _inner.Key; @@ -480,7 +480,7 @@ namespace umbraco { // handle context culture if (culture == ".") - culture = _variationContextAccessor.Context.Culture; + culture = _variationAccessor.CurrentVariation.Culture; // no invariant culture infos if (culture == null) return null; @@ -564,6 +564,8 @@ namespace umbraco get { throw new NotImplementedException(); } } + public string GetUrl(string culture = ".") => throw new NotSupportedException(); + public PublishedItemType ItemType { get { return PublishedItemType.Content; } From 038fc87a40ee2467d02e057c42aed7673945d271 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 30 Apr 2018 15:11:01 +0200 Subject: [PATCH 08/22] Cleanup, fix tests --- .../IPublishedValueFallback.cs | 29 ++--------- .../PublishedContent/PublishedCultureInfos.cs | 4 +- src/Umbraco.Core/StringExtensions.cs | 24 +++------ .../Strings/ContentBaseExtensions.cs | 25 ++-------- .../Strings/DefaultShortStringHelper.cs | 50 ++++++++++--------- .../Strings/DefaultShortStringHelperConfig.cs | 12 +++-- .../Strings/DefaultUrlSegmentProvider.cs | 15 +----- .../Strings/IShortStringHelper.cs | 15 +++--- .../Strings/IUrlSegmentProvider.cs | 9 +--- .../PublishedContentCacheTests.cs | 3 +- .../PublishedContentMoreTests.cs | 5 +- .../PublishedContent/PublishedContentTests.cs | 4 +- .../Scoping/ScopedNuCacheTests.cs | 3 +- .../Security/BackOfficeCookieManagerTests.cs | 5 +- .../Strings/DefaultShortStringHelperTests.cs | 10 ++-- .../Strings/MockShortStringHelper.cs | 20 ++------ .../Strings/StringExtensionsTests.cs | 6 +-- .../TestControllerActivatorBase.cs | 3 +- .../TestHelpers/TestObjects-Mocks.cs | 2 +- .../TestHelpers/TestWithDatabaseBase.cs | 2 +- .../Testing/TestingTests/MockTests.cs | 2 +- ...RenderIndexActionSelectorAttributeTests.cs | 8 +-- .../Web/Mvc/SurfaceControllerTests.cs | 10 ++-- .../Web/Mvc/UmbracoViewPageTests.cs | 2 +- .../Web/TemplateUtilitiesTests.cs | 4 +- .../Web/WebExtensionMethodTests.cs | 6 +-- .../Cache/CacheRefresherComponent.cs | 6 +-- .../Models/PublishedContentBase.cs | 11 ++-- src/Umbraco.Web/PublishedContentExtensions.cs | 41 ++++----------- src/Umbraco.Web/PublishedElementExtensions.cs | 14 +++--- src/Umbraco.Web/Routing/UrlProvider.cs | 20 ++++---- .../Runtime/WebRuntimeComponent.cs | 3 +- src/Umbraco.Web/UmbracoContext.cs | 16 +++--- src/Umbraco.Web/UmbracoModule.cs | 16 +++--- src/Umbraco.Web/umbraco.presentation/item.cs | 4 +- 35 files changed, 159 insertions(+), 250 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index d83438ab06..db6470e3cc 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -5,31 +5,10 @@ /// public interface IPublishedValueFallback { - /// - /// Gets a value. - /// - /// - /// This is invoked when getting a value for the specified and - /// could not return a value, and fallback rules should apply to get the value for another language and/or segment. - /// - TValue GetValue(IPublishedProperty property, string culture, string segment); - } + // todo - define & implement - // fixme question - // this is working at property level at the moment, should we move it up to element, - // so that the decision can be made based upon the entire element, other properties, etc? - // or, would we need the *two* levels? - - /// - /// Provides a default implementation of that does not fall back at all. - /// - public class NoPublishedValueFallback : IPublishedValueFallback - { - /// - public TValue GetValue(IPublishedProperty property, string culture, string segment) - { - // we don't implement fallback - return default; - } + // property level ... should we move it up to element, + // so that the decision can be made based upon the entire element, other properties, etc? + // or, would we need the *two* levels? } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs index 2522f0366f..3f9f7e9882 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs @@ -43,9 +43,9 @@ namespace Umbraco.Core.Models.PublishedContent /// /// /// A published content item will only have published cultures, and therefore this - /// value will always be true. On the other hand, fixme drafts? + /// value will always be true. On the other hand, ... ??? /// - public bool Published { get; } + public bool Published { get; } // fixme - what is culture.Published? /// /// Gets the date associated with the culture. diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index cded44a7e9..d894af47ee 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -1111,7 +1111,7 @@ namespace Umbraco.Core /// The text to filter. /// The culture. /// The safe alias. - public static string ToSafeAlias(this string alias, CultureInfo culture) + public static string ToSafeAlias(this string alias, string culture) { return Current.ShortStringHelper.CleanStringForSafeAlias(alias, culture); } @@ -1134,7 +1134,7 @@ namespace Umbraco.Core /// The culture. /// The safe alias. /// Checks UmbracoSettings.ForceSafeAliases to determine whether it should filter the text. - public static string ToSafeAliasWithForcingCheck(this string alias, CultureInfo culture) + public static string ToSafeAliasWithForcingCheck(this string alias, string culture) { return UmbracoConfig.For.UmbracoSettings().Content.ForceSafeAliases ? alias.ToSafeAlias(culture) : alias; } @@ -1158,20 +1158,8 @@ namespace Umbraco.Core /// The culture. /// The safe url segment. public static string ToUrlSegment(this string text, string culture) - => text.ToUrlSegment(CultureInfo.GetCultureInfo(culture)); + => Current.ShortStringHelper.CleanStringForUrlSegment(text, culture); - /// - /// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an url segment. - /// - /// The text to filter. - /// The culture. - /// The safe url segment. - // todo: obsolete that one and use the string one (requires changes to IShortStringHelper) - public static string ToUrlSegment(this string text, CultureInfo culture) - { - return Current.ShortStringHelper.CleanStringForUrlSegment(text, culture); - } - // the new methods to clean a string (to alias, url segment...) /// @@ -1209,7 +1197,7 @@ namespace Umbraco.Core /// strings are cleaned up to camelCase and Ascii. /// The culture. /// The clean string. - public static string ToCleanString(this string text, CleanStringType stringType, CultureInfo culture) + public static string ToCleanString(this string text, CleanStringType stringType, string culture) { return Current.ShortStringHelper.CleanString(text, stringType, culture); } @@ -1223,7 +1211,7 @@ namespace Umbraco.Core /// The separator. /// The culture. /// The clean string. - public static string ToCleanString(this string text, CleanStringType stringType, char separator, CultureInfo culture) + public static string ToCleanString(this string text, CleanStringType stringType, char separator, string culture) { return Current.ShortStringHelper.CleanString(text, stringType, separator, culture); } @@ -1269,7 +1257,7 @@ namespace Umbraco.Core /// The text to filter. /// The culture. /// The safe filename. - public static string ToSafeFileName(this string text, CultureInfo culture) + public static string ToSafeFileName(this string text, string culture) { return Current.ShortStringHelper.CleanStringForSafeFileName(text, culture); } diff --git a/src/Umbraco.Core/Strings/ContentBaseExtensions.cs b/src/Umbraco.Core/Strings/ContentBaseExtensions.cs index 29ef235f2b..1ae43b96e6 100644 --- a/src/Umbraco.Core/Strings/ContentBaseExtensions.cs +++ b/src/Umbraco.Core/Strings/ContentBaseExtensions.cs @@ -11,23 +11,6 @@ namespace Umbraco.Core.Strings /// internal static class ContentBaseExtensions { - - /// - /// Gets the default url segment for a specified content. - /// - /// The content. - /// - /// The url segment. - public static string GetUrlSegment(this IContentBase content, IEnumerable urlSegmentProviders) - { - if (content == null) throw new ArgumentNullException("content"); - if (urlSegmentProviders == null) throw new ArgumentNullException("urlSegmentProviders"); - - var url = urlSegmentProviders.Select(p => p.GetUrlSegment(content)).FirstOrDefault(u => u != null); - url = url ?? new DefaultUrlSegmentProvider().GetUrlSegment(content); // be safe - return url; - } - /// /// Gets the url segment for a specified content and culture. /// @@ -35,11 +18,11 @@ namespace Umbraco.Core.Strings /// The culture. /// /// The url segment. - public static string GetUrlSegment(this IContentBase content, CultureInfo culture, IEnumerable urlSegmentProviders) + public static string GetUrlSegment(this IContentBase content, IEnumerable urlSegmentProviders, string culture = null) { - if (content == null) throw new ArgumentNullException("content"); - if (culture == null) throw new ArgumentNullException("culture"); - if (urlSegmentProviders == null) throw new ArgumentNullException("urlSegmentProviders"); + if (content == null) throw new ArgumentNullException(nameof(content)); + if (culture == null) throw new ArgumentNullException(nameof(culture)); + if (urlSegmentProviders == null) throw new ArgumentNullException(nameof(urlSegmentProviders)); var url = urlSegmentProviders.Select(p => p.GetUrlSegment(content, culture)).FirstOrDefault(u => u != null); url = url ?? new DefaultUrlSegmentProvider().GetUrlSegment(content, culture); // be safe diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index 3a9d9433f4..d587893d99 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -18,7 +17,7 @@ namespace Umbraco.Core.Strings /// public class DefaultShortStringHelper : IShortStringHelper { - #region Ctor and vars + #region Ctor, consts and vars public DefaultShortStringHelper(IUmbracoSettingsSection settings) { @@ -31,6 +30,8 @@ namespace Umbraco.Core.Strings _config = config.Clone(); } + public const string InvariantCulture = "xx-xx"; + // see notes for CleanAsciiString //// beware! the order is quite important here! //const string ValidStringCharactersSource = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -138,7 +139,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ /// /// Safe aliases are Ascii only. /// - public virtual string CleanStringForSafeAlias(string text, CultureInfo culture) + public virtual string CleanStringForSafeAlias(string text, string culture) { return CleanString(text, CleanStringType.Alias, culture); } @@ -166,7 +167,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ /// /// Url segments are Ascii only (no accents...). /// - public virtual string CleanStringForUrlSegment(string text, CultureInfo culture) + public virtual string CleanStringForUrlSegment(string text, string culture) { return CleanString(text, CleanStringType.UrlSegment, culture); } @@ -190,11 +191,12 @@ function validateSafeAlias(input, value, immediate, callback) {{ /// The text to filter. /// The culture. /// The safe filename. - public virtual string CleanStringForSafeFileName(string text, CultureInfo culture) + public virtual string CleanStringForSafeFileName(string text, string culture) { if (string.IsNullOrWhiteSpace(text)) return string.Empty; + culture = culture ?? InvariantCulture; text = text.ReplaceMany(Path.GetInvalidFileNameChars(), '-'); var name = Path.GetFileNameWithoutExtension(text); @@ -266,7 +268,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ /// strings are cleaned up to camelCase and Ascii. /// The culture. /// The clean string. - public string CleanString(string text, CleanStringType stringType, CultureInfo culture) + public string CleanString(string text, CleanStringType stringType, string culture) { return CleanString(text, stringType, culture, null); } @@ -280,16 +282,16 @@ function validateSafeAlias(input, value, immediate, callback) {{ /// The separator. /// The culture. /// The clean string. - public string CleanString(string text, CleanStringType stringType, char separator, CultureInfo culture) + public string CleanString(string text, CleanStringType stringType, char separator, string culture) { return CleanString(text, stringType, culture, separator); } - protected virtual string CleanString(string text, CleanStringType stringType, CultureInfo culture, char? separator) + protected virtual string CleanString(string text, CleanStringType stringType, string culture, char? separator) { // be safe if (text == null) throw new ArgumentNullException(nameof(text)); - if (culture == null) throw new ArgumentNullException(nameof(culture)); + culture = culture ?? InvariantCulture; // get config var config = _config.For(stringType, culture); @@ -367,11 +369,12 @@ function validateSafeAlias(input, value, immediate, callback) {{ // that the utf8 version. Micro-optimizing sometimes isn't such a good idea. // note: does NOT support surrogate pairs in text - internal string CleanCodeString(string text, CleanStringType caseType, char separator, CultureInfo culture, DefaultShortStringHelperConfig.Config config) + internal string CleanCodeString(string text, CleanStringType caseType, char separator, string culture, DefaultShortStringHelperConfig.Config config) { int opos = 0, ipos = 0; var state = StateBreak; + culture = culture ?? InvariantCulture; caseType &= CleanStringType.CaseMask; // if we apply global ToUpper or ToLower to text here @@ -501,9 +504,10 @@ function validateSafeAlias(input, value, immediate, callback) {{ // note: supports surrogate pairs in input string internal void CopyTerm(string input, int ipos, char[] output, ref int opos, int len, - CleanStringType caseType, CultureInfo culture, bool isAcronym) + CleanStringType caseType, string culture, bool isAcronym) { var term = input.Substring(ipos, len); + var cultureInfo = culture == null || culture == InvariantCulture ? CultureInfo.InvariantCulture : CultureInfo.GetCultureInfo(culture); if (isAcronym) { @@ -529,13 +533,13 @@ function validateSafeAlias(input, value, immediate, callback) {{ break; case CleanStringType.LowerCase: - term = term.ToLower(culture); + term = term.ToLower(cultureInfo); term.CopyTo(0, output, opos, term.Length); opos += term.Length; break; case CleanStringType.UpperCase: - term = term.ToUpper(culture); + term = term.ToUpper(cultureInfo); term.CopyTo(0, output, opos, term.Length); opos += term.Length; break; @@ -546,18 +550,18 @@ function validateSafeAlias(input, value, immediate, callback) {{ if (char.IsSurrogate(c)) { s = term.Substring(ipos, 2); - s = opos == 0 ? s.ToLower(culture) : s.ToUpper(culture); + s = opos == 0 ? s.ToLower(cultureInfo) : s.ToUpper(cultureInfo); s.CopyTo(0, output, opos, s.Length); opos += s.Length; i++; // surrogate pair len is 2 } else { - output[opos] = opos++ == 0 ? char.ToLower(c, culture) : char.ToUpper(c, culture); + output[opos] = opos++ == 0 ? char.ToLower(c, cultureInfo) : char.ToUpper(c, cultureInfo); } if (len > i) { - term = term.Substring(i).ToLower(culture); + term = term.Substring(i).ToLower(cultureInfo); term.CopyTo(0, output, opos, term.Length); opos += term.Length; } @@ -569,18 +573,18 @@ function validateSafeAlias(input, value, immediate, callback) {{ if (char.IsSurrogate(c)) { s = term.Substring(ipos, 2); - s = s.ToUpper(culture); + s = s.ToUpper(cultureInfo); s.CopyTo(0, output, opos, s.Length); opos += s.Length; i++; // surrogate pair len is 2 } else { - output[opos++] = char.ToUpper(c, culture); + output[opos++] = char.ToUpper(c, cultureInfo); } if (len > i) { - term = term.Substring(i).ToLower(culture); + term = term.Substring(i).ToLower(cultureInfo); term.CopyTo(0, output, opos, term.Length); opos += term.Length; } @@ -592,14 +596,14 @@ function validateSafeAlias(input, value, immediate, callback) {{ if (char.IsSurrogate(c)) { s = term.Substring(ipos, 2); - s = opos == 0 ? s : s.ToUpper(culture); + s = opos == 0 ? s : s.ToUpper(cultureInfo); s.CopyTo(0, output, opos, s.Length); opos += s.Length; i++; // surrogate pair len is 2 } else { - output[opos] = opos++ == 0 ? c : char.ToUpper(c, culture); + output[opos] = opos++ == 0 ? c : char.ToUpper(c, cultureInfo); } if (len > i) { @@ -668,8 +672,6 @@ function validateSafeAlias(input, value, immediate, callback) {{ return new string(output, 0, opos); } - #endregion - - + #endregion } } diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs index 8a6aca632b..7c61175836 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using Umbraco.Core.Configuration.UmbracoSettings; @@ -8,7 +7,7 @@ namespace Umbraco.Core.Strings { public class DefaultShortStringHelperConfig { - private readonly Dictionary> _configs = new Dictionary>(); + private readonly Dictionary> _configs = new Dictionary>(); public DefaultShortStringHelperConfig Clone() { @@ -29,7 +28,7 @@ namespace Umbraco.Core.Strings return config; } - public CultureInfo DefaultCulture { get; set; } = CultureInfo.InvariantCulture; + public string DefaultCulture { get; set; } = DefaultShortStringHelper.InvariantCulture; public Dictionary UrlReplaceCharacters { get; set; } @@ -45,10 +44,12 @@ namespace Umbraco.Core.Strings return WithConfig(DefaultCulture, stringRole, config); } - public DefaultShortStringHelperConfig WithConfig(CultureInfo culture, CleanStringType stringRole, Config config) + public DefaultShortStringHelperConfig WithConfig(string culture, CleanStringType stringRole, Config config) { if (config == null) throw new ArgumentNullException(nameof(config)); + culture = culture ?? DefaultShortStringHelper.InvariantCulture; + if (_configs.ContainsKey(culture) == false) _configs[culture] = new Dictionary(); _configs[culture][stringRole] = config; @@ -112,8 +113,9 @@ namespace Umbraco.Core.Strings // internal: we don't want ppl to retrieve a config and modify it // (the helper uses a private clone to prevent modifications) - internal Config For(CleanStringType stringType, CultureInfo culture) + internal Config For(CleanStringType stringType, string culture) { + culture = culture ?? DefaultShortStringHelper.InvariantCulture; stringType = stringType & CleanStringType.RoleMask; Dictionary config; diff --git a/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs b/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs index d2e47e04b4..87a6bccd13 100644 --- a/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs +++ b/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs @@ -1,5 +1,4 @@ -using System.Globalization; -using Umbraco.Core.Models; +using Umbraco.Core.Models; namespace Umbraco.Core.Strings { @@ -8,23 +7,13 @@ namespace Umbraco.Core.Strings /// public class DefaultUrlSegmentProvider : IUrlSegmentProvider { - /// - /// Gets the default url segment for a specified content. - /// - /// The content. - /// The url segment. - public string GetUrlSegment(IContentBase content) - { - return GetUrlSegmentSource(content).ToUrlSegment(); - } - /// /// Gets the url segment for a specified content and culture. /// /// The content. /// The culture. /// The url segment. - public string GetUrlSegment(IContentBase content, CultureInfo culture) + public string GetUrlSegment(IContentBase content, string culture = null) { return GetUrlSegmentSource(content).ToUrlSegment(culture); } diff --git a/src/Umbraco.Core/Strings/IShortStringHelper.cs b/src/Umbraco.Core/Strings/IShortStringHelper.cs index 7232b0efe7..afe2166330 100644 --- a/src/Umbraco.Core/Strings/IShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/IShortStringHelper.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; -using System.Globalization; - -namespace Umbraco.Core.Strings +namespace Umbraco.Core.Strings { /// /// Provides string functions for short strings such as aliases or url segments. @@ -31,7 +28,7 @@ namespace Umbraco.Core.Strings /// The text to filter. /// The culture. /// The safe alias. - string CleanStringForSafeAlias(string text, CultureInfo culture); + string CleanStringForSafeAlias(string text, string culture); /// /// Cleans a string to produce a string that can safely be used in an url segment. @@ -47,7 +44,7 @@ namespace Umbraco.Core.Strings /// The text to filter. /// The culture. /// The safe url segment. - string CleanStringForUrlSegment(string text, CultureInfo culture); + string CleanStringForUrlSegment(string text, string culture); /// /// Cleans a string, in the context of the invariant culture, to produce a string that can safely be used as a filename, @@ -66,7 +63,7 @@ namespace Umbraco.Core.Strings /// The culture. /// The safe filename. /// Legacy says this was used to "overcome an issue when Umbraco is used in IE in an intranet environment" but that issue is not documented. - string CleanStringForSafeFileName(string text, CultureInfo culture); + string CleanStringForSafeFileName(string text, string culture); /// /// Splits a pascal-cased string by inserting a separator in between each term. @@ -106,7 +103,7 @@ namespace Umbraco.Core.Strings /// strings are cleaned up to camelCase and Ascii. /// The culture. /// The clean string. - string CleanString(string text, CleanStringType stringType, CultureInfo culture); + string CleanString(string text, CleanStringType stringType, string culture); /// /// Cleans a string in the context of a specified culture, using a specified separator. @@ -117,6 +114,6 @@ namespace Umbraco.Core.Strings /// The separator. /// The culture. /// The clean string. - string CleanString(string text, CleanStringType stringType, char separator, CultureInfo culture); + string CleanString(string text, CleanStringType stringType, char separator, string culture); } } diff --git a/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs b/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs index 4674361b95..1acbcea769 100644 --- a/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs +++ b/src/Umbraco.Core/Strings/IUrlSegmentProvider.cs @@ -9,13 +9,6 @@ namespace Umbraco.Core.Strings /// Url segments should comply with IETF RFCs regarding content, encoding, etc. public interface IUrlSegmentProvider { - /// - /// Gets the default url segment for a specified content. - /// - /// The content. - /// The url segment. - string GetUrlSegment(IContentBase content); // fixme do we need to have both? - /// /// Gets the url segment for a specified content and culture. /// @@ -25,7 +18,7 @@ namespace Umbraco.Core.Strings /// This is for when Umbraco is capable of managing more than one url /// per content, in 1-to-1 multilingual configurations. Then there would be one /// url per culture. - string GetUrlSegment(IContentBase content, CultureInfo culture); + string GetUrlSegment(IContentBase content, string culture = null); //TODO: For the 301 tracking, we need to add another extended interface to this so that // the RedirectTrackingEventHandler can ask the IUrlSegmentProvider if the URL is changing. diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index 1068ba3243..516fdbdd0e 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -6,6 +6,7 @@ using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.XmlPublishedCache; @@ -78,7 +79,7 @@ namespace Umbraco.Tests.Cache.PublishedCache umbracoSettings, Enumerable.Empty(), globalSettings, - ServiceContext.EntityService); + new TestCurrentVariationAccessor()); _cache = _umbracoContext.ContentCache; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 465034db05..8a465a1da8 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -18,6 +18,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Tests.Testing.Objects.Accessors; namespace Umbraco.Tests.PublishedContent { @@ -74,8 +75,8 @@ namespace Umbraco.Tests.PublishedContent new WebSecurity(httpContext, Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - globalSettings, - ServiceContext.EntityService); + globalSettings, + new TestCurrentVariationAccessor()); return umbracoContext; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 2cf555efda..2816af7a90 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -324,8 +324,8 @@ namespace Umbraco.Tests.PublishedContent public void GetPropertyValueRecursiveTest() { var doc = GetNode(1174); - var rVal = doc.Value("testRecursive", true); - var nullVal = doc.Value("DoNotFindThis", true); + var rVal = doc.Value("testRecursive", recurse: true); + var nullVal = doc.Value("DoNotFindThis", recurse: true); Assert.AreEqual("This is the recursive val", rVal); Assert.AreEqual(null, nullVal); } diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index b6eff1e0ef..c1be083c38 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -21,6 +21,7 @@ using Umbraco.Core.Services.Implement; using Umbraco.Core.Sync; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.Cache; using Umbraco.Web.PublishedCache; @@ -113,7 +114,7 @@ namespace Umbraco.Tests.Scoping umbracoSettings ?? SettingsForTests.GetDefaultUmbracoSettings(), urlProviders ?? Enumerable.Empty(), globalSettings, - Mock.Of()); + new TestCurrentVariationAccessor()); if (setSingleton) Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; diff --git a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs index 51d6a7fa7d..49b4f3f5aa 100644 --- a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs @@ -9,6 +9,7 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Services; using Umbraco.Tests.Testing; +using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -33,7 +34,7 @@ namespace Umbraco.Tests.Security Mock.Of(), new WebSecurity(Mock.Of(), Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), new List(),globalSettings, - Mock.Of()); + new TestCurrentVariationAccessor()); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Install); var mgr = new BackOfficeCookieManager( @@ -53,7 +54,7 @@ namespace Umbraco.Tests.Security Mock.Of(), new WebSecurity(Mock.Of(), Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), new List(), globalSettings, - Mock.Of()); + new TestCurrentVariationAccessor()); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); var mgr = new BackOfficeCookieManager(Mock.Of(accessor => accessor.UmbracoContext == umbCtx), runtime, TestObjects.GetGlobalSettings()); diff --git a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs index 543b4133f9..36e5874e14 100644 --- a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs +++ b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs @@ -43,7 +43,7 @@ namespace Umbraco.Tests.Strings StringType = CleanStringType.LowerCase | CleanStringType.Ascii, Separator = '-' }) - .WithConfig(new CultureInfo("fr-FR"), CleanStringType.UrlSegment, new DefaultShortStringHelperConfig.Config + .WithConfig("fr-FR", CleanStringType.UrlSegment, new DefaultShortStringHelperConfig.Config { PreFilter = FilterFrenchElisions, IsTerm = (c, leading) => leading ? char.IsLetter(c) : (char.IsLetterOrDigit(c) || c == '_'), @@ -56,7 +56,7 @@ namespace Umbraco.Tests.Strings IsTerm = (c, leading) => leading ? char.IsLetter(c) : char.IsLetterOrDigit(c), StringType = CleanStringType.UmbracoCase | CleanStringType.Ascii }) - .WithConfig(new CultureInfo("fr-FR"), CleanStringType.Alias, new DefaultShortStringHelperConfig.Config + .WithConfig("fr-FR", CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { PreFilter = WhiteQuotes, IsTerm = (c, leading) => leading ? char.IsLetter(c) : char.IsLetterOrDigit(c), @@ -588,16 +588,12 @@ namespace Umbraco.Tests.Strings #endregion public void CleanStringWithTypeAndCulture(string input, string expected, string culture, CleanStringType stringType) { - var cinfo = culture == null ? CultureInfo.InvariantCulture : new CultureInfo(culture); - // picks the proper config per culture // and overrides some stringType params (ascii...) - var output = _helper.CleanString(input, stringType, cinfo); + var output = _helper.CleanString(input, stringType, culture); Assert.AreEqual(expected, output); } - - #region Cases [TestCase("foo.txt", "foo.txt")] [TestCase("foo", "foo")] diff --git a/src/Umbraco.Tests/Strings/MockShortStringHelper.cs b/src/Umbraco.Tests/Strings/MockShortStringHelper.cs index 964e1d7ad2..e0df7ea4e9 100644 --- a/src/Umbraco.Tests/Strings/MockShortStringHelper.cs +++ b/src/Umbraco.Tests/Strings/MockShortStringHelper.cs @@ -19,17 +19,7 @@ namespace Umbraco.Tests.Strings return "SAFE-ALIAS::" + text; } - public string CleanStringForSafeCamelAlias(string text) - { - return "SAFE-ALIAS::" + text; - } - - public string CleanStringForSafeAlias(string text, System.Globalization.CultureInfo culture) - { - return "SAFE-ALIAS-CULTURE::" + text; - } - - public string CleanStringForSafeCamelAlias(string text, System.Globalization.CultureInfo culture) + public string CleanStringForSafeAlias(string text, string culture) { return "SAFE-ALIAS-CULTURE::" + text; } @@ -39,7 +29,7 @@ namespace Umbraco.Tests.Strings return "URL-SEGMENT::" + text; } - public string CleanStringForUrlSegment(string text, System.Globalization.CultureInfo culture) + public string CleanStringForUrlSegment(string text, string culture) { return "URL-SEGMENT-CULTURE::" + text; } @@ -49,7 +39,7 @@ namespace Umbraco.Tests.Strings return "SAFE-FILE-NAME::" + text; } - public string CleanStringForSafeFileName(string text, System.Globalization.CultureInfo culture) + public string CleanStringForSafeFileName(string text, string culture) { return "SAFE-FILE-NAME-CULTURE::" + text; } @@ -69,12 +59,12 @@ namespace Umbraco.Tests.Strings return "CLEAN-STRING-B::" + text; } - public string CleanString(string text, CleanStringType stringType, System.Globalization.CultureInfo culture) + public string CleanString(string text, CleanStringType stringType, string culture) { return "CLEAN-STRING-C::" + text; } - public string CleanString(string text, CleanStringType stringType, char separator, System.Globalization.CultureInfo culture) + public string CleanString(string text, CleanStringType stringType, char separator, string culture) { return "CLEAN-STRING-D::" + text; } diff --git a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs index b297f27656..55976e0d89 100644 --- a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs @@ -188,7 +188,7 @@ namespace Umbraco.Tests.Strings [Test] public void ToSafeAliasWithCulture() { - var output = "JUST-ANYTHING".ToSafeAlias(CultureInfo.InvariantCulture); + var output = "JUST-ANYTHING".ToSafeAlias(null); Assert.AreEqual("SAFE-ALIAS-CULTURE::JUST-ANYTHING", output); } @@ -202,7 +202,7 @@ namespace Umbraco.Tests.Strings [Test] public void ToUrlSegmentWithCulture() { - var output = "JUST-ANYTHING".ToUrlSegment(CultureInfo.InvariantCulture); + var output = "JUST-ANYTHING".ToUrlSegment(null); Assert.AreEqual("URL-SEGMENT-CULTURE::JUST-ANYTHING", output); } @@ -216,7 +216,7 @@ namespace Umbraco.Tests.Strings [Test] public void ToSafeFileNameWithCulture() { - var output = "JUST-ANYTHING".ToSafeFileName(CultureInfo.InvariantCulture); + var output = "JUST-ANYTHING".ToSafeFileName(null); Assert.AreEqual("SAFE-FILE-NAME-CULTURE::JUST-ANYTHING", output); } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index d5f10cc324..6053a1fad3 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -29,6 +29,7 @@ using Umbraco.Web.Security; using Umbraco.Web.WebApi; using LightInject; using System.Globalization; +using Umbraco.Tests.Testing.Objects.Accessors; namespace Umbraco.Tests.TestHelpers.ControllerTesting { @@ -149,7 +150,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == UrlProviderMode.Auto.ToString())), Enumerable.Empty(), globalSettings, - mockedEntityService, + new TestCurrentVariationAccessor(), true); //replace it var urlHelper = new Mock(); diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 4952360b6a..4b7fddb337 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -120,7 +120,7 @@ namespace Umbraco.Tests.TestHelpers var urlProviders = Enumerable.Empty(); if (accessor == null) accessor = new TestUmbracoContextAccessor(); - return UmbracoContext.EnsureContext(accessor, httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, Mock.Of(), true); + return UmbracoContext.EnsureContext(accessor, httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, new TestCurrentVariationAccessor(), true); } public IUmbracoSettingsSection GetUmbracoSettings() diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 2c2b275e7f..b08376fe8a 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -379,7 +379,7 @@ namespace Umbraco.Tests.TestHelpers umbracoSettings ?? Container.GetInstance(), urlProviders ?? Enumerable.Empty(), globalSettings ?? Container.GetInstance(), - ServiceContext.EntityService); + new TestCurrentVariationAccessor()); if (setSingleton) Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index f44cba624f..bf4169e723 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -81,7 +81,7 @@ namespace Umbraco.Tests.Testing.TestingTests .Returns("/hello/world/1234"); var urlProvider = urlProviderMock.Object; - var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }); + var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }, umbracoContext.CurrentVariationAccessor); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.InvariantNeutral); diff --git a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs index 6e8b22378c..240cf2369a 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs @@ -73,7 +73,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - Mock.Of(), + new TestCurrentVariationAccessor(), true); var ctrl = new MatchesDefaultIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); @@ -97,7 +97,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - Mock.Of(), + new TestCurrentVariationAccessor(), true); var ctrl = new MatchesOverriddenIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); @@ -121,7 +121,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - Mock.Of(), + new TestCurrentVariationAccessor(), true); var ctrl = new MatchesCustomIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); @@ -145,7 +145,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - Mock.Of(), + new TestCurrentVariationAccessor(), true); var ctrl = new MatchesAsyncIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index e467889831..7ca258affc 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -46,7 +46,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - Mock.Of(), + new TestCurrentVariationAccessor(), true); var ctrl = new TestSurfaceController { UmbracoContext = umbracoContext }; @@ -68,7 +68,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - Mock.Of(), + new TestCurrentVariationAccessor(), true); var ctrl = new TestSurfaceController { UmbracoContext = umbCtx }; @@ -88,7 +88,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - Mock.Of(), + new TestCurrentVariationAccessor(), true); var controller = new TestSurfaceController { UmbracoContext = umbracoContext }; @@ -115,7 +115,7 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "AutoLegacy")), Enumerable.Empty(), globalSettings, - Mock.Of(), + new TestCurrentVariationAccessor(), true); var helper = new UmbracoHelper( @@ -153,7 +153,7 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(section => section.WebRouting == webRoutingSettings), Enumerable.Empty(), globalSettings, - Mock.Of(), + new TestCurrentVariationAccessor(), true); var content = Mock.Of(publishedContent => publishedContent.Id == 12345); diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index a71612823e..fba1e6b4f6 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -441,7 +441,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - Mock.Of()); + new TestCurrentVariationAccessor()); //if (setSingleton) //{ diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index 27a4541733..571f74089d 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -82,7 +82,7 @@ namespace Umbraco.Tests.Web var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); - var contentType = new PublishedContentType(666, "alia", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.InvariantNeutral); + var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.InvariantNeutral); var publishedContent = Mock.Of(); Mock.Get(publishedContent).Setup(x => x.Id).Returns(1234); Mock.Get(publishedContent).Setup(x => x.ContentType).Returns(contentType); @@ -104,7 +104,7 @@ namespace Umbraco.Tests.Web //pass in the custom url provider new[]{ testUrlProvider.Object }, globalSettings, - entityService.Object, + new TestCurrentVariationAccessor(), true)) { var output = TemplateUtilities.ParseInternalLinks(input, umbCtx.UrlProvider); diff --git a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs index aa1fce8c85..2cc5861156 100644 --- a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs +++ b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Web TestObjects.GetUmbracoSettings(), new List(), TestObjects.GetGlobalSettings(), - Mock.Of()); + new TestCurrentVariationAccessor()); var r1 = new RouteData(); r1.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbCtx); @@ -50,7 +50,7 @@ namespace Umbraco.Tests.Web TestObjects.GetUmbracoSettings(), new List(), TestObjects.GetGlobalSettings(), - Mock.Of()); + new TestCurrentVariationAccessor()); var r1 = new RouteData(); r1.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbCtx); @@ -78,7 +78,7 @@ namespace Umbraco.Tests.Web TestObjects.GetUmbracoSettings(), new List(), TestObjects.GetGlobalSettings(), - Mock.Of()); + new TestCurrentVariationAccessor()); var httpContext = Mock.Of(); diff --git a/src/Umbraco.Web/Cache/CacheRefresherComponent.cs b/src/Umbraco.Web/Cache/CacheRefresherComponent.cs index 07dcebd763..5e3d2658f7 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherComponent.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherComponent.cs @@ -6,7 +6,6 @@ using Umbraco.Core; using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; -using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Services; using System.Linq; using System.Reflection; @@ -15,12 +14,13 @@ using System.Web.Hosting; using Umbraco.Core.Components; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services.Changes; using Umbraco.Core.Services.Implement; using Umbraco.Web.Composing; using Umbraco.Web.Security; using Umbraco.Web.Services; -using Content = Umbraco.Core.Models.Content; +using LightInject; using ApplicationTree = Umbraco.Core.Models.ApplicationTree; namespace Umbraco.Web.Cache @@ -237,7 +237,7 @@ namespace Umbraco.Web.Cache UmbracoConfig.For.UmbracoSettings(), Current.UrlProviders, UmbracoConfig.For.GlobalSettings(), - Current.Services.EntityService, + Current.Container.GetInstance(), true); } diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 1c91b41bff..c5a0e5ac0b 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -15,7 +15,7 @@ namespace Umbraco.Web.Models [DebuggerDisplay("Content Id: {Id}, Name: {Name}")] public abstract class PublishedContentBase : IPublishedContent { - private string _url; // fixme meaning? + private string _url; // fixme - cannot cache urls! depends on the current request! #region ContentType @@ -84,7 +84,7 @@ namespace Umbraco.Web.Models switch (ItemType) { case PublishedItemType.Content: - // fixme inject an umbraco context accessor! + // fixme - consider injecting an umbraco context accessor if (UmbracoContext.Current == null) throw new InvalidOperationException("Cannot compute Url for a content item when UmbracoContext.Current is null."); if (UmbracoContext.Current.UrlProvider == null) @@ -103,7 +103,8 @@ namespace Umbraco.Web.Models var propType = ContentType.GetPropertyType(Constants.Conventions.Media.File); - // fixme this is horrible we need url providers for media too + this does NOT support variations + // fixme - consider implementing media url providers + // note: that one does not support variations //This is a hack - since we now have 2 properties that support a URL: upload and cropper, we need to detect this since we always // want to return the normal URL and the cropper stores data as json switch (propType.EditorAlias) @@ -130,7 +131,7 @@ namespace Umbraco.Web.Models throw new NotSupportedException(); } } - + /// public abstract PublishedCultureInfos GetCulture(string culture = "."); @@ -166,7 +167,7 @@ namespace Umbraco.Web.Models /// public virtual IPublishedProperty GetProperty(string alias, bool recurse) { - // fixme - but can recurse work with variants? + // fixme - refactor with fallback var property = GetProperty(alias); if (recurse == false) return property; diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index c6d39aa0b0..3b4c976d70 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Data; -using System.Globalization; using System.Linq; using System.Web; using Examine; @@ -123,28 +122,6 @@ namespace Umbraco.Web #region Value - // fixme missing variations, but recurse/variations/fallback = ? - - /// - /// Recursively gets the value of a content's property identified by its alias. - /// - /// The content. - /// The property alias. - /// A value indicating whether to recurse. - /// The recursive value of the content's property identified by the alias. - /// - /// Recursively means: walking up the tree from , get the first value that can be found. - /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. - /// If no property with the specified alias exists, or if the property has no value, returns null. - /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. - /// The alias is case-insensitive. - /// - public static object Value(this IPublishedContent content, string alias, bool recurse) - { - var property = content.GetProperty(alias, recurse); - return property?.GetValue(); - } - /// /// Recursively the value of a content's property identified by its alias, if it exists, otherwise a default value. /// @@ -164,7 +141,7 @@ namespace Umbraco.Web /// public static object Value(this IPublishedContent content, string alias, string culture = ".", string segment = ".", object defaultValue = default, bool recurse = false) { - // fixme - variations+recurse not implemented here + // fixme - refactor with fallback var property = content.GetProperty(alias, recurse); return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(); } @@ -193,7 +170,7 @@ namespace Umbraco.Web /// public static T Value(this IPublishedContent content, string alias, string culture = ".", string segment = ".", T defaultValue = default, bool recurse = false) { - // fixme - variations+recurse not implemented here + // fixme - refactor with fallback var property = content.GetProperty(alias, recurse); if (property == null) return defaultValue; @@ -209,8 +186,8 @@ namespace Umbraco.Web //TODO: we should pass in the IExamineManager? var searcher = string.IsNullOrEmpty(indexName) - ? Examine.ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer) - : Examine.ExamineManager.Instance.GetSearcher(indexName); + ? ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer) + : ExamineManager.Instance.GetSearcher(indexName); if (searcher == null) throw new InvalidOperationException("No searcher found for index " + indexName); @@ -235,8 +212,8 @@ namespace Umbraco.Web //TODO: we should pass in the IExamineManager? var searcher = string.IsNullOrEmpty(indexName) - ? Examine.ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer) - : Examine.ExamineManager.Instance.GetSearcher(indexName); + ? ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer) + : ExamineManager.Instance.GetSearcher(indexName); if (searcher == null) throw new InvalidOperationException("No searcher found for index " + indexName); @@ -255,7 +232,7 @@ namespace Umbraco.Web { //TODO: we should pass in the IExamineManager? - var s = searchProvider ?? Examine.ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer); + var s = searchProvider ?? ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer); var results = s.Search(criteria); return results.ToPublishedSearchResults(UmbracoContext.Current.ContentCache); @@ -1170,8 +1147,8 @@ namespace Umbraco.Web /// internal static Func> GetPropertyAliasesAndNames { - get { return _getPropertyAliasesAndNames ?? GetAliasesAndNames; } - set { _getPropertyAliasesAndNames = value; } + get => _getPropertyAliasesAndNames ?? GetAliasesAndNames; + set => _getPropertyAliasesAndNames = value; } private static Dictionary GetAliasesAndNames(ServiceContext services, string alias) diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index 57e71573b5..a7776bb374 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; -using System.Reflection; using System.Web; using Umbraco.Core.Models.PublishedContent; @@ -56,7 +54,9 @@ namespace Umbraco.Web return prop != null && prop.HasValue(culture, segment); } - // fixme - that one is missing variations + // fixme - .Value() refactoring - in progress + // missing variations... + /// /// Returns one of two strings depending on whether the content has a value for a property identified by its alias. /// @@ -134,7 +134,7 @@ namespace Umbraco.Web #region Value or Umbraco.Field - WORK IN PROGRESS - // fixme - more work-in-progress for element.Value() and element.Value() here + // fixme - .Value() refactoring - in progress // trying to reproduce Umbraco.Field so we can get rid of it // // what we want: @@ -150,10 +150,10 @@ namespace Umbraco.Web // see UmbracoComponentRenderer.Field - which is ugly ;-( // recurse first, on each alias (that's how it's done in Field) - // TODO: strongly typed properties howto? + // // there is no strongly typed recurse, etc => needs to be in ModelsBuilder? - // todo - that one can only happen in ModelsBuilder as that's where the attributes are defined + // that one can only happen in ModelsBuilder as that's where the attributes are defined // the attribute that carries the alias is in ModelsBuilder! //public static TValue Value(this TModel content, Expression> propertySelector, ...) // where TModel : IPublishedElement @@ -164,6 +164,8 @@ namespace Umbraco.Web // return content.Value(alias, ...) //} + // recurse should be implemented via fallback + // todo - that one should be refactored, missing culture and so many things public static IHtmlString Value(this IPublishedElement content, string aliases, Func format, string alt = "") { diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index b8c28814b5..b88245b917 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -20,15 +20,16 @@ namespace Umbraco.Web.Routing /// Initializes a new instance of the class with an Umbraco context and a list of url providers. /// /// The Umbraco context. - /// + /// Routing settings. /// The list of url providers. - public UrlProvider(UmbracoContext umbracoContext, IWebRoutingSection routingSettings, IEnumerable urlProviders, IEntityService entityService) + /// The current variation accessor. + public UrlProvider(UmbracoContext umbracoContext, IWebRoutingSection routingSettings, IEnumerable urlProviders, ICurrentVariationAccessor variationAccessor) { if (routingSettings == null) throw new ArgumentNullException(nameof(routingSettings)); _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); - _urlProviders = urlProviders; - _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); + _urlProviders = urlProviders; + _variationAccessor = variationAccessor ?? throw new ArgumentNullException(nameof(variationAccessor)); var provider = UrlProviderMode.Auto; Mode = provider; @@ -43,19 +44,20 @@ namespace Umbraco.Web.Routing /// /// The Umbraco context. /// The list of url providers. - /// - public UrlProvider(UmbracoContext umbracoContext, IEnumerable urlProviders, UrlProviderMode provider = UrlProviderMode.Auto) + /// The current variation accessor. + /// An optional provider mode. + public UrlProvider(UmbracoContext umbracoContext, IEnumerable urlProviders, ICurrentVariationAccessor variationAccessor, UrlProviderMode mode = UrlProviderMode.Auto) { _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); _urlProviders = urlProviders; + _variationAccessor = variationAccessor; - Mode = provider; + Mode = mode; } private readonly UmbracoContext _umbracoContext; private readonly IEnumerable _urlProviders; - private readonly IEntityService _entityService; - private readonly ICurrentVariationAccessor _variationAccessor; // fixme set! + private readonly ICurrentVariationAccessor _variationAccessor; /// /// Gets or sets the provider url mode. diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index bd5acab5de..d8602422d5 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -218,6 +218,7 @@ namespace Umbraco.Web.Runtime IUmbracoSettingsSection umbracoSettings, IGlobalSettings globalSettings, IEntityService entityService, + ICurrentVariationAccessor variationAccessor, UrlProviderCollection urlProviders) { // setup mvc and webapi services @@ -255,7 +256,7 @@ namespace Umbraco.Web.Runtime umbracoSettings, urlProviders, globalSettings, - entityService); + variationAccessor); // ensure WebAPI is initialized, after everything GlobalConfiguration.Configuration.EnsureInitialized(); diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index b86c8c29a6..53fab2e57a 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -4,6 +4,7 @@ using System.Web; using Umbraco.Core; 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; @@ -67,9 +68,9 @@ namespace Umbraco.Web IPublishedSnapshotService publishedSnapshotService, WebSecurity webSecurity, IUmbracoSettingsSection umbracoSettings, - IEnumerable urlProviders, - IGlobalSettings globalSettings, - IEntityService entityService, + IEnumerable urlProviders, + IGlobalSettings globalSettings, + ICurrentVariationAccessor variationAccessor, bool replace = false) { if (umbracoContextAccessor == null) throw new ArgumentNullException(nameof(umbracoContextAccessor)); @@ -87,7 +88,7 @@ namespace Umbraco.Web // create & assign to accessor, dispose existing if any umbracoContextAccessor.UmbracoContext?.Dispose(); - return umbracoContextAccessor.UmbracoContext = new UmbracoContext(httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, entityService); + return umbracoContextAccessor.UmbracoContext = new UmbracoContext(httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, variationAccessor); } // initializes a new instance of the UmbracoContext class @@ -100,13 +101,14 @@ namespace Umbraco.Web IUmbracoSettingsSection umbracoSettings, IEnumerable urlProviders, IGlobalSettings globalSettings, - IEntityService entityService) + ICurrentVariationAccessor variationAccessor) { 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)); + CurrentVariationAccessor = variationAccessor ?? throw new ArgumentNullException(nameof(variationAccessor)); _globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings)); // ensure that this instance is disposed when the request terminates, though we *also* ensure @@ -136,7 +138,7 @@ namespace Umbraco.Web // OriginalRequestUrl = GetRequestFromContext()?.Url ?? new Uri("http://localhost"); CleanedUmbracoUrl = UriUtility.UriToUmbraco(OriginalRequestUrl); - UrlProvider = new UrlProvider(this, umbracoSettings.WebRouting, urlProviders, entityService); + UrlProvider = new UrlProvider(this, umbracoSettings.WebRouting, urlProviders, variationAccessor); } #endregion @@ -213,6 +215,8 @@ namespace Umbraco.Web /// public HttpContextBase HttpContext { get; } + public ICurrentVariationAccessor CurrentVariationAccessor { get; } + /// /// Creates and caches an instance of a DomainHelper /// diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 1578f5dc53..5a866939a8 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -1,10 +1,8 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Text; -using System.Threading; using System.Web; using System.Web.Routing; using LightInject; @@ -17,12 +15,12 @@ using Umbraco.Web.Security; using Umbraco.Core.Collections; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Exceptions; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; -using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings; namespace Umbraco.Web { @@ -55,9 +53,6 @@ namespace Umbraco.Web [Inject] public IUserService UserService { get; set; } - [Inject] - public IEntityService EntityService { get; set; } - [Inject] public UrlProviderCollection UrlProviders { get; set; } @@ -71,7 +66,10 @@ namespace Umbraco.Web internal PublishedRouter PublishedRouter { get; set; } [Inject] - internal IUmbracoDatabaseFactory DatabaseFactory { get; set; } + internal IUmbracoDatabaseFactory DatabaseFactory { get; set; } + + [Inject] + internal ICurrentVariationAccessor CurrentVariationAccessor { get; set; } #endregion @@ -114,8 +112,8 @@ namespace Umbraco.Web new WebSecurity(httpContext, UserService, GlobalSettings), UmbracoConfig.For.UmbracoSettings(), UrlProviders, - GlobalSettings, - EntityService, + GlobalSettings, + CurrentVariationAccessor, true); } diff --git a/src/Umbraco.Web/umbraco.presentation/item.cs b/src/Umbraco.Web/umbraco.presentation/item.cs index 80089afde7..3f648d66c4 100644 --- a/src/Umbraco.Web/umbraco.presentation/item.cs +++ b/src/Umbraco.Web/umbraco.presentation/item.cs @@ -75,7 +75,7 @@ namespace umbraco //check for published content and get its value using that if (publishedContent != null && (publishedContent.HasProperty(_fieldName) || recursive)) { - var pval = publishedContent.Value(_fieldName, recursive); + var pval = publishedContent.Value(_fieldName, recurse: recursive); var rval = pval == null ? string.Empty : pval.ToString(); _fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval; } @@ -95,7 +95,7 @@ namespace umbraco { if (publishedContent != null && (publishedContent.HasProperty(altFieldName) || recursive)) { - var pval = publishedContent.Value(altFieldName, recursive); + var pval = publishedContent.Value(altFieldName, recurse: recursive); var rval = pval == null ? string.Empty : pval.ToString(); _fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval; } From dbf310caf1d6d6df9e04736acec604ad6fb2c5a6 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 30 Apr 2018 21:03:43 +0200 Subject: [PATCH 09/22] Now use empty string for invariant in published --- .../PublishedContent/CurrentVariation.cs | 8 +++---- .../PublishedContent/IPublishedContent.cs | 4 ++-- .../PublishedContent/IPublishedProperty.cs | 8 +++---- .../PublishedContentWrapped.cs | 4 ++-- .../PublishedContent/PublishedPropertyBase.cs | 8 +++---- .../PublishedContent/RawValueProperty.cs | 14 +++++------ .../ThreadCultureCurrentVariationAccessor.cs | 4 ++-- .../Strings/ContentBaseExtensions.cs | 2 -- .../Strings/DefaultShortStringHelper.cs | 10 ++++---- .../Strings/DefaultShortStringHelperConfig.cs | 6 ++--- .../Strings/DefaultUrlSegmentProvider.cs | 8 +++---- src/Umbraco.Examine/UmbracoContentIndexer.cs | 4 ++-- .../Published/NestedContentTests.cs | 8 +++---- .../PublishedContent/NuCacheTests.cs | 16 ++++++------- .../PublishedContentDataTableTests.cs | 4 ++-- .../PublishedContent/PublishedMediaTests.cs | 1 - .../SolidPublishedSnapshot.cs | 12 +++++----- .../ContentFinderByUrlWithDomainsTests.cs | 4 ++-- .../Routing/DomainsAndCulturesTests.cs | 2 +- .../TestHelpers/Stubs/TestPublishedContent.cs | 8 +++---- .../TestSystemDefaultCultureAccessor.cs | 8 ++++++- .../ExamineDemoDataContentService.cs | 2 -- .../UmbracoExamine/IndexInitializer.cs | 4 +++- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 16 ++++--------- .../UmbracoExamine/SearchTests.cs | 2 +- .../Models/PublishedContentBase.cs | 6 ++--- .../ISystemDefaultCultureAccessor.cs | 3 +++ .../NuCache/DataSource/PropertyData.cs | 18 +++++++++++--- .../PublishedCache/NuCache/Property.cs | 24 +++++++++---------- .../NuCache/PublishedContent.cs | 10 ++++---- .../PublishedElementPropertyBase.cs | 8 +++---- .../PublishedCache/PublishedMember.cs | 2 +- .../SystemDefaultCultureAccessor.cs | 2 +- .../DictionaryPublishedContent.cs | 2 +- .../XmlPublishedCache/XmlPublishedContent.cs | 2 +- .../XmlPublishedCache/XmlPublishedProperty.cs | 6 ++--- src/Umbraco.Web/PublishedContentExtensions.cs | 4 ++-- .../PublishedContentPropertyExtension.cs | 2 +- src/Umbraco.Web/PublishedElementExtensions.cs | 6 ++--- src/Umbraco.Web/Routing/UrlProvider.cs | 22 ++++++++--------- src/Umbraco.Web/umbraco.presentation/page.cs | 16 ++++++------- 41 files changed, 154 insertions(+), 146 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/CurrentVariation.cs b/src/Umbraco.Core/Models/PublishedContent/CurrentVariation.cs index f52763ecf0..74a8c403c3 100644 --- a/src/Umbraco.Core/Models/PublishedContent/CurrentVariation.cs +++ b/src/Umbraco.Core/Models/PublishedContent/CurrentVariation.cs @@ -10,18 +10,18 @@ /// public CurrentVariation(string culture = null, string segment = null) { - Culture = culture; - Segment = segment; + Culture = culture ?? ""; // cannot be null, default to invariant + Segment = segment ?? ""; // cannot be null, default to neutral } /// /// Gets the culture. /// - public string Culture { get; set; } + public string Culture { get; } /// /// Gets the segment. /// - public string Segment { get; set; } + public string Segment { get; } } } diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index b3358c4676..91412bce3f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -117,12 +117,12 @@ namespace Umbraco.Core.Models.PublishedContent /// if any. In addition, when the content type is multi-lingual, this is the url for the /// specified culture. Otherwise, it is the invariant url. /// - string GetUrl(string culture = "."); + string GetUrl(string culture = null); /// /// Gets culture infos for a culture. /// - PublishedCultureInfos GetCulture(string culture = "."); + PublishedCultureInfos GetCulture(string culture = null); /// /// Gets culture infos. diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs index bfe1389921..9d2cca3e6d 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs @@ -21,7 +21,7 @@ /// Other caches that get their raw value from the database would consider that a property has "no /// value" if it is missing, null, or an empty string (including whitespace-only). /// - bool HasValue(string culture = ".", string segment = "."); + bool HasValue(string culture = null, string segment = null); /// /// Gets the source value of the property. @@ -35,7 +35,7 @@ /// If you're using that value, you're probably wrong, unless you're doing some internal /// Umbraco stuff. /// - object GetSourceValue(string culture = ".", string segment = "."); + object GetSourceValue(string culture = null, string segment = null); /// /// Gets the object value of the property. @@ -45,7 +45,7 @@ /// It can be null, or any type of CLR object. /// It has been fully prepared and processed by the appropriate converter. /// - object GetValue(string culture = ".", string segment = "."); + object GetValue(string culture = null, string segment = null); /// /// Gets the XPath value of the property. @@ -55,6 +55,6 @@ /// It must be either null, or a string, or an XPathNavigator. /// It has been fully prepared and processed by the appropriate converter. /// - object GetXPathValue(string culture = ".", string segment = "."); + object GetXPathValue(string culture = null, string segment = null); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 51fe3045f7..8187b7498e 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -97,10 +97,10 @@ namespace Umbraco.Core.Models.PublishedContent public virtual string Url => _content.Url; /// - public virtual string GetUrl(string culture = ".") => _content.GetUrl(culture); + public virtual string GetUrl(string culture = null) => _content.GetUrl(culture); /// - public PublishedCultureInfos GetCulture(string culture = ".") => _content.GetCulture(culture); + public PublishedCultureInfos GetCulture(string culture = null) => _content.GetCulture(culture); /// public IReadOnlyDictionary Cultures => _content.Cultures; diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs index c6626be1b2..7e2a5b5498 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs @@ -53,15 +53,15 @@ namespace Umbraco.Core.Models.PublishedContent public string Alias => PropertyType.Alias; /// - public abstract bool HasValue(string culture = ".", string segment = "."); + public abstract bool HasValue(string culture = null, string segment = null); /// - public abstract object GetSourceValue(string culture = ".", string segment = "."); + public abstract object GetSourceValue(string culture = null, string segment = null); /// - public abstract object GetValue(string culture = ".", string segment = "."); + public abstract object GetValue(string culture = null, string segment = null); /// - public abstract object GetXPathValue(string culture = ".", string segment = "."); + public abstract object GetXPathValue(string culture = null, string segment = null); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs index d7d71cea08..5dc4a280e6 100644 --- a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs +++ b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs @@ -23,20 +23,20 @@ namespace Umbraco.Core.Models.PublishedContent // RawValueProperty does not (yet?) support variants, // only manages the current "default" value - public override object GetSourceValue(string culture = ".", string segment = ".") - => culture == "." & segment == "." ? _sourceValue : null; + public override object GetSourceValue(string culture = null, string segment = null) + => string.IsNullOrEmpty(culture) & string.IsNullOrEmpty(segment) ? _sourceValue : null; - public override bool HasValue(string culture = ".", string segment = ".") + public override bool HasValue(string culture = null, string segment = null) { var sourceValue = GetSourceValue(culture, segment); return sourceValue is string s ? !string.IsNullOrWhiteSpace(s) : sourceValue != null; } - public override object GetValue(string culture = ".", string segment = ".") - => culture == "." & segment == "." ? _objectValue.Value : null; + public override object GetValue(string culture = null, string segment = null) + => string.IsNullOrEmpty(culture) & string.IsNullOrEmpty(segment) ? _objectValue.Value : null; - public override object GetXPathValue(string culture = ".", string segment = ".") - => culture == "." & segment == "." ? _xpathValue.Value : null; + public override object GetXPathValue(string culture = null, string segment = null) + => string.IsNullOrEmpty(culture) & string.IsNullOrEmpty(segment) ? _xpathValue.Value : null; public RawValueProperty(PublishedPropertyType propertyType, IPublishedElement content, object sourceValue, bool isPreviewing = false) : base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored diff --git a/src/Umbraco.Core/Models/PublishedContent/ThreadCultureCurrentVariationAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/ThreadCultureCurrentVariationAccessor.cs index 9883cf9e3f..f0c9e87a29 100644 --- a/src/Umbraco.Core/Models/PublishedContent/ThreadCultureCurrentVariationAccessor.cs +++ b/src/Umbraco.Core/Models/PublishedContent/ThreadCultureCurrentVariationAccessor.cs @@ -16,8 +16,8 @@ namespace Umbraco.Core.Models.PublishedContent public CurrentVariation CurrentVariation { - get => _contexts.GetOrAdd(Thread.CurrentThread.CurrentUICulture.Name, culture => new CurrentVariation { Culture = culture }); + get => _contexts.GetOrAdd(Thread.CurrentThread.CurrentUICulture.Name, culture => new CurrentVariation(culture)); set => throw new NotSupportedException(); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Strings/ContentBaseExtensions.cs b/src/Umbraco.Core/Strings/ContentBaseExtensions.cs index 1ae43b96e6..c63b546cdc 100644 --- a/src/Umbraco.Core/Strings/ContentBaseExtensions.cs +++ b/src/Umbraco.Core/Strings/ContentBaseExtensions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using Umbraco.Core.Models; @@ -21,7 +20,6 @@ namespace Umbraco.Core.Strings public static string GetUrlSegment(this IContentBase content, IEnumerable urlSegmentProviders, string culture = null) { if (content == null) throw new ArgumentNullException(nameof(content)); - if (culture == null) throw new ArgumentNullException(nameof(culture)); if (urlSegmentProviders == null) throw new ArgumentNullException(nameof(urlSegmentProviders)); var url = urlSegmentProviders.Select(p => p.GetUrlSegment(content, culture)).FirstOrDefault(u => u != null); diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index d587893d99..eb06d0279a 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -30,8 +30,6 @@ namespace Umbraco.Core.Strings _config = config.Clone(); } - public const string InvariantCulture = "xx-xx"; - // see notes for CleanAsciiString //// beware! the order is quite important here! //const string ValidStringCharactersSource = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -196,7 +194,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ if (string.IsNullOrWhiteSpace(text)) return string.Empty; - culture = culture ?? InvariantCulture; + culture = culture ?? ""; text = text.ReplaceMany(Path.GetInvalidFileNameChars(), '-'); var name = Path.GetFileNameWithoutExtension(text); @@ -291,7 +289,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ { // be safe if (text == null) throw new ArgumentNullException(nameof(text)); - culture = culture ?? InvariantCulture; + culture = culture ?? ""; // get config var config = _config.For(stringType, culture); @@ -374,7 +372,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ int opos = 0, ipos = 0; var state = StateBreak; - culture = culture ?? InvariantCulture; + culture = culture ?? ""; caseType &= CleanStringType.CaseMask; // if we apply global ToUpper or ToLower to text here @@ -507,7 +505,7 @@ function validateSafeAlias(input, value, immediate, callback) {{ CleanStringType caseType, string culture, bool isAcronym) { var term = input.Substring(ipos, len); - var cultureInfo = culture == null || culture == InvariantCulture ? CultureInfo.InvariantCulture : CultureInfo.GetCultureInfo(culture); + var cultureInfo = string.IsNullOrEmpty(culture) ? CultureInfo.InvariantCulture : CultureInfo.GetCultureInfo(culture); if (isAcronym) { diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs index 7c61175836..2bbade0fd8 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs @@ -28,7 +28,7 @@ namespace Umbraco.Core.Strings return config; } - public string DefaultCulture { get; set; } = DefaultShortStringHelper.InvariantCulture; + public string DefaultCulture { get; set; } = ""; // invariant public Dictionary UrlReplaceCharacters { get; set; } @@ -48,7 +48,7 @@ namespace Umbraco.Core.Strings { if (config == null) throw new ArgumentNullException(nameof(config)); - culture = culture ?? DefaultShortStringHelper.InvariantCulture; + culture = culture ?? ""; if (_configs.ContainsKey(culture) == false) _configs[culture] = new Dictionary(); @@ -115,7 +115,7 @@ namespace Umbraco.Core.Strings // (the helper uses a private clone to prevent modifications) internal Config For(CleanStringType stringType, string culture) { - culture = culture ?? DefaultShortStringHelper.InvariantCulture; + culture = culture ?? ""; stringType = stringType & CleanStringType.RoleMask; Dictionary config; diff --git a/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs b/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs index 87a6bccd13..bc52e94f7b 100644 --- a/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs +++ b/src/Umbraco.Core/Strings/DefaultUrlSegmentProvider.cs @@ -15,16 +15,16 @@ namespace Umbraco.Core.Strings /// The url segment. public string GetUrlSegment(IContentBase content, string culture = null) { - return GetUrlSegmentSource(content).ToUrlSegment(culture); + return GetUrlSegmentSource(content, culture).ToUrlSegment(culture); } - private static string GetUrlSegmentSource(IContentBase content) + private static string GetUrlSegmentSource(IContentBase content, string culture) { string source = null; if (content.HasProperty(Constants.Conventions.Content.UrlName)) - source = (content.GetValue(Constants.Conventions.Content.UrlName) ?? string.Empty).Trim(); + source = (content.GetValue(Constants.Conventions.Content.UrlName, culture) ?? string.Empty).Trim(); if (string.IsNullOrWhiteSpace(source)) - source = content.Name; + source = content.GetName(culture); return source; } } diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 0c02ce02b9..d799e12eed 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -321,7 +321,7 @@ namespace Umbraco.Examine { foreach (var c in content) { - var urlValue = c.GetUrlSegment(urlSegmentProviders); + var urlValue = c.GetUrlSegment(urlSegmentProviders, ""); // for now, index with invariant culture var values = new Dictionary { {"icon", new object[] {c.ContentType.Icon}}, @@ -348,7 +348,7 @@ namespace Umbraco.Examine { //only add the value if its not null or empty (we'll check for string explicitly here too) //fixme support variants with language id - var val = property.GetValue(); + var val = property.GetValue("", ""); // for now, index the invariant values switch (val) { case null: diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index ea5fa6f7e6..0bc4fb9de4 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -242,10 +242,10 @@ namespace Umbraco.Tests.Published _owner = owner; } - public override bool HasValue(string culture = ".", string segment = ".") => _hasValue; - public override object GetSourceValue(string culture = ".", string segment = ".") => _sourceValue; - public override object GetValue(string culture = ".", string segment = ".") => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview); - public override object GetXPathValue(string culture = ".", string segment = ".") => throw new WontImplementException(); + public override bool HasValue(string culture = null, string segment = null) => _hasValue; + public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue; + public override object GetValue(string culture = null, string segment = null) => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview); + public override object GetXPathValue(string culture = null, string segment = null) => throw new WontImplementException(); } class TestPublishedContent : PublishedContentBase diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index ffdaf71711..fbe6708008 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -45,9 +45,9 @@ namespace Umbraco.Tests.PublishedContent DraftData = new ContentData { Name="It Works2!", Published = false, TemplateId = 0, VersionId = 2, VersionDate = DateTime.Now, WriterId = 0, Properties = new Dictionary { { "prop", new[] { - new PropertyData { Value = "val2" }, - new PropertyData { Culture = "fr-FR", Value = "val-fr2" }, - new PropertyData { Culture = "en-UK", Value = "val-uk2" } + new PropertyData { Culture = "", Segment = "", Value = "val2" }, + new PropertyData { Culture = "fr-FR", Segment = "", Value = "val-fr2" }, + new PropertyData { Culture = "en-UK", Segment = "", Value = "val-uk2" } } } }, CultureInfos = new Dictionary { @@ -58,9 +58,9 @@ namespace Umbraco.Tests.PublishedContent PublishedData = new ContentData { Name="It Works1!", Published = true, TemplateId = 0, VersionId = 1, VersionDate = DateTime.Now, WriterId = 0, Properties = new Dictionary { { "prop", new[] { - new PropertyData { Value = "val1" }, - new PropertyData { Culture = "fr-FR", Value = "val-fr1" }, - new PropertyData { Culture = "en-UK", Value = "val-uk1" } + new PropertyData { Culture = "", Segment = "", Value = "val1" }, + new PropertyData { Culture = "fr-FR", Segment = "", Value = "val-fr1" }, + new PropertyData { Culture = "en-UK", Segment = "", Value = "val-uk1" } } } }, CultureInfos = new Dictionary { @@ -160,7 +160,7 @@ namespace Umbraco.Tests.PublishedContent Assert.IsNotNull(publishedContent); Assert.AreEqual("It Works1!", publishedContent.Name); Assert.AreEqual("val1", publishedContent.Value("prop")); - Assert.AreEqual("val-fr1", publishedContent.Value("prop", "fr-FR")); + Assert.AreEqual("val-fr1", publishedContent.Value("prop", "fr-FR")); // fixme wtf is happening here? Assert.AreEqual("val-uk1", publishedContent.Value("prop", "en-UK")); Assert.AreEqual("name-fr1", publishedContent.GetCulture("fr-FR").Name); @@ -190,7 +190,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(new DateTime(2018, 01, 02, 01, 00, 00), publishedContent.GetCulture().Date); // invariant needs to be retrieved explicitely, when it's not default - Assert.AreEqual("val1", publishedContent.Value("prop", culture: null)); + Assert.AreEqual("val1", publishedContent.Value("prop", culture: "")); // but, // if the content type / property type does not vary, then it's all invariant again diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 70a349e0cb..8244600994 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -184,7 +184,7 @@ namespace Umbraco.Tests.PublishedContent private class TestPublishedContent : IPublishedContent { public string Url { get; set; } - public string GetUrl(string culture = ".") => throw new NotSupportedException(); + public string GetUrl(string culture = null) => throw new NotSupportedException(); public PublishedItemType ItemType { get; set; } @@ -204,7 +204,7 @@ namespace Umbraco.Tests.PublishedContent public int TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } - public PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); + public PublishedCultureInfos GetCulture(string culture = null) => throw new NotSupportedException(); public IReadOnlyDictionary Cultures => throw new NotSupportedException(); public string UrlSegment { get; set; } public string WriterName { get; set; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index adfc9f535a..9fcc6125bc 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -133,7 +133,6 @@ namespace Umbraco.Tests.PublishedContent } } - [Test] public void Do_Not_Find_In_Recycle_Bin() { diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 7cdea14008..e502097819 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -176,7 +176,7 @@ namespace Umbraco.Tests.PublishedContent public int TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } - public PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); + public PublishedCultureInfos GetCulture(string culture = null) => throw new NotSupportedException(); public IReadOnlyDictionary Cultures => throw new NotSupportedException(); public string UrlSegment { get; set; } public string WriterName { get; set; } @@ -189,7 +189,7 @@ namespace Umbraco.Tests.PublishedContent public Guid Version { get; set; } public int Level { get; set; } public string Url { get; set; } - public string GetUrl(string culture = ".") => throw new NotSupportedException(); + public string GetUrl(string culture = null) => throw new NotSupportedException(); public PublishedItemType ItemType { get { return PublishedItemType.Content; } } public bool IsDraft { get; set; } @@ -256,10 +256,10 @@ namespace Umbraco.Tests.PublishedContent public bool SolidHasValue { get; set; } public object SolidXPathValue { get; set; } - public object GetSourceValue(string culture = ".", string segment = ".") => SolidSourceValue; - public object GetValue(string culture = ".", string segment = ".") => SolidValue; - public object GetXPathValue(string culture = ".", string segment = ".") => SolidXPathValue; - public bool HasValue(string culture = ".", string segment = ".") => SolidHasValue; + public object GetSourceValue(string culture = null, string segment = null) => SolidSourceValue; + public object GetValue(string culture = null, string segment = null) => SolidValue; + public object GetXPathValue(string culture = null, string segment = null) => SolidXPathValue; + public bool HasValue(string culture = null, string segment = null) => SolidHasValue; } [PublishedModel("ContentType2")] diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs index 949467a6fe..5787d3e613 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs @@ -148,7 +148,7 @@ namespace Umbraco.Tests.Routing [TestCase("http://domain1.com/fr/1001-2-1", 100121, "fr-FR")] [TestCase("http://domain1.com/1001-3", 10013, "en-US")] - [TestCase("http://domain2.com/1002", 1002, null)] + [TestCase("http://domain2.com/1002", 1002, "")] [TestCase("http://domain3.com/", 1003, "en-US")] [TestCase("http://domain3.com/en", 10031, "en-US")] @@ -158,7 +158,7 @@ namespace Umbraco.Tests.Routing [TestCase("http://domain3.com/1003-3", 10033, "en-US")] [TestCase("https://domain1.com/", 1001, "en-US")] - [TestCase("https://domain3.com/", 1001, null)] // because domain3 is explicitely set on http + [TestCase("https://domain3.com/", 1001, "")] // because domain3 is explicitely set on http public void Lookup_NestedDomains(string url, int expectedId, string expectedCulture) { diff --git a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs index e4fcfc46f8..ad6a9cf0a9 100644 --- a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs +++ b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs @@ -303,7 +303,7 @@ namespace Umbraco.Tests.Routing [TestCase("http://domain1.com/fr", "fr-FR", 10012)] // domain takes over local wildcard at 10012 [TestCase("http://domain1.com/fr/1001-2-1", "fr-FR", 100121)] // domain takes over local wildcard at 10012 - [TestCase("/1003", null, 1003)] // default culture (no domain) + [TestCase("/1003", "", 1003)] // default culture (no domain) [TestCase("/1003/1003-1", "nl-NL", 10031)] // wildcard on 10031 applies [TestCase("/1003/1003-1/1003-1-1", "nl-NL", 100311)] // wildcard on 10031 applies #endregion diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 3f2a05dfda..19cb5473e3 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -19,14 +19,14 @@ namespace Umbraco.Tests.TestHelpers.Stubs public int SortOrder { get; set; } public string Name { get; set; } public ICurrentVariationAccessor VariationAccessor { get; set; } - public PublishedCultureInfos GetCulture(string culture = ".") + public PublishedCultureInfos GetCulture(string culture = null) { // handle context culture - if (culture == ".") + if (culture == null) culture = VariationAccessor?.CurrentVariation.Culture; // no invariant culture infos - if (culture == null || Cultures == null) return null; + if (culture == "" || Cultures == null) return null; // get return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null; @@ -45,7 +45,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs public Guid Version { get; set; } public int Level { get; set; } public string Url { get; set; } - public string GetUrl(string culture = ".") => throw new NotSupportedException(); + public string GetUrl(string culture = null) => throw new NotSupportedException(); public PublishedItemType ItemType => ContentType.ItemType; public bool IsDraft { get; set; } public IPublishedContent Parent { get; set; } diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestSystemDefaultCultureAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestSystemDefaultCultureAccessor.cs index fe94a0237f..6d78f0d290 100644 --- a/src/Umbraco.Tests/Testing/Objects/Accessors/TestSystemDefaultCultureAccessor.cs +++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestSystemDefaultCultureAccessor.cs @@ -4,6 +4,12 @@ namespace Umbraco.Tests.Testing.Objects.Accessors { public class TestSystemDefaultCultureAccessor : ISystemDefaultCultureAccessor { - public string DefaultCulture { get; set; } + private string _defaultCulture = string.Empty; + + public string DefaultCulture + { + get => _defaultCulture; + set => _defaultCulture = value ?? string.Empty; + } } } diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs b/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs index ffbd4f82d0..5bc1fbfdaa 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs +++ b/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs @@ -58,7 +58,5 @@ namespace Umbraco.Tests.UmbracoExamine } private readonly XDocument _xContent; - - } } diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 30bcc3766d..c7599e63e4 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -58,7 +58,8 @@ namespace Umbraco.Tests.UmbracoExamine m.SortOrder == (int)x.Attribute("sortOrder") && m.CreateDate == (DateTime)x.Attribute("createDate") && m.UpdateDate == (DateTime)x.Attribute("updateDate") && - m.Name == (string)x.Attribute("nodeName") && + m.Name == (string)x.Attribute("nodeName") && + m.GetName(It.IsAny()) == (string)x.Attribute("nodeName") && m.Path == (string)x.Attribute("path") && m.Properties == new PropertyCollection() && m.ContentType == Mock.Of(mt => @@ -102,6 +103,7 @@ namespace Umbraco.Tests.UmbracoExamine m.CreateDate == (DateTime) x.Attribute("createDate") && m.UpdateDate == (DateTime) x.Attribute("updateDate") && m.Name == (string) x.Attribute("nodeName") && + m.GetName(It.IsAny()) == (string)x.Attribute("nodeName") && m.Path == (string) x.Attribute("path") && m.Properties == new PropertyCollection() && m.ContentType == Mock.Of(mt => diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 9d17be3476..8eb347c214 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -23,7 +23,6 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Rebuild_Index() { - using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) @@ -46,7 +45,6 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Protected_Content_Not_Indexed() { - using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext)) using (indexer.ProcessNonAsync()) @@ -178,7 +176,7 @@ namespace Umbraco.Tests.UmbracoExamine //create the whole thing indexer.RebuildIndex(); - + var result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndexer.CategoryFieldName, IndexTypes.Content).Compile()); Assert.AreEqual(21, result.TotalItemCount); @@ -188,7 +186,7 @@ namespace Umbraco.Tests.UmbracoExamine { indexer.DeleteFromIndex(r.Id); } - + //ensure it's all gone result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndexer.CategoryFieldName, IndexTypes.Content).Compile()); @@ -197,14 +195,11 @@ namespace Umbraco.Tests.UmbracoExamine //call our indexing methods indexer.IndexAll(IndexTypes.Content); - + result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndexer.CategoryFieldName, IndexTypes.Content).Compile()); Assert.AreEqual(21, result.TotalItemCount); - } - - } /// @@ -221,15 +216,13 @@ namespace Umbraco.Tests.UmbracoExamine //create the whole thing indexer.RebuildIndex(); - + //now delete a node that has children indexer.DeleteFromIndex(1140.ToString()); //this node had children: 1141 & 1142, let's ensure they are also removed - - var results = searcher.Search(searcher.CreateCriteria().Id(1141).Compile()); Assert.AreEqual(0, results.Count()); @@ -240,6 +233,5 @@ namespace Umbraco.Tests.UmbracoExamine } private readonly ExamineDemoDataMediaService _mediaService = new ExamineDemoDataMediaService(); - } } diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index c71dc6f242..80dbe08343 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -23,7 +23,6 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Test_Sort_Order_Sorting() { - long totalRecs; var demoData = new ExamineDemoDataContentService(TestFiles.umbraco_sort); var allRecs = demoData.GetLatestContentByXPath("//*[@isDoc]") @@ -39,6 +38,7 @@ namespace Umbraco.Tests.UmbracoExamine m.CreateDate == (DateTime)x.Attribute("createDate") && m.UpdateDate == (DateTime)x.Attribute("updateDate") && m.Name == (string)x.Attribute("nodeName") && + m.GetName(It.IsAny()) == (string)x.Attribute("nodeName") && m.Path == (string)x.Attribute("path") && m.Properties == new PropertyCollection() && m.Published == true && diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index c5a0e5ac0b..60fb746423 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -15,7 +15,7 @@ namespace Umbraco.Web.Models [DebuggerDisplay("Content Id: {Id}, Name: {Name}")] public abstract class PublishedContentBase : IPublishedContent { - private string _url; // fixme - cannot cache urls! depends on the current request! + private string _url; // fixme - cannot cache urls, they depends on the current request #region ContentType @@ -79,7 +79,7 @@ namespace Umbraco.Web.Models /// The url of documents are computed by the document url providers. The url of medias are, at the moment, /// computed here from the 'umbracoFile' property -- but we should move to media url providers at some point. /// - public virtual string GetUrl(string culture = ".") // fixme - consider .GetCulture("fr-FR").Url + public virtual string GetUrl(string culture = null) // fixme - consider .GetCulture("fr-FR").Url { switch (ItemType) { @@ -133,7 +133,7 @@ namespace Umbraco.Web.Models } /// - public abstract PublishedCultureInfos GetCulture(string culture = "."); + public abstract PublishedCultureInfos GetCulture(string culture = null); /// public abstract IReadOnlyDictionary Cultures { get; } diff --git a/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureAccessor.cs b/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureAccessor.cs index 109187c770..080149864b 100644 --- a/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureAccessor.cs +++ b/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureAccessor.cs @@ -8,6 +8,9 @@ /// /// Gets the system default culture. /// + /// + /// Implementations must NOT return a null value. Return an empty string for the invariant culture. + /// string DefaultCulture { get; } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs index ddb9607575..9f0b3cf7fa 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs @@ -1,14 +1,26 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; namespace Umbraco.Web.PublishedCache.NuCache.DataSource { internal class PropertyData { + private string _culture; + private string _segment; + [JsonProperty("culture")] - public string Culture { get; set; } + public string Culture + { + get => _culture; + set => _culture = value ?? throw new ArgumentNullException(nameof(value)); + } [JsonProperty("seg")] - public string Segment { get; set; } + public string Segment + { + get => _segment; + set => _segment = value ?? throw new ArgumentNullException(nameof(value)); + } [JsonProperty("val")] public object Value { get; set; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index 6231425e50..22e10c1cb7 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -52,7 +52,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { foreach (var sourceValue in sourceValues) { - if (sourceValue.Culture == null && sourceValue.Segment == null) + if (sourceValue.Culture == "" && sourceValue.Segment == "") { _sourceValue = sourceValue.Value; } @@ -89,7 +89,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _variations = origin._variations; } - public override bool HasValue(string culture = ".", string segment = ".") => _sourceValue != null + public override bool HasValue(string culture = null, string segment = null) => _sourceValue != null && (!(_sourceValue is string) || string.IsNullOrWhiteSpace((string) _sourceValue) == false); // used to cache the recursive *property* for this property @@ -150,7 +150,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // this is always invoked from within a lock, so does not require its own lock private object GetInterValue(string culture, string segment) { - if (culture == null && segment == null) + if (culture == "" && segment == "") { if (_interInitialized) return _interValue; _interValue = PropertyType.ConvertSourceToInter(_content, _sourceValue, _isPreviewing); @@ -163,7 +163,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var k = new CompositeStringStringKey(culture, segment); if (!_sourceValues.TryGetValue(k, out var vvalue)) - _sourceValues[k] = vvalue = new SourceInterValue { Culture = culture, Segment = segment }; + _sourceValues[k] = vvalue = new SourceInterValue { Culture = culture, Segment = segment, SourceValue = GetSourceValue(culture, segment) }; // fixme where is the source? if (vvalue.InterInitialized) return vvalue.InterValue; vvalue.InterValue = PropertyType.ConvertSourceToInter(_content, vvalue.SourceValue, _isPreviewing); @@ -171,11 +171,11 @@ namespace Umbraco.Web.PublishedCache.NuCache return vvalue.InterValue; } - public override object GetSourceValue(string culture = ".", string segment = ".") + public override object GetSourceValue(string culture = null, string segment = null) { ContextualizeVariation(ref culture, ref segment); - if (culture == null && segment == null) + if (culture == "" && segment == "") return _sourceValue; lock (_locko) @@ -187,16 +187,16 @@ namespace Umbraco.Web.PublishedCache.NuCache private void ContextualizeVariation(ref string culture, ref string segment) { - if (culture != "." && segment != ".") return; + if (culture != null && segment != null) return; // use context values // fixme CultureSegment? var publishedVariationContext = _content.VariationAccessor?.CurrentVariation; - if (culture == ".") culture = _variations.Has(ContentVariation.CultureNeutral) ? publishedVariationContext?.Culture : null; - if (segment == ".") segment = _variations.Has(ContentVariation.CultureNeutral) ? publishedVariationContext?.Segment : null; + if (culture == null) culture = _variations.Has(ContentVariation.CultureNeutral) ? publishedVariationContext?.Culture : ""; + if (segment == null) segment = _variations.Has(ContentVariation.CultureNeutral) ? publishedVariationContext?.Segment : ""; } - public override object GetValue(string culture = ".", string segment = ".") + public override object GetValue(string culture = null, string segment = null) { ContextualizeVariation(ref culture, ref segment); @@ -217,7 +217,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return value; } - public override object GetXPathValue(string culture = ".", string segment = ".") + public override object GetXPathValue(string culture = null, string segment = null) { ContextualizeVariation(ref culture, ref segment); @@ -252,7 +252,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // this is always invoked from within a lock, so does not require its own lock public CacheValue For(string culture, string segment) { - if (culture == null && segment == null) + if (culture == "" && segment == "") return this; if (_values == null) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index ea2480ec7a..5382c3e184 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -182,7 +182,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return _contentData.Name; var culture = VariationAccessor.CurrentVariation.Culture; - if (culture == null) + if (culture == "") return _contentData.Name; return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos.Name : null; @@ -198,7 +198,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return _urlSegment; var culture = VariationAccessor.CurrentVariation.Culture; - if (culture == null) + if (culture == "") return _urlSegment; return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos.UrlSegment : null; @@ -240,14 +240,14 @@ namespace Umbraco.Web.PublishedCache.NuCache private static readonly IReadOnlyDictionary NoCultureInfos = new Dictionary(); /// - public override PublishedCultureInfos GetCulture(string culture = ".") + public override PublishedCultureInfos GetCulture(string culture = null) { // handle context culture - if (culture == ".") + if (culture == null) culture = VariationAccessor.CurrentVariation.Culture; // no invariant culture infos - if (culture == null) return null; + if (culture == "") return null; // get return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null; diff --git a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs b/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs index 9fa16d184c..d8db937ca8 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web.PublishedCache IsMember = propertyType.ContentType.ItemType == PublishedItemType.Member; } - public override bool HasValue(string culture = ".", string segment = ".") + public override bool HasValue(string culture = null, string segment = null) => _sourceValue != null && (!(_sourceValue is string s) || !string.IsNullOrWhiteSpace(s)); // used to cache the CacheValues of this property @@ -136,9 +136,9 @@ namespace Umbraco.Web.PublishedCache return _interValue; } - public override object GetSourceValue(string culture = ".", string segment = ".") => _sourceValue; + public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue; - public override object GetValue(string culture = ".", string segment = ".") + public override object GetValue(string culture = null, string segment = null) { GetCacheLevels(out var cacheLevel, out var referenceCacheLevel); @@ -152,7 +152,7 @@ namespace Umbraco.Web.PublishedCache } } - public override object GetXPathValue(string culture = ".", string segment = ".") + public override object GetXPathValue(string culture = null, string segment = null) { GetCacheLevels(out var cacheLevel, out var referenceCacheLevel); diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index 8983f1e152..c480e5f4ae 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -144,7 +144,7 @@ namespace Umbraco.Web.PublishedCache public override string Name => _member.Name; - public override PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); + public override PublishedCultureInfos GetCulture(string culture = null) => throw new NotSupportedException(); public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); diff --git a/src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs b/src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs index 42e1c4dbca..ced9a0e134 100644 --- a/src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs +++ b/src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs @@ -19,6 +19,6 @@ namespace Umbraco.Web.PublishedCache } /// - public string DefaultCulture => _localizationService.GetDefaultLanguageIsoCode(); // fast + public string DefaultCulture => _localizationService.GetDefaultLanguageIsoCode() ?? ""; // fast } } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs index b72425085e..45a8ebbb7a 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs @@ -154,7 +154,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public override string Name => _name; - public override PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); + public override PublishedCultureInfos GetCulture(string culture = null) => throw new NotSupportedException(); public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs index 3ad30b4d3f..f85f8f8640 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs @@ -150,7 +150,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache } } - public override PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException(); + public override PublishedCultureInfos GetCulture(string culture = null) => throw new NotSupportedException(); public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs index 57b81b8c73..ea8ab925c6 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs @@ -27,13 +27,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache /// /// Gets the raw value of the property. /// - public override object GetSourceValue(string culture = ".", string segment = ".") => _sourceValue; + public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue; // in the Xml cache, everything is a string, and to have a value // you want to have a non-null, non-empty string. - public override bool HasValue(string culture = ".", string segment = ".") => _sourceValue.Trim().Length > 0; + public override bool HasValue(string culture = null, string segment = null) => _sourceValue.Trim().Length > 0; - public override object GetValue(string culture = ".", string segment = ".") + public override object GetValue(string culture = null, string segment = null) { // NOT caching the source (intermediate) value since we'll never need it // everything in Xml cache is per-request anyways diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 3b4c976d70..4edd6b3016 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -139,7 +139,7 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedContent content, string alias, string culture = ".", string segment = ".", object defaultValue = default, bool recurse = false) + public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, bool recurse = false) { // fixme - refactor with fallback var property = content.GetProperty(alias, recurse); @@ -168,7 +168,7 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedContent content, string alias, string culture = ".", string segment = ".", T defaultValue = default, bool recurse = false) + public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, bool recurse = false) { // fixme - refactor with fallback var property = content.GetProperty(alias, recurse); diff --git a/src/Umbraco.Web/PublishedContentPropertyExtension.cs b/src/Umbraco.Web/PublishedContentPropertyExtension.cs index e460445c6c..d57c2437a8 100644 --- a/src/Umbraco.Web/PublishedContentPropertyExtension.cs +++ b/src/Umbraco.Web/PublishedContentPropertyExtension.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web { #region Value - public static T Value(this IPublishedProperty property, string culture = ".", string segment = ".", T defaultValue = default) + public static T Value(this IPublishedProperty property, string culture = null, string segment = null, T defaultValue = default) { // for Value when defaultValue is not specified, and HasValue() is false, we still want to convert the result (see below) // but we have no way to tell whether default value is specified or not - we could do it with overloads, but then defaultValue diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index a7776bb374..2529146b17 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -48,7 +48,7 @@ namespace Umbraco.Web /// Gets a value indicating whether the content has a value for a property identified by its alias. /// /// Returns true if GetProperty(alias) is not null and GetProperty(alias).HasValue is true. - public static bool HasValue(this IPublishedElement content, string alias, string culture = ".", string segment = ".") + public static bool HasValue(this IPublishedElement content, string alias, string culture = null, string segment = null) { var prop = content.GetProperty(alias); return prop != null && prop.HasValue(culture, segment); @@ -92,7 +92,7 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedElement content, string alias, string culture = ".", string segment = ".", object defaultValue = default) + public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default) { var property = content.GetProperty(alias); if (property == null || !property.HasValue(culture, segment)) return defaultValue; @@ -121,7 +121,7 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedElement content, string alias, string culture = ".", string segment = ".", T defaultValue = default) + public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default) { var property = content.GetProperty(alias); if (property == null) return defaultValue; diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index b88245b917..d2fba48472 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -79,7 +79,7 @@ namespace Umbraco.Web.Routing /// A culture. /// The current absolute url. /// The url for the published content. - public string GetUrl(IPublishedContent content, string culture = ".", Uri current = null) + public string GetUrl(IPublishedContent content, string culture = null, Uri current = null) => GetUrl(content, Mode, culture, current); /// @@ -94,7 +94,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on Mode and on current, unless /// absolute is true, in which case the url is always absolute. /// - public string GetUrl(IPublishedContent content, bool absolute, Uri current = null, string culture = ".") + public string GetUrl(IPublishedContent content, bool absolute, Uri current = null, string culture = null) => GetUrl(content, GetMode(absolute), culture, current); /// @@ -105,7 +105,7 @@ namespace Umbraco.Web.Routing /// A culture. /// The current absolute url. /// The url for the published content. - public string GetUrl(IPublishedContent content, UrlProviderMode mode, Uri current = null, string culture = ".") + public string GetUrl(IPublishedContent content, UrlProviderMode mode, Uri current = null, string culture = null) => GetUrl(content, mode, culture, current); /// @@ -115,7 +115,7 @@ namespace Umbraco.Web.Routing /// A culture. /// The current absolute url. /// The url for the published content. - public string GetUrl(Guid id, string culture = ".", Uri current = null) + public string GetUrl(Guid id, string culture = null, Uri current = null) => GetUrl(GetDocument(id), Mode, culture, current); /// @@ -130,7 +130,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on Mode and on current, unless /// absolute is true, in which case the url is always absolute. /// - public string GetUrl(Guid id, bool absolute, string culture = ".", Uri current = null) + public string GetUrl(Guid id, bool absolute, string culture = null, Uri current = null) => GetUrl(GetDocument(id), GetMode(absolute), culture, current); /// @@ -141,7 +141,7 @@ namespace Umbraco.Web.Routing /// A culture. /// The current absolute url. /// The url for the published content. - public string GetUrl(Guid id, UrlProviderMode mode, string culture = ".", Uri current = null) + public string GetUrl(Guid id, UrlProviderMode mode, string culture = null, Uri current = null) => GetUrl(GetDocument(id), mode, culture, current); /// @@ -151,7 +151,7 @@ namespace Umbraco.Web.Routing /// A culture. /// The current absolute url. /// The url for the published content. - public string GetUrl(int id, string culture = ".", Uri current = null) + public string GetUrl(int id, string culture = null, Uri current = null) => GetUrl(GetDocument(id), Mode, culture, current); /// @@ -166,7 +166,7 @@ namespace Umbraco.Web.Routing /// The url is absolute or relative depending on Mode and on current, unless /// absolute is true, in which case the url is always absolute. /// - public string GetUrl(int id, bool absolute, string culture = ".", Uri current = null) + public string GetUrl(int id, bool absolute, string culture = null, Uri current = null) => GetUrl(GetDocument(id), GetMode(absolute), culture, current); /// @@ -177,7 +177,7 @@ namespace Umbraco.Web.Routing /// A culture. /// The current absolute url. /// The url for the published content. - public string GetUrl(int id, UrlProviderMode mode, string culture = ".", Uri current = null) + public string GetUrl(int id, UrlProviderMode mode, string culture = null, Uri current = null) => GetUrl(GetDocument(id), mode, culture, current); /// @@ -194,13 +194,13 @@ namespace Umbraco.Web.Routing /// when no culture is specified, the current culture. /// If the provider is unable to provide a url, it returns "#". /// - public string GetUrl(IPublishedContent content, UrlProviderMode mode, string culture = ".", Uri current = null) + public string GetUrl(IPublishedContent content, UrlProviderMode mode, string culture = null, Uri current = null) { if (content == null) return "#"; // this the ONLY place where we deal with default culture - IUrlProvider always receive a culture - if (culture == ".") + if (culture == null) { culture = content.ContentType.Variations.Has(ContentVariation.CultureNeutral) // fixme CultureSegment ? _variationAccessor.CurrentVariation.Culture diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index f7660987ea..e3643aea2c 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -374,17 +374,17 @@ namespace umbraco _content = content; } - public override bool HasValue(string culture = ".", string segment = ".") + public override bool HasValue(string culture = null, string segment = null) { return _sourceValue != null && ((_sourceValue is string) == false || string.IsNullOrWhiteSpace((string)_sourceValue) == false); } - public override object GetSourceValue(string culture = ".", string segment = ".") + public override object GetSourceValue(string culture = null, string segment = null) { return _sourceValue; } - public override object GetValue(string culture = ".", string segment = ".") + public override object GetValue(string culture = null, string segment = null) { // isPreviewing is true here since we want to preview anyway... const bool isPreviewing = true; @@ -392,7 +392,7 @@ namespace umbraco return PropertyType.ConvertInterToObject(_content, PropertyCacheLevel.Unknown, source, isPreviewing); } - public override object GetXPathValue(string culture = ".", string segment = ".") + public override object GetXPathValue(string culture = null, string segment = null) { throw new NotImplementedException(); } @@ -476,14 +476,14 @@ namespace umbraco get { return _inner.Name; } } - public PublishedCultureInfos GetCulture(string culture = ".") + public PublishedCultureInfos GetCulture(string culture = null) { // handle context culture - if (culture == ".") + if (culture == null) culture = _variationAccessor.CurrentVariation.Culture; // no invariant culture infos - if (culture == null) return null; + if (culture == "") return null; // get return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null; @@ -564,7 +564,7 @@ namespace umbraco get { throw new NotImplementedException(); } } - public string GetUrl(string culture = ".") => throw new NotSupportedException(); + public string GetUrl(string culture = null) => throw new NotSupportedException(); public PublishedItemType ItemType { From ff7c74ec8aaa4ce33dc5b0eb482f9baac302c086 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 30 Apr 2018 21:29:49 +0200 Subject: [PATCH 10/22] Rename accessors --- .../ICurrentVariationAccessor.cs | 13 --------- .../IVariationContextAccessor.cs | 13 +++++++++ ... ThreadCultureVariationContextAccessor.cs} | 10 +++---- ...> ThreadStaticVariationContextAccessor.cs} | 8 +++--- ...urrentVariation.cs => VariationContext.cs} | 8 +++--- src/Umbraco.Core/Umbraco.Core.csproj | 8 +++--- .../PublishedContentCacheTests.cs | 4 +-- .../PublishedContent/NuCacheTests.cs | 10 +++---- .../PublishedContentMoreTests.cs | 2 +- .../Scoping/ScopedNuCacheTests.cs | 6 ++-- .../Security/BackOfficeCookieManagerTests.cs | 4 +-- .../TestControllerActivatorBase.cs | 2 +- .../TestHelpers/Stubs/TestPublishedContent.cs | 4 +-- .../TestHelpers/TestObjects-Mocks.cs | 2 +- .../TestHelpers/TestWithDatabaseBase.cs | 12 ++++---- ...essor.cs => TestDefaultCultureAccessor.cs} | 2 +- ...sor.cs => TestVariationContextAccessor.cs} | 6 ++-- .../Testing/TestingTests/MockTests.cs | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 4 +-- ...RenderIndexActionSelectorAttributeTests.cs | 8 +++--- .../Web/Mvc/SurfaceControllerTests.cs | 10 +++---- .../Web/Mvc/UmbracoViewPageTests.cs | 4 +-- .../Web/TemplateUtilitiesTests.cs | 2 +- .../Web/WebExtensionMethodTests.cs | 6 ++-- .../Cache/CacheRefresherComponent.cs | 2 +- src/Umbraco.Web/Editors/MacroController.cs | 8 +++--- ...eAccessor.cs => DefaultCultureAccessor.cs} | 8 +++--- ...Accessor.cs => IDefaultCultureAccessor.cs} | 4 +-- .../PublishedCache/NuCache/ContentNode.cs | 16 +++++------ .../PublishedCache/NuCache/ContentNodeKit.cs | 4 +-- .../PublishedCache/NuCache/ContentStore.cs | 12 ++++---- .../PublishedCache/NuCache/MemberCache.cs | 14 +++++----- .../PublishedCache/NuCache/Property.cs | 2 +- .../NuCache/PublishedContent.cs | 16 +++++------ .../PublishedCache/NuCache/PublishedMember.cs | 8 +++--- .../NuCache/PublishedSnapshotService.cs | 22 +++++++-------- .../PublishedSnapshotServiceBase.cs | 6 ++-- .../XmlPublishedCache/DomainCache.cs | 4 +-- .../PublishedSnapshotService.cs | 28 +++++++++---------- .../XmlPublishedCache/XmlCacheComponent.cs | 4 +-- src/Umbraco.Web/Routing/UrlProvider.cs | 16 +++++------ .../Runtime/WebRuntimeComponent.cs | 8 +++--- src/Umbraco.Web/Umbraco.Web.csproj | 4 +-- src/Umbraco.Web/UmbracoContext.cs | 12 ++++---- src/Umbraco.Web/UmbracoModule.cs | 4 +-- src/Umbraco.Web/umbraco.presentation/page.cs | 12 ++++---- 46 files changed, 182 insertions(+), 182 deletions(-) delete mode 100644 src/Umbraco.Core/Models/PublishedContent/ICurrentVariationAccessor.cs create mode 100644 src/Umbraco.Core/Models/PublishedContent/IVariationContextAccessor.cs rename src/Umbraco.Core/Models/PublishedContent/{ThreadCultureCurrentVariationAccessor.cs => ThreadCultureVariationContextAccessor.cs} (58%) rename src/Umbraco.Core/Models/PublishedContent/{ThreadStaticCurrentVariationAccessor.cs => ThreadStaticVariationContextAccessor.cs} (66%) rename src/Umbraco.Core/Models/PublishedContent/{CurrentVariation.cs => VariationContext.cs} (71%) rename src/Umbraco.Tests/Testing/Objects/Accessors/{TestSystemDefaultCultureAccessor.cs => TestDefaultCultureAccessor.cs} (79%) rename src/Umbraco.Tests/Testing/Objects/Accessors/{TestCurrentVariationAccessor.cs => TestVariationContextAccessor.cs} (52%) rename src/Umbraco.Web/PublishedCache/{SystemDefaultCultureAccessor.cs => DefaultCultureAccessor.cs} (58%) rename src/Umbraco.Web/PublishedCache/{ISystemDefaultCultureAccessor.cs => IDefaultCultureAccessor.cs} (80%) diff --git a/src/Umbraco.Core/Models/PublishedContent/ICurrentVariationAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/ICurrentVariationAccessor.cs deleted file mode 100644 index 969601f080..0000000000 --- a/src/Umbraco.Core/Models/PublishedContent/ICurrentVariationAccessor.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Umbraco.Core.Models.PublishedContent -{ - /// - /// Gives access to the current . - /// - public interface ICurrentVariationAccessor - { - /// - /// Gets or sets the current . - /// - CurrentVariation CurrentVariation { get; set; } - } -} diff --git a/src/Umbraco.Core/Models/PublishedContent/IVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/IVariationContextAccessor.cs new file mode 100644 index 0000000000..b9c416da00 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/IVariationContextAccessor.cs @@ -0,0 +1,13 @@ +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Gives access to the current . + /// + public interface IVariationContextAccessor + { + /// + /// Gets or sets the current . + /// + VariationContext VariationContext { get; set; } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/ThreadCultureCurrentVariationAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/ThreadCultureVariationContextAccessor.cs similarity index 58% rename from src/Umbraco.Core/Models/PublishedContent/ThreadCultureCurrentVariationAccessor.cs rename to src/Umbraco.Core/Models/PublishedContent/ThreadCultureVariationContextAccessor.cs index f0c9e87a29..7a000c223a 100644 --- a/src/Umbraco.Core/Models/PublishedContent/ThreadCultureCurrentVariationAccessor.cs +++ b/src/Umbraco.Core/Models/PublishedContent/ThreadCultureVariationContextAccessor.cs @@ -5,18 +5,18 @@ using System.Threading; namespace Umbraco.Core.Models.PublishedContent { /// - /// Provides a CurrentUICulture-based implementation of . + /// Provides a CurrentUICulture-based implementation of . /// /// /// This accessor does not support segments. There is no need to set the current context. /// - public class ThreadCultureCurrentVariationAccessor : ICurrentVariationAccessor + public class ThreadCultureVariationContextAccessor : IVariationContextAccessor { - private readonly ConcurrentDictionary _contexts = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _contexts = new ConcurrentDictionary(); - public CurrentVariation CurrentVariation + public VariationContext VariationContext { - get => _contexts.GetOrAdd(Thread.CurrentThread.CurrentUICulture.Name, culture => new CurrentVariation(culture)); + get => _contexts.GetOrAdd(Thread.CurrentThread.CurrentUICulture.Name, culture => new VariationContext(culture)); set => throw new NotSupportedException(); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/ThreadStaticCurrentVariationAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/ThreadStaticVariationContextAccessor.cs similarity index 66% rename from src/Umbraco.Core/Models/PublishedContent/ThreadStaticCurrentVariationAccessor.cs rename to src/Umbraco.Core/Models/PublishedContent/ThreadStaticVariationContextAccessor.cs index 19ec89ff97..f77bb3514c 100644 --- a/src/Umbraco.Core/Models/PublishedContent/ThreadStaticCurrentVariationAccessor.cs +++ b/src/Umbraco.Core/Models/PublishedContent/ThreadStaticVariationContextAccessor.cs @@ -3,18 +3,18 @@ namespace Umbraco.Core.Models.PublishedContent { /// - /// Provides a ThreadStatic-based implementation of . + /// Provides a ThreadStatic-based implementation of . /// /// /// Something must set the current context. /// - public class ThreadStaticCurrentVariationAccessor : ICurrentVariationAccessor + public class ThreadStaticVariationContextAccessor : IVariationContextAccessor { [ThreadStatic] - private static CurrentVariation _context; + private static VariationContext _context; /// - public CurrentVariation CurrentVariation + public VariationContext VariationContext { get => _context; set => _context = value; diff --git a/src/Umbraco.Core/Models/PublishedContent/CurrentVariation.cs b/src/Umbraco.Core/Models/PublishedContent/VariationContext.cs similarity index 71% rename from src/Umbraco.Core/Models/PublishedContent/CurrentVariation.cs rename to src/Umbraco.Core/Models/PublishedContent/VariationContext.cs index 74a8c403c3..b83002fdce 100644 --- a/src/Umbraco.Core/Models/PublishedContent/CurrentVariation.cs +++ b/src/Umbraco.Core/Models/PublishedContent/VariationContext.cs @@ -1,14 +1,14 @@ namespace Umbraco.Core.Models.PublishedContent { /// - /// Represents the current variation. + /// Represents the variation context. /// - public class CurrentVariation + public class VariationContext { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public CurrentVariation(string culture = null, string segment = null) + public VariationContext(string culture = null, string segment = null) { Culture = culture ?? ""; // cannot be null, default to invariant Segment = segment ?? ""; // cannot be null, default to neutral diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 9284a12cbd..2d4b59cf3f 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -369,11 +369,11 @@ - + - - - + + + diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index 516fdbdd0e..bf70922768 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Cache.PublishedCache _xml.LoadXml(GetXml()); var xmlStore = new XmlStore(() => _xml, null, null, null); var cacheProvider = new StaticCacheProvider(); - var domainCache = new DomainCache(ServiceContext.DomainService, SystemDefaultCultureAccessor); + var domainCache = new DomainCache(ServiceContext.DomainService, DefaultCultureAccessor); var publishedShapshot = new Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedSnapshot( new PublishedContentCache(xmlStore, domainCache, cacheProvider, globalSettings, new SiteDomainHelper(), ContentTypesCache, null, null), new PublishedMediaCache(xmlStore, ServiceContext.MediaService, ServiceContext.UserService, cacheProvider, ContentTypesCache), @@ -79,7 +79,7 @@ namespace Umbraco.Tests.Cache.PublishedCache umbracoSettings, Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor()); + new TestVariationContextAccessor()); _cache = _umbracoContext.ContentCache; } diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index fbe6708008..1ae6a9f6fb 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -128,7 +128,7 @@ namespace Umbraco.Tests.PublishedContent dataTypeService); // create a variation accessor - var variationAccessor = new TestCurrentVariationAccessor(); + var variationAccessor = new TestVariationContextAccessor(); // at last, create the complete NuCache snapshot service! var options = new PublishedSnapshotService.Options { IgnoreLocalDb = true }; @@ -145,7 +145,7 @@ namespace Umbraco.Tests.PublishedContent Mock.Of(), Mock.Of(), Mock.Of(), - new TestSystemDefaultCultureAccessor(), + new TestDefaultCultureAccessor(), dataSource, globalSettings, new SiteDomainHelper()); @@ -155,7 +155,7 @@ namespace Umbraco.Tests.PublishedContent var publishedContent = snapshot.Content.GetById(1); // invariant is the current default - variationAccessor.CurrentVariation = new CurrentVariation(); + variationAccessor.VariationContext = new VariationContext(); Assert.IsNotNull(publishedContent); Assert.AreEqual("It Works1!", publishedContent.Name); @@ -176,14 +176,14 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual("name-uk2", draftContent.GetCulture("en-UK").Name); // now french is default - variationAccessor.CurrentVariation = new CurrentVariation("fr-FR"); + variationAccessor.VariationContext = new VariationContext("fr-FR"); Assert.AreEqual("val-fr1", publishedContent.Value("prop")); Assert.AreEqual("name-fr1", publishedContent.GetCulture().Name); Assert.AreEqual("name-fr1", publishedContent.Name); Assert.AreEqual(new DateTime(2018, 01, 01, 01, 00, 00), publishedContent.GetCulture().Date); // now uk is default - variationAccessor.CurrentVariation = new CurrentVariation("en-UK"); + variationAccessor.VariationContext = new VariationContext("en-UK"); Assert.AreEqual("val-uk1", publishedContent.Value("prop")); Assert.AreEqual("name-uk1", publishedContent.GetCulture().Name); Assert.AreEqual("name-uk1", publishedContent.Name); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 8a465a1da8..d0ac8b0216 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -76,7 +76,7 @@ namespace Umbraco.Tests.PublishedContent TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor()); + new TestVariationContextAccessor()); return umbracoContext; } diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index c1be083c38..d59fe0bb51 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -90,11 +90,11 @@ namespace Umbraco.Tests.Scoping contentTypeFactory, null, publishedSnapshotAccessor, - Mock.Of(), + Mock.Of(), Logger, ScopeProvider, documentRepository, mediaRepository, memberRepository, - SystemDefaultCultureAccessor, + DefaultCultureAccessor, new DatabaseDataSource(), Container.GetInstance(), new SiteDomainHelper()); } @@ -114,7 +114,7 @@ namespace Umbraco.Tests.Scoping umbracoSettings ?? SettingsForTests.GetDefaultUmbracoSettings(), urlProviders ?? Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor()); + new TestVariationContextAccessor()); if (setSingleton) Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; diff --git a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs index 49b4f3f5aa..11ef8a9411 100644 --- a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs @@ -34,7 +34,7 @@ namespace Umbraco.Tests.Security Mock.Of(), new WebSecurity(Mock.Of(), Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), new List(),globalSettings, - new TestCurrentVariationAccessor()); + new TestVariationContextAccessor()); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Install); var mgr = new BackOfficeCookieManager( @@ -54,7 +54,7 @@ namespace Umbraco.Tests.Security Mock.Of(), new WebSecurity(Mock.Of(), Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), new List(), globalSettings, - new TestCurrentVariationAccessor()); + new TestVariationContextAccessor()); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); var mgr = new BackOfficeCookieManager(Mock.Of(accessor => accessor.UmbracoContext == umbCtx), runtime, TestObjects.GetGlobalSettings()); diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 6053a1fad3..ded14c40ab 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -150,7 +150,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == UrlProviderMode.Auto.ToString())), Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor(), + new TestVariationContextAccessor(), true); //replace it var urlHelper = new Mock(); diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 19cb5473e3..46ff57c1f3 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -18,12 +18,12 @@ namespace Umbraco.Tests.TestHelpers.Stubs public int TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } - public ICurrentVariationAccessor VariationAccessor { get; set; } + public IVariationContextAccessor VariationContextAccessor { get; set; } public PublishedCultureInfos GetCulture(string culture = null) { // handle context culture if (culture == null) - culture = VariationAccessor?.CurrentVariation.Culture; + culture = VariationContextAccessor?.VariationContext.Culture; // no invariant culture infos if (culture == "" || Cultures == null) return null; diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 4b7fddb337..5745e501ab 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -120,7 +120,7 @@ namespace Umbraco.Tests.TestHelpers var urlProviders = Enumerable.Empty(); if (accessor == null) accessor = new TestUmbracoContextAccessor(); - return UmbracoContext.EnsureContext(accessor, httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, new TestCurrentVariationAccessor(), true); + return UmbracoContext.EnsureContext(accessor, httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, new TestVariationContextAccessor(), true); } public IUmbracoSettingsSection GetUmbracoSettings() diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index b08376fe8a..25a0dc72fa 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -78,7 +78,7 @@ namespace Umbraco.Tests.TestHelpers Container.Register(); Container.Register(factory => PublishedSnapshotService); - Container.Register(factory => SystemDefaultCultureAccessor); + Container.Register(factory => DefaultCultureAccessor); Container.GetInstance() .Clear() @@ -231,7 +231,7 @@ namespace Umbraco.Tests.TestHelpers } } - protected ISystemDefaultCultureAccessor SystemDefaultCultureAccessor { get; set; } + protected IDefaultCultureAccessor DefaultCultureAccessor { get; set; } protected IPublishedSnapshotService PublishedSnapshotService { get; set; } @@ -239,7 +239,7 @@ namespace Umbraco.Tests.TestHelpers { base.Initialize(); - SystemDefaultCultureAccessor = new TestSystemDefaultCultureAccessor(); + DefaultCultureAccessor = new TestDefaultCultureAccessor(); CreateAndInitializeDatabase(); @@ -264,14 +264,14 @@ namespace Umbraco.Tests.TestHelpers // testing=true so XmlStore will not use the file nor the database var publishedSnapshotAccessor = new UmbracoContextPublishedSnapshotAccessor(Umbraco.Web.Composing.Current.UmbracoContextAccessor); - var variationContextAccessor = new TestCurrentVariationAccessor(); + var variationContextAccessor = new TestVariationContextAccessor(); var service = new PublishedSnapshotService( ServiceContext, Container.GetInstance(), ScopeProvider, cache, publishedSnapshotAccessor, variationContextAccessor, Container.GetInstance(), Container.GetInstance(), Container.GetInstance(), - SystemDefaultCultureAccessor, + DefaultCultureAccessor, Logger, Container.GetInstance(), new SiteDomainHelper(), ContentTypesCache, @@ -379,7 +379,7 @@ namespace Umbraco.Tests.TestHelpers umbracoSettings ?? Container.GetInstance(), urlProviders ?? Enumerable.Empty(), globalSettings ?? Container.GetInstance(), - new TestCurrentVariationAccessor()); + new TestVariationContextAccessor()); if (setSingleton) Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestSystemDefaultCultureAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestDefaultCultureAccessor.cs similarity index 79% rename from src/Umbraco.Tests/Testing/Objects/Accessors/TestSystemDefaultCultureAccessor.cs rename to src/Umbraco.Tests/Testing/Objects/Accessors/TestDefaultCultureAccessor.cs index 6d78f0d290..41bd70626c 100644 --- a/src/Umbraco.Tests/Testing/Objects/Accessors/TestSystemDefaultCultureAccessor.cs +++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestDefaultCultureAccessor.cs @@ -2,7 +2,7 @@ namespace Umbraco.Tests.Testing.Objects.Accessors { - public class TestSystemDefaultCultureAccessor : ISystemDefaultCultureAccessor + public class TestDefaultCultureAccessor : IDefaultCultureAccessor { private string _defaultCulture = string.Empty; diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestCurrentVariationAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestVariationContextAccessor.cs similarity index 52% rename from src/Umbraco.Tests/Testing/Objects/Accessors/TestCurrentVariationAccessor.cs rename to src/Umbraco.Tests/Testing/Objects/Accessors/TestVariationContextAccessor.cs index 3b2ad36f00..3c7377f2cc 100644 --- a/src/Umbraco.Tests/Testing/Objects/Accessors/TestCurrentVariationAccessor.cs +++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestVariationContextAccessor.cs @@ -3,12 +3,12 @@ namespace Umbraco.Tests.Testing.Objects.Accessors { /// - /// Provides an implementation of for tests. + /// Provides an implementation of for tests. /// - public class TestCurrentVariationAccessor : ICurrentVariationAccessor + public class TestVariationContextAccessor : IVariationContextAccessor { /// - public CurrentVariation CurrentVariation + public VariationContext VariationContext { get; set; diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index bf4169e723..f21a953ae7 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -81,7 +81,7 @@ namespace Umbraco.Tests.Testing.TestingTests .Returns("/hello/world/1234"); var urlProvider = urlProviderMock.Object; - var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }, umbracoContext.CurrentVariationAccessor); + var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }, umbracoContext.VariationContextAccessor); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.InvariantNeutral); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index bf8dc2285a..a80728838b 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -181,9 +181,9 @@ - + - + diff --git a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs index 240cf2369a..f4c15f7c19 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs @@ -73,7 +73,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor(), + new TestVariationContextAccessor(), true); var ctrl = new MatchesDefaultIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); @@ -97,7 +97,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor(), + new TestVariationContextAccessor(), true); var ctrl = new MatchesOverriddenIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); @@ -121,7 +121,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor(), + new TestVariationContextAccessor(), true); var ctrl = new MatchesCustomIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); @@ -145,7 +145,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor(), + new TestVariationContextAccessor(), true); var ctrl = new MatchesAsyncIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 7ca258affc..4186c56636 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -46,7 +46,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor(), + new TestVariationContextAccessor(), true); var ctrl = new TestSurfaceController { UmbracoContext = umbracoContext }; @@ -68,7 +68,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor(), + new TestVariationContextAccessor(), true); var ctrl = new TestSurfaceController { UmbracoContext = umbCtx }; @@ -88,7 +88,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor(), + new TestVariationContextAccessor(), true); var controller = new TestSurfaceController { UmbracoContext = umbracoContext }; @@ -115,7 +115,7 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "AutoLegacy")), Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor(), + new TestVariationContextAccessor(), true); var helper = new UmbracoHelper( @@ -153,7 +153,7 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(section => section.WebRouting == webRoutingSettings), Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor(), + new TestVariationContextAccessor(), true); var content = Mock.Of(publishedContent => publishedContent.Id == 12345); diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index fba1e6b4f6..d5ac55827d 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -427,7 +427,7 @@ namespace Umbraco.Tests.Web.Mvc _service = new PublishedSnapshotService(svcCtx, factory, scopeProvider, cache, Enumerable.Empty(), null, null, null, null, null, - new TestSystemDefaultCultureAccessor(), + new TestDefaultCultureAccessor(), Current.Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), null, true, false); // no events var http = GetHttpContextFactory(url, routeData).HttpContext; @@ -441,7 +441,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, - new TestCurrentVariationAccessor()); + new TestVariationContextAccessor()); //if (setSingleton) //{ diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index 571f74089d..c6f7d8551e 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -104,7 +104,7 @@ namespace Umbraco.Tests.Web //pass in the custom url provider new[]{ testUrlProvider.Object }, globalSettings, - new TestCurrentVariationAccessor(), + new TestVariationContextAccessor(), true)) { var output = TemplateUtilities.ParseInternalLinks(input, umbCtx.UrlProvider); diff --git a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs index 2cc5861156..21b72a3832 100644 --- a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs +++ b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Web TestObjects.GetUmbracoSettings(), new List(), TestObjects.GetGlobalSettings(), - new TestCurrentVariationAccessor()); + new TestVariationContextAccessor()); var r1 = new RouteData(); r1.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbCtx); @@ -50,7 +50,7 @@ namespace Umbraco.Tests.Web TestObjects.GetUmbracoSettings(), new List(), TestObjects.GetGlobalSettings(), - new TestCurrentVariationAccessor()); + new TestVariationContextAccessor()); var r1 = new RouteData(); r1.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbCtx); @@ -78,7 +78,7 @@ namespace Umbraco.Tests.Web TestObjects.GetUmbracoSettings(), new List(), TestObjects.GetGlobalSettings(), - new TestCurrentVariationAccessor()); + new TestVariationContextAccessor()); var httpContext = Mock.Of(); diff --git a/src/Umbraco.Web/Cache/CacheRefresherComponent.cs b/src/Umbraco.Web/Cache/CacheRefresherComponent.cs index 5e3d2658f7..2ee0ff5f84 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherComponent.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherComponent.cs @@ -237,7 +237,7 @@ namespace Umbraco.Web.Cache UmbracoConfig.For.UmbracoSettings(), Current.UrlProviders, UmbracoConfig.For.GlobalSettings(), - Current.Container.GetInstance(), + Current.Container.GetInstance(), true); } diff --git a/src/Umbraco.Web/Editors/MacroController.cs b/src/Umbraco.Web/Editors/MacroController.cs index bc0d1f4a0c..0d765e9241 100644 --- a/src/Umbraco.Web/Editors/MacroController.cs +++ b/src/Umbraco.Web/Editors/MacroController.cs @@ -27,11 +27,11 @@ namespace Umbraco.Web.Editors [PluginController("UmbracoApi")] public class MacroController : UmbracoAuthorizedJsonController, IRequiresSessionState { - private readonly ICurrentVariationAccessor _variationAccessor; + private readonly IVariationContextAccessor _variationContextAccessor; - public MacroController(ICurrentVariationAccessor variationAccessor) + public MacroController(IVariationContextAccessor variationContextAccessor) { - _variationAccessor = variationAccessor; + _variationContextAccessor = variationContextAccessor; } /// @@ -121,7 +121,7 @@ namespace Umbraco.Web.Editors //the 'easiest' way might be to create an IPublishedContent manually and populate the legacy 'page' object with that //and then set the legacy parameters. - var legacyPage = new global::umbraco.page(doc, _variationAccessor); + var legacyPage = new global::umbraco.page(doc, _variationContextAccessor); UmbracoContext.HttpContext.Items["pageID"] = doc.Id; UmbracoContext.HttpContext.Items["pageElements"] = legacyPage.Elements; UmbracoContext.HttpContext.Items[global::Umbraco.Core.Constants.Conventions.Url.AltTemplate] = null; diff --git a/src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs b/src/Umbraco.Web/PublishedCache/DefaultCultureAccessor.cs similarity index 58% rename from src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs rename to src/Umbraco.Web/PublishedCache/DefaultCultureAccessor.cs index ced9a0e134..80d2d9f3a5 100644 --- a/src/Umbraco.Web/PublishedCache/SystemDefaultCultureAccessor.cs +++ b/src/Umbraco.Web/PublishedCache/DefaultCultureAccessor.cs @@ -3,17 +3,17 @@ namespace Umbraco.Web.PublishedCache { /// - /// Provides the default implementation of . + /// Provides the default implementation of . /// - public class SystemDefaultCultureAccessor : ISystemDefaultCultureAccessor + public class DefaultCultureAccessor : IDefaultCultureAccessor { private readonly ILocalizationService _localizationService; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// - public SystemDefaultCultureAccessor(ILocalizationService localizationService) + public DefaultCultureAccessor(ILocalizationService localizationService) { _localizationService = localizationService; } diff --git a/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureAccessor.cs b/src/Umbraco.Web/PublishedCache/IDefaultCultureAccessor.cs similarity index 80% rename from src/Umbraco.Web/PublishedCache/ISystemDefaultCultureAccessor.cs rename to src/Umbraco.Web/PublishedCache/IDefaultCultureAccessor.cs index 080149864b..b1c1edd4ee 100644 --- a/src/Umbraco.Web/PublishedCache/ISystemDefaultCultureAccessor.cs +++ b/src/Umbraco.Web/PublishedCache/IDefaultCultureAccessor.cs @@ -1,9 +1,9 @@ namespace Umbraco.Web.PublishedCache { /// - /// Provides the system default culture. + /// Gives access to the default culture. /// - public interface ISystemDefaultCultureAccessor + public interface IDefaultCultureAccessor { /// /// Gets the system default culture. diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs index 1a562dad79..2c2101b066 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs @@ -34,10 +34,10 @@ namespace Umbraco.Web.PublishedCache.NuCache DateTime createDate, int creatorId, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, - ICurrentVariationAccessor variationAccessor) + IVariationContextAccessor variationContextAccessor) : this(id, uid, level, path, sortOrder, parentContentId, createDate, creatorId) { - SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor, variationAccessor); + SetContentTypeAndData(contentType, draftData, publishedData, publishedSnapshotAccessor, variationContextAccessor); } // 2-phases ctor, phase 1 @@ -59,7 +59,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // two-phase ctor, phase 2 - public void SetContentTypeAndData(PublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) + public void SetContentTypeAndData(PublishedContentType contentType, ContentData draftData, ContentData publishedData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor) { ContentType = contentType; @@ -67,9 +67,9 @@ namespace Umbraco.Web.PublishedCache.NuCache throw new ArgumentException("Both draftData and publishedData cannot be null at the same time."); if (draftData != null) - Draft = new PublishedContent(this, draftData, publishedSnapshotAccessor, variationAccessor).CreateModel(); + Draft = new PublishedContent(this, draftData, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); if (publishedData != null) - Published = new PublishedContent(this, publishedData, publishedSnapshotAccessor, variationAccessor).CreateModel(); + Published = new PublishedContent(this, publishedData, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); } // clone parent @@ -98,7 +98,7 @@ namespace Umbraco.Web.PublishedCache.NuCache } // clone with new content type - public ContentNode(ContentNode origin, PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) + public ContentNode(ContentNode origin, PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor) { Id = origin.Id; Uid = origin.Uid; @@ -113,8 +113,8 @@ namespace Umbraco.Web.PublishedCache.NuCache var originDraft = origin.Draft == null ? null : PublishedContent.UnwrapIPublishedContent(origin.Draft); var originPublished = origin.Published == null ? null : PublishedContent.UnwrapIPublishedContent(origin.Published); - Draft = originDraft == null ? null : new PublishedContent(this, originDraft._contentData, publishedSnapshotAccessor, variationAccessor).CreateModel(); - Published = originPublished == null ? null : new PublishedContent(this, originPublished._contentData, publishedSnapshotAccessor, variationAccessor).CreateModel(); + Draft = originDraft == null ? null : new PublishedContent(this, originDraft._contentData, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); + Published = originPublished == null ? null : new PublishedContent(this, originPublished._contentData, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); ChildContentIds = origin.ChildContentIds; // can be the *same* list FIXME oh really? } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs index d4d4221bf0..f284d54cf1 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNodeKit.cs @@ -17,9 +17,9 @@ namespace Umbraco.Web.PublishedCache.NuCache public static ContentNodeKit Null { get; } = new ContentNodeKit { ContentTypeId = -1 }; - public void Build(PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) + public void Build(PublishedContentType contentType, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor) { - Node.SetContentTypeAndData(contentType, DraftData, PublishedData, publishedSnapshotAccessor, variationAccessor); + Node.SetContentTypeAndData(contentType, DraftData, PublishedData, publishedSnapshotAccessor, variationContextAccessor); } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index b19e9f5ddf..8a22259204 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // SnapDictionary has unit tests to ensure it all works correctly private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; - private readonly ICurrentVariationAccessor _variationAccessor; + private readonly IVariationContextAccessor _variationContextAccessor; private readonly ConcurrentDictionary> _contentNodes; private readonly ConcurrentDictionary> _contentRootNodes; private readonly ConcurrentDictionary> _contentTypesById; @@ -44,10 +44,10 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Ctor - public ContentStore(IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor, ILogger logger, BPlusTree localDb = null) + public ContentStore(IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, ILogger logger, BPlusTree localDb = null) { _publishedSnapshotAccessor = publishedSnapshotAccessor; - _variationAccessor = variationAccessor; + _variationContextAccessor = variationContextAccessor; _logger = logger; _localDb = localDb; @@ -279,7 +279,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (node == null) continue; var contentTypeId = node.ContentType.Id; if (index.TryGetValue(contentTypeId, out PublishedContentType contentType) == false) continue; - SetValueLocked(_contentNodes, node.Id, new ContentNode(node, contentType, _publishedSnapshotAccessor, _variationAccessor)); + SetValueLocked(_contentNodes, node.Id, new ContentNode(node, contentType, _publishedSnapshotAccessor, _variationContextAccessor)); } } finally @@ -393,7 +393,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _contentNodes.TryGetValue(id, out LinkedNode link); if (link?.Value == null) continue; - var node = new ContentNode(link.Value, contentType, _publishedSnapshotAccessor, _variationAccessor); + var node = new ContentNode(link.Value, contentType, _publishedSnapshotAccessor, _variationContextAccessor); SetValueLocked(_contentNodes, id, node); if (_localDb != null) RegisterChange(id, node.ToKit()); } @@ -416,7 +416,7 @@ namespace Umbraco.Web.PublishedCache.NuCache return false; // and use - kit.Build(link.Value, _publishedSnapshotAccessor, _variationAccessor); + kit.Build(link.Value, _publishedSnapshotAccessor, _variationContextAccessor); return true; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs index 61f3d9862d..ce8b5835e2 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs @@ -16,7 +16,7 @@ namespace Umbraco.Web.PublishedCache.NuCache class MemberCache : IPublishedMemberCache, INavigableData { private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; - public readonly ICurrentVariationAccessor VariationAccessor; + public readonly IVariationContextAccessor VariationContextAccessor; private readonly ICacheProvider _snapshotCache; private readonly IMemberService _memberService; private readonly IDataTypeService _dataTypeService; @@ -24,11 +24,11 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly PublishedContentTypeCache _contentTypeCache; private readonly bool _previewDefault; - public MemberCache(bool previewDefault, ICacheProvider snapshotCache, IMemberService memberService, IDataTypeService dataTypeService, ILocalizationService localizationService, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) + public MemberCache(bool previewDefault, ICacheProvider snapshotCache, IMemberService memberService, IDataTypeService dataTypeService, ILocalizationService localizationService, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor) { _snapshotCache = snapshotCache; _publishedSnapshotAccessor = publishedSnapshotAccessor; - VariationAccessor = variationAccessor; + VariationContextAccessor = variationContextAccessor; _memberService = memberService; _dataTypeService = dataTypeService; _localizationService = localizationService; @@ -65,14 +65,14 @@ namespace Umbraco.Web.PublishedCache.NuCache var member = _memberService.GetById(memberId); return member == null ? null - : PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, VariationAccessor); + : PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, VariationContextAccessor); }); } private IPublishedContent /*IPublishedMember*/ GetById(IMember member, bool previewing) { return GetCacheItem(CacheKeys.MemberCacheMember("ById", _previewDefault, member.Id), () => - PublishedMember.Create(member, GetContentType(member.ContentTypeId), previewing, _publishedSnapshotAccessor, VariationAccessor)); + PublishedMember.Create(member, GetContentType(member.ContentTypeId), previewing, _publishedSnapshotAccessor, VariationContextAccessor)); } public IPublishedContent /*IPublishedMember*/ GetByProviderKey(object key) @@ -107,7 +107,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public IPublishedContent /*IPublishedMember*/ GetByMember(IMember member) { - return PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, VariationAccessor); + return PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, VariationContextAccessor); } public IEnumerable GetAtRoot(bool preview) @@ -115,7 +115,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // because members are flat (not a tree) everything is at root // because we're loading everything... let's just not cache? var members = _memberService.GetAllMembers(); - return members.Select(m => PublishedMember.Create(m, GetContentType(m.ContentTypeId), preview, _publishedSnapshotAccessor, VariationAccessor)); + return members.Select(m => PublishedMember.Create(m, GetContentType(m.ContentTypeId), preview, _publishedSnapshotAccessor, VariationContextAccessor)); } public XPathNavigator CreateNavigator() diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index 22e10c1cb7..0c48b4b52c 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -191,7 +191,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // use context values // fixme CultureSegment? - var publishedVariationContext = _content.VariationAccessor?.CurrentVariation; + var publishedVariationContext = _content.VariationContextAccessor?.VariationContext; if (culture == null) culture = _variations.Has(ContentVariation.CultureNeutral) ? publishedVariationContext?.Culture : ""; if (segment == null) segment = _variations.Has(ContentVariation.CultureNeutral) ? publishedVariationContext?.Segment : ""; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 5382c3e184..844f962056 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -22,12 +22,12 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Constructors - public PublishedContent(ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) + public PublishedContent(ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor) { _contentNode = contentNode; _contentData = contentData; _publishedSnapshotAccessor = publishedSnapshotAccessor; - VariationAccessor = variationAccessor; // fixme why is this a property? should be be on the base class? + VariationContextAccessor = variationContextAccessor; // fixme why is this a property? should be be on the base class? _urlSegment = _contentData.Name.ToUrlSegment(); IsPreviewing = _contentData.Published == false; @@ -70,7 +70,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { _contentNode = contentNode; _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; - VariationAccessor = origin.VariationAccessor; + VariationContextAccessor = origin.VariationContextAccessor; _contentData = origin._contentData; _urlSegment = origin._urlSegment; @@ -86,7 +86,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private PublishedContent(PublishedContent origin) { _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; - VariationAccessor = origin.VariationAccessor; + VariationContextAccessor = origin.VariationContextAccessor; _contentNode = origin._contentNode; _contentData = origin._contentData; @@ -181,7 +181,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (!ContentType.Variations.Has(ContentVariation.CultureNeutral)) // fixme CultureSegment? return _contentData.Name; - var culture = VariationAccessor.CurrentVariation.Culture; + var culture = VariationContextAccessor.VariationContext.Culture; if (culture == "") return _contentData.Name; @@ -197,7 +197,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (!ContentType.Variations.Has(ContentVariation.CultureNeutral)) // fixme CultureSegment? return _urlSegment; - var culture = VariationAccessor.CurrentVariation.Culture; + var culture = VariationContextAccessor.VariationContext.Culture; if (culture == "") return _urlSegment; @@ -244,7 +244,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { // handle context culture if (culture == null) - culture = VariationAccessor.CurrentVariation.Culture; + culture = VariationContextAccessor.VariationContext.Culture; // no invariant culture infos if (culture == "") return null; @@ -397,7 +397,7 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Internal - internal ICurrentVariationAccessor VariationAccessor { get; } + internal IVariationContextAccessor VariationContextAccessor { get; } // used by navigable content internal IPublishedProperty[] PropertiesArray { get; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs index 4e01df0cd9..47c8d738f1 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs @@ -15,13 +15,13 @@ namespace Umbraco.Web.PublishedCache.NuCache { private readonly IMember _member; - private PublishedMember(IMember member, ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) - : base(contentNode, contentData, publishedSnapshotAccessor, variationAccessor) + private PublishedMember(IMember member, ContentNode contentNode, ContentData contentData, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor) + : base(contentNode, contentData, publishedSnapshotAccessor, variationContextAccessor) { _member = member; } - public static IPublishedContent Create(IMember member, PublishedContentType contentType, bool previewing, IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) + public static IPublishedContent Create(IMember member, PublishedContentType contentType, bool previewing, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor) { var d = new ContentData { @@ -37,7 +37,7 @@ namespace Umbraco.Web.PublishedCache.NuCache member.Level, member.Path, member.SortOrder, member.ParentId, member.CreateDate, member.CreatorId); - return new PublishedMember(member, n, d, publishedSnapshotAccessor, variationAccessor).CreateModel(); + return new PublishedMember(member, n, d, publishedSnapshotAccessor, variationContextAccessor).CreateModel(); } private static Dictionary GetPropertyValues(PublishedContentType contentType, IMember member) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 3108f24090..8a7f956e56 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -43,7 +43,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IMemberRepository _memberRepository; private readonly IGlobalSettings _globalSettings; private readonly ISiteDomainHelper _siteDomainHelper; - private readonly ISystemDefaultCultureAccessor _systemDefaultCultureAccessor; + private readonly IDefaultCultureAccessor _defaultCultureAccessor; // volatile because we read it with no lock private volatile bool _isReady; @@ -81,12 +81,12 @@ namespace Umbraco.Web.PublishedCache.NuCache public PublishedSnapshotService(Options options, MainDom mainDom, IRuntimeState runtime, ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IdkMap idkMap, - IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor, + IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, ILogger logger, IScopeProvider scopeProvider, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, - ISystemDefaultCultureAccessor systemDefaultCultureAccessor, + IDefaultCultureAccessor defaultCultureAccessor, IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) - : base(publishedSnapshotAccessor, variationAccessor) + : base(publishedSnapshotAccessor, variationContextAccessor) { //if (Interlocked.Increment(ref _singletonCheck) > 1) // throw new Exception("Singleton must be instancianted only once!"); @@ -99,7 +99,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _documentRepository = documentRepository; _mediaRepository = mediaRepository; _memberRepository = memberRepository; - _systemDefaultCultureAccessor = systemDefaultCultureAccessor; + _defaultCultureAccessor = defaultCultureAccessor; _globalSettings = globalSettings; _siteDomainHelper = siteDomainHelper; @@ -145,13 +145,13 @@ namespace Umbraco.Web.PublishedCache.NuCache // stores are created with a db so they can write to it, but they do not read from it, // stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to // figure out whether it can read the dbs or it should populate them from sql - _contentStore = new ContentStore(publishedSnapshotAccessor, variationAccessor, logger, _localContentDb); - _mediaStore = new ContentStore(publishedSnapshotAccessor, variationAccessor, logger, _localMediaDb); + _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localContentDb); + _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localMediaDb); } else { - _contentStore = new ContentStore(publishedSnapshotAccessor, variationAccessor, logger); - _mediaStore = new ContentStore(publishedSnapshotAccessor, variationAccessor, logger); + _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger); + _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger); } _domainStore = new SnapDictionary(); @@ -996,7 +996,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var memberTypeCache = new PublishedContentTypeCache(null, null, _serviceContext.MemberTypeService, _publishedContentTypeFactory, _logger); - var defaultCulture = _systemDefaultCultureAccessor.DefaultCulture; + var defaultCulture = _defaultCultureAccessor.DefaultCulture; var domainCache = new DomainCache(domainSnap, defaultCulture); var domainHelper = new DomainHelper(domainCache, _siteDomainHelper); @@ -1004,7 +1004,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainHelper, _globalSettings, _serviceContext.LocalizationService), MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache), - MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, _serviceContext.DataTypeService, _serviceContext.LocalizationService, memberTypeCache, PublishedSnapshotAccessor, VariationAccessor), + MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, _serviceContext.DataTypeService, _serviceContext.LocalizationService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor), DomainCache = domainCache, SnapshotCache = snapshotCache, ElementsCache = elementsCache diff --git a/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs b/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs index b5d721e03c..74e08e92e8 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedSnapshotServiceBase.cs @@ -7,14 +7,14 @@ namespace Umbraco.Web.PublishedCache { abstract class PublishedSnapshotServiceBase : IPublishedSnapshotService { - protected PublishedSnapshotServiceBase(IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor) + protected PublishedSnapshotServiceBase(IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor) { PublishedSnapshotAccessor = publishedSnapshotAccessor; - VariationAccessor = variationAccessor; + VariationContextAccessor = variationContextAccessor; } public IPublishedSnapshotAccessor PublishedSnapshotAccessor { get; } - public ICurrentVariationAccessor VariationAccessor { get; } + public IVariationContextAccessor VariationContextAccessor { get; } // note: NOT setting _publishedSnapshotAccessor.PublishedSnapshot here because it is the // responsibility of the caller to manage what the 'current' facade is diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs index b0c5033c41..20397c07f8 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DomainCache.cs @@ -11,10 +11,10 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { private readonly IDomainService _domainService; - public DomainCache(IDomainService domainService, ISystemDefaultCultureAccessor systemDefaultCultureAccessor) + public DomainCache(IDomainService domainService, IDefaultCultureAccessor defaultCultureAccessor) { _domainService = domainService; - DefaultCulture = systemDefaultCultureAccessor.DefaultCulture; + DefaultCulture = defaultCultureAccessor.DefaultCulture; } public IEnumerable GetAll(bool includeWildcards) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs index ab59fc6aca..78585ba2e2 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs @@ -32,7 +32,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private readonly IUserService _userService; private readonly ICacheProvider _requestCache; private readonly IGlobalSettings _globalSettings; - private readonly ISystemDefaultCultureAccessor _systemDefaultCultureAccessor; + private readonly IDefaultCultureAccessor _defaultCultureAccessor; private readonly ISiteDomainHelper _siteDomainHelper; #region Constructors @@ -43,18 +43,18 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IScopeProvider scopeProvider, ICacheProvider requestCache, IEnumerable segmentProviders, - IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor, + IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, - ISystemDefaultCultureAccessor systemDefaultCultureAccessor, + IDefaultCultureAccessor defaultCultureAccessor, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, MainDom mainDom, bool testing = false, bool enableRepositoryEvents = true) : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, segmentProviders, - publishedSnapshotAccessor, variationAccessor, + publishedSnapshotAccessor, variationContextAccessor, documentRepository, mediaRepository, memberRepository, - systemDefaultCultureAccessor, + defaultCultureAccessor, logger, globalSettings, siteDomainHelper, null, mainDom, testing, enableRepositoryEvents) { } @@ -63,9 +63,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IPublishedContentTypeFactory publishedContentTypeFactory, IScopeProvider scopeProvider, ICacheProvider requestCache, - IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor, + IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, - ISystemDefaultCultureAccessor systemDefaultCultureAccessor, + IDefaultCultureAccessor defaultCultureAccessor, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, @@ -73,9 +73,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache MainDom mainDom, bool testing, bool enableRepositoryEvents) : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, Enumerable.Empty(), - publishedSnapshotAccessor, variationAccessor, + publishedSnapshotAccessor, variationContextAccessor, documentRepository, mediaRepository, memberRepository, - systemDefaultCultureAccessor, + defaultCultureAccessor, logger, globalSettings, siteDomainHelper, contentTypeCache, mainDom, testing, enableRepositoryEvents) { } @@ -84,16 +84,16 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IScopeProvider scopeProvider, ICacheProvider requestCache, IEnumerable segmentProviders, - IPublishedSnapshotAccessor publishedSnapshotAccessor, ICurrentVariationAccessor variationAccessor, + IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, - ISystemDefaultCultureAccessor systemDefaultCultureAccessor, + IDefaultCultureAccessor defaultCultureAccessor, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, PublishedContentTypeCache contentTypeCache, MainDom mainDom, bool testing, bool enableRepositoryEvents) - : base(publishedSnapshotAccessor, variationAccessor) + : base(publishedSnapshotAccessor, variationContextAccessor) { _routesCache = new RoutesCache(); _publishedContentTypeFactory = publishedContentTypeFactory; @@ -108,7 +108,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _memberService = serviceContext.MemberService; _mediaService = serviceContext.MediaService; _userService = serviceContext.UserService; - _systemDefaultCultureAccessor = systemDefaultCultureAccessor; + _defaultCultureAccessor = defaultCultureAccessor; _requestCache = requestCache; _globalSettings = globalSettings; @@ -153,7 +153,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // the current caches, but that would mean creating an extra cache (StaticCache // probably) so better use RequestCache. - var domainCache = new DomainCache(_domainService, _systemDefaultCultureAccessor); + var domainCache = new DomainCache(_domainService, _defaultCultureAccessor); return new PublishedSnapshot( new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _siteDomainHelper, _contentTypeCache, _routesCache, previewToken), diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs index 76af7102f5..496818ab28 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs @@ -29,11 +29,11 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache factory.GetInstance().RequestCache, factory.GetInstance(), factory.GetInstance(), - factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), - factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index d2fba48472..a1fd31a127 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -22,14 +22,14 @@ namespace Umbraco.Web.Routing /// The Umbraco context. /// Routing settings. /// The list of url providers. - /// The current variation accessor. - public UrlProvider(UmbracoContext umbracoContext, IWebRoutingSection routingSettings, IEnumerable urlProviders, ICurrentVariationAccessor variationAccessor) + /// The current variation accessor. + public UrlProvider(UmbracoContext umbracoContext, IWebRoutingSection routingSettings, IEnumerable urlProviders, IVariationContextAccessor variationContextAccessor) { if (routingSettings == null) throw new ArgumentNullException(nameof(routingSettings)); _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); _urlProviders = urlProviders; - _variationAccessor = variationAccessor ?? throw new ArgumentNullException(nameof(variationAccessor)); + _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); var provider = UrlProviderMode.Auto; Mode = provider; @@ -44,20 +44,20 @@ namespace Umbraco.Web.Routing /// /// The Umbraco context. /// The list of url providers. - /// The current variation accessor. + /// The current variation accessor. /// An optional provider mode. - public UrlProvider(UmbracoContext umbracoContext, IEnumerable urlProviders, ICurrentVariationAccessor variationAccessor, UrlProviderMode mode = UrlProviderMode.Auto) + public UrlProvider(UmbracoContext umbracoContext, IEnumerable urlProviders, IVariationContextAccessor variationContextAccessor, UrlProviderMode mode = UrlProviderMode.Auto) { _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); _urlProviders = urlProviders; - _variationAccessor = variationAccessor; + _variationContextAccessor = variationContextAccessor; Mode = mode; } private readonly UmbracoContext _umbracoContext; private readonly IEnumerable _urlProviders; - private readonly ICurrentVariationAccessor _variationAccessor; + private readonly IVariationContextAccessor _variationContextAccessor; /// /// Gets or sets the provider url mode. @@ -203,7 +203,7 @@ namespace Umbraco.Web.Routing if (culture == null) { culture = content.ContentType.Variations.Has(ContentVariation.CultureNeutral) // fixme CultureSegment - ? _variationAccessor.CurrentVariation.Culture + ? _variationContextAccessor.VariationContext.Culture : null; } diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index d8602422d5..e472286902 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -71,8 +71,8 @@ namespace Umbraco.Web.Runtime composition.Container.RegisterFrom(); // register accessors for cultures - composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton(); var typeLoader = composition.Container.GetInstance(); var logger = composition.Container.GetInstance(); @@ -218,7 +218,7 @@ namespace Umbraco.Web.Runtime IUmbracoSettingsSection umbracoSettings, IGlobalSettings globalSettings, IEntityService entityService, - ICurrentVariationAccessor variationAccessor, + IVariationContextAccessor variationContextAccessor, UrlProviderCollection urlProviders) { // setup mvc and webapi services @@ -256,7 +256,7 @@ namespace Umbraco.Web.Runtime umbracoSettings, urlProviders, globalSettings, - variationAccessor); + variationContextAccessor); // ensure WebAPI is initialized, after everything GlobalConfiguration.Configuration.EnsureInitialized(); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e730c0b29c..362802787f 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -343,7 +343,7 @@ - + @@ -389,7 +389,7 @@ - + diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 53fab2e57a..a88716bec3 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -70,7 +70,7 @@ namespace Umbraco.Web IUmbracoSettingsSection umbracoSettings, IEnumerable urlProviders, IGlobalSettings globalSettings, - ICurrentVariationAccessor variationAccessor, + IVariationContextAccessor variationContextAccessor, bool replace = false) { if (umbracoContextAccessor == null) throw new ArgumentNullException(nameof(umbracoContextAccessor)); @@ -88,7 +88,7 @@ namespace Umbraco.Web // create & assign to accessor, dispose existing if any umbracoContextAccessor.UmbracoContext?.Dispose(); - return umbracoContextAccessor.UmbracoContext = new UmbracoContext(httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, variationAccessor); + return umbracoContextAccessor.UmbracoContext = new UmbracoContext(httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, variationContextAccessor); } // initializes a new instance of the UmbracoContext class @@ -101,14 +101,14 @@ namespace Umbraco.Web IUmbracoSettingsSection umbracoSettings, IEnumerable urlProviders, IGlobalSettings globalSettings, - ICurrentVariationAccessor variationAccessor) + IVariationContextAccessor variationContextAccessor) { 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)); - CurrentVariationAccessor = variationAccessor ?? throw new ArgumentNullException(nameof(variationAccessor)); + VariationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); _globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings)); // ensure that this instance is disposed when the request terminates, though we *also* ensure @@ -138,7 +138,7 @@ namespace Umbraco.Web // OriginalRequestUrl = GetRequestFromContext()?.Url ?? new Uri("http://localhost"); CleanedUmbracoUrl = UriUtility.UriToUmbraco(OriginalRequestUrl); - UrlProvider = new UrlProvider(this, umbracoSettings.WebRouting, urlProviders, variationAccessor); + UrlProvider = new UrlProvider(this, umbracoSettings.WebRouting, urlProviders, variationContextAccessor); } #endregion @@ -215,7 +215,7 @@ namespace Umbraco.Web /// public HttpContextBase HttpContext { get; } - public ICurrentVariationAccessor CurrentVariationAccessor { get; } + public IVariationContextAccessor VariationContextAccessor { get; } /// /// Creates and caches an instance of a DomainHelper diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 5a866939a8..8a2ae4ddb7 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -69,7 +69,7 @@ namespace Umbraco.Web internal IUmbracoDatabaseFactory DatabaseFactory { get; set; } [Inject] - internal ICurrentVariationAccessor CurrentVariationAccessor { get; set; } + internal IVariationContextAccessor VariationContextAccessor { get; set; } #endregion @@ -113,7 +113,7 @@ namespace Umbraco.Web UmbracoConfig.For.UmbracoSettings(), UrlProviders, GlobalSettings, - CurrentVariationAccessor, + VariationContextAccessor, true); } diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index e3643aea2c..837882eef4 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -108,8 +108,8 @@ namespace umbraco /// /// The content. /// This is for usage only. - internal page(IContent content, ICurrentVariationAccessor variationAccessor) - : this(new PagePublishedContent(content, variationAccessor)) + internal page(IContent content, IVariationContextAccessor variationContextAccessor) + : this(new PagePublishedContent(content, variationContextAccessor)) { } #endregion @@ -409,7 +409,7 @@ namespace umbraco private readonly IPublishedProperty[] _properties; private readonly IPublishedContent _parent; private IReadOnlyDictionary _cultureInfos; - private readonly ICurrentVariationAccessor _variationAccessor; + private readonly IVariationContextAccessor _variationContextAccessor; private static readonly IReadOnlyDictionary NoCultureInfos = new Dictionary(); @@ -418,13 +418,13 @@ namespace umbraco _id = id; } - public PagePublishedContent(IContent inner, ICurrentVariationAccessor variationAccessor) + public PagePublishedContent(IContent inner, IVariationContextAccessor variationContextAccessor) { if (inner == null) throw new NullReferenceException("content"); _inner = inner; - _variationAccessor = variationAccessor; + _variationContextAccessor = variationContextAccessor; _id = _inner.Id; _key = _inner.Key; @@ -480,7 +480,7 @@ namespace umbraco { // handle context culture if (culture == null) - culture = _variationAccessor.CurrentVariation.Culture; + culture = _variationContextAccessor.VariationContext.Culture; // no invariant culture infos if (culture == "") return null; From 92b9184706b43ebd1803ef1f002088b4589cdfe8 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 30 Apr 2018 22:05:07 +0200 Subject: [PATCH 11/22] Swap order of IContentBase.SetName parameters --- src/Umbraco.Core/Models/Content.cs | 10 +++++----- src/Umbraco.Core/Models/IContentBase.cs | 2 +- src/Umbraco.Tests/Models/VariationTests.cs | 14 +++++++------- src/Umbraco.Tests/Services/ContentServiceTests.cs | 12 ++++++------ src/Umbraco.Tests/Services/EntityServiceTests.cs | 8 ++++---- src/Umbraco.Web/Editors/ContentController.cs | 2 +- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 73f462b65d..6d8c0bbb05 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -350,7 +350,7 @@ namespace Umbraco.Core.Models var name = GetName(culture); if (string.IsNullOrWhiteSpace(name)) return false; //fixme this should return an attempt with error results - + SetPublishInfos(culture, name, DateTime.Now); } @@ -470,8 +470,8 @@ namespace Umbraco.Core.Models // copy names ClearNames(); - foreach (var (languageId, name) in other.Names) - SetName(languageId, name); + foreach (var (culture, name) in other.Names) + SetName(name, culture); Name = other.Name; } @@ -511,7 +511,7 @@ namespace Umbraco.Core.Models } // copy name - SetName(culture, other.GetName(culture)); + SetName(other.GetName(culture), culture); } /// @@ -545,7 +545,7 @@ namespace Umbraco.Core.Models } // copy name - SetName(culture, other.GetName(culture)); + SetName(other.GetName(culture), culture); } /// diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs index b6e05fa7dd..3007eea900 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Core/Models/IContentBase.cs @@ -35,7 +35,7 @@ namespace Umbraco.Core.Models /// When is null, sets the invariant /// language, which sets the property. /// - void SetName(string culture, string value); + void SetName(string value, string culture); /// /// Gets the name of the content item for a specified language. diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index 8502af0e68..1b9000617e 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -165,7 +165,7 @@ namespace Umbraco.Tests.Models const string langUk = "en-UK"; // throws if the content type does not support the variation - Assert.Throws(() => content.SetName(langFr, "name-fr")); + Assert.Throws(() => content.SetName("name-fr", langFr)); // now it will work contentType.Variations = ContentVariation.CultureNeutral; @@ -173,13 +173,13 @@ namespace Umbraco.Tests.Models // invariant name works content.Name = "name"; Assert.AreEqual("name", content.GetName(null)); - content.SetName(null, "name2"); + content.SetName("name2", null); Assert.AreEqual("name2", content.Name); Assert.AreEqual("name2", content.GetName(null)); // variant names work - content.SetName(langFr, "name-fr"); - content.SetName(langUk, "name-uk"); + content.SetName("name-fr", langFr); + content.SetName("name-uk", langUk); Assert.AreEqual("name-fr", content.GetName(langFr)); Assert.AreEqual("name-uk", content.GetName(langUk)); @@ -245,7 +245,7 @@ namespace Umbraco.Tests.Models // can publish value // and get edited and published values Assert.IsFalse(content.TryPublishValues(langFr)); // no name - content.SetName(langFr, "name-fr"); + content.SetName("name-fr", langFr); content.TryPublishValues(langFr); Assert.AreEqual("b", content.GetValue("prop")); Assert.IsNull(content.GetValue("prop", published: true)); @@ -326,11 +326,11 @@ namespace Umbraco.Tests.Models // works with a name // and then FR is available, and published - content.SetName(langFr, "name-fr"); + content.SetName("name-fr", langFr); content.TryPublishValues(langFr); // now UK is available too - content.SetName(langUk, "name-uk"); + content.SetName("name-uk", langUk); // test available, published Assert.IsTrue(content.IsCultureAvailable(langFr)); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 47f1d49c2a..354ff85351 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -2512,8 +2512,8 @@ namespace Umbraco.Tests.Services content.SetValue("author", "Barack Obama"); content.SetValue("prop", "value-fr1", langFr.IsoCode); content.SetValue("prop", "value-uk1", langUk.IsoCode); - content.SetName(langFr.IsoCode, "name-fr"); - content.SetName(langUk.IsoCode, "name-uk"); + content.SetName("name-fr", langFr.IsoCode); + content.SetName("name-uk", langUk.IsoCode); contentService.Save(content); // content has been saved, @@ -2603,9 +2603,9 @@ namespace Umbraco.Tests.Services // act - content.SetName(null, "Home US2"); - content.SetName(langFr.IsoCode, "name-fr2"); - content.SetName(langUk.IsoCode, "name-uk2"); + content.SetName("Home US2", null); + content.SetName("name-fr2", langFr.IsoCode); + content.SetName("name-uk2", langUk.IsoCode); content.SetValue("author", "Barack Obama2"); content.SetValue("prop", "value-fr2", langFr.IsoCode); content.SetValue("prop", "value-uk2", langUk.IsoCode); @@ -2803,7 +2803,7 @@ namespace Umbraco.Tests.Services // act - content.SetName(langUk.IsoCode, "name-uk3"); + content.SetName("name-uk3", langUk.IsoCode); contentService.Save(content); content2 = contentService.GetById(content.Id); diff --git a/src/Umbraco.Tests/Services/EntityServiceTests.cs b/src/Umbraco.Tests/Services/EntityServiceTests.cs index 8d8e127131..9c8d5c7b68 100644 --- a/src/Umbraco.Tests/Services/EntityServiceTests.cs +++ b/src/Umbraco.Tests/Services/EntityServiceTests.cs @@ -457,8 +457,8 @@ namespace Umbraco.Tests.Services ServiceContext.ContentTypeService.Save(contentType); var c1 = MockedContent.CreateSimpleContent(contentType, "Test", -1); - c1.SetName(_langFr.IsoCode, "Test - FR"); - c1.SetName(_langEs.IsoCode, "Test - ES"); + c1.SetName("Test - FR", _langFr.IsoCode); + c1.SetName("Test - ES", _langEs.IsoCode); ServiceContext.ContentService.Save(c1); var result = service.Get(c1.Id, UmbracoObjectTypes.Document); @@ -486,8 +486,8 @@ namespace Umbraco.Tests.Services var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root); if (i % 2 == 0) { - c1.SetName(_langFr.IsoCode, "Test " + i + " - FR"); - c1.SetName(_langEs.IsoCode, "Test " + i + " - ES"); + c1.SetName("Test " + i + " - FR", _langFr.IsoCode); + c1.SetName("Test " + i + " - ES", _langEs.IsoCode); } ServiceContext.ContentService.Save(c1); } diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index f1528c7fe6..12d11172fe 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -957,7 +957,7 @@ namespace Umbraco.Web.Editors if (contentItem.LanguageId.HasValue && contentItem.PersistedContent.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) { var culture = Services.LocalizationService.GetLanguageById(contentItem.LanguageId.Value).IsoCode; - contentItem.PersistedContent.SetName(culture, contentItem.Name); + contentItem.PersistedContent.SetName(contentItem.Name, culture); } else { From 65481542613e83e3a9520ad4719d11c87540cdf1 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 1 May 2018 12:45:07 +0200 Subject: [PATCH 12/22] Fixes --- src/Umbraco.Core/Models/ContentBase.cs | 2 +- .../Models/PublishedContent/IPublishedContent.cs | 14 ++++++++++++-- .../PublishedContent/PublishedCultureInfos.cs | 12 +----------- ...BTree.DictionaryOfCultureVariationSerializer.cs | 4 ++-- .../PublishedCache/NuCache/PublishedContent.cs | 6 ++++-- src/Umbraco.Web/umbraco.presentation/page.cs | 4 ++-- 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index b40e11e25d..61d0ddbb9c 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -165,7 +165,7 @@ namespace Umbraco.Core.Models } /// - public virtual void SetName(string culture, string name) + public virtual void SetName(string name, string culture) { if (string.IsNullOrWhiteSpace(name)) { diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 91412bce3f..a8db8a98a7 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -142,10 +142,20 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets a value indicating whether the content is draft. /// - /// A content is draft when it is the unpublished version of a content, which may - /// have a published version, or not. + /// + /// A content is draft when it is the unpublished version of a content, which may + /// have a published version, or not. + /// When retrieving documents from cache in non-preview mode, IsDraft is always false, + /// as only published documents are returned. When retrieving in preview mode, IsDraft can + /// either be true (document is not published, or has been edited, and what is returned + /// is the edited version) or false (document is published, and has not been edited, and + /// what is returned is the published version). + /// bool IsDraft { get; } + // fixme - consider having an IsPublished flag too + // so that when IsDraft is true, we can check whether there is a published version? + #endregion #region Tree diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs index 3f9f7e9882..a93f94672a 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Initializes a new instance of the class. /// - public PublishedCultureInfos(string culture, string name, bool published, DateTime date) + public PublishedCultureInfos(string culture, string name, DateTime date) { if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentNullOrEmptyException(nameof(culture)); if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); @@ -19,7 +19,6 @@ namespace Umbraco.Core.Models.PublishedContent Culture = culture; Name = name; UrlSegment = name.ToUrlSegment(culture); - Published = published; Date = date; } @@ -38,15 +37,6 @@ namespace Umbraco.Core.Models.PublishedContent /// public string UrlSegment { get; } - /// - /// Gets a value indicating whether the culture is published. - /// - /// - /// A published content item will only have published cultures, and therefore this - /// value will always be true. On the other hand, ... ??? - /// - public bool Published { get; } // fixme - what is culture.Published? - /// /// Gets the date associated with the culture. /// diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs index c9d809714d..ccd0e18dd7 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfCultureVariationSerializer.cs @@ -9,12 +9,12 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource { public IReadOnlyDictionary ReadFrom(Stream stream) { - var dict = new Dictionary(); - // read variations count var pcount = PrimitiveSerializer.Int32.ReadFrom(stream); + if (pcount == 0) return Empty; // read each variation + var dict = new Dictionary(); for (var i = 0; i < pcount; i++) { var languageId = PrimitiveSerializer.String.ReadFrom(stream); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 844f962056..40a127335a 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -263,8 +263,10 @@ namespace Umbraco.Web.PublishedCache.NuCache if (_cultureInfos != null) return _cultureInfos; - return _cultureInfos = _contentData.CultureInfos // fixme can it be null? - .ToDictionary(x => x.Key, x => new PublishedCultureInfos(x.Key, x.Value.Name, false, x.Value.Date)); // fixme values! + if (_contentData.CultureInfos == null) + throw new Exception("oops: _contentDate.CultureInfos is null."); + return _cultureInfos = _contentData.CultureInfos + .ToDictionary(x => x.Key, x => new PublishedCultureInfos(x.Key, x.Value.Name, x.Value.Date)); } } diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index 837882eef4..73461e1dc5 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -499,8 +499,8 @@ namespace umbraco if (_cultureInfos != null) return _cultureInfos; - return _cultureInfos = _inner.Names // fixme names, or PublishNames? - .ToDictionary(x => x.Key, x => new PublishedCultureInfos(x.Key, x.Value, false, _inner.GetCulturePublishDate(x.Key))); // fixme values! + return _cultureInfos = _inner.PublishNames + .ToDictionary(x => x.Key, x => new PublishedCultureInfos(x.Key, x.Value, _inner.GetCulturePublishDate(x.Key))); } } From a695b8aed5e24ee1952f54f9486799877c8f09e1 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 2 May 2018 08:55:21 +0200 Subject: [PATCH 13/22] Test host-less domains --- src/Umbraco.Core/UriExtensions.cs | 11 ++++ .../Routing/DomainsAndCulturesTests.cs | 58 ++++++++++++++++++- src/Umbraco.Web/Routing/DomainHelper.cs | 12 ++-- 3 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs index 742e71ad93..0c240336b7 100644 --- a/src/Umbraco.Core/UriExtensions.cs +++ b/src/Umbraco.Core/UriExtensions.cs @@ -322,5 +322,16 @@ namespace Umbraco.Core { return new Uri(uri.GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped)); } + + /// + /// Replaces the host of a uri. + /// + /// The uri. + /// A replacement host. + /// The same uri, with its host replaced. + public static Uri ReplaceHost(this Uri uri, string host) + { + return new UriBuilder(uri) { Host = host }.Uri; + } } } diff --git a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs index ad6a9cf0a9..a402a7cb0a 100644 --- a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs +++ b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs @@ -276,7 +276,7 @@ namespace Umbraco.Tests.Routing publishedRouter.FindDomain(frequest); Assert.AreEqual(expectedCulture, frequest.Culture.Name); - + var finder = new ContentFinderByUrl(Logger); var result = finder.TryFindContent(frequest); @@ -316,7 +316,7 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); + SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext(inputUrl, globalSettings:globalSettings.Object); var publishedRouter = CreatePublishedRouter(Container); @@ -335,9 +335,61 @@ namespace Umbraco.Tests.Routing Assert.IsTrue(result); Assert.AreEqual(expectedCulture, frequest.Culture.Name); Assert.AreEqual(frequest.PublishedContent.Id, expectedNode); + } + + // domains such as "/en" are natively supported, and when instanciating + // DomainAndUri for them, the host will come from the current request + // + private void SetDomains3() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("/en") + { + Id = 1, + LanguageId = LangEngId, + RootContentId = 10011, + LanguageIsoCode = "en-US" + }, + new UmbracoDomain("/fr") + { + Id = 1, + LanguageId = LangFrId, + RootContentId = 10012, + LanguageIsoCode = "fr-FR" + } + }); } + + #region Cases + [TestCase("http://domain1.com/en", "en-US", 10011)] + [TestCase("http://domain1.com/en/1001-1-1", "en-US", 100111)] + [TestCase("http://domain1.com/fr", "fr-FR", 10012)] + [TestCase("http://domain1.com/fr/1001-2-1", "fr-FR", 100121)] + #endregion + public void DomainGeneric(string inputUrl, string expectedCulture, int expectedNode) + { + SetDomains3(); + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + SettingsForTests.ConfigureSettings(globalSettings.Object); - + var umbracoContext = GetUmbracoContext(inputUrl, globalSettings:globalSettings.Object); + var publishedRouter = CreatePublishedRouter(Container); + var frequest = publishedRouter.CreateRequest(umbracoContext); + + // lookup domain + publishedRouter.FindDomain(frequest); + Assert.IsNotNull(frequest.Domain); + + Assert.AreEqual(expectedCulture, frequest.Culture.Name); + + var finder = new ContentFinderByUrl(Logger); + var result = finder.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.AreEqual(frequest.PublishedContent.Id, expectedNode); + } } } diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainHelper.cs index 3d6d435179..14779848bb 100644 --- a/src/Umbraco.Web/Routing/DomainHelper.cs +++ b/src/Umbraco.Web/Routing/DomainHelper.cs @@ -154,7 +154,7 @@ namespace Umbraco.Web.Routing { if (cultureDomains.Count == 1) // only 1, return return cultureDomains.First(); - + // else restrict to those domains, for base lookup considerForBaseDomains = cultureDomains; } @@ -163,7 +163,7 @@ namespace Umbraco.Web.Routing var baseDomains = SelectByBase(considerForBaseDomains, uri); if (baseDomains.Count > 0) // found, return return baseDomains.First(); - + // if nothing works, then try to run the filter to select a domain // either restricting on cultureDomains, or on all domains if (filter != null) @@ -179,17 +179,21 @@ namespace Umbraco.Web.Routing return null; } + private static bool IsBaseOf(DomainAndUri domain, Uri uri) + => domain.Uri.EndPathWithSlash().IsBaseOf(uri); + private static IReadOnlyCollection SelectByBase(IReadOnlyCollection domainsAndUris, Uri uri) { // look for domains that would be the base of the uri // ie current is www.example.com/foo/bar, look for domain www.example.com var currentWithSlash = uri.EndPathWithSlash(); - var baseDomains = domainsAndUris.Where(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash)).ToList(); + var baseDomains = domainsAndUris.Where(d => IsBaseOf(d, currentWithSlash)).ToList(); // if none matches, try again without the port // ie current is www.example.com:1234/foo/bar, look for domain www.example.com + var currentWithoutPort = currentWithSlash.WithoutPort(); if (baseDomains.Count == 0) - baseDomains = domainsAndUris.Where(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash.WithoutPort())).ToList(); + baseDomains = domainsAndUris.Where(d => IsBaseOf(d, currentWithoutPort)).ToList(); return baseDomains; } From 211267d500e30a817e5d48c3e68cc86a25e41138 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 2 May 2018 09:31:30 +0200 Subject: [PATCH 14/22] Deal with FIXMEs --- src/Umbraco.Tests/PublishedContent/NuCacheTests.cs | 2 +- src/Umbraco.Web/PublishedCache/NuCache/Property.cs | 3 +-- src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs | 3 ++- src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 1ae6a9f6fb..2703cd448d 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -160,7 +160,7 @@ namespace Umbraco.Tests.PublishedContent Assert.IsNotNull(publishedContent); Assert.AreEqual("It Works1!", publishedContent.Name); Assert.AreEqual("val1", publishedContent.Value("prop")); - Assert.AreEqual("val-fr1", publishedContent.Value("prop", "fr-FR")); // fixme wtf is happening here? + Assert.AreEqual("val-fr1", publishedContent.Value("prop", "fr-FR")); Assert.AreEqual("val-uk1", publishedContent.Value("prop", "en-UK")); Assert.AreEqual("name-fr1", publishedContent.GetCulture("fr-FR").Name); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index 0c48b4b52c..63e82ffa6c 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Xml.Serialization; using Umbraco.Core; using Umbraco.Core.Cache; @@ -163,7 +162,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var k = new CompositeStringStringKey(culture, segment); if (!_sourceValues.TryGetValue(k, out var vvalue)) - _sourceValues[k] = vvalue = new SourceInterValue { Culture = culture, Segment = segment, SourceValue = GetSourceValue(culture, segment) }; // fixme where is the source? + _sourceValues[k] = vvalue = new SourceInterValue { Culture = culture, Segment = segment, SourceValue = GetSourceValue(culture, segment) }; if (vvalue.InterInitialized) return vvalue.InterValue; vvalue.InterValue = PropertyType.ConvertSourceToInter(_content, vvalue.SourceValue, _isPreviewing); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 40a127335a..d5294c723f 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -27,7 +27,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _contentNode = contentNode; _contentData = contentData; _publishedSnapshotAccessor = publishedSnapshotAccessor; - VariationContextAccessor = variationContextAccessor; // fixme why is this a property? should be be on the base class? + VariationContextAccessor = variationContextAccessor; _urlSegment = _contentData.Name.ToUrlSegment(); IsPreviewing = _contentData.Published == false; @@ -399,6 +399,7 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Internal + // used by property internal IVariationContextAccessor VariationContextAccessor { get; } // used by navigable content diff --git a/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs b/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs index 893445add6..6d7932d4e2 100644 --- a/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs +++ b/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs @@ -22,7 +22,7 @@ namespace Umbraco.Web.Routing //ok so it's a custom route with published content assigned, check if the id being requested for is the same id as the assigned published content return content.Id == umbracoContext.PublishedRequest.PublishedContent.Id - ? umbracoContext.PublishedRequest.PublishedContent.GetUrl(culture) // fixme ∞ loop. + ? umbracoContext.PublishedRequest.PublishedContent.GetUrl(culture) : null; } From d56af5872d4abf894769b250c41fe60479906090 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 2 May 2018 13:38:45 +0200 Subject: [PATCH 15/22] Implement basic fallback plumbing (in progress) --- src/SolutionInfo.cs | 2 +- src/Umbraco.Core/Composing/Current.cs | 5 + .../Configuration/UmbracoVersion.cs | 2 +- .../PublishedContent/IPublishedContent.cs | 18 ---- .../IPublishedValueFallback.cs | 33 ++++++- .../NoopPublishedValueFallback.cs | 29 ++++++ .../PublishedContentWrapped.cs | 6 -- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../PublishedContent/PublishedContentTests.cs | 4 +- src/Umbraco.Web/Composing/Current.cs | 4 +- .../PublishedValueFallback.cs | 92 +++++++++++++++++++ .../Models/PublishedContentBase.cs | 25 ----- .../PublishedCache/IPublishedSnapshot.cs | 7 ++ .../PublishedCache/NuCache/CacheKeys.cs | 5 - .../PublishedCache/NuCache/Property.cs | 4 - .../NuCache/PublishedContent.cs | 14 --- .../NuCache/PublishedSnapshot.cs | 3 +- .../NuCache/PublishedSnapshotService.cs | 3 + .../PublishedCache/PublishedMember.cs | 9 -- .../DictionaryPublishedContent.cs | 12 --- .../XmlPublishedCache/XmlPublishedContent.cs | 14 --- src/Umbraco.Web/PublishedContentExtensions.cs | 61 +++++++++--- .../PublishedContentPropertyExtension.cs | 21 ++++- src/Umbraco.Web/PublishedElementExtensions.cs | 43 ++++----- .../Runtime/WebRuntimeComponent.cs | 6 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 26 files changed, 270 insertions(+), 154 deletions(-) create mode 100644 src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs create mode 100644 src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index e1071334d6..124dc93140 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -17,4 +17,4 @@ using System.Resources; // these are FYI and changed automatically [assembly: AssemblyFileVersion("8.0.0")] -[assembly: AssemblyInformationalVersion("8.0.0-alpha.33")] +[assembly: AssemblyInformationalVersion("8.0.0-alpha.34")] diff --git a/src/Umbraco.Core/Composing/Current.cs b/src/Umbraco.Core/Composing/Current.cs index f462c1b6ea..a763b22e36 100644 --- a/src/Umbraco.Core/Composing/Current.cs +++ b/src/Umbraco.Core/Composing/Current.cs @@ -33,6 +33,7 @@ namespace Umbraco.Core.Composing private static ILogger _logger; private static IProfiler _profiler; private static ProfilingLogger _profilingLogger; + private static IPublishedValueFallback _publishedValueFallback; /// /// Gets or sets the DI container. @@ -63,6 +64,7 @@ namespace Umbraco.Core.Composing _logger = null; _profiler = null; _profilingLogger = null; + _publishedValueFallback = null; Resetted?.Invoke(null, EventArgs.Empty); } @@ -153,6 +155,9 @@ namespace Umbraco.Core.Composing public static IPublishedContentTypeFactory PublishedContentTypeFactory => Container.GetInstance(); + public static IPublishedValueFallback PublishedValueFallback + => _publishedValueFallback ?? Container.GetInstance() ?? new NoopPublishedValueFallback(); + #endregion } } diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 0e8e473cf0..063125e462 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core.Configuration /// /// Gets the version comment of the executing code (eg "beta"). /// - public static string CurrentComment => "alpha.33"; + public static string CurrentComment => "alpha.34"; /// /// Gets the assembly version of Umbraco.Code.dll. diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index a8db8a98a7..d9c0b8a35a 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -173,23 +173,5 @@ namespace Umbraco.Core.Models.PublishedContent IEnumerable Children { get; } #endregion - - #region Properties - - /// - /// Gets a property identified by its alias. - /// - /// The property alias. - /// A value indicating whether to navigate the tree upwards until a property with a value is found. - /// The property identified by the alias. - /// - /// Navigate the tree upwards and look for a property with that alias and with a value (ie HasValue is true). - /// If found, return the property. If no property with that alias is found, having a value or not, return null. Otherwise - /// return the first property that was found with the alias but had no value (ie HasValue is false). - /// The alias is case-insensitive. - /// - IPublishedProperty GetProperty(string alias, bool recurse); - - #endregion } } diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index db6470e3cc..8e1dcfd543 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -1,14 +1,37 @@ -namespace Umbraco.Core.Models.PublishedContent +using Umbraco.Core.Composing; + +namespace Umbraco.Core.Models.PublishedContent { /// /// Provides a fallback strategy for getting values. /// + // fixme - IPublishedValueFallback is still WorkInProgress + // todo - properly document methods, etc + // todo - understand caching vs fallback (recurse etc) public interface IPublishedValueFallback { - // todo - define & implement + // note that at property level, property.GetValue() does NOT implement fallback, and one has + // to get property.Value() or property.Value() to trigger fallback - // property level ... should we move it up to element, - // so that the decision can be made based upon the entire element, other properties, etc? - // or, would we need the *two* levels? + // this method is called whenever property.Value(culture, segment, defaultValue) is called, and + // property.HasValue(culture, segment) is false. it can only fallback at property level (no recurse). + + object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue); + + // this method is called whenever property.Value(culture, segment, defaultValue) is called, and + // property.HasValue(culture, segment) is false. it can only fallback at property level (no recurse). + + T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue); + + // these methods to be called whenever getting the property value for the specified alias, culture and segment, + // either returned no property at all, or a property that does not HasValue for the specified culture and segment. + + object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue); + + T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue); + + object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse); + + T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs new file mode 100644 index 0000000000..b99b4ad415 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs @@ -0,0 +1,29 @@ +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides a noop implementation for . + /// + /// + /// This is for tests etc - does not implement fallback at all. + /// + public class NoopPublishedValueFallback : IPublishedValueFallback + { + /// + public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) => defaultValue; + + /// + public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) => defaultValue; + + /// + public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) => defaultValue; + + /// + public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) => defaultValue; + + /// + public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse) => defaultValue; + + /// + public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse) => defaultValue; + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 8187b7498e..dd3e7b6d9f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -134,12 +134,6 @@ namespace Umbraco.Core.Models.PublishedContent return _content.GetProperty(alias); } - /// - public virtual IPublishedProperty GetProperty(string alias, bool recurse) - { - return _content.GetProperty(alias, recurse); - } - #endregion } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 2d4b59cf3f..d0d633211c 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -368,6 +368,7 @@ + diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 2816af7a90..9a0034a0ef 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -16,6 +16,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.PropertyEditors; namespace Umbraco.Tests.PublishedContent @@ -32,7 +33,8 @@ namespace Umbraco.Tests.PublishedContent base.Compose(); Container.RegisterSingleton(f => new PublishedModelFactory(f.GetInstance().GetTypes())); - Container.RegisterSingleton(); + Container.RegisterSingleton(); + Container.RegisterSingleton(); var logger = Mock.Of(); var dataTypeService = new TestObjects.TestDataTypeService( diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index d2a1ee6f80..0869cdbb68 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -142,7 +142,7 @@ namespace Umbraco.Web.Composing internal static IPublishedSnapshotService PublishedSnapshotService => Container.GetInstance(); - + public static ThumbnailProviderCollection ThumbnailProviders => Container.GetInstance(); @@ -257,6 +257,8 @@ namespace Umbraco.Web.Composing public static IPublishedContentTypeFactory PublishedContentTypeFactory => CoreCurrent.PublishedContentTypeFactory; + public static IPublishedValueFallback PublishedValueFallback => CoreCurrent.PublishedValueFallback; + #endregion } } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs new file mode 100644 index 0000000000..47e4b3d872 --- /dev/null +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -0,0 +1,92 @@ +using Umbraco.Core.Models.PublishedContent; + +namespace Umbraco.Web.Models.PublishedContent +{ + /// + /// Provides a default implementation for . + /// + public class PublishedValueFallback : IPublishedValueFallback + { + // this is our default implementation + // kinda reproducing what was available in v7 + + /// + public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) + { + // no fallback here + return defaultValue; + } + + /// + public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) + { + // no fallback here + return defaultValue; + } + + /// + public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) + { + // no fallback here + return defaultValue; + } + + /// + public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) + { + // no fallback here + return defaultValue; + } + + /// + public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse) + { + // no fallback here + if (!recurse) return defaultValue; + + // is that ok? + return GetValue(content, alias, culture, segment, defaultValue, recurse); + } + + /// + public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse) + { + // no fallback here + if (!recurse) return defaultValue; + + // otherwise, implement recursion as it was implemented in PublishedContentBase + + // fixme caching? + // + // all caches were using PublishedContentBase.GetProperty(alias, recurse) to get the property, + // then, + // NuCache.PublishedContent was storing the property in GetAppropriateCache() with key "NuCache.Property.Recurse[" + DraftOrPub(previewing) + contentUid + ":" + typeAlias + "]"; + // XmlPublishedContent was storing the property in _cacheProvider with key $"XmlPublishedCache.PublishedContentCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}"; + // DictionaryPublishedContent was storing the property in _cacheProvider with key $"XmlPublishedCache.PublishedMediaCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}"; + // + // at the moment, caching has been entirely removed, until we better understand caching + fallback + + IPublishedProperty property = null; // if we are here, content's property has no value + IPublishedProperty noValueProperty = null; + do + { + content = content.Parent; + property = content?.GetProperty(alias); + if (property != null) noValueProperty = property; + } while (content != null && (property == null || property.HasValue(culture, segment) == false)); + + // if we found a content with the property having a value, return that property value + if (property != null && property.HasValue(culture, segment)) + return property.Value(culture, segment); + + // if we found a property, even though with no value, return that property value + // because the converter may want to handle the missing value. ie if defaultValue is default, + // either specified or by default, the converter may want to substitute something else. + if (noValueProperty != null) + return noValueProperty.Value(culture, segment, defaultValue: defaultValue); + + // else return default + return defaultValue; + } + } +} diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 60fb746423..ffa72796a9 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -164,31 +164,6 @@ namespace Umbraco.Web.Models /// public abstract IPublishedProperty GetProperty(string alias); - /// - public virtual IPublishedProperty GetProperty(string alias, bool recurse) - { - // fixme - refactor with fallback - - var property = GetProperty(alias); - if (recurse == false) return property; - - IPublishedContent content = this; - var firstNonNullProperty = property; - while (content != null && (property == null || property.HasValue() == false)) - { - content = content.Parent; - property = content?.GetProperty(alias); - if (firstNonNullProperty == null && property != null) firstNonNullProperty = property; - } - - // if we find a content with the property with a value, return that property - // if we find no content with the property, return null - // if we find a content with the property without a value, return that property - // have to save that first property while we look further up, hence firstNonNullProperty - - return property != null && property.HasValue() ? property : firstNonNullProperty; - } - #endregion } } diff --git a/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs b/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs index 628a67b561..0636eb808c 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedSnapshot.cs @@ -33,11 +33,18 @@ namespace Umbraco.Web.PublishedCache /// /// Gets the snapshot-level cache. /// + /// + /// The snapshot-level cache belongs to this snapshot only. + /// ICacheProvider SnapshotCache { get; } /// /// Gets the elements-level cache. /// + /// + /// The elements-level cache is shared by all snapshots relying on the same elements, + /// ie all snapshots built on top of unchanging content / media / etc. + /// ICacheProvider ElementsCache { get; } /// diff --git a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs b/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs index 5bdee5a416..0b67c3a1e2 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/CacheKeys.cs @@ -43,11 +43,6 @@ namespace Umbraco.Web.PublishedCache.NuCache return "NuCache.Profile.Name[" + userId + "]"; } - public static string PropertyRecurse(Guid contentUid, string typeAlias, bool previewing) - { - return "NuCache.Property.Recurse[" + DraftOrPub(previewing) + contentUid + ":" + typeAlias + "]"; - } - public static string PropertyCacheValues(Guid contentUid, string typeAlias, bool previewing) { return "NuCache.Property.CacheValues[" + DraftOrPub(previewing) + contentUid + ":" + typeAlias + "]"; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index 63e82ffa6c..1de7724706 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -91,10 +91,6 @@ namespace Umbraco.Web.PublishedCache.NuCache public override bool HasValue(string culture = null, string segment = null) => _sourceValue != null && (!(_sourceValue is string) || string.IsNullOrWhiteSpace((string) _sourceValue) == false); - // used to cache the recursive *property* for this property - internal string RecurseCacheKey => _recurseCacheKey - ?? (_recurseCacheKey = CacheKeys.PropertyRecurse(_contentUid, Alias, _isPreviewing)); - // used to cache the CacheValues of this property internal string ValuesCacheKey => _valuesCacheKey ?? (_valuesCacheKey = CacheKeys.PropertyCacheValues(_contentUid, Alias, _isPreviewing)); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index d5294c723f..d944c5f824 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -359,20 +359,6 @@ namespace Umbraco.Web.PublishedCache.NuCache return property; } - /// - public override IPublishedProperty GetProperty(string alias, bool recurse) - { - var property = GetProperty(alias); - if (recurse == false) return property; - - var cache = GetAppropriateCache(); - if (cache == null) - return base.GetProperty(alias, true); - - var key = ((Property)property).RecurseCacheKey; - return (Property)cache.GetCacheItem(key, () => base.GetProperty(alias, true)); - } - #endregion #region Caching diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs index 741e4d09f5..9b8982c69c 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshot.cs @@ -17,7 +17,6 @@ namespace Umbraco.Web.PublishedCache.NuCache { _service = service; _defaultPreview = defaultPreview; - SnapshotCache = new ObjectCacheRuntimeCacheProvider(); } public class PublishedSnapshotElements : IDisposable @@ -49,7 +48,7 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Caches - public ICacheProvider SnapshotCache { get; } + public ICacheProvider SnapshotCache => Elements.SnapshotCache; public ICacheProvider ElementsCache => Elements.ElementsCache; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 8a7f956e56..2b7615b5cc 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -937,6 +937,9 @@ namespace Umbraco.Web.PublishedCache.NuCache return new PublishedSnapshot(this, preview); } + // gets a new set of elements + // always creates a new set of elements, + // even though the underlying elements may not change (store snapshots) public PublishedSnapshot.PublishedSnapshotElements GetElements(bool previewDefault) { // note: using ObjectCacheRuntimeCacheProvider for elements and snapshot caches diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index c480e5f4ae..2064bf51ea 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -87,15 +87,6 @@ namespace Umbraco.Web.PublishedCache public override IEnumerable Properties => _properties; - public override IPublishedProperty GetProperty(string alias, bool recurse) - { - if (recurse) - { - throw new NotSupportedException(); - } - return GetProperty(alias); - } - public override IPublishedProperty GetProperty(string alias) { return _properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs index 45a8ebbb7a..5cd9dd0069 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs @@ -189,18 +189,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public override PublishedContentType ContentType => _contentType; - // override to implement cache - // cache at context level, ie once for the whole request - // but cache is not shared by requests because we wouldn't know how to clear it - public override IPublishedProperty GetProperty(string alias, bool recurse) - { - if (recurse == false) return GetProperty(alias); - - var key = $"XmlPublishedCache.PublishedMediaCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}"; - var cacheProvider = _cacheProvider; - return cacheProvider.GetCacheItem(key, () => base.GetProperty(alias, true)); - } - private readonly List _keysAdded = new List(); private int _id; private Guid _key; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs index f85f8f8640..2535f61996 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs @@ -79,20 +79,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return _properties.TryGetValue(alias, out property) ? property : null; } - // override to implement cache - // cache at context level, ie once for the whole request - // but cache is not shared by requests because we wouldn't know how to clear it - public override IPublishedProperty GetProperty(string alias, bool recurse) - { - if (recurse == false) return GetProperty(alias); - - var key = $"XmlPublishedCache.PublishedContentCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}"; - var cacheProvider = _cacheProvider; - return cacheProvider.GetCacheItem(key, () => base.GetProperty(alias, true)); - - // note: cleared by PublishedContentCache.Resync - any change here must be applied there - } - public override PublishedItemType ItemType => PublishedItemType.Content; public override IPublishedContent Parent diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 4edd6b3016..017f248f70 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -20,6 +20,10 @@ namespace Umbraco.Web /// public static class PublishedContentExtensions { + // see notes in PublishedElementExtensions + // + private static IPublishedValueFallback PublishedValueFallback => Current.PublishedValueFallback; + #region Urls /// @@ -84,6 +88,8 @@ namespace Umbraco.Web #endregion + // fixme - .HasValue() and .Value() refactoring - in progress - see exceptions below + #region HasValue /// @@ -96,8 +102,10 @@ namespace Umbraco.Web /// Returns true if GetProperty(alias, recurse) is not null and GetProperty(alias, recurse).HasValue is true. public static bool HasValue(this IPublishedContent content, string alias, bool recurse) { - var prop = content.GetProperty(alias, recurse); - return prop != null && prop.HasValue(); + throw new NotImplementedException("WorkInProgress"); + + //var prop = content.GetProperty(alias, recurse); + //return prop != null && prop.HasValue(); } /// @@ -113,9 +121,11 @@ namespace Umbraco.Web public static IHtmlString HasValue(this IPublishedContent content, string alias, bool recurse, string valueIfTrue, string valueIfFalse = null) { - return content.HasValue(alias, recurse) - ? new HtmlString(valueIfTrue) - : new HtmlString(valueIfFalse ?? string.Empty); + throw new NotImplementedException("WorkInProgress"); + + //return content.HasValue(alias, recurse) + // ? new HtmlString(valueIfTrue) + // : new HtmlString(valueIfFalse ?? string.Empty); } #endregion @@ -141,9 +151,12 @@ namespace Umbraco.Web /// public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, bool recurse = false) { - // fixme - refactor with fallback - var property = content.GetProperty(alias, recurse); - return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(); + var property = content.GetProperty(alias); + + if (property != null && property.HasValue(culture, segment)) + return property.GetValue(culture, segment); + + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse); } #endregion @@ -170,11 +183,35 @@ namespace Umbraco.Web /// public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, bool recurse = false) { - // fixme - refactor with fallback - var property = content.GetProperty(alias, recurse); - if (property == null) return defaultValue; + var property = content.GetProperty(alias); - return property.Value(culture, segment, defaultValue); + if (property != null && property.HasValue(culture, segment)) + return property.Value(culture, segment); + + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse); + } + + // fixme - .Value() refactoring - in progress + public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", bool recurse = false) + { + var aliasesA = aliases.Split(','); + if (aliasesA.Length == 0) + return new HtmlString(string.Empty); + + throw new NotImplementedException("WorkInProgress"); + + var property = content.GetProperty(aliasesA[0]); + + //var property = aliases.Split(',') + // .Where(x => string.IsNullOrWhiteSpace(x) == false) + // .Select(x => content.GetProperty(x.Trim(), recurse)) + // .FirstOrDefault(x => x != null); + + //if (format == null) format = x => x.ToString(); + + //return property != null + // ? new HtmlString(format(property.Value())) + // : new HtmlString(alt); } #endregion diff --git a/src/Umbraco.Web/PublishedContentPropertyExtension.cs b/src/Umbraco.Web/PublishedContentPropertyExtension.cs index d57c2437a8..de9321888f 100644 --- a/src/Umbraco.Web/PublishedContentPropertyExtension.cs +++ b/src/Umbraco.Web/PublishedContentPropertyExtension.cs @@ -1,4 +1,5 @@ using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web @@ -8,8 +9,24 @@ namespace Umbraco.Web /// public static class PublishedPropertyExtension { - #region Value + // see notes in PublishedElementExtensions + // + private static IPublishedValueFallback PublishedValueFallback => Current.PublishedValueFallback; + + #region Value + public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default) + { + if (property.HasValue(culture, segment)) + return property.GetValue(culture, segment); + + return PublishedValueFallback.GetValue(property, culture, segment, defaultValue); + } + + #endregion + + #region Value + public static T Value(this IPublishedProperty property, string culture = null, string segment = null, T defaultValue = default) { // for Value when defaultValue is not specified, and HasValue() is false, we still want to convert the result (see below) @@ -21,7 +38,7 @@ namespace Umbraco.Web // give the converter a chance to handle the default value differently // eg for IEnumerable it may return Enumerable.Empty instead of null - + var value = property.GetValue(culture, segment); // if value is null (strange but why not) it still is OK to call TryConvertTo diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index 2529146b17..945270cb9e 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Web; +using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web @@ -11,6 +12,19 @@ namespace Umbraco.Web /// public static class PublishedElementExtensions { + // lots of debates about accessing dependencies (IPublishedValueFallback) from extension methods, ranging + // from "don't do it" i.e. if an extension method is relying on dependencies, a proper service should be + // created instead, to discussing method injection vs service locator vs other subtleties, see for example + // this post http://marisks.net/2016/12/19/dependency-injection-with-extension-methods/ + // + // point is, we do NOT want a service, we DO want to write model.Value("alias", "fr-FR") and hit + // fallback somehow - which pretty much rules out method injection, and basically anything but service + // locator - bah, let's face it, it works + // + // besides, for tests, Current support setting a fallback without even a container + // + private static IPublishedValueFallback PublishedValueFallback => Current.PublishedValueFallback; + #region IsComposedOf /// @@ -95,10 +109,11 @@ namespace Umbraco.Web public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default) { var property = content.GetProperty(alias); - if (property == null || !property.HasValue(culture, segment)) return defaultValue; - // note: supporting the "." notation for 'current' is the responsibility of the IPublishedProperty - return property.GetValue(culture, segment); // tested HasValue() right above + if (property != null && property.HasValue(culture, segment)) + return property.GetValue(culture, segment); + + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); } #endregion @@ -124,10 +139,11 @@ namespace Umbraco.Web public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default) { var property = content.GetProperty(alias); - if (property == null) return defaultValue; - // note: supporting the "." notation for 'current' is the responsibility of the IPublishedProperty - return property.Value(culture, segment, defaultValue); + if (property != null && property.HasValue(culture, segment)) + return property.Value(culture, segment); + + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); } #endregion @@ -181,21 +197,6 @@ namespace Umbraco.Web : new HtmlString(alt); } - // todo - that one should move to PublishedContentExtensions - public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", bool recurse = false) - { - if (format == null) format = x => x.ToString(); - - var property = aliases.Split(',') - .Where(x => string.IsNullOrWhiteSpace(x) == false) - .Select(x => content.GetProperty(x.Trim(), recurse)) - .FirstOrDefault(x => x != null); - - return property != null - ? new HtmlString(format(property.Value())) - : new HtmlString(alt); - } - #endregion #region ToIndexedArray diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index e472286902..811dd7358b 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -40,6 +40,7 @@ using Umbraco.Web.HealthCheck; using Umbraco.Web.Install; using Umbraco.Web.Media; using Umbraco.Web.Media.ThumbnailProviders; +using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -206,6 +207,9 @@ namespace Umbraco.Web.Runtime // register preview SignalR hub composition.Container.Register(_ => GlobalHost.ConnectionManager.GetHubContext(), new PerContainerLifetime()); + + // register properties fallback + composition.Container.RegisterSingleton(); } internal void Initialize( @@ -408,7 +412,7 @@ namespace Umbraco.Web.Runtime // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not // utilizing an old path appDomainHash); - + //set the file map and composite file default location to the %temp% location BaseCompositeFileProcessingProvider.CompositeFilePathDefaultFolder = XmlFileMapper.FileMapDefaultFolder diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 362802787f..85150e3079 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -267,6 +267,7 @@ + From 5c96b32c647fcb6c14b41591cf9e5ba75d86134b Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 2 May 2018 16:23:58 +0200 Subject: [PATCH 16/22] Fix tests --- .../Services/Importing/XsltSearch-Package.xml | 8 +++----- src/Umbraco.Tests/Services/Importing/uBlogsy-Package.xml | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Tests/Services/Importing/XsltSearch-Package.xml b/src/Umbraco.Tests/Services/Importing/XsltSearch-Package.xml index 8096cfb833..4839f3c0db 100644 --- a/src/Umbraco.Tests/Services/Importing/XsltSearch-Package.xml +++ b/src/Umbraco.Tests/Services/Importing/XsltSearch-Package.xml @@ -161,10 +161,8 @@ p.xsltsearch_result_description {padding-bottom: 10px;} XSLTsearch XSLTsearch - - - - + Unknown + XSLTsearch.xslt False 0 @@ -251,4 +249,4 @@ p.xsltsearch_result_description {padding-bottom: 10px;} - \ No newline at end of file + diff --git a/src/Umbraco.Tests/Services/Importing/uBlogsy-Package.xml b/src/Umbraco.Tests/Services/Importing/uBlogsy-Package.xml index 98e12465f5..e1af13bb5e 100644 --- a/src/Umbraco.Tests/Services/Importing/uBlogsy-Package.xml +++ b/src/Umbraco.Tests/Services/Importing/uBlogsy-Package.xml @@ -2033,10 +2033,8 @@ e.g Mid-code Crisis [uBlogsy] Show Some Love uBlogsyShowSomeLove - - - - + Unknown + True @@ -2160,4 +2158,4 @@ e.g Mid-code Crisis - \ No newline at end of file + From a545c3c3333b3e0d64b7830f13c226bfd1596a5b Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 2 May 2018 18:15:22 +0200 Subject: [PATCH 17/22] Fix --- src/Umbraco.Core/StringExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 04cc2857ba..7d8d4042f2 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -16,6 +16,7 @@ using System.Web.Security; using Umbraco.Core.Strings; using Umbraco.Core.CodeAnnotations; using Umbraco.Core.Composing; +using Umbraco.Core.Exceptions; using Umbraco.Core.IO; namespace Umbraco.Core From 71ed99f080ebcad48dcb2167c82fe259b90a3080 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 4 May 2018 18:13:06 +0200 Subject: [PATCH 18/22] Fix some tests --- src/Umbraco.Core/Models/ContentBase.cs | 2 +- src/Umbraco.Core/StringExtensions.cs | 1 - src/Umbraco.Tests/Integration/ContentEventsTests.cs | 2 +- src/Umbraco.Tests/Services/ContentServiceTests.cs | 8 ++++++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index bbeb3f943e..80267a99a6 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -56,7 +56,7 @@ namespace Umbraco.Core.Models Id = 0; // no identity VersionId = 0; // no versions - SetName(culture, name); + SetName(name, culture); _contentTypeId = contentType.Id; _properties = properties ?? throw new ArgumentNullException(nameof(properties)); diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 6c1bdb021c..98e831f73f 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -1163,7 +1163,6 @@ namespace Umbraco.Core public static string ToUrlSegment(this string text, string culture) { if (string.IsNullOrWhiteSpace(text)) throw new ArgumentNullOrEmptyException(nameof(text)); - if (culture == null) throw new ArgumentNullException(nameof(culture)); return Current.ShortStringHelper.CleanStringForUrlSegment(text, culture); } diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index 246626f0a8..73f95c44a7 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -76,7 +76,7 @@ namespace Umbraco.Tests.Integration { base.TearDown(); - _h1.Unbind(); + _h1?.Unbind(); // clear ALL events diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 354ff85351..009020e6e4 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -978,7 +978,7 @@ namespace Umbraco.Tests.Services var content = new Content(string.Empty, -1, ServiceContext.ContentTypeService.Get("umbTextpage")); // Act & Assert - Assert.Throws(() => contentService.Save(content)); + Assert.Throws(() => contentService.Save(content)); } [Test] @@ -2562,7 +2562,11 @@ namespace Umbraco.Tests.Services Assert.AreEqual("name-fr", content2.GetName(langFr.IsoCode)); Assert.AreEqual("name-uk", content2.GetName(langUk.IsoCode)); - Assert.IsNull(content2.PublishName); // we haven't published InvariantNeutral + // we haven't published InvariantNeutral, but a document cannot be published without an invariant name, + // so when we tried and published for the first time above the french culture, the french name was used + // to populate the invariant name + Assert.AreEqual("name-fr", content2.PublishName); + Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode)); Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); From aa35b28349a3ab105a63b1a1605b972c855a9b76 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 7 May 2018 08:25:09 +0200 Subject: [PATCH 19/22] Cleanup --- src/Umbraco.Web/PublishedCache/NuCache/Property.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs index 1de7724706..6d161dc3b9 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs @@ -36,7 +36,6 @@ namespace Umbraco.Web.PublishedCache.NuCache private CacheValues _cacheValues; private string _valuesCacheKey; - private string _recurseCacheKey; // initializes a published content property with no value public Property(PublishedPropertyType propertyType, PublishedContent content, IPublishedSnapshotAccessor publishedSnapshotAccessor, PropertyCacheLevel referenceCacheLevel = PropertyCacheLevel.Element) From d13a6f5f5851c591d7bcd443c86be2650158fc37 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 7 May 2018 08:26:10 +0200 Subject: [PATCH 20/22] Fixes --- src/Umbraco.Core/Models/Content.cs | 2 +- src/Umbraco.Core/Models/ContentBase.cs | 2 +- .../Repositories/ContentRepositoryTest.cs | 38 +++++++++---------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index cc273bc7b0..7abaac3404 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -232,7 +232,7 @@ namespace Umbraco.Core.Models /// [IgnoreDataMember] - public IReadOnlyDictionary PublishNames => _publishInfos?.ToDictionary(x => x.Key, x => x.Value.Name) ?? NoNames; + public IReadOnlyDictionary PublishNames => _publishInfos?.ToDictionary(x => x.Key, x => x.Value.Name, StringComparer.OrdinalIgnoreCase) ?? NoNames; /// public string GetPublishName(string culture) diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 80267a99a6..0ac8d8c864 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -141,7 +141,7 @@ namespace Umbraco.Core.Models /// [DataMember] - public virtual IReadOnlyDictionary Names => _cultureInfos?.ToDictionary(x => x.Key, x => x.Value.Name) ?? NoNames; + public virtual IReadOnlyDictionary Names => _cultureInfos?.ToDictionary(x => x.Key, x => x.Value.Name, StringComparer.OrdinalIgnoreCase) ?? NoNames; // sets culture infos // internal for repositories diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index 035c857e35..9675707439 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -685,40 +685,40 @@ namespace Umbraco.Tests.Persistence.Repositories { //2x content types, one invariant, one variant - var invariantCT = MockedContentTypes.CreateSimpleContentType("umbInvariantTextpage", "Invariant Textpage"); - invariantCT.Variations = ContentVariation.InvariantNeutral; - foreach (var p in invariantCT.PropertyTypes) p.Variations = ContentVariation.InvariantNeutral; - ServiceContext.FileService.SaveTemplate(invariantCT.DefaultTemplate); // else, FK violation on contentType! - ServiceContext.ContentTypeService.Save(invariantCT); + var invariantCt = MockedContentTypes.CreateSimpleContentType("umbInvariantTextpage", "Invariant Textpage"); + invariantCt.Variations = ContentVariation.InvariantNeutral; + foreach (var p in invariantCt.PropertyTypes) p.Variations = ContentVariation.InvariantNeutral; + ServiceContext.FileService.SaveTemplate(invariantCt.DefaultTemplate); // else, FK violation on contentType! + ServiceContext.ContentTypeService.Save(invariantCt); - var variantCT = MockedContentTypes.CreateSimpleContentType("umbVariantTextpage", "Variant Textpage"); - variantCT.Variations = ContentVariation.CultureNeutral; - var propTypes = variantCT.PropertyTypes.ToList(); - for (int i = 0; i < propTypes.Count; i++) + var variantCt = MockedContentTypes.CreateSimpleContentType("umbVariantTextpage", "Variant Textpage"); + variantCt.Variations = ContentVariation.CultureNeutral; + var propTypes = variantCt.PropertyTypes.ToList(); + for (var i = 0; i < propTypes.Count; i++) { var p = propTypes[i]; //every 2nd one is variant p.Variations = i % 2 == 0 ? ContentVariation.CultureNeutral : ContentVariation.InvariantNeutral; } - ServiceContext.FileService.SaveTemplate(variantCT.DefaultTemplate); // else, FK violation on contentType! - ServiceContext.ContentTypeService.Save(variantCT); + ServiceContext.FileService.SaveTemplate(variantCt.DefaultTemplate); // else, FK violation on contentType! + ServiceContext.ContentTypeService.Save(variantCt); - invariantCT.AllowedContentTypes = new[] { new ContentTypeSort(invariantCT.Id, 0), new ContentTypeSort(variantCT.Id, 1) }; - ServiceContext.ContentTypeService.Save(invariantCT); + invariantCt.AllowedContentTypes = new[] { new ContentTypeSort(invariantCt.Id, 0), new ContentTypeSort(variantCt.Id, 1) }; + ServiceContext.ContentTypeService.Save(invariantCt); //create content - var root = MockedContent.CreateSimpleContent(invariantCT); + var root = MockedContent.CreateSimpleContent(invariantCt); ServiceContext.ContentService.Save(root); - for (int i = 0; i < 25; i++) + for (var i = 0; i < 25; i++) { var isInvariant = i % 2 == 0; - var name = (isInvariant ? "INV" : "VAR") + "_" + Guid.NewGuid().ToString(); + var name = (isInvariant ? "INV" : "VAR") + "_" + Guid.NewGuid(); var culture = isInvariant ? null : "en-US"; var child = MockedContent.CreateSimpleContent( - isInvariant ? invariantCT : variantCT, + isInvariant ? invariantCt : variantCt, name, root, culture, setPropertyValues: isInvariant); @@ -751,9 +751,9 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.AreEqual(25, totalRecords); foreach (var r in result) { - var isInvariant = r.ContentType.Alias == "umbInvariantTextpage"; + var isInvariant = r.ContentType.Alias == "umbInvariantTextpage"; var name = isInvariant ? r.Name : r.Names["en-US"]; - var namePrefix = (isInvariant ? "INV" : "VAR"); + var namePrefix = isInvariant ? "INV" : "VAR"; //ensure the correct name (invariant vs variant) is in the result Assert.IsTrue(name.StartsWith(namePrefix)); From 5d37df182a9a534fa66e263f7b2cd200717f478a Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 7 May 2018 09:36:51 +0200 Subject: [PATCH 21/22] Fix tests --- src/Umbraco.Tests/Services/EntityServiceTests.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Tests/Services/EntityServiceTests.cs b/src/Umbraco.Tests/Services/EntityServiceTests.cs index 30a690d702..cdbfb2b66e 100644 --- a/src/Umbraco.Tests/Services/EntityServiceTests.cs +++ b/src/Umbraco.Tests/Services/EntityServiceTests.cs @@ -464,16 +464,16 @@ namespace Umbraco.Tests.Services var result = service.Get(c1.Id, UmbracoObjectTypes.Document); Assert.AreEqual("Test", result.Name); Assert.IsTrue(result.AdditionalData.ContainsKey("CultureNames")); - var cultureNames = (IDictionary)result.AdditionalData["CultureNames"]; - Assert.AreEqual("Test - FR", cultureNames[_langFr.Id]); - Assert.AreEqual("Test - ES", cultureNames[_langEs.Id]); + var cultureNames = (IDictionary)result.AdditionalData["CultureNames"]; + Assert.AreEqual("Test - FR", cultureNames[_langFr.IsoCode]); + Assert.AreEqual("Test - ES", cultureNames[_langEs.IsoCode]); } [Test] public void EntityService_Can_Get_Child_Content_By_ParentId_And_UmbracoObjectType_With_Variant_Names() { var service = ServiceContext.EntityService; - + var contentType = MockedContentTypes.CreateSimpleContentType("test1", "Test1", false); contentType.Variations = ContentVariation.CultureNeutral; ServiceContext.ContentTypeService.Save(contentType); @@ -502,13 +502,13 @@ namespace Umbraco.Tests.Services { Assert.AreEqual(1, entities[i].AdditionalData.Count); Assert.AreEqual("CultureNames", entities[i].AdditionalData.Keys.First()); - var variantInfo = entities[i].AdditionalData.First().Value as IDictionary; + var variantInfo = entities[i].AdditionalData.First().Value as IDictionary; Assert.IsNotNull(variantInfo); var keys = variantInfo.Keys.ToList(); var vals = variantInfo.Values.ToList(); - Assert.AreEqual(_langFr.Id, keys[0]); + Assert.AreEqual(_langFr.IsoCode.ToLowerInvariant(), keys[0].ToLowerInvariant()); Assert.AreEqual("Test " + i + " - FR", vals[0]); - Assert.AreEqual(_langEs.Id, keys[1]); + Assert.AreEqual(_langEs.IsoCode.ToLowerInvariant(), keys[1].ToLowerInvariant()); Assert.AreEqual("Test " + i + " - ES", vals[1]); } else From 3f73452bc74e9e75cd52b704d65390cfd779d00a Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 7 May 2018 18:22:23 +0200 Subject: [PATCH 22/22] Various fixes, and U4-11287 --- .../Models/Entities/EntitySlim.cs | 10 +++ .../Implement/DocumentRepository.cs | 2 +- .../Implement/EntityRepository.cs | 16 ++--- src/Umbraco.Examine/UmbracoContentIndexer.cs | 2 +- .../Services/EntityServiceTests.cs | 6 +- .../PublishedCache/NuCache/ContentCache.cs | 8 ++- .../Trees/ContentTreeControllerBase.cs | 62 ++++++++++++------- 7 files changed, 69 insertions(+), 37 deletions(-) diff --git a/src/Umbraco.Core/Models/Entities/EntitySlim.cs b/src/Umbraco.Core/Models/Entities/EntitySlim.cs index 163879bbe0..7808cea19b 100644 --- a/src/Umbraco.Core/Models/Entities/EntitySlim.cs +++ b/src/Umbraco.Core/Models/Entities/EntitySlim.cs @@ -26,6 +26,16 @@ namespace Umbraco.Core.Models.Entities /// public static readonly IEntitySlim Root = new EntitySlim { Path = "-1", Name = "root", HasChildren = true }; + /// + /// Gets the AdditionalData key for culture names. + /// + public const string AdditionalCultureNames = "CultureNames"; + + /// + /// Gets the AdditionalData key for variations. + /// + public const string AdditionalVariations = "Variations"; + // implement IEntity diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index e66e0f125b..e723ece853 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -1062,7 +1062,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement NodeId = content.Id, LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), Culture = culture, - Edited = !content.IsCulturePublished(culture) || editedCultures.Contains(culture) // if not published, always edited + Edited = !content.IsCulturePublished(culture) || (editedCultures != null && editedCultures.Contains(culture)) // if not published, always edited }; } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs index 0dd7a25798..6b74c27f1a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs @@ -435,7 +435,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement /// - /// The DTO used to fetch results for a content item with it's variation info + /// The DTO used to fetch results for a content item with its variation info /// private class ContentEntityDto : BaseDto { @@ -483,6 +483,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement public string Icon { get; set; } public string Thumbnail { get; set; } public bool IsContainer { get; set; } + public ContentVariation Variations { get; set; } // ReSharper restore UnusedAutoPropertyAccessor.Local // ReSharper restore UnusedMember.Local } @@ -506,8 +507,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (isContent || isMedia) sql - .AndSelect(x => NPocoSqlExtensions.Statics.Alias(x.Id, "versionId")) - .AndSelect(x => x.Alias, x => x.Icon, x => x.Thumbnail, x => x.IsContainer); + .AndSelect(x => Alias(x.Id, "versionId")) + .AndSelect(x => x.Alias, x => x.Icon, x => x.Thumbnail, x => x.IsContainer, x => x.Variations); if (isContent) { @@ -616,7 +617,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (isContent || isMedia) sql .AndBy(x => x.Id) - .AndBy(x => x.Alias, x => x.Icon, x => x.Thumbnail, x => x.IsContainer); + .AndBy(x => x.Alias, x => x.Icon, x => x.Thumbnail, x => x.IsContainer, x => x.Variations); if (sort) sql.OrderBy(x => x.SortOrder); @@ -869,6 +870,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement BuildContentEntity(entity, dto); entity.Published = dto.Published; entity.Edited = dto.Edited; + entity.AdditionalData[EntitySlim.AdditionalVariations] = dto.Variations; } private static EntitySlim BuildContentEntity(BaseDto dto) @@ -897,16 +899,16 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // EntitySlim does not track changes var entity = new DocumentEntitySlim(); BuildDocumentEntity(entity, dto); - var variantInfo = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - if (dto.VariationInfo != null) + if (dto.Variations.Has(ContentVariation.CultureNeutral) && dto.VariationInfo != null && dto.VariationInfo.Count > 0) { + var variantInfo = new Dictionary(StringComparer.InvariantCultureIgnoreCase); foreach (var info in dto.VariationInfo) { var isoCode = _langRepository.GetIsoCodeById(info.LanguageId); if (isoCode != null) variantInfo[isoCode] = info.Name; } - entity.AdditionalData["CultureNames"] = variantInfo; + entity.AdditionalData[EntitySlim.AdditionalCultureNames] = variantInfo; } return entity; } diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index d799e12eed..487d238c25 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -321,7 +321,7 @@ namespace Umbraco.Examine { foreach (var c in content) { - var urlValue = c.GetUrlSegment(urlSegmentProviders, ""); // for now, index with invariant culture + var urlValue = c.GetUrlSegment(urlSegmentProviders); // for now, index with invariant culture var values = new Dictionary { {"icon", new object[] {c.ContentType.Icon}}, diff --git a/src/Umbraco.Tests/Services/EntityServiceTests.cs b/src/Umbraco.Tests/Services/EntityServiceTests.cs index cdbfb2b66e..053a6f39ac 100644 --- a/src/Umbraco.Tests/Services/EntityServiceTests.cs +++ b/src/Umbraco.Tests/Services/EntityServiceTests.cs @@ -463,8 +463,8 @@ namespace Umbraco.Tests.Services var result = service.Get(c1.Id, UmbracoObjectTypes.Document); Assert.AreEqual("Test", result.Name); - Assert.IsTrue(result.AdditionalData.ContainsKey("CultureNames")); - var cultureNames = (IDictionary)result.AdditionalData["CultureNames"]; + Assert.IsTrue(result.AdditionalData.ContainsKey(EntitySlim.AdditionalCultureNames)); + var cultureNames = (IDictionary)result.AdditionalData[EntitySlim.AdditionalCultureNames]; Assert.AreEqual("Test - FR", cultureNames[_langFr.IsoCode]); Assert.AreEqual("Test - ES", cultureNames[_langEs.IsoCode]); } @@ -501,7 +501,7 @@ namespace Umbraco.Tests.Services if (i % 2 == 0) { Assert.AreEqual(1, entities[i].AdditionalData.Count); - Assert.AreEqual("CultureNames", entities[i].AdditionalData.Keys.First()); + Assert.AreEqual(EntitySlim.AdditionalCultureNames, entities[i].AdditionalData.Keys.First()); var variantInfo = entities[i].AdditionalData.First().Value as IDictionary; Assert.IsNotNull(variantInfo); var keys = variantInfo.Keys.ToList(); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 4b235e42b9..221295f770 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -147,7 +147,13 @@ namespace Umbraco.Web.PublishedCache.NuCache var hasDomains = _domainHelper.NodeHasDomains(n.Id); while (hasDomains == false && n != null) // n is null at root { - var urlSegment = n.GetCulture(culture).UrlSegment; + var varies = n.ContentType.Variations.Has(ContentVariation.CultureNeutral); + var urlSegment = varies ? n.GetCulture(culture)?.UrlSegment : n.UrlSegment; + + // at that point we should have an urlSegment, unless something weird is happening + // at content level, such as n.GetCulture() returning null for some (weird) reason, + // and then what? fallback to the invariant segment... far from perfect but eh... + if (string.IsNullOrWhiteSpace(urlSegment)) urlSegment = n.UrlSegment; pathParts.Add(urlSegment); diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 0c0308a471..1f7b54e232 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -209,33 +209,47 @@ namespace Umbraco.Web.Trees { result = Services.EntityService.GetChildren(entityId, UmbracoObjectType).ToArray(); } - - //This should really never be null, but we'll error check anyways - culture = culture ?? Services.LocalizationService.GetDefaultLanguageIsoCode(); - - //Try to see if there is a variant name for the current language for the item and set the name accordingly. - //If any of this fails, the tree node name will remain the default invariant culture name. - - //fixme - what if there is no name found at all ? This could occur if the doc type is variant and the user fills in all language values, then creates a new lang and sets it as the default - //fixme - what if the user changes this document type to not allow culture variants after it's already been created with culture variants, this means we'll be displaying the culture variant name when in fact we should be displaying the invariant name... but that would be null - - if (!culture.IsNullOrWhiteSpace()) - { - foreach (var e in result) - { - if (e.AdditionalData.TryGetValue("CultureNames", out var cultureNames) - && cultureNames is IDictionary cnd) - { - if (cnd.TryGetValue(culture, out var name)) - { - e.Name = name; - } - } - } - } + + // should really never be null, but we'll error check anyways + culture = culture ?? Services.LocalizationService.GetDefaultLanguageIsoCode(); + + // set names according to variations + if (!culture.IsNullOrWhiteSpace()) + foreach (var entity in result) + EnsureName(entity, culture); return result; } + + // set name according to variations + // + private void EnsureName(IEntitySlim entity, string culture) + { + if (culture == null) + { + if (string.IsNullOrWhiteSpace(entity.Name)) + entity.Name = "[[" + entity.Id + "]]"; + return; + } + + // we are getting the tree for a given culture, + // for those items that DO support cultures, we need to get the proper name, IF it exists + // otherwise, invariant is fine + + if (entity.AdditionalData.TryGetValue(EntitySlim.AdditionalVariations, out var variationsObject) && + variationsObject is ContentVariation variations && + variations.Has(ContentVariation.CultureNeutral) && + entity.AdditionalData.TryGetValue(EntitySlim.AdditionalCultureNames, out var namesObject) && + namesObject is IDictionary names && + names.TryGetValue(culture, out var name) && + !string.IsNullOrWhiteSpace(name)) + { + entity.Name = name; + } + + if (string.IsNullOrWhiteSpace(entity.Name)) + entity.Name = "[[" + entity.Id + "]]"; + } /// /// Returns true or false if the current user has access to the node based on the user's allowed start node (path) access