diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs index eedc97dbfc..d5096158a7 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureInfos.cs @@ -13,10 +13,9 @@ namespace Umbraco.Core.Models.PublishedContent /// public PublishedCultureInfo(string culture, string name, string urlSegment, DateTime date) { - if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentNullOrEmptyException(nameof(culture)); if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); - Culture = culture; + Culture = culture ?? throw new ArgumentNullException(nameof(culture)); Name = name; UrlSegment = urlSegment; Date = date; diff --git a/src/Umbraco.Core/PublishedContentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs index a8cb0cdaf2..f220f307d6 100644 --- a/src/Umbraco.Core/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/PublishedContentExtensions.cs @@ -37,7 +37,7 @@ namespace Umbraco.Core culture = culture ?? Current.VariationContextAccessor.VariationContext?.Culture ?? ""; // either does not vary by culture, or has the specified culture - return contents.Where(x => !ContentVariationExtensions.VariesByCulture((IPublishedContentType) x.ContentType) || HasCulture(x, culture)); + return contents.Where(x => !x.ContentType.VariesByCulture() || HasCulture(x, culture)); } /// @@ -49,7 +49,7 @@ namespace Umbraco.Core { // invariant has invariant value (whatever the requested culture) if (!content.ContentType.VariesByCulture()) - return "NAME??"; // fixme where should the invariant one come from? should Cultures contain it? + return content.Cultures.TryGetValue("", out var invariantInfos) ? invariantInfos.Name : null; // handle context culture for variant if (culture == null) @@ -69,7 +69,7 @@ namespace Umbraco.Core { // invariant has invariant value (whatever the requested culture) if (!content.ContentType.VariesByCulture()) - return "URLSEGMENT??"; // fixme where should the invariant one come from? should Cultures contain it? + return content.Cultures.TryGetValue("", out var invariantInfos) ? invariantInfos.UrlSegment : null; // handle context culture for variant if (culture == null) @@ -125,4 +125,4 @@ namespace Umbraco.Core : children.Where(x => x.IsInvariantOrHasCulture(culture)); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index 8de0209c69..8ce6b10983 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -382,7 +382,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache return GetXml(preview).CreateNavigator().MoveToId(contentId.ToString(CultureInfo.InvariantCulture)); } - public override IEnumerable GetAtRoot(bool preview) + public override IEnumerable GetAtRoot(bool preview, string culture = null) { return ConvertToDocuments(GetXml(preview).SelectNodes(XPathStrings.RootDocuments), preview); } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs index ba43921f1c..999d7f040d 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs @@ -105,7 +105,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache return GetUmbracoMedia(contentId) != null; } - public override IEnumerable GetAtRoot(bool preview) + public override IEnumerable GetAtRoot(bool preview, string culture = null) { var searchProvider = GetSearchProviderSafe(); diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs index 4a60912757..3697863cb4 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs @@ -145,8 +145,15 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache } } - private static readonly Lazy> NoCultures = new Lazy>(() => new Dictionary()); - public override IReadOnlyDictionary Cultures => NoCultures.Value; + private Dictionary _cultures; + + private Dictionary GetCultures() + { + EnsureNodeInitialized(); + return new Dictionary { { "", new PublishedCultureInfo("", _name, _urlName, _updateDate) } }; + } + + public override IReadOnlyDictionary Cultures => _cultures ?? (_cultures = GetCultures()); public override string WriterName { diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs index 7ad81922e2..671129848c 100644 --- a/src/Umbraco.Tests/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests/Published/ConvertersTests.cs @@ -4,16 +4,14 @@ using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Cache; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.Components; +using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache; @@ -127,7 +125,7 @@ namespace Umbraco.Tests.Published var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); var cntType1 = contentTypeFactory.CreateContentType(1001, "cnt1", t => Enumerable.Empty()); - var cnt1 = new TestPublishedContent(cntType1, 1234, Guid.NewGuid(), new Dictionary(), false); + var cnt1 = new SolidPublishedContent(cntType1) { Id = 1234 }; cacheContent[cnt1.Id] = cnt1; Assert.AreSame(cnt1, element1.Value("prop1")); @@ -224,8 +222,16 @@ namespace Umbraco.Tests.Published var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); var element2 = new PublishedElement(elementType2, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); - var cnt1 = new TestPublishedContent(contentType1, 1003, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); - var cnt2 = new TestPublishedContent(contentType2, 1004, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); + var cnt1 = new SolidPublishedContent(contentType1) + { + Id = 1003, + Properties = new[] { new SolidPublishedProperty { Alias = "prop1", SolidHasValue = true, SolidValue = "val1" } } + }; + var cnt2 = new SolidPublishedContent(contentType1) + { + Id = 1004, + Properties = new[] { new SolidPublishedProperty { Alias = "prop2", SolidHasValue = true, SolidValue = "1003" } } + }; cacheContent[cnt1.Id] = cnt1.CreateModel(); cacheContent[cnt2.Id] = cnt2.CreateModel(); diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 432247b09f..9385b8955a 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; +using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; using Umbraco.Web; using Umbraco.Web.Models; @@ -167,12 +168,16 @@ namespace Umbraco.Tests.Published var key = Guid.NewGuid(); var keyA = Guid.NewGuid(); - var content = new TestPublishedContent(contentType1, key, new[] + var content = new SolidPublishedContent(contentType1) { - new TestPublishedProperty(contentType1.GetPropertyType("property1"), $@"[ + Key = key, + Properties = new [] + { + new TestPublishedProperty(contentType1.GetPropertyType("property1"), $@"[ {{ ""key"": ""{keyA}"", ""propertyN1"": ""foo"", ""ncContentTypeAlias"": ""contentN1"" }} ]") - }); + } + }; var value = content.Value("property1"); // nested single converter returns proper TestModel value @@ -194,13 +199,17 @@ namespace Umbraco.Tests.Published var key = Guid.NewGuid(); var keyA = Guid.NewGuid(); var keyB = Guid.NewGuid(); - var content = new TestPublishedContent(contentType2, key, new[] + var content = new SolidPublishedContent(contentType2) { - new TestPublishedProperty(contentType2.GetPropertyType("property2"), $@"[ + Key = key, + Properties = new[] + { + new TestPublishedProperty(contentType2.GetPropertyType("property2"), $@"[ {{ ""key"": ""{keyA}"", ""propertyN1"": ""foo"", ""ncContentTypeAlias"": ""contentN1"" }}, {{ ""key"": ""{keyB}"", ""propertyN1"": ""bar"", ""ncContentTypeAlias"": ""contentN1"" }} ]") - }); + } + }; var value = content.Value("property2"); // nested many converter returns proper IEnumerable value @@ -257,52 +266,5 @@ namespace Umbraco.Tests.Published 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 - { - public TestPublishedContent(IPublishedContentType contentType, Guid key, IEnumerable properties) - { - ContentType = contentType; - Key = key; - var propertiesA = properties.ToArray(); - Properties = propertiesA; - foreach (var property in propertiesA) - property.SetOwner(this); - } - - // ReSharper disable UnassignedGetOnlyAutoProperty - public override PublishedItemType ItemType { get; } - public override bool IsDraft(string culture = null) => false; - public override bool IsPublished(string culture = null) => true; - public override IPublishedContent Parent { get; } - public override IEnumerable Children { get; } - public override IEnumerable ChildrenForAllCultures => Children; - public override IPublishedContentType ContentType { get; } - // ReSharper restore UnassignedGetOnlyAutoProperty - - // ReSharper disable UnassignedGetOnlyAutoProperty - public override int Id { get; } - public override int? TemplateId { get; } - public override int SortOrder { get; } - public override string Name { get; } - public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public override string UrlSegment { get; } - public override string WriterName { get; } - public override string CreatorName { get; } - public override int WriterId { get; } - public override int CreatorId { get; } - public override string Path { get; } - public override DateTime CreateDate { get; } - public override DateTime UpdateDate { get; } - public override int Level { get; } - public override Guid Key { get; } - // ReSharper restore UnassignedGetOnlyAutoProperty - - public override IEnumerable Properties { get; } - public override IPublishedProperty GetProperty(string alias) - { - return Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); - } - } } } diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index d77d1e4701..7e8a4b18e1 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -20,6 +20,7 @@ using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects; using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Web; using Umbraco.Web.Cache; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.NuCache; @@ -33,20 +34,25 @@ namespace Umbraco.Tests.PublishedContent private IPublishedSnapshotService _snapshotService; private IVariationContextAccessor _variationAccesor; private IPublishedSnapshotAccessor _snapshotAccessor; - private ContentType _contentType; - private PropertyType _propertyType; + private ContentType _contentTypeInvariant; + private ContentType _contentTypeVariant; private TestDataSource _source; private void Init(IEnumerable kits) { Current.Reset(); - Current.UnlockConfigs(); - Current.Configs.Add(SettingsForTests.GenerateMockUmbracoSettings); - Current.Configs.Add(() => new GlobalSettings()); - var globalSettings = Current.Configs.Global(); - // create a data source for NuCache - _source = new TestDataSource(kits); + var factory = Mock.Of(); + Current.Factory = factory; + + var configs = new Configs(); + Mock.Get(factory).Setup(x => x.GetInstance(typeof(Configs))).Returns(configs); + var globalSettings = new GlobalSettings(); + configs.Add(SettingsForTests.GenerateMockUmbracoSettings); + configs.Add(() => globalSettings); + + var publishedModelFactory = new NoopPublishedModelFactory(); + Mock.Get(factory).Setup(x => x.GetInstance(typeof(IPublishedModelFactory))).Returns(publishedModelFactory); var runtime = Mock.Of(); Mock.Get(runtime).Setup(x => x.Level).Returns(RuntimeLevel.Run); @@ -59,13 +65,18 @@ namespace Umbraco.Tests.PublishedContent dataType }; - _propertyType = new PropertyType("Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.Nothing }; - _contentType = new ContentType(-1) { Id = 2, Alias = "ctype", Variations = ContentVariation.Nothing }; - _contentType.AddPropertyType(_propertyType); + var propertyType = new PropertyType("Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.Nothing }; + _contentTypeInvariant = new ContentType(-1) { Id = 2, Alias = "itype", Variations = ContentVariation.Nothing }; + _contentTypeInvariant.AddPropertyType(propertyType); + + propertyType = new PropertyType("Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.Culture }; + _contentTypeVariant = new ContentType(-1) { Id = 3, Alias = "vtype", Variations = ContentVariation.Culture }; + _contentTypeVariant.AddPropertyType(propertyType); var contentTypes = new[] { - _contentType + _contentTypeInvariant, + _contentTypeVariant }; var contentTypeService = Mock.Of(); @@ -109,6 +120,9 @@ namespace Umbraco.Tests.PublishedContent _variationAccesor = new TestVariationContextAccessor(); _snapshotAccessor = new TestPublishedSnapshotAccessor(); + // create a data source for NuCache + _source = new TestDataSource(kits); + // at last, create the complete NuCache snapshot service! var options = new PublishedSnapshotService.Options { IgnoreLocalDb = true }; _snapshotService = new PublishedSnapshotService(options, @@ -133,9 +147,11 @@ namespace Umbraco.Tests.PublishedContent // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); + + Mock.Get(factory).Setup(x => x.GetInstance(typeof(IVariationContextAccessor))).Returns(_variationAccesor); } - private IEnumerable GetKits() + private IEnumerable GetInvariantKits() { var paths = new Dictionary { { -1, "-1" } }; @@ -146,10 +162,11 @@ namespace Umbraco.Tests.PublishedContent var path = paths[id] = parentPath + "," + id; var level = path.Count(x => x == ','); + var now = DateTime.Now; return new ContentNodeKit { - ContentTypeId = 2, + ContentTypeId = _contentTypeInvariant.Id, Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0), DraftData = null, PublishedData = new ContentData @@ -158,7 +175,7 @@ namespace Umbraco.Tests.PublishedContent Published = true, TemplateId = 0, VersionId = 1, - VersionDate = DateTime.Now, + VersionDate = now, WriterId = 0, Properties = new Dictionary(), CultureInfos = new Dictionary() @@ -184,6 +201,69 @@ namespace Umbraco.Tests.PublishedContent yield return CreateKit(12, 4, 2); } + private IEnumerable GetVariantKits() + { + var paths = new Dictionary { { -1, "-1" } }; + + Dictionary GetCultureInfos(int id, DateTime now) + { + var en = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; + var fr = new[] { 1, 3, 4, 6, 7, 9, 10, 12 }; + + var infos = new Dictionary(); + if (en.Contains(id)) + infos["en-US"] = new CultureVariation { Name = "N" + id + "-" + "en-US", Date = now, IsDraft = false }; + if (fr.Contains(id)) + infos["fr-FR"] = new CultureVariation { Name = "N" + id + "-" + "fr-FR", Date = now, IsDraft = false }; + return infos; + } + + ContentNodeKit CreateKit(int id, int parentId, int sortOrder) + { + if (!paths.TryGetValue(parentId, out var parentPath)) + throw new Exception("Unknown parent."); + + var path = paths[id] = parentPath + "," + id; + var level = path.Count(x => x == ','); + var now = DateTime.Now; + + return new ContentNodeKit + { + ContentTypeId = _contentTypeVariant.Id, + Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0), + DraftData = null, + PublishedData = new ContentData + { + Name = "N" + id, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = GetCultureInfos(id, now) + } + }; + } + + yield return CreateKit(1, -1, 1); + yield return CreateKit(2, -1, 2); + yield return CreateKit(3, -1, 3); + + yield return CreateKit(4, 1, 1); + yield return CreateKit(5, 1, 2); + yield return CreateKit(6, 1, 3); + + yield return CreateKit(7, 2, 3); + yield return CreateKit(8, 2, 2); + yield return CreateKit(9, 2, 1); + + yield return CreateKit(10, 3, 1); + + yield return CreateKit(11, 4, 1); + yield return CreateKit(12, 4, 2); + } + [Test] public void EmptyTest() { @@ -199,7 +279,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void ChildrenTest() { - Init(GetKits()); + Init(GetInvariantKits()); var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); _snapshotAccessor.PublishedSnapshot = snapshot; @@ -226,7 +306,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void ParentTest() { - Init(GetKits()); + Init(GetInvariantKits()); var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); _snapshotAccessor.PublishedSnapshot = snapshot; @@ -252,7 +332,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void MoveToRootTest() { - Init(GetKits()); + Init(GetInvariantKits()); // get snapshot var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); @@ -294,7 +374,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void MoveFromRootTest() { - Init(GetKits()); + Init(GetInvariantKits()); // get snapshot var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); @@ -336,7 +416,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void ReOrderTest() { - Init(GetKits()); + Init(GetInvariantKits()); // get snapshot var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); @@ -411,7 +491,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void MoveTest() { - Init(GetKits()); + Init(GetInvariantKits()); // get snapshot var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); @@ -512,11 +592,90 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(1, snapshot.Content.GetById(7).Parent?.Id); } + [Test] + public void VariantChildrenTest() + { + Init(GetVariantKits()); + + var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); + _snapshotAccessor.PublishedSnapshot = snapshot; + + _variationAccesor.VariationContext = new VariationContext("en-US"); + + var documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N1-en-US", "N2-en-US", "N3-en-US"); + + documents = snapshot.Content.GetById(1).Children().ToArray(); + AssertDocuments(documents, "N4-en-US", "N5-en-US", "N6-en-US"); + + documents = snapshot.Content.GetById(2).Children().ToArray(); + AssertDocuments(documents, "N9-en-US", "N8-en-US", "N7-en-US"); + + documents = snapshot.Content.GetById(3).Children().ToArray(); + AssertDocuments(documents, "N10-en-US"); + + documents = snapshot.Content.GetById(4).Children().ToArray(); + AssertDocuments(documents, "N11-en-US", "N12-en-US"); + + documents = snapshot.Content.GetById(10).Children().ToArray(); + AssertDocuments(documents); + + + _variationAccesor.VariationContext = new VariationContext("fr-FR"); + + documents = snapshot.Content.GetAtRoot().ToArray(); + AssertDocuments(documents, "N1-fr-FR", "N3-fr-FR"); + + documents = snapshot.Content.GetById(1).Children().ToArray(); + AssertDocuments(documents, "N4-fr-FR", "N6-fr-FR"); + + documents = snapshot.Content.GetById(2).Children().ToArray(); + AssertDocuments(documents, "N9-fr-FR", "N7-fr-FR"); + + documents = snapshot.Content.GetById(3).Children().ToArray(); + AssertDocuments(documents, "N10-fr-FR"); + + documents = snapshot.Content.GetById(4).Children().ToArray(); + AssertDocuments(documents, "N12-fr-FR"); + + documents = snapshot.Content.GetById(10).Children().ToArray(); + AssertDocuments(documents); + + documents = snapshot.Content.GetById(1).Children("*").ToArray(); + AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR"); + AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US"); + + documents = snapshot.Content.GetById(1).Children("en-US").ToArray(); + AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR"); + AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US"); + + documents = snapshot.Content.GetById(1).ChildrenForAllCultures.ToArray(); + AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR"); + AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US"); + + + documents = snapshot.Content.GetAtRoot("*").ToArray(); + AssertDocuments(documents, "N1-fr-FR", null, "N3-fr-FR"); + + documents = snapshot.Content.GetById(1).DescendantsOrSelf().ToArray(); + AssertDocuments(documents, "N1-fr-FR", "N4-fr-FR", "N12-fr-FR", "N6-fr-FR"); + + documents = snapshot.Content.GetById(1).DescendantsOrSelf("*").ToArray(); + AssertDocuments(documents, "N1-fr-FR", "N4-fr-FR", null /*11*/, "N12-fr-FR", null /*5*/, "N6-fr-FR"); + } + private void AssertDocuments(IPublishedContent[] documents, params string[] names) { Assert.AreEqual(names.Length, documents.Length); for (var i = 0; i < names.Length; i++) - Assert.AreEqual(names[i], documents[i].Name()); + Assert.AreEqual(names[i], documents[i].Name); + } + + private void AssertDocuments(string culture, IPublishedContent[] documents, params string[] names) + { + Assert.AreEqual(names.Length, documents.Length); + for (var i = 0; i < names.Length; i++) + Assert.AreEqual(names[i], documents[i].Name(culture)); } } } diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index dc035c1645..b66404c954 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -38,10 +38,18 @@ namespace Umbraco.Tests.PublishedContent private void Init() { Current.Reset(); - Current.UnlockConfigs(); - Current.Configs.Add(SettingsForTests.GenerateMockUmbracoSettings); - Current.Configs.Add(() => new GlobalSettings()); - var globalSettings = Current.Configs.Global(); + + var factory = Mock.Of(); + Current.Factory = factory; + + var configs = new Configs(); + Mock.Get(factory).Setup(x => x.GetInstance(typeof(Configs))).Returns(configs); + var globalSettings = new GlobalSettings(); + configs.Add(SettingsForTests.GenerateMockUmbracoSettings); + configs.Add(() => globalSettings); + + var publishedModelFactory = new NoopPublishedModelFactory(); + Mock.Get(factory).Setup(x => x.GetInstance(typeof(IPublishedModelFactory))).Returns(publishedModelFactory); // create a content node kit var kit = new ContentNodeKit @@ -184,6 +192,8 @@ namespace Umbraco.Tests.PublishedContent // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); + + Mock.Get(factory).Setup(x => x.GetInstance(typeof(IVariationContextAccessor))).Returns(_variationAccesor); } [Test] diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 5c22295547..cc455b8e5d 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -97,7 +97,7 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetContent(true, 1); //change a doc type alias - var c = (TestPublishedContent)doc.Children.ElementAt(0); + var c = (SolidPublishedContent)doc.Children.ElementAt(0); c.ContentType = new PublishedContentType(22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var dt = doc.ChildrenAsTable(Current.Services, "Child"); @@ -129,7 +129,8 @@ namespace Umbraco.Tests.PublishedContent var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); var contentTypeAlias = createChildren ? "Parent" : "Child"; - var d = new TestPublishedContent + var contentType = new PublishedContentType(22, contentTypeAlias, PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var d = new SolidPublishedContent(contentType) { CreateDate = DateTime.Now, CreatorId = 1, @@ -140,7 +141,7 @@ namespace Umbraco.Tests.PublishedContent UpdateDate = DateTime.Now, Path = "-1,3", UrlSegment = "home-page", - Name = "Page" + Guid.NewGuid().ToString(), + Name = "Page" + Guid.NewGuid(), Version = Guid.NewGuid(), WriterId = 1, WriterName = "Shannon", @@ -175,62 +176,7 @@ namespace Umbraco.Tests.PublishedContent new RawValueProperty(factory.CreatePropertyType("property3", 1), d, "value" + (indexVals + 2))); } - d.ContentType = new PublishedContentType(22, contentTypeAlias, PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); return d; } - - // note - could probably rewrite those tests using SolidPublishedContentCache - // l8tr... - private class TestPublishedContent : IPublishedContent - { - public string Url { get; set; } - public PublishedItemType ItemType { get; set; } - public IPublishedContent Parent { get; set; } - public int Id { get; set; } - public Guid Key { get; set; } - public int? TemplateId { get; set; } - public int SortOrder { get; set; } - public string Name { get; set; } - public IReadOnlyDictionary Cultures => throw new NotSupportedException(); - public string UrlSegment { get; set; } - public string WriterName { get; set; } - public string CreatorName { get; set; } - public int WriterId { get; set; } - public int CreatorId { get; set; } - public string Path { get; set; } - public DateTime CreateDate { get; set; } - public DateTime UpdateDate { get; set; } - public Guid Version { get; set; } - public int Level { get; set; } - public bool IsDraft(string culture = null) => false; - public bool IsPublished(string culture = null) => true; - - public IEnumerable Properties { get; set; } - - public IEnumerable Children { get; set; } - public IEnumerable ChildrenForAllCultures => Children; - - public IPublishedProperty GetProperty(string alias) - { - return Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); - } - - public IPublishedProperty GetProperty(string alias, bool recurse) - { - var property = GetProperty(alias); - if (recurse == false) return property; - - IPublishedContent content = this; - while (content != null && (property == null || property.HasValue() == false)) - { - content = content.Parent; - property = content == null ? null : content.GetProperty(alias); - } - - return property; - } - - public IPublishedContentType ContentType { get; set; } - } } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index fd0837b0ab..f54971a197 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -821,7 +821,7 @@ namespace Umbraco.Tests.PublishedContent var level1_2 = GetNode(1175); var level1_3 = GetNode(4444); - _publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot()).Returns(new []{root}); + _publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot(It.IsAny())).Returns(new []{root}); CollectionAssertAreEqual(new []{root}, root.SiblingsAndSelf()); @@ -860,7 +860,7 @@ namespace Umbraco.Tests.PublishedContent var level1_2 = GetNode(1175); var level1_3 = GetNode(4444); - _publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot()).Returns(new []{root}); + _publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot(It.IsAny())).Returns(new []{root}); CollectionAssertAreEqual(new IPublishedContent[0], root.Siblings()); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index b789eb0ef8..f801d02c5b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -495,7 +495,7 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(nodeId, converted.Id); Assert.AreEqual(3, converted.Level); Assert.AreEqual(1, converted.SortOrder); - Assert.AreEqual("Sam's Umbraco Image", converted.Name()); + Assert.AreEqual("Sam's Umbraco Image", converted.Name); Assert.AreEqual("-1,1111,2222,2112", converted.Path); } diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index ab5a65146a..860b9b9179 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -100,7 +100,7 @@ namespace Umbraco.Tests.PublishedContent return _content.ContainsKey(contentId); } - public override IEnumerable GetAtRoot(bool preview) + public override IEnumerable GetAtRoot(bool preview, string culture = null) { return _content.Values.Where(x => x.Parent == null); } @@ -176,13 +176,19 @@ namespace Umbraco.Tests.PublishedContent #region Content + private Dictionary _cultures; + + private Dictionary GetCultures() + { + return new Dictionary { { "", new PublishedCultureInfo("", Name, UrlSegment, UpdateDate) } }; + } + public int Id { get; set; } public Guid Key { get; set; } public int? TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } - public PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); - public IReadOnlyDictionary Cultures => throw new NotSupportedException(); + public IReadOnlyDictionary Cultures => _cultures ?? (_cultures = GetCultures()); public string UrlSegment { get; set; } public string WriterName { get; set; } public string CreatorName { get; set; } @@ -195,7 +201,7 @@ namespace Umbraco.Tests.PublishedContent public int Level { get; set; } public string Url { get; set; } - public PublishedItemType ItemType { get { return PublishedItemType.Content; } } + public PublishedItemType ItemType => PublishedItemType.Content; public bool IsDraft(string culture = null) => false; public bool IsPublished(string culture = null) => true; @@ -214,7 +220,7 @@ namespace Umbraco.Tests.PublishedContent #region ContentType - public IPublishedContentType ContentType { get; private set; } + public IPublishedContentType ContentType { get; set; } #endregion @@ -236,7 +242,7 @@ namespace Umbraco.Tests.PublishedContent while (content != null && (property == null || property.HasValue() == false)) { content = content.Parent; - property = content == null ? null : content.GetProperty(alias); + property = content?.GetProperty(alias); } return property; diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index 9eee6eb32d..5af48e64b1 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using Moq; using Newtonsoft.Json; @@ -11,7 +10,6 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Web.Routing; @@ -56,10 +54,10 @@ namespace Umbraco.Tests.Routing const string expected = "/media/rfeiw584/test.jpg"; var configuration = new ImageCropperConfiguration(); - var imageCropperValue = JsonConvert.SerializeObject(new ImageCropperValue + var imageCropperValue = new ImageCropperValue { Src = expected - }); + }; var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.ImageCropper, imageCropperValue, configuration); @@ -120,15 +118,28 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(daMediaUrl, resolvedUrl); } - private static TestPublishedContent CreatePublishedContent(string propertyEditorAlias, object propertyValue, object dataTypeConfiguration) + private static IPublishedContent CreatePublishedContent(string propertyEditorAlias, object propertyValue, object dataTypeConfiguration) { var umbracoFilePropertyType = CreatePropertyType(propertyEditorAlias, dataTypeConfiguration, ContentVariation.Nothing); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), new[] {umbracoFilePropertyType}, ContentVariation.Nothing); - return new TestPublishedContent(contentType, 1234, Guid.NewGuid(), - new Dictionary {{"umbracoFile", propertyValue } }, false); + return new SolidPublishedContent(contentType) + { + Id = 1234, + Key = Guid.NewGuid(), + Properties = new[] + { + new SolidPublishedProperty + { + Alias = "umbracoFile", + SolidValue = propertyValue, + SolidHasValue = true, + PropertyType = umbracoFilePropertyType + } + } + }; } private static PublishedPropertyType CreatePropertyType(string propertyEditorAlias, object dataTypeConfiguration, ContentVariation variation) diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index ca13e06f0a..02aa95cd2e 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -10,8 +10,8 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Tests.LegacyXmlPublishedCache; +using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -142,7 +142,7 @@ namespace Umbraco.Tests.Routing new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings: globalSettings.Object); - + var result = umbracoContext.UrlProvider.GetUrl(nodeId); Assert.AreEqual(niceUrlMatch, result); } @@ -159,7 +159,7 @@ namespace Umbraco.Tests.Routing var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); - var publishedContent = new TestPublishedContent(contentType, 1234, Guid.NewGuid(), new Dictionary(), false); + var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; var publishedContentCache = new Mock(); publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) @@ -204,7 +204,7 @@ namespace Umbraco.Tests.Routing var umbracoSettings = Current.Configs.Settings(); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); - var publishedContent = new TestPublishedContent(contentType, 1234, Guid.NewGuid(), new Dictionary(), false); + var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; var publishedContentCache = new Mock(); publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) @@ -258,7 +258,7 @@ namespace Umbraco.Tests.Routing var umbracoSettings = Current.Configs.Settings(); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); - var publishedContent = new TestPublishedContent(contentType, 1234, Guid.NewGuid(), new Dictionary(), false); + var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; var publishedContentCache = new Mock(); publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) @@ -332,7 +332,7 @@ namespace Umbraco.Tests.Routing }, globalSettings: globalSettings.Object); //mock the Umbraco settings that we need - + Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); umbracoContext.UrlProvider.Mode = UrlMode.Absolute; diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs deleted file mode 100644 index 62f93d106f..0000000000 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web.PublishedCache; - -namespace Umbraco.Tests.TestHelpers.Stubs -{ - internal class TestPublishedContent : PublishedElement, IPublishedContent - { - public TestPublishedContent(IPublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultures = null) - : base(contentType, key, values, previewing) - { - Id = id; - Cultures = cultures ?? new Dictionary(); - } - - public int Id { get; } - public int? TemplateId { get; set; } - public int SortOrder { get; set; } - public string Name { get; set; } - public IVariationContextAccessor VariationContextAccessor { get; set; } - public IReadOnlyDictionary Cultures { get; set; } - public string UrlSegment { get; set; } - public string DocumentTypeAlias => ContentType.Alias; - public int DocumentTypeId { get; set; } - public string WriterName { get; set; } - public string CreatorName { get; set; } - public int WriterId { get; set; } - public int CreatorId { get; set; } - public string Path { get; set; } - public DateTime CreateDate { get; set; } - public DateTime UpdateDate { get; set; } - public Guid Version { get; set; } - public int Level { get; set; } - public string Url { get; set; } - public PublishedItemType ItemType => ContentType.ItemType; - public bool IsDraft(string culture = null) => false; - public bool IsPublished(string culture = null) => true; - public IPublishedContent Parent { get; set; } - public IEnumerable Children { get; set; } - public IEnumerable ChildrenForAllCultures => Children; - - // copied from PublishedContentBase - public IPublishedProperty GetProperty(string alias, bool recurse) - { - 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; - } - } -} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index cd3155c7c3..4f4a83dc26 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -206,7 +206,6 @@ - diff --git a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs index 33094221f7..0370088f77 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs @@ -84,16 +84,18 @@ namespace Umbraco.Web.PublishedCache /// Gets contents at root. /// /// A value indicating whether to consider unpublished content. + /// A culture. /// The contents. /// The value of overrides defaults. - IEnumerable GetAtRoot(bool preview); + IEnumerable GetAtRoot(bool preview, string culture = null); /// /// Gets contents at root. /// + /// A culture. /// The contents. /// Considers published or unpublished content depending on defaults. - IEnumerable GetAtRoot(); + IEnumerable GetAtRoot(string culture = null); /// /// Gets a content resulting from an XPath query. diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 4dcee5eb24..fa879b7879 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -1,20 +1,15 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Xml.XPath; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; -using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Services; using Umbraco.Core.Xml; using Umbraco.Core.Xml.XPath; using Umbraco.Web.PublishedCache.NuCache.Navigable; -using Umbraco.Web.Routing; namespace Umbraco.Web.PublishedCache.NuCache { @@ -25,6 +20,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IAppCache _elementsCache; private readonly IDomainCache _domainCache; private readonly IGlobalSettings _globalSettings; + private readonly IVariationContextAccessor _variationContextAccessor; #region Constructor @@ -33,7 +29,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // it's too late for UmbracoContext which has captured previewDefault and stuff into these ctor vars // but, no, UmbracoContext returns snapshot.Content which comes from elements SO a resync should create a new cache - public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, IAppCache elementsCache, IDomainCache domainCache, IGlobalSettings globalSettings) + public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, IAppCache elementsCache, IDomainCache domainCache, IGlobalSettings globalSettings, IVariationContextAccessor variationContextAccessor) : base(previewDefault) { _snapshot = snapshot; @@ -41,6 +37,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _elementsCache = elementsCache; _domainCache = domainCache; _globalSettings = globalSettings; + _variationContextAccessor = variationContextAccessor; } private bool HideTopLevelNodeFromPath => _globalSettings.HideTopLevelNodeFromPath; @@ -241,7 +238,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var guidUdi = contentId as GuidUdi; if (guidUdi == null) throw new ArgumentException($"Udi must be of type {typeof(GuidUdi).Name}.", nameof(contentId)); - + if (guidUdi.EntityType != Constants.UdiEntityType.Document) throw new ArgumentException($"Udi entity type must be \"{Constants.UdiEntityType.Document}\".", nameof(contentId)); @@ -256,11 +253,18 @@ namespace Umbraco.Web.PublishedCache.NuCache return preview || n.PublishedModel != null; } - public override IEnumerable GetAtRoot(bool preview) + IEnumerable INavigableData.GetAtRoot(bool preview) => GetAtRoot(preview); + + public override IEnumerable GetAtRoot(bool preview, string culture = null) { + // handle context culture for variant + if (culture == null) + culture = _variationContextAccessor?.VariationContext?.Culture ?? ""; + // both .Draft and .Published cannot be null at the same time // root is already sorted by sortOrder, and does not contain nulls - return _snapshot.GetAtRoot().Select(n => GetNodePublishedContent(n, preview)); + var atRoot = _snapshot.GetAtRoot().Select(n => GetNodePublishedContent(n, preview)); + return culture == "*" ? atRoot : atRoot.Where(x => x.IsInvariantOrHasCulture(culture)); } private static IPublishedContent GetNodePublishedContent(ContentNode node, bool preview) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs index fc04d9d1b3..182086ed7f 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs @@ -14,17 +14,15 @@ namespace Umbraco.Web.PublishedCache.NuCache internal class MediaCache : PublishedCacheBase, IPublishedMediaCache, INavigableData, IDisposable { private readonly ContentStore.Snapshot _snapshot; - private readonly IAppCache _snapshotCache; - private readonly IAppCache _elementsCache; + private readonly IVariationContextAccessor _variationContextAccessor; #region Constructors - public MediaCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, IAppCache elementsCache) + public MediaCache(bool previewDefault, ContentStore.Snapshot snapshot, IVariationContextAccessor variationContextAccessor) : base(previewDefault) { _snapshot = snapshot; - _snapshotCache = snapshotCache; - _elementsCache = elementsCache; + _variationContextAccessor = variationContextAccessor; } #endregion @@ -65,30 +63,16 @@ namespace Umbraco.Web.PublishedCache.NuCache return n != null; } - public override IEnumerable GetAtRoot(bool preview) + IEnumerable INavigableData.GetAtRoot(bool preview) => GetAtRoot(preview); + + public override IEnumerable GetAtRoot(bool preview, string culture = null) { - if (PublishedSnapshotService.CacheContentCacheRoots == false) - return GetAtRootNoCache(); + // handle context culture for variant + if (culture == null) + culture = _variationContextAccessor?.VariationContext?.Culture ?? ""; - var cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing - ? _elementsCache - : _snapshotCache; - - if (cache == null) - return GetAtRootNoCache(); - - // note: ToArray is important here, we want to cache the result, not the function! - return (IEnumerable)cache.Get( - CacheKeys.MediaCacheRoots(false), // ignore preview, only 1 key! - () => GetAtRootNoCache().ToArray()); - } - - private IEnumerable GetAtRootNoCache() - { - var c = _snapshot.GetAtRoot(); - - // ignore preview, there's only draft for media - return c.Select(n => n.PublishedModel); + var atRoot = _snapshot.GetAtRoot().Select(x => x.PublishedModel); + return culture == "*" ? atRoot : atRoot.Where(x => x.IsInvariantOrHasCulture(culture)); } public override bool HasContent(bool preview) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 67f219be21..1995e8b424 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -186,11 +186,14 @@ namespace Umbraco.Web.PublishedCache.NuCache { get { - if (!ContentType.VariesByCulture()) - return EmptyCultures; - if (_cultures != null) return _cultures; + if (!ContentType.VariesByCulture()) + return _cultures = new Dictionary + { + { "", new PublishedCultureInfo("", ContentData.Name, _urlSegment, CreateDate) } + }; + if (ContentData.CultureInfos == null) throw new Exception("panic: _contentDate.CultureInfos is null."); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 5a3672ed57..7a7682a797 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -63,18 +63,6 @@ namespace Umbraco.Web.PublishedCache.NuCache // so making it configurable. public static readonly bool FullCacheWhenPreviewing = true; - // define constant - determines whether to cache the published content - // objects (in the elements cache, or snapshot cache, depending on preview) - // or to re-fetch them all the time. caching is faster but uses more - // memory. not sure what we want. - public static readonly bool CachePublishedContentChildren = true; - - // define constant - determines whether to cache the content cache root - // objects (in the elements cache, or snapshot cache, depending on preview) - // or to re-fetch them all the time. caching is faster but uses more - // memory - not sure what we want. - public static readonly bool CacheContentCacheRoots = true; - #region Constructors //private static int _singletonCheck; @@ -1070,8 +1058,8 @@ namespace Umbraco.Web.PublishedCache.NuCache return new PublishedSnapshot.PublishedSnapshotElements { - ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainCache, _globalSettings), - MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache), + ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainCache, _globalSettings, VariationContextAccessor), + MediaCache = new MediaCache(previewDefault, mediaSnap, VariationContextAccessor), MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor, _entitySerializer), DomainCache = domainCache, SnapshotCache = snapshotCache, diff --git a/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs b/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs index d726664db0..1f637663e5 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs @@ -37,11 +37,11 @@ namespace Umbraco.Web.PublishedCache public bool HasById(int contentId) => HasById(PreviewDefault, contentId); - public abstract IEnumerable GetAtRoot(bool preview); + public abstract IEnumerable GetAtRoot(bool preview, string culture = null); - public IEnumerable GetAtRoot() + public IEnumerable GetAtRoot(string culture = null) { - return GetAtRoot(PreviewDefault); + return GetAtRoot(PreviewDefault, culture); } public abstract IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars); diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index fbfc52f4d8..d7283a1e90 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -833,7 +833,7 @@ namespace Umbraco.Web if (content == null) throw new ArgumentNullException(nameof(content)); if (orSelf) yield return content; - foreach (var desc in content.Children(culture).SelectMany(x => x.EnumerateDescendants())) + foreach (var desc in content.Children(culture).SelectMany(x => x.EnumerateDescendants(culture))) yield return desc; } @@ -841,7 +841,7 @@ namespace Umbraco.Web { yield return content; - foreach (var desc in content.Children(culture).SelectMany(x => x.EnumerateDescendants())) + foreach (var desc in content.Children(culture).SelectMany(x => x.EnumerateDescendants(culture))) yield return desc; }