diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index e24ae133df..049461522d 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -231,7 +231,7 @@ namespace Umbraco.Core.Models internal virtual void PublishValues() { foreach (var property in Properties) - property.PublishValues(); + property.PublishValues(null, null); _publishedState = PublishedState.Publishing; } @@ -241,13 +241,23 @@ namespace Umbraco.Core.Models internal virtual void PublishValues(int? nLanguageId) { foreach (var property in Properties) - property.PublishValues(nLanguageId); + property.PublishValues(nLanguageId, null); _publishedState = PublishedState.Publishing; } /// /// Publish the segment value. /// + internal virtual void PublishValues(string segment) + { + foreach (var property in Properties) + property.PublishValues(null, segment); + _publishedState = PublishedState.Publishing; + } + + /// + /// Publish the culture+segment value. + /// internal virtual void PublishValues(int? nLanguageId, string segment) { foreach (var property in Properties) diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index 5329bf96dc..557a46afd9 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -20,8 +20,7 @@ namespace Umbraco.Core.Models private List _values = new List(); private PropertyValue _pvalue; - private Dictionary _lvalues; - private Dictionary> _svalues; + private Dictionary<(int?, string), PropertyValue> _vvalues; private static readonly Lazy Ps = new Lazy(); @@ -95,12 +94,7 @@ namespace Umbraco.Core.Models _pvalue = value.FirstOrDefault(x => !x.LanguageId.HasValue && x.Segment == null); - _lvalues = value.Where(x => x.LanguageId.HasValue && x.Segment == null) - .ToDictionary(x => x.LanguageId.Value, x => x); - - _svalues = value.Where(x => x.LanguageId.HasValue && x.Segment != null) - .GroupBy(x => x.LanguageId.Value) - .ToDictionary(x => x.Key, x => x.ToDictionary(y => y.Segment, y => y)); + _vvalues = value.ToDictionary(x => (x.LanguageId, x.Segment), x => x); } } @@ -146,22 +140,34 @@ namespace Umbraco.Core.Models /// /// Gets the culture value. /// - public object GetValue(int languageId, bool published = false) + public object GetValue(int? languageId, bool published = false) { - if (_lvalues == null) return null; - if (!_lvalues.TryGetValue(languageId, out var pvalue)) return null; - return GetPropertyValue(pvalue, published); + if (_vvalues == null) return null; + return _vvalues.TryGetValue((languageId, null), out var pvalue) + ? GetPropertyValue(pvalue, published) + : null; } /// /// Gets the segment value. /// - public object GetValue(int languageId, string segment, bool published = false) + public object GetValue(string segment, bool published = false) { - if (_svalues == null) return null; - if (!_svalues.TryGetValue(languageId, out var svalues)) return null; - if (!svalues.TryGetValue(segment, out var pvalue)) return null; - return GetPropertyValue(pvalue, published); + if (_vvalues == null) return null; + return _vvalues.TryGetValue((null, segment), out var pvalue) + ? GetPropertyValue(pvalue, published) + : null; + } + + /// + /// Gets the culture+segment value. + /// + public object GetValue(int? languageId, string segment, bool published = false) + { + if (_vvalues == null) return null; + return _vvalues.TryGetValue((languageId, segment), out var pvalue) + ? GetPropertyValue(pvalue, published) + : null; } private object GetPropertyValue(PropertyValue pvalue, bool published) @@ -171,42 +177,9 @@ namespace Umbraco.Core.Models : pvalue.EditedValue; } - internal void PublishValues() + internal void PublishValues(int? languageId, string segment) { - (var pvalue, _) = GetPropertyValue(false); - if (pvalue == null) return; - PublishPropertyValue(pvalue); - } - - internal void PublishValues(int? nLanguageId) - { - if (nLanguageId == null) - { - PublishValues(); - return; - } - - var languageId = nLanguageId.Value; - - (var pvalue, _) = GetPropertyValue(languageId, false); - if (pvalue == null) return; - PublishPropertyValue(pvalue); - } - - internal void PublishValues(int? nLanguageId, string segment) - { - if (segment == null) - { - PublishValues(nLanguageId); - return; - } - - if (!nLanguageId.HasValue) - throw new ArgumentException("Cannot be null when segment is not null.", nameof(nLanguageId)); - - var languageId = nLanguageId.Value; - - (var pvalue, _) = GetPropertyValue(languageId, segment, false); + (var pvalue, _) = GetPValue(languageId, segment, false); if (pvalue == null) return; PublishPropertyValue(pvalue); } @@ -227,47 +200,38 @@ namespace Umbraco.Core.Models } /// - /// Sets a (edit) neutral value. + /// Sets a (edited) neutral value. /// public void SetValue(object value) { - (var pvalue, var change) = GetPropertyValue(true); + (var pvalue, var change) = GetPValue(true); SetPropertyValue(pvalue, value, change); } /// - /// Sets a (edit) culture value. + /// Sets a (edited) culture value. /// - public void SetValue(int? nLanguageId, object value) + public void SetValue(int? languageId, object value) { - if (nLanguageId == null) - { - SetValue(value); - return; - } - - var languageId = nLanguageId.Value; - - (var pvalue, var change) = GetPropertyValue(languageId, true); + (var pvalue, var change) = GetPValue(languageId, null, true); SetPropertyValue(pvalue, value, change); } /// - /// Sets a (edit) segment value. + /// Sets a (edited) culture value. /// - public void SetValue(int? nLanguageId, string segment, object value) + public void SetValue(string segment, object value) { - if (segment == null) - { - SetValue(nLanguageId, value); - return; - } + (var pvalue, var change) = GetPValue(null, segment, true); + SetPropertyValue(pvalue, value, change); + } - if (!nLanguageId.HasValue) - throw new ArgumentException("Cannot be null when segment is not null.", nameof(nLanguageId)); - var languageId = nLanguageId.Value; - - (var pvalue, var change) = GetPropertyValue(languageId, segment, true); + /// + /// Sets a (edited) culture+segment value. + /// + public void SetValue(int? languageId, string segment, object value) + { + (var pvalue, var change) = GetPValue(languageId, segment, true); SetPropertyValue(pvalue, value, change); } @@ -281,39 +245,10 @@ namespace Umbraco.Core.Models DetectChanges(setValue, origValue, Ps.Value.ValuesSelector, Ps.Value.PropertyValueComparer, change); } - private void FactorySetValue(bool published, object value) - { - (var pvalue, _) = GetPropertyValue(true); - FactorySetPropertyValue(pvalue, published, value); - } - - private void FactorySetValue(int? nLanguageId, bool published, object value) - { - if (nLanguageId == null) - { - FactorySetValue(published, value); - return; - } - - var languageId = nLanguageId.Value; - (var pvalue, _) = GetPropertyValue(languageId, true); - FactorySetPropertyValue(pvalue, published, value); - } - // bypasses all changes detection and is the *only* to set the published value - internal void FactorySetValue(int? nLanguageId, string segment, bool published, object value) + internal void FactorySetValue(int? languageId, string segment, bool published, object value) { - if (segment == null) - { - FactorySetValue(nLanguageId, published, value); - return; - } - - if (!nLanguageId.HasValue) - throw new ArgumentException("Cannot be null when segment is not null.", nameof(nLanguageId)); - var languageId = nLanguageId.Value; - - (var pvalue, _) = GetPropertyValue(languageId, segment, true); + (var pvalue, _) = GetPValue(languageId, segment, true); FactorySetPropertyValue(pvalue, published, value); } @@ -325,7 +260,7 @@ namespace Umbraco.Core.Models pvalue.EditedValue = value; } - private (PropertyValue, bool) GetPropertyValue(bool create) + private (PropertyValue, bool) GetPValue(bool create) { var change = false; if (_pvalue == null) @@ -338,45 +273,22 @@ namespace Umbraco.Core.Models return (_pvalue, change); } - private (PropertyValue, bool) GetPropertyValue(int languageId, bool create) + private (PropertyValue, bool) GetPValue(int? languageId, string segment, bool create) { - var change = false; - if (_lvalues == null) - { - if (!create) return (null, false); - _lvalues = new Dictionary(); - change = true; - } - if (!_lvalues.TryGetValue(languageId, out var pvalue)) - { - if (!create) return (null, false); - pvalue = _lvalues[languageId] = new PropertyValue(); - pvalue.LanguageId = languageId; - _values.Add(pvalue); - change = true; - } - return (pvalue, change); - } + if (!languageId.HasValue && segment == null) + return GetPValue(create); - private (PropertyValue, bool) GetPropertyValue(int languageId, string segment, bool create) - { var change = false; - if (_svalues == null) + if (_vvalues == null) { if (!create) return (null, false); - _svalues = new Dictionary>(); + _vvalues = new Dictionary<(int?, string), PropertyValue>(); change = true; } - if (!_svalues.TryGetValue(languageId, out var svalue)) + if (_vvalues.TryGetValue((languageId, segment), out var pvalue)) { if (!create) return (null, false); - svalue = _svalues[languageId] = new Dictionary(); - change = true; - } - if (!svalue.TryGetValue(segment, out var pvalue)) - { - if (!create) return (null, false); - pvalue = svalue[segment] = new PropertyValue(); + pvalue = _vvalues[(languageId, segment)] = new PropertyValue(); pvalue.LanguageId = languageId; pvalue.Segment = segment; _values.Add(pvalue); diff --git a/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs b/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs index 36068f6776..f7de760472 100644 --- a/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs @@ -35,6 +35,13 @@ namespace Umbraco.Core.Models.Rdbms [Column("current")] public bool Current { get; set; } + // about current: + // there is nothing in the DB that guarantees that there will be one, and exactly one, current version per content item. + // that would require circular FKs that are impossible (well, it is possible to create them, but not to insert). + // we could use a content.currentVersionId FK that would need to be nullable, or (better?) an additional table + // linking a content itemt to its current version (nodeId, versionId) - that would guarantee uniqueness BUT it would + // not guarantee existence - so, really... we are trusting our code to manage 'current' correctly. + [Column("text")] [NullSetting(NullSetting = NullSettings.Null)] public string Text { get; set; } diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 2228ab8c1a..f63e9d2a84 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Services [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true)] public class ContentServiceTests : TestWithSomeContentBase { - //TODO Add test to verify there is only ONE newest document/content in cmsDocument table after updating. + //TODO Add test to verify there is only ONE newest document/content in uDocument table after updating. //TODO Add test to delete specific version (with and without deleting prior versions) and versions by date. public override void SetUp() diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 17e6c46291..c5b161f44a 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -168,7 +168,7 @@ namespace Umbraco.Tests.UmbracoExamine //var query = new Mock>(); //query // .Setup(x => x.GetWhereClauses()) - // .Returns(new List> { new Tuple("cmsDocument.published", new object[] { 1 }) }); + // .Returns(new List> { new Tuple("uDocument.published", new object[] { 1 }) }); var scopeProvider = new Mock(); //scopeProvider // .Setup(x => x.Query()) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index ca2f78749b..4048e95a69 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -1253,13 +1253,12 @@ WHERE cmsContentNu.nodeId IN ( long total; do { - // .GetPagedResultsByQuery implicitely adds (cmsDocument.newest = 1) var descendants = repository.GetPagedResultsByQuery(query, pageIndex++, groupSize, out total, "Path", Direction.Ascending, true); var items = new List(); - var guids = new List(); + //var guids = new List(); // fixme wtf? foreach (var c in descendants) items.Add(GetDto(c, c.Published)); - items.AddRange(guids.Select(x => GetDto(repository.GetByVersion(x), true))); + //items.AddRange(guids.Select(x => GetDto(repository.GetByVersion(x), true))); db.BulkInsertRecords(items); processed += items.Count; @@ -1410,8 +1409,8 @@ WHERE cmsContentNu.nodeId IN ( var count = db.ExecuteScalar(@"SELECT COUNT(*) FROM umbracoNode -JOIN cmsDocument ON (umbracoNode.id=cmsDocument.nodeId AND (cmsDocument.newest=1 OR cmsDocument.published=1)) -LEFT JOIN cmsContentNu ON (umbracoNode.id=cmsContentNu.nodeId AND cmsContentNu.published=cmsDocument.published) +JOIN uDocument ON umbracoNode.id=uDocument.nodeId +LEFT JOIN cmsContentNu ON (umbracoNode.id=cmsContentNu.nodeId AND cmsContentNu.published=uDocument.published) WHERE umbracoNode.nodeObjectType=@objType AND cmsContentNu.nodeId IS NULL;" , new { objType = contentObjectType }); @@ -1440,7 +1439,6 @@ AND cmsContentNu.nodeId IS NULL;" var count = db.ExecuteScalar(@"SELECT COUNT(*) FROM umbracoNode -JOIN cmsDocument ON (umbracoNode.id=cmsDocument.nodeId AND cmsDocument.published=1) LEFT JOIN cmsContentNu ON (umbracoNode.id=cmsContentNu.nodeId AND cmsContentNu.published=1) WHERE umbracoNode.nodeObjectType=@objType AND cmsContentNu.nodeId IS NULL