From 7dd1efb29f16477a341f16198fc46016dfddeed2 Mon Sep 17 00:00:00 2001 From: Stephan Date: Sat, 28 Apr 2018 21:57:07 +0200 Subject: [PATCH] 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! } }