diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs index ab6920377c..cfc789324a 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs @@ -1,7 +1,21 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace Umbraco.Core.Models.PublishedContent { + /// + /// Represents an type. + /// + /// Instances implementing the interface should be + /// immutable, ie if the content type changes, then a new instance needs to be created. + public interface IPublishedContentType2 : IPublishedContentType + { + /// + /// Gets the unique key for the content type. + /// + Guid Key { get; } + } + /// /// Represents an type. /// diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs index 89009ac7b8..1a29f970d9 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs @@ -1,5 +1,6 @@ namespace Umbraco. Core.Models.PublishedContent { + /// /// Creates published content types. /// diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs index ae4caf352e..60fa0fe603 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs @@ -3,6 +3,7 @@ using System.Collections; namespace Umbraco.Core.Models.PublishedContent { + /// /// Provides the published model creation service. /// diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs index bfc65b70d6..033396e4a1 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs @@ -3,6 +3,7 @@ using Umbraco.Core.Composing; namespace Umbraco.Core.Models.PublishedContent { + /// /// Provides strongly typed published content models services. /// diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index 458b63ade3..7aa9b0dfd9 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Instances of the class are immutable, ie /// if the content type changes, then a new class needs to be created. - public class PublishedContentType : IPublishedContentType + public class PublishedContentType : IPublishedContentType2 { private readonly IPublishedPropertyType[] _propertyTypes; @@ -20,7 +20,7 @@ namespace Umbraco.Core.Models.PublishedContent /// Initializes a new instance of the class with a content type. /// public PublishedContentType(IContentTypeComposition contentType, IPublishedContentTypeFactory factory) - : this(contentType.Id, contentType.Alias, contentType.GetItemType(), contentType.CompositionAliases(), contentType.Variations, contentType.IsElement) + : this(contentType.Key, contentType.Id, contentType.Alias, contentType.GetItemType(), contentType.CompositionAliases(), contentType.Variations, contentType.IsElement) { var propertyTypes = contentType.CompositionPropertyTypes .Select(x => factory.CreatePropertyType(this, x)) @@ -40,8 +40,20 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Values are assumed to be consistent and are not checked. /// + public PublishedContentType(Guid key, int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, IEnumerable propertyTypes, ContentVariation variations, bool isElement = false) + : this(key, id, alias, itemType, compositionAliases, variations, isElement) + { + var propertyTypesA = propertyTypes.ToArray(); + foreach (var propertyType in propertyTypesA) + propertyType.ContentType = this; + _propertyTypes = propertyTypesA; + + InitializeIndexes(); + } + + [Obsolete("Use the overload specifying a key instead")] public PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, IEnumerable propertyTypes, ContentVariation variations, bool isElement = false) - : this (id, alias, itemType, compositionAliases, variations, isElement) + : this (Guid.Empty, id, alias, itemType, compositionAliases, variations, isElement) { var propertyTypesA = propertyTypes.ToArray(); foreach (var propertyType in propertyTypesA) @@ -57,16 +69,26 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Values are assumed to be consistent and are not checked. /// + public PublishedContentType(Guid key, int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, Func> propertyTypes, ContentVariation variations, bool isElement = false) + : this(key, id, alias, itemType, compositionAliases, variations, isElement) + { + _propertyTypes = propertyTypes(this).ToArray(); + + InitializeIndexes(); + } + + [Obsolete("Use the overload specifying a key instead")] public PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, Func> propertyTypes, ContentVariation variations, bool isElement = false) - : this(id, alias, itemType, compositionAliases, variations, isElement) + : this(Guid.Empty, id, alias, itemType, compositionAliases, variations, isElement) { _propertyTypes = propertyTypes(this).ToArray(); InitializeIndexes(); } - private PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, ContentVariation variations, bool isElement) + private PublishedContentType(Guid key, int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, ContentVariation variations, bool isElement) { + Key = key; Id = id; Alias = alias; ItemType = itemType; @@ -116,6 +138,9 @@ namespace Umbraco.Core.Models.PublishedContent #region Content type + /// + public Guid Key { get; } + /// public int Id { get; } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeExtensions.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeExtensions.cs new file mode 100644 index 0000000000..feab33c1d6 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeExtensions.cs @@ -0,0 +1,24 @@ +using System; + +namespace Umbraco.Core.Models.PublishedContent +{ + public static class PublishedContentTypeExtensions + { + /// + /// Get the GUID key from an + /// + /// + /// + /// + public static bool TryGetKey(this IPublishedContentType publishedContentType, out Guid key) + { + if (publishedContentType is IPublishedContentType2 contentTypeWithKey) + { + key = contentTypeWithKey.Key; + return true; + } + key = Guid.Empty; + return false; + } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs index 34094508c3..c1548d3c3d 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs @@ -35,18 +35,18 @@ namespace Umbraco.Core.Models.PublishedContent /// This method is for tests and is not intended to be used directly from application code. /// /// Values are assumed to be consisted and are not checked. - internal IPublishedContentType CreateContentType(int id, string alias, Func> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) + internal IPublishedContentType CreateContentType(Guid key, int id, string alias, Func> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) { - return new PublishedContentType(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, variations, isElement); + return new PublishedContentType(key, id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, variations, isElement); } /// /// This method is for tests and is not intended to be used directly from application code. /// /// Values are assumed to be consisted and are not checked. - internal IPublishedContentType CreateContentType(int id, string alias, IEnumerable compositionAliases, Func> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) + internal IPublishedContentType CreateContentType(Guid key, int id, string alias, IEnumerable compositionAliases, Func> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) { - return new PublishedContentType(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, variations, isElement); + return new PublishedContentType(key, id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, variations, isElement); } /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 809074073b..408abed4e0 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -139,6 +139,7 @@ + diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index f3d9f895ef..2346740ffb 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -42,9 +42,9 @@ namespace Umbraco.Tests.Cache.PublishedCache protected override void Initialize() { base.Initialize(); - var type = new AutoPublishedContentType(22, "myType", new PublishedPropertyType[] { }); - var image = new AutoPublishedContentType(23, "Image", new PublishedPropertyType[] { }); - var testMediaType = new AutoPublishedContentType(24, "TestMediaType", new PublishedPropertyType[] { }); + var type = new AutoPublishedContentType(Guid.NewGuid(), 22, "myType", new PublishedPropertyType[] { }); + var image = new AutoPublishedContentType(Guid.NewGuid(), 23, "Image", new PublishedPropertyType[] { }); + var testMediaType = new AutoPublishedContentType(Guid.NewGuid(), 24, "TestMediaType", new PublishedPropertyType[] { }); _mediaTypes = new Dictionary { { type.Alias, type }, diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index 8ce6b10983..48f0e7b27e 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -14,7 +14,7 @@ using Umbraco.Web.Routing; namespace Umbraco.Tests.LegacyXmlPublishedCache { - internal class PublishedContentCache : PublishedCacheBase, IPublishedContentCache + internal class PublishedContentCache : PublishedCacheBase, IPublishedContentCache2 { private readonly IAppCache _appCache; private readonly IGlobalSettings _globalSettings; @@ -532,15 +532,11 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache #region Content types - public override IPublishedContentType GetContentType(int id) - { - return _contentTypeCache.Get(PublishedItemType.Content, id); - } + public override IPublishedContentType GetContentType(int id) => _contentTypeCache.Get(PublishedItemType.Content, id); - public override IPublishedContentType GetContentType(string alias) - { - return _contentTypeCache.Get(PublishedItemType.Content, alias); - } + public override IPublishedContentType GetContentType(string alias) => _contentTypeCache.Get(PublishedItemType.Content, alias); + + public override IPublishedContentType GetContentType(Guid key) => _contentTypeCache.Get(PublishedItemType.Content, key); #endregion } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs index 999d7f040d..56033e6b0a 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache /// /// NOTE: In the future if we want to properly cache all media this class can be extended or replaced when these classes/interfaces are exposed publicly. /// - internal class PublishedMediaCache : PublishedCacheBase, IPublishedMediaCache + internal class PublishedMediaCache : PublishedCacheBase, IPublishedMediaCache2 { private readonly IMediaService _mediaService; private readonly IUserService _userService; @@ -612,15 +612,11 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache #region Content types - public override IPublishedContentType GetContentType(int id) - { - return _contentTypeCache.Get(PublishedItemType.Media, id); - } + public override IPublishedContentType GetContentType(int id) => _contentTypeCache.Get(PublishedItemType.Media, id); - public override IPublishedContentType GetContentType(string alias) - { - return _contentTypeCache.Get(PublishedItemType.Media, alias); - } + public override IPublishedContentType GetContentType(string alias) => _contentTypeCache.Get(PublishedItemType.Media, alias); + + public override IPublishedContentType GetContentType(Guid key) => _contentTypeCache.Get(PublishedItemType.Media, key); public override IEnumerable GetByContentType(IPublishedContentType contentType) { diff --git a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs index 6a7ec33a5a..655db4e337 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -18,21 +18,28 @@ namespace Umbraco.Tests.PropertyEditors [TestFixture] public class BlockListPropertyValueConverterTests { + private readonly Guid Key1 = Guid.NewGuid(); + private readonly Guid Key2 = Guid.NewGuid(); + private readonly string Alias1 = "Test1"; + private readonly string Alias2 = "Test2"; + /// /// Setup mocks for IPublishedSnapshotAccessor /// /// private IPublishedSnapshotAccessor GetPublishedSnapshotAccessor() { - var test1ContentType = Mock.Of(x => + var test1ContentType = Mock.Of(x => x.IsElement == true - && x.Alias == "Test1"); - var test2ContentType = Mock.Of(x => + && x.Key == Key1 + && x.Alias == Alias1); + var test2ContentType = Mock.Of(x => x.IsElement == true - && x.Alias == "Test2"); - var contentCache = new Mock(); - contentCache.Setup(x => x.GetContentType("Test1")).Returns(test1ContentType); - contentCache.Setup(x => x.GetContentType("Test2")).Returns(test2ContentType); + && x.Key == Key2 + && x.Alias == Alias2); + var contentCache = new Mock(); + contentCache.Setup(x => x.GetContentType(Key1)).Returns(test1ContentType); + contentCache.Setup(x => x.GetContentType(Key2)).Returns(test2ContentType); var publishedSnapshot = Mock.Of(x => x.Content == contentCache.Object); var publishedSnapshotAccessor = Mock.Of(x => x.PublishedSnapshot == publishedSnapshot); return publishedSnapshotAccessor; @@ -44,7 +51,6 @@ namespace Umbraco.Tests.PropertyEditors var publishedModelFactory = new NoopPublishedModelFactory(); var editor = new BlockListPropertyValueConverter( Mock.Of(), - publishedModelFactory, new BlockEditorConverter(publishedSnapshotAccessor, publishedModelFactory)); return editor; } @@ -54,11 +60,11 @@ namespace Umbraco.Tests.PropertyEditors Blocks = new[] { new BlockListConfiguration.BlockConfiguration { - Alias = "Test1" + Key = Key1 }, new BlockListConfiguration.BlockConfiguration { - Alias = "Test2" + Key = Key2 } } }; @@ -68,7 +74,7 @@ namespace Umbraco.Tests.PropertyEditors Blocks = new[] { new BlockListConfiguration.BlockConfiguration { - Alias = "Test1" + Key = Key1 } } }; @@ -101,7 +107,8 @@ namespace Umbraco.Tests.PropertyEditors var valueType = editor.GetPropertyValueType(propType); - Assert.AreEqual(typeof(IEnumerable), valueType); + // the result is always block list model + Assert.AreEqual(typeof(BlockListModel), valueType); } [Test] @@ -115,10 +122,8 @@ namespace Umbraco.Tests.PropertyEditors var valueType = editor.GetPropertyValueType(propType); - var modelType = typeof(IEnumerable<>).MakeGenericType(ModelType.For(config.Blocks[0].Alias)); - - // we can't compare the exact match of types because ModelType.For generates a new/different type even if the same alias is used - Assert.AreEqual(modelType.FullName, valueType.FullName); + // the result is always block list model + Assert.AreEqual(typeof(BlockListModel), valueType); } [Test] @@ -225,7 +230,7 @@ data: []}"; }, data: [ { - 'contentTypeAlias': 'home', + 'contentTypeKey': '" + Key1 + @"', 'key': '1304E1DD-0000-4396-84FE-8A399231CB3D' } ] @@ -258,7 +263,7 @@ data: []}"; }, data: [ { - 'contentTypeAlias': 'Test1', + 'contentTypeKey': '" + Key1 + @"', 'udi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D' } ] @@ -300,15 +305,15 @@ data: []}"; }, data: [ { - 'contentTypeAlias': 'Test1', + 'contentTypeKey': '" + Key1 + @"', 'udi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D' }, { - 'contentTypeAlias': 'Test2', + 'contentTypeKey': '" + Key2 + @"', 'udi': 'umb://element/E05A034704424AB3A520E048E6197E79' }, { - 'contentTypeAlias': 'Test2', + 'contentTypeKey': '" + Key2 + @"', 'udi': 'umb://element/0A4A416E547D464FABCC6F345C17809A' } ] diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs index 43c1a83d33..a9e3e8b9db 100644 --- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs @@ -93,7 +93,7 @@ namespace Umbraco.Tests.PropertyEditors }))); var publishedPropType = new PublishedPropertyType( - new PublishedContentType(1234, "test", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing), + new PublishedContentType(Guid.NewGuid(), 1234, "test", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing), new PropertyType("test", ValueStorageType.Nvarchar) { DataTypeId = 123 }, new PropertyValueConverterCollection(Enumerable.Empty()), Mock.Of(), mockPublishedContentTypeFactory.Object); diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs index 671129848c..3c60f4ddd0 100644 --- a/src/Umbraco.Tests/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests/Published/ConvertersTests.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.Published yield return contentTypeFactory.CreatePropertyType(contentType, "prop1", 1); } - var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", CreatePropertyTypes); + var elementType1 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1000, "element1", CreatePropertyTypes); var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); @@ -74,7 +74,7 @@ namespace Umbraco.Tests.Published => propertyType.EditorAlias.InvariantEquals("Umbraco.Void"); public Type GetPropertyValueType(IPublishedPropertyType propertyType) - => typeof (int); + => typeof(int); public PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; @@ -83,10 +83,10 @@ namespace Umbraco.Tests.Published => int.TryParse(source as string, out int i) ? i : 0; public object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) - => (int) inter; + => (int)inter; public object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) - => ((int) inter).ToString(); + => ((int)inter).ToString(); } #endregion @@ -120,11 +120,11 @@ namespace Umbraco.Tests.Published yield return contentTypeFactory.CreatePropertyType(contentType, "prop1", 1); } - var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", CreatePropertyTypes); + var elementType1 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1000, "element1", CreatePropertyTypes); var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); - var cntType1 = contentTypeFactory.CreateContentType(1001, "cnt1", t => Enumerable.Empty()); + var cntType1 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1001, "cnt1", t => Enumerable.Empty()); var cnt1 = new SolidPublishedContent(cntType1) { Id = 1234 }; cacheContent[cnt1.Id] = cnt1; @@ -143,7 +143,7 @@ namespace Umbraco.Tests.Published } public bool? IsValue(object value, PropertyValueLevel level) - => value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false); + => value != null && (!(value is string) || string.IsNullOrWhiteSpace((string)value) == false); public bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals("Umbraco.Void"); @@ -162,10 +162,10 @@ namespace Umbraco.Tests.Published => int.TryParse(source as string, out int i) ? i : -1; public object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) - => _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById((int) inter); + => _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById((int)inter); public object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) - => ((int) inter).ToString(); + => ((int)inter).ToString(); } #endregion @@ -215,10 +215,10 @@ namespace Umbraco.Tests.Published yield return contentTypeFactory.CreatePropertyType(contentType, "prop" + i, i); } - var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", t => CreatePropertyTypes(t, 1)); - var elementType2 = contentTypeFactory.CreateContentType(1001, "element2", t => CreatePropertyTypes(t, 2)); - var contentType1 = contentTypeFactory.CreateContentType(1002, "content1", t => CreatePropertyTypes(t, 1)); - var contentType2 = contentTypeFactory.CreateContentType(1003, "content2", t => CreatePropertyTypes(t, 2)); + var elementType1 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1000, "element1", t => CreatePropertyTypes(t, 1)); + var elementType2 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1001, "element2", t => CreatePropertyTypes(t, 2)); + var contentType1 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1002, "content1", t => CreatePropertyTypes(t, 1)); + var contentType2 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1003, "content2", t => CreatePropertyTypes(t, 2)); var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); var element2 = new PublishedElement(elementType2, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); @@ -239,22 +239,22 @@ namespace Umbraco.Tests.Published // can get the actual property Clr type // ie ModelType gets properly mapped by IPublishedContentModelFactory // must test ModelClrType with special equals 'cos they are not ref-equals - Assert.IsTrue(ModelType.Equals(typeof (IEnumerable<>).MakeGenericType(ModelType.For("content1")), contentType2.GetPropertyType("prop2").ModelClrType)); - Assert.AreEqual(typeof (IEnumerable), contentType2.GetPropertyType("prop2").ClrType); + Assert.IsTrue(ModelType.Equals(typeof(IEnumerable<>).MakeGenericType(ModelType.For("content1")), contentType2.GetPropertyType("prop2").ModelClrType)); + Assert.AreEqual(typeof(IEnumerable), contentType2.GetPropertyType("prop2").ClrType); // can create a model for an element var model1 = factory.CreateModel(element1); Assert.IsInstanceOf(model1); - Assert.AreEqual("val1", ((PublishedSnapshotTestObjects.TestElementModel1) model1).Prop1); + Assert.AreEqual("val1", ((PublishedSnapshotTestObjects.TestElementModel1)model1).Prop1); // can create a model for a published content var model2 = factory.CreateModel(element2); Assert.IsInstanceOf(model2); - var mmodel2 = (PublishedSnapshotTestObjects.TestElementModel2) model2; + var mmodel2 = (PublishedSnapshotTestObjects.TestElementModel2)model2; // and get direct property Assert.IsInstanceOf(model2.Value("prop2")); - Assert.AreEqual(1, ((PublishedSnapshotTestObjects.TestContentModel1[]) model2.Value("prop2")).Length); + Assert.AreEqual(1, ((PublishedSnapshotTestObjects.TestContentModel1[])model2.Value("prop2")).Length); // and get model property Assert.IsInstanceOf>(mmodel2.Prop2); @@ -271,7 +271,7 @@ namespace Umbraco.Tests.Published => propertyType.EditorAlias == "Umbraco.Void"; public override Type GetPropertyValueType(IPublishedPropertyType propertyType) - => typeof (string); + => typeof(string); public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; @@ -290,7 +290,7 @@ namespace Umbraco.Tests.Published => propertyType.EditorAlias == "Umbraco.Void.2"; public override Type GetPropertyValueType(IPublishedPropertyType propertyType) - => typeof (IEnumerable<>).MakeGenericType(ModelType.For("content1")); + => typeof(IEnumerable<>).MakeGenericType(ModelType.For("content1")); public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Elements; @@ -303,7 +303,7 @@ namespace Umbraco.Tests.Published public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { - return ((int[]) inter).Select(x => (PublishedSnapshotTestObjects.TestContentModel1) _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(x)).ToArray(); + return ((int[])inter).Select(x => (PublishedSnapshotTestObjects.TestContentModel1)_publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(x)).ToArray(); } } diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index a102b9f93e..70da652a9a 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -144,9 +144,9 @@ namespace Umbraco.Tests.Published yield return factory.CreatePropertyType(contentType, "propertyN1", 3); } - var contentType1 = factory.CreateContentType(1, "content1", CreatePropertyTypes1); - var contentType2 = factory.CreateContentType(2, "content2", CreatePropertyTypes2); - var contentTypeN1 = factory.CreateContentType(2, "contentN1", CreatePropertyTypesN1, isElement: true); + var contentType1 = factory.CreateContentType(Guid.NewGuid(), 1, "content1", CreatePropertyTypes1); + var contentType2 = factory.CreateContentType(Guid.NewGuid(), 2, "content2", CreatePropertyTypes2); + var contentTypeN1 = factory.CreateContentType(Guid.NewGuid(), 2, "contentN1", CreatePropertyTypesN1, isElement: true); // mocked content cache returns content types contentCache diff --git a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs index 9db539d142..a795ca433e 100644 --- a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs @@ -41,7 +41,7 @@ namespace Umbraco.Tests.Published yield return publishedContentTypeFactory.CreatePropertyType(contentType, "prop1", 1); } - var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); + var setType1 = publishedContentTypeFactory.CreateContentType(Guid.NewGuid(), 1000, "set1", CreatePropertyTypes); // PublishedElementPropertyBase.GetCacheLevels: // @@ -122,7 +122,7 @@ namespace Umbraco.Tests.Published yield return publishedContentTypeFactory.CreatePropertyType(contentType, "prop1", 1); } - var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); + var setType1 = publishedContentTypeFactory.CreateContentType(Guid.NewGuid(), 1000, "set1", CreatePropertyTypes); var elementsCache = new FastDictionaryAppCache(); var snapshotCache = new FastDictionaryAppCache(); @@ -199,7 +199,7 @@ namespace Umbraco.Tests.Published yield return publishedContentTypeFactory.CreatePropertyType(contentType, "prop1", 1); } - var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); + var setType1 = publishedContentTypeFactory.CreateContentType(Guid.NewGuid(), 1000, "set1", CreatePropertyTypes); Assert.Throws(() => { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index cc455b8e5d..7277f75be2 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -98,7 +98,7 @@ namespace Umbraco.Tests.PublishedContent var doc = GetContent(true, 1); //change a doc type alias var c = (SolidPublishedContent)doc.Children.ElementAt(0); - c.ContentType = new PublishedContentType(22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + c.ContentType = new PublishedContentType(Guid.NewGuid(), 22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var dt = doc.ChildrenAsTable(Current.Services, "Child"); @@ -129,7 +129,7 @@ namespace Umbraco.Tests.PublishedContent var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); var contentTypeAlias = createChildren ? "Parent" : "Child"; - var contentType = new PublishedContentType(22, contentTypeAlias, PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var contentType = new PublishedContentType(Guid.NewGuid(), 22, contentTypeAlias, PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var d = new SolidPublishedContent(contentType) { CreateDate = DateTime.Now, diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index 62447742ff..636f8502ed 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -73,14 +73,14 @@ namespace Umbraco.Tests.PublishedContent yield return factory.CreatePropertyType(contentType, "noprop", 1, variations: ContentVariation.Culture); } - var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), CreatePropertyTypes1); + var contentType1 = factory.CreateContentType(Guid.NewGuid(), 1, "ContentType1", Enumerable.Empty(), CreatePropertyTypes1); IEnumerable CreatePropertyTypes2(IPublishedContentType contentType) { yield return factory.CreatePropertyType(contentType, "prop3", 1, variations: ContentVariation.Culture); } - var contentType2 = factory.CreateContentType(2, "contentType2", Enumerable.Empty(), CreatePropertyTypes2); + var contentType2 = factory.CreateContentType(Guid.NewGuid(), 2, "contentType2", Enumerable.Empty(), CreatePropertyTypes2); var prop1 = new SolidPublishedPropertyWithLanguageVariants { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 440474ae74..e2a1721d26 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -7,6 +7,7 @@ using Umbraco.Web; using Umbraco.Core; using Umbraco.Tests.Testing; using Umbraco.Web.Composing; +using System; namespace Umbraco.Tests.PublishedContent { @@ -21,9 +22,9 @@ namespace Umbraco.Tests.PublishedContent yield return factory.CreatePropertyType(contentType, "prop1", 1); } - var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), CreatePropertyTypes); - var contentType2 = factory.CreateContentType(2, "ContentType2", Enumerable.Empty(), CreatePropertyTypes); - var contentType2Sub = factory.CreateContentType(3, "ContentType2Sub", Enumerable.Empty(), CreatePropertyTypes); + var contentType1 = factory.CreateContentType(Guid.NewGuid(), 1, "ContentType1", Enumerable.Empty(), CreatePropertyTypes); + var contentType2 = factory.CreateContentType(Guid.NewGuid(), 2, "ContentType2", Enumerable.Empty(), CreatePropertyTypes); + var contentType2Sub = factory.CreateContentType(Guid.NewGuid(), 3, "ContentType2Sub", Enumerable.Empty(), CreatePropertyTypes); var content = new SolidPublishedContent(contentType1) { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index 59791fc645..7e0d0f332e 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Services; using Umbraco.Web; using Umbraco.Web.Templates; using Umbraco.Web.Models; +using System; namespace Umbraco.Tests.PublishedContent { @@ -56,7 +57,7 @@ namespace Umbraco.Tests.PublishedContent yield return publishedContentTypeFactory.CreatePropertyType(contentType, "content", 1); } - var type = new AutoPublishedContentType(0, "anything", CreatePropertyTypes); + var type = new AutoPublishedContentType(Guid.NewGuid(), 0, "anything", CreatePropertyTypes); ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; var umbracoContext = GetUmbracoContext("/test"); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 998fc92380..cafda161f4 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -83,8 +83,8 @@ namespace Umbraco.Tests.PublishedContent } var compositionAliases = new[] { "MyCompositionAlias" }; - var anythingType = new AutoPublishedContentType(0, "anything", compositionAliases, CreatePropertyTypes); - var homeType = new AutoPublishedContentType(0, "home", compositionAliases, CreatePropertyTypes); + var anythingType = new AutoPublishedContentType(Guid.NewGuid(), 0, "anything", compositionAliases, CreatePropertyTypes); + var homeType = new AutoPublishedContentType(Guid.NewGuid(), 0, "home", compositionAliases, CreatePropertyTypes); ContentTypesCache.GetPublishedContentTypeByAlias = alias => alias.InvariantEquals("home") ? homeType : anythingType; } @@ -398,8 +398,8 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Children_GroupBy_DocumentTypeAlias() { - var home = new AutoPublishedContentType(22, "Home", new PublishedPropertyType[] { }); - var custom = new AutoPublishedContentType(23, "CustomDocument", new PublishedPropertyType[] { }); + var home = new AutoPublishedContentType(Guid.NewGuid(), 22, "Home", new PublishedPropertyType[] { }); + var custom = new AutoPublishedContentType(Guid.NewGuid(), 23, "CustomDocument", new PublishedPropertyType[] { }); var contentTypes = new Dictionary { { home.Alias, home }, @@ -419,8 +419,8 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Children_Where_DocumentTypeAlias() { - var home = new AutoPublishedContentType(22, "Home", new PublishedPropertyType[] { }); - var custom = new AutoPublishedContentType(23, "CustomDocument", new PublishedPropertyType[] { }); + var home = new AutoPublishedContentType(Guid.NewGuid(), 22, "Home", new PublishedPropertyType[] { }); + var custom = new AutoPublishedContentType(Guid.NewGuid(), 23, "CustomDocument", new PublishedPropertyType[] { }); var contentTypes = new Dictionary { { home.Alias, home }, @@ -903,7 +903,7 @@ namespace Umbraco.Tests.PublishedContent yield return factory.CreatePropertyType(contentType, "detached", 1003); } - var ct = factory.CreateContentType(0, "alias", CreatePropertyTypes); + var ct = factory.CreateContentType(Guid.NewGuid(), 0, "alias", CreatePropertyTypes); var pt = ct.GetPropertyType("detached"); var prop = new PublishedElementPropertyBase(pt, null, false, PropertyCacheLevel.None, 5548); Assert.IsInstanceOf(prop.GetValue()); @@ -935,7 +935,7 @@ namespace Umbraco.Tests.PublishedContent var guid = Guid.NewGuid(); - var ct = factory.CreateContentType(0, "alias", CreatePropertyTypes); + var ct = factory.CreateContentType(Guid.NewGuid(), 0, "alias", CreatePropertyTypes); var c = new ImageWithLegendModel(ct, guid, new Dictionary { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs index d8dbabb569..4a93fadbdf 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs @@ -66,7 +66,7 @@ namespace Umbraco.Tests.PublishedContent pc.Setup(content => content.Path).Returns("-1,1"); pc.Setup(content => content.Parent).Returns(() => null); pc.Setup(content => content.Properties).Returns(new Collection()); - pc.Setup(content => content.ContentType).Returns(new PublishedContentType(22, "anything", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing)); + pc.Setup(content => content.ContentType).Returns(new PublishedContentType(Guid.NewGuid(), 22, "anything", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing)); return pc; } } diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index a7b6d3d18a..4a0af69999 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -44,7 +44,7 @@ namespace Umbraco.Tests.PublishedContent { } } - class SolidPublishedContentCache : PublishedCacheBase, IPublishedContentCache, IPublishedMediaCache + class SolidPublishedContentCache : PublishedCacheBase, IPublishedContentCache2, IPublishedMediaCache2 { private readonly Dictionary _content = new Dictionary(); @@ -150,6 +150,11 @@ namespace Umbraco.Tests.PublishedContent throw new NotImplementedException(); } + public override IPublishedContentType GetContentType(Guid key) + { + throw new NotImplementedException(); + } + public override IEnumerable GetByContentType(IPublishedContentType contentType) { throw new NotImplementedException(); @@ -378,7 +383,7 @@ namespace Umbraco.Tests.PublishedContent #endregion } - class PublishedContentStrong1 : PublishedContentModel + internal class PublishedContentStrong1 : PublishedContentModel { public PublishedContentStrong1(IPublishedContent content) : base(content) @@ -387,7 +392,7 @@ namespace Umbraco.Tests.PublishedContent public int StrongValue => this.Value("strongValue"); } - class PublishedContentStrong1Sub : PublishedContentStrong1 + internal class PublishedContentStrong1Sub : PublishedContentStrong1 { public PublishedContentStrong1Sub(IPublishedContent content) : base(content) @@ -396,7 +401,7 @@ namespace Umbraco.Tests.PublishedContent public int AnotherValue => this.Value("anotherValue"); } - class PublishedContentStrong2 : PublishedContentModel + internal class PublishedContentStrong2 : PublishedContentModel { public PublishedContentStrong2(IPublishedContent content) : base(content) @@ -405,7 +410,7 @@ namespace Umbraco.Tests.PublishedContent public int StrongValue => this.Value("strongValue"); } - class AutoPublishedContentType : PublishedContentType + internal class AutoPublishedContentType : PublishedContentType { private static readonly IPublishedPropertyType Default; @@ -418,20 +423,20 @@ namespace Umbraco.Tests.PublishedContent Default = factory.CreatePropertyType("*", 666); } - public AutoPublishedContentType(int id, string alias, IEnumerable propertyTypes) - : base(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) + public AutoPublishedContentType(Guid key, int id, string alias, IEnumerable propertyTypes) + : base(key, id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) { } - public AutoPublishedContentType(int id, string alias, Func> propertyTypes) - : base(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) + public AutoPublishedContentType(Guid key, int id, string alias, Func> propertyTypes) + : base(key, id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) { } - public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) - : base(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) + public AutoPublishedContentType(Guid key, int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) + : base(key, id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) { } - public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, Func> propertyTypes) - : base(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) + public AutoPublishedContentType(Guid key, int id, string alias, IEnumerable compositionAliases, Func> propertyTypes) + : base(key, id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) { } public override IPublishedPropertyType GetPropertyType(string alias) diff --git a/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs index 2aa01916fb..25d3eda081 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -27,7 +28,7 @@ namespace Umbraco.Tests.Routing Mock.Of(), Mock.Of()), }; - _publishedContentType = new PublishedContentType(0, "Doc", PublishedItemType.Content, Enumerable.Empty(), properties, ContentVariation.Nothing); + _publishedContentType = new PublishedContentType(Guid.NewGuid(), 0, "Doc", PublishedItemType.Content, Enumerable.Empty(), properties, ContentVariation.Nothing); } protected override PublishedContentType GetPublishedContentTypeByAlias(string alias) diff --git a/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs index 0c1f89f430..18fe268a9a 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -25,7 +26,7 @@ namespace Umbraco.Tests.Routing Mock.Of(), Mock.Of()), }; - _publishedContentType = new PublishedContentType(0, "Doc", PublishedItemType.Content, Enumerable.Empty(), properties, ContentVariation.Nothing); + _publishedContentType = new PublishedContentType(Guid.NewGuid(), 0, "Doc", PublishedItemType.Content, Enumerable.Empty(), properties, ContentVariation.Nothing); } protected override PublishedContentType GetPublishedContentTypeByAlias(string alias) diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index 2f960d498d..3a1ff36a0a 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -139,7 +139,7 @@ namespace Umbraco.Tests.Routing property.SetSourceValue("en", enMediaUrl, true); property.SetSourceValue("da", daMediaUrl); - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), new [] { umbracoFilePropertyType }, ContentVariation.Culture); + var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), new [] { umbracoFilePropertyType }, ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) {Properties = new[] {property}}; var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Auto, "da"); @@ -150,7 +150,7 @@ namespace Umbraco.Tests.Routing { var umbracoFilePropertyType = CreatePropertyType(propertyEditorAlias, dataTypeConfiguration, ContentVariation.Nothing); - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), + var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), new[] {umbracoFilePropertyType}, ContentVariation.Nothing); return new SolidPublishedContent(contentType) diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 135172460d..786eebea9f 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -140,7 +140,7 @@ namespace Umbraco.Tests.Routing frequest.TemplateModel = template; var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var type = new AutoPublishedContentType(22, "CustomDocument", new PublishedPropertyType[] { }); + var type = new AutoPublishedContentType(Guid.NewGuid(), 22, "CustomDocument", new PublishedPropertyType[] { }); ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; var handler = new RenderRouteHandler(umbracoContext, new TestControllerFactory(umbracoContextAccessor, Mock.Of(), context => diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 02aa95cd2e..8043e25661 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -158,7 +158,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 contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; var publishedContentCache = new Mock(); @@ -203,7 +203,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 contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; var publishedContentCache = new Mock(); @@ -257,7 +257,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 contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; var publishedContentCache = new Mock(); diff --git a/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs b/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs index bce9bd4155..6c40e2842d 100644 --- a/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs +++ b/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs @@ -65,7 +65,7 @@ namespace Umbraco.Tests.Templates { //setup a mock url provider which we'll use for testing - var mediaType = new PublishedContentType(777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var mediaType = new PublishedContentType(Guid.NewGuid(), 777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var media = new Mock(); media.Setup(x => x.ContentType).Returns(mediaType); var mediaUrlProvider = new Mock(); diff --git a/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs b/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs index 7cd96a32ed..861a7e4db6 100644 --- a/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs +++ b/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs @@ -54,12 +54,12 @@ namespace Umbraco.Tests.Templates contentUrlProvider .Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/my-test-url")); - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var publishedContent = new Mock(); publishedContent.Setup(x => x.Id).Returns(1234); publishedContent.Setup(x => x.ContentType).Returns(contentType); - var mediaType = new PublishedContentType(777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var mediaType = new PublishedContentType(Guid.NewGuid(), 777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var media = new Mock(); media.Setup(x => x.ContentType).Returns(mediaType); var mediaUrlProvider = new Mock(); diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index f7e3744600..7d8cedc9c6 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -42,7 +42,7 @@ namespace Umbraco.Tests.TestHelpers new DataType(new VoidEditor(Mock.Of())) { Id = 1 }); var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); - var type = new AutoPublishedContentType(0, "anything", new PublishedPropertyType[] { }); + var type = new AutoPublishedContentType(Guid.NewGuid(), 0, "anything", new PublishedPropertyType[] { }); ContentTypesCache.GetPublishedContentTypeByAlias = alias => GetPublishedContentTypeByAlias(alias) ?? type; } diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index ee91427a63..f53b0bfff0 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -86,7 +86,7 @@ namespace Umbraco.Tests.Testing.TestingTests var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }, Enumerable.Empty(), umbracoContext.VariationContextAccessor); - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var publishedContent = Mock.Of(); Mock.Get(publishedContent).Setup(x => x.ContentType).Returns(contentType); diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index 71e74e05fe..7b0f98903c 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using System; using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors @@ -39,7 +40,7 @@ namespace Umbraco.Web.PropertyEditors public string Thumbnail { get; set; } [JsonProperty("contentTypeKey")] - public string Key { get; set; } + public Guid Key { get; set; } [JsonProperty("settingsElementTypeKey")] public string SettingsElementTypeKey { get; set; } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs index 3ca70cbd22..917462e2f2 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs @@ -23,13 +23,17 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters JObject sourceObject, string contentTypeKeyPropertyKey, PropertyCacheLevel referenceCacheLevel, bool preview) { - var elementTypeKey = sourceObject[contentTypeKeyPropertyKey]?.ToObject(); - if (string.IsNullOrEmpty(elementTypeKey)) + var elementTypeKey = sourceObject[contentTypeKeyPropertyKey]?.ToObject(); + if (!elementTypeKey.HasValue) return null; + // hack! we need to cast, we have n ochoice beacuse we cannot make breaking changes. + var publishedContentCache = _publishedSnapshotAccessor.PublishedSnapshot.Content as IPublishedContentCache2; + if (publishedContentCache == null) + throw new InvalidOperationException("The published content cache is not " + typeof(IPublishedContentCache2)); + // only convert element types - content types will cause an exception when PublishedModelFactory creates the model - // TODO: make this work with keys. - var publishedContentType = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetContentType(elementTypeKey); + var publishedContentType = publishedContentCache.GetContentType(elementTypeKey.Value); if (publishedContentType == null || publishedContentType.IsElement == false) return null; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs index 7bd410f55a..a833efa2e5 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -17,13 +17,11 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters public class BlockListPropertyValueConverter : PropertyValueConverterBase { private readonly IProfilingLogger _proflog; - private readonly IPublishedModelFactory _publishedModelFactory; private readonly BlockEditorConverter _blockConverter; - public BlockListPropertyValueConverter(IProfilingLogger proflog, IPublishedModelFactory publishedModelFactory, BlockEditorConverter blockConverter) + public BlockListPropertyValueConverter(IProfilingLogger proflog, BlockEditorConverter blockConverter) { _proflog = proflog; - _publishedModelFactory = publishedModelFactory; _blockConverter = blockConverter; } @@ -54,11 +52,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var configuration = propertyType.DataType.ConfigurationAs(); var contentTypes = configuration.Blocks; var contentTypeMap = contentTypes.ToDictionary(x => x.Key); - var elements = (contentTypes.Length == 1 - // TODO: make this work with key - ? (IList)_publishedModelFactory.CreateModelList(contentTypes[0].Key) - : new List()) - .ToDictionary(x => x.Key, x => x); + + var elements = new Dictionary(); var layout = new List(); var model = new BlockListModel(elements.Values, layout); @@ -102,8 +97,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters if (!elements.TryGetValue(guidUdi.Guid, out var data)) continue; - // Make this work with Key, not alias, since contentTypeMap is now a dctionary with contentTypeKey as the dictionary key. - if (!contentTypeMap.TryGetValue(data.ContentType.Key, out var blockConfig)) + if (!data.ContentType.TryGetKey(out var contentTypeKey)) + throw new InvalidOperationException("The content type was not of type " + typeof(IPublishedContentType2)); + + if (!contentTypeMap.TryGetValue(contentTypeKey, out var blockConfig)) continue; // this can happen if they have a settings type, save content, remove the settings type, and display the front-end page before saving the content again diff --git a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs index 0370088f77..4760082908 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs @@ -7,6 +7,16 @@ using Umbraco.Core.Xml; namespace Umbraco.Web.PublishedCache { + public interface IPublishedCache2 : IPublishedCache + { + /// + /// Gets a content type identified by its alias. + /// + /// The content type key. + /// The content type, or null. + IPublishedContentType GetContentType(Guid key); + } + /// /// Provides access to cached contents. /// diff --git a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs index b4a6e3d1e0..8175285c3a 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs @@ -4,6 +4,11 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.PublishedCache { + public interface IPublishedContentCache2 : IPublishedContentCache, IPublishedCache2 + { + // NOTE: this is here purely to avoid API breaking changes + } + public interface IPublishedContentCache : IPublishedCache { /// diff --git a/src/Umbraco.Web/PublishedCache/IPublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedMediaCache.cs index 0b461882b7..702b4fe49d 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedMediaCache.cs @@ -1,5 +1,10 @@ namespace Umbraco.Web.PublishedCache { + public interface IPublishedMediaCache2 : IPublishedMediaCache, IPublishedCache2 + { + // NOTE: this is here purely to avoid API breaking changes + } + public interface IPublishedMediaCache : IPublishedCache { } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 24c6a7018b..8e6e517aea 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -13,7 +13,7 @@ using Umbraco.Web.PublishedCache.NuCache.Navigable; namespace Umbraco.Web.PublishedCache.NuCache { - internal class ContentCache : PublishedCacheBase, IPublishedContentCache, INavigableData, IDisposable + internal class ContentCache : PublishedCacheBase, IPublishedContentCache2, INavigableData, IDisposable { private readonly ContentStore.Snapshot _snapshot; private readonly IAppCache _snapshotCache; @@ -384,15 +384,11 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Content types - public override IPublishedContentType GetContentType(int id) - { - return _snapshot.GetContentType(id); - } + public override IPublishedContentType GetContentType(int id) => _snapshot.GetContentType(id); - public override IPublishedContentType GetContentType(string alias) - { - return _snapshot.GetContentType(alias); - } + public override IPublishedContentType GetContentType(string alias) => _snapshot.GetContentType(alias); + + public override IPublishedContentType GetContentType(Guid key) => _snapshot.GetContentType(key); #endregion diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index a3f918c92c..b39b38ca32 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -37,9 +37,14 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IVariationContextAccessor _variationContextAccessor; private readonly ConcurrentDictionary> _contentNodes; private LinkedNode _root; - private readonly ConcurrentDictionary> _contentTypesById; + + // We must keep separate dictionaries for by id and by alias because we track these in snapshot/layers + // and it is possible that the alias of a content type can be different for the same id in another layer + // whereas the GUID -> INT cross reference can never be different + private readonly ConcurrentDictionary> _contentTypesById; private readonly ConcurrentDictionary> _contentTypesByAlias; - private readonly ConcurrentDictionary _xmap; + private readonly ConcurrentDictionary _contentTypeKeyToIdMap; + private readonly ConcurrentDictionary _contentKeyToIdMap; private readonly ILogger _logger; private BPlusTree _localDb; @@ -73,7 +78,8 @@ namespace Umbraco.Web.PublishedCache.NuCache _root = new LinkedNode(new ContentNode(), 0); _contentTypesById = new ConcurrentDictionary>(); _contentTypesByAlias = new ConcurrentDictionary>(StringComparer.InvariantCultureIgnoreCase); - _xmap = new ConcurrentDictionary(); + _contentTypeKeyToIdMap = new ConcurrentDictionary(); + _contentKeyToIdMap = new ConcurrentDictionary(); _genObjs = new ConcurrentQueue(); _genObj = null; // no initial gen exists @@ -136,7 +142,7 @@ namespace Umbraco.Web.PublishedCache.NuCache Monitor.Enter(_wlocko, ref lockInfo.Taken); - lock(_rlocko) + lock (_rlocko) { // see SnapDictionary try { } @@ -152,7 +158,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _nextGen = true; } } - } + } } private void Release(WriteLockInfo lockInfo, bool commit = true) @@ -291,8 +297,7 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var type in types) { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); + SetContentTypeLocked(type); } } @@ -318,8 +323,7 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var type in index.Values) { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); + SetContentTypeLocked(type); } foreach (var link in _contentNodes.Values) @@ -354,8 +358,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // set all new content types foreach (var type in types) { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); + SetContentTypeLocked(type); } // beware! at that point the cache is inconsistent, @@ -419,8 +422,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // perform update of refreshed content types foreach (var type in refreshedTypesA) { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); + SetContentTypeLocked(type); } // perform update of content with refreshed content type - from the kits @@ -638,7 +640,7 @@ namespace Umbraco.Web.PublishedCache.NuCache kit.Node.PreviousSiblingContentId = existing.PreviousSiblingContentId; } - _xmap[kit.Node.Uid] = kit.Node.Id; + _contentKeyToIdMap[kit.Node.Uid] = kit.Node.Id; return true; } @@ -734,7 +736,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // this node becomes the previous node previousNode = thisNode; - _xmap[kit.Node.Uid] = kit.Node.Id; + _contentKeyToIdMap[kit.Node.Uid] = kit.Node.Id; } return ok; @@ -757,7 +759,7 @@ namespace Umbraco.Web.PublishedCache.NuCache EnsureLocked(); var ok = true; - + ClearLocked(_contentNodes); ClearRootLocked(); @@ -778,7 +780,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (_localDb != null) RegisterChange(kit.Node.Id, kit); AddTreeNodeLocked(kit.Node, parent); - _xmap[kit.Node.Uid] = kit.Node.Id; + _contentKeyToIdMap[kit.Node.Uid] = kit.Node.Id; } return ok; @@ -807,7 +809,7 @@ namespace Umbraco.Web.PublishedCache.NuCache EnsureLocked(); var ok = true; - + // get existing _contentNodes.TryGetValue(rootContentId, out var link); var existing = link?.Value; @@ -833,7 +835,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (_localDb != null) RegisterChange(kit.Node.Id, kit); AddTreeNodeLocked(kit.Node, parent); - _xmap[kit.Node.Uid] = kit.Node.Id; + _contentKeyToIdMap[kit.Node.Uid] = kit.Node.Id; } return ok; @@ -885,11 +887,11 @@ namespace Umbraco.Web.PublishedCache.NuCache // This should never be null, all code that calls this method is null checking but we've seen // issues of null ref exceptions in issue reports so we'll double check here if (content == null) throw new ArgumentNullException(nameof(content)); - + SetValueLocked(_contentNodes, content.Id, null); if (_localDb != null) RegisterChange(content.Id, ContentNodeKit.Null); - _xmap.TryRemove(content.Uid, out _); + _contentKeyToIdMap.TryRemove(content.Uid, out _); var id = content.FirstChildContentId; while (id > 0) @@ -913,10 +915,10 @@ namespace Umbraco.Web.PublishedCache.NuCache { if (_contentNodes.TryGetValue(id, out var link)) { - link = GetLinkedNodeGen(link, gen); + link = GetLinkedNodeGen(link, gen); if (link != null && link.Value != null) return link; - } + } throw new PanicException($"failed to get {description} with id={id}"); } @@ -929,13 +931,13 @@ namespace Umbraco.Web.PublishedCache.NuCache { if (content.ParentContentId < 0) { - var root = GetLinkedNodeGen(_root, gen); + var root = GetLinkedNodeGen(_root, gen); return root; } if (_contentNodes.TryGetValue(content.ParentContentId, out var link)) link = GetLinkedNodeGen(link, gen); - + return link; } @@ -1154,6 +1156,15 @@ namespace Umbraco.Web.PublishedCache.NuCache } } + private void SetContentTypeLocked(IPublishedContentType type) + { + SetValueLocked(_contentTypesById, type.Id, type); + SetValueLocked(_contentTypesByAlias, type.Alias, type); + // ensure the key/id map is accurate + if (type.TryGetKey(out var key)) + _contentTypeKeyToIdMap[key] = type.Id; + } + // set a node (just the node, not the tree) private void SetValueLocked(ConcurrentDictionary> dict, TKey key, TValue value) where TValue : class @@ -1211,14 +1222,14 @@ namespace Umbraco.Web.PublishedCache.NuCache public ContentNode Get(Guid uid, long gen) { - return _xmap.TryGetValue(uid, out var id) + return _contentKeyToIdMap.TryGetValue(uid, out var id) ? GetValue(_contentNodes, id, gen) : null; } public IEnumerable GetAtRoot(long gen) { - var root = GetLinkedNodeGen(_root, gen); + var root = GetLinkedNodeGen(_root, gen); if (root == null) yield break; @@ -1274,13 +1285,20 @@ namespace Umbraco.Web.PublishedCache.NuCache return GetValue(_contentTypesByAlias, alias, gen); } + public IPublishedContentType GetContentType(Guid key, long gen) + { + if (!_contentTypeKeyToIdMap.TryGetValue(key, out var id)) + return null; + return GetContentType(id, gen); + } + #endregion #region Snapshots public Snapshot CreateSnapshot() { - lock(_rlocko) + lock (_rlocko) { // if no next generation is required, and we already have one, // use it and create a new snapshot @@ -1606,6 +1624,13 @@ namespace Umbraco.Web.PublishedCache.NuCache return _store.GetContentType(alias, _gen); } + public IPublishedContentType GetContentType(Guid key) + { + if (_gen < 0) + throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); + return _store.GetContentType(key, _gen); + } + // this code is here just so you don't try to implement it // the only way we can iterate over "all" without locking the entire cache forever // is by shallow cloning the cache, which is quite expensive, so we should probably not do it, diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs index 182086ed7f..a466460ede 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs @@ -11,7 +11,7 @@ using Umbraco.Web.PublishedCache.NuCache.Navigable; namespace Umbraco.Web.PublishedCache.NuCache { - internal class MediaCache : PublishedCacheBase, IPublishedMediaCache, INavigableData, IDisposable + internal class MediaCache : PublishedCacheBase, IPublishedMediaCache2, INavigableData, IDisposable { private readonly ContentStore.Snapshot _snapshot; private readonly IVariationContextAccessor _variationContextAccessor; @@ -155,15 +155,11 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Content types - public override IPublishedContentType GetContentType(int id) - { - return _snapshot.GetContentType(id); - } + public override IPublishedContentType GetContentType(int id) => _snapshot.GetContentType(id); - public override IPublishedContentType GetContentType(string alias) - { - return _snapshot.GetContentType(alias); - } + public override IPublishedContentType GetContentType(string alias) => _snapshot.GetContentType(alias); + + public override IPublishedContentType GetContentType(Guid key) => _snapshot.GetContentType(key); #endregion diff --git a/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs b/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs index 1f637663e5..1b4a9bb92a 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Xml; namespace Umbraco.Web.PublishedCache { - abstract class PublishedCacheBase : IPublishedCache + internal abstract class PublishedCacheBase : IPublishedCache2 { public bool PreviewDefault { get; } @@ -89,8 +89,8 @@ namespace Umbraco.Web.PublishedCache } public abstract IPublishedContentType GetContentType(int id); - public abstract IPublishedContentType GetContentType(string alias); + public abstract IPublishedContentType GetContentType(Guid key); public virtual IEnumerable GetByContentType(IPublishedContentType contentType) { diff --git a/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs b/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs index e453471bb8..8eb50b0588 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs @@ -15,8 +15,10 @@ namespace Umbraco.Web.PublishedCache /// This cache is not snapshotted, so it refreshes any time things change. public class PublishedContentTypeCache { + // NOTE: These are not concurrent dictionaries because all access is done within a lock private readonly Dictionary _typesByAlias = new Dictionary(); private readonly Dictionary _typesById = new Dictionary(); + private readonly Dictionary _keyToIdMap = new Dictionary(); private readonly IContentTypeService _contentTypeService; private readonly IMediaTypeService _mediaTypeService; private readonly IMemberTypeService _memberTypeService; @@ -130,6 +132,42 @@ namespace Umbraco.Web.PublishedCache } } + /// + /// Gets a published content type. + /// + /// An item type. + /// An key. + /// The published content type corresponding to the item key. + public IPublishedContentType Get(PublishedItemType itemType, Guid key) + { + try + { + _lock.EnterUpgradeableReadLock(); + + if (_keyToIdMap.TryGetValue(key, out var id)) + return Get(itemType, id); + + var type = CreatePublishedContentType(itemType, key); + + try + { + _lock.EnterWriteLock(); + _keyToIdMap[key] = type.Id; + return _typesByAlias[GetAliasKey(type)] = _typesById[type.Id] = type; + } + finally + { + if (_lock.IsWriteLockHeld) + _lock.ExitWriteLock(); + } + } + finally + { + if (_lock.IsUpgradeableReadLockHeld) + _lock.ExitUpgradeableReadLock(); + } + } + /// /// Gets a published content type. /// @@ -152,7 +190,8 @@ namespace Umbraco.Web.PublishedCache try { _lock.EnterWriteLock(); - + if (type.TryGetKey(out var key)) + _keyToIdMap[key] = type.Id; return _typesByAlias[aliasKey] = _typesById[type.Id] = type; } finally @@ -188,7 +227,8 @@ namespace Umbraco.Web.PublishedCache try { _lock.EnterWriteLock(); - + if (type.TryGetKey(out var key)) + _keyToIdMap[key] = type.Id; return _typesByAlias[GetAliasKey(type)] = _typesById[type.Id] = type; } finally @@ -204,27 +244,32 @@ namespace Umbraco.Web.PublishedCache } } + private IPublishedContentType CreatePublishedContentType(PublishedItemType itemType, Guid key) + { + IContentTypeComposition contentType = itemType switch + { + PublishedItemType.Content => _contentTypeService.Get(key), + PublishedItemType.Media => _mediaTypeService.Get(key), + PublishedItemType.Member => _memberTypeService.Get(key), + _ => throw new ArgumentOutOfRangeException(nameof(itemType)), + }; + if (contentType == null) + throw new Exception($"ContentTypeService failed to find a {itemType.ToString().ToLower()} type with key \"{key}\"."); + + return _publishedContentTypeFactory.CreateContentType(contentType); + } + private IPublishedContentType CreatePublishedContentType(PublishedItemType itemType, string alias) { if (GetPublishedContentTypeByAlias != null) return GetPublishedContentTypeByAlias(alias); - - IContentTypeComposition contentType; - switch (itemType) + IContentTypeComposition contentType = itemType switch { - case PublishedItemType.Content: - contentType = _contentTypeService.Get(alias); - break; - case PublishedItemType.Media: - contentType = _mediaTypeService.Get(alias); - break; - case PublishedItemType.Member: - contentType = _memberTypeService.Get(alias); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType)); - } - + PublishedItemType.Content => _contentTypeService.Get(alias), + PublishedItemType.Media => _mediaTypeService.Get(alias), + PublishedItemType.Member => _memberTypeService.Get(alias), + _ => throw new ArgumentOutOfRangeException(nameof(itemType)), + }; if (contentType == null) throw new Exception($"ContentTypeService failed to find a {itemType.ToString().ToLower()} type with alias \"{alias}\"."); @@ -235,23 +280,13 @@ namespace Umbraco.Web.PublishedCache { if (GetPublishedContentTypeById != null) return GetPublishedContentTypeById(id); - - IContentTypeComposition contentType; - switch (itemType) + IContentTypeComposition contentType = itemType switch { - case PublishedItemType.Content: - contentType = _contentTypeService.Get(id); - break; - case PublishedItemType.Media: - contentType = _mediaTypeService.Get(id); - break; - case PublishedItemType.Member: - contentType = _memberTypeService.Get(id); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType)); - } - + PublishedItemType.Content => _contentTypeService.Get(id), + PublishedItemType.Media => _mediaTypeService.Get(id), + PublishedItemType.Member => _memberTypeService.Get(id), + _ => throw new ArgumentOutOfRangeException(nameof(itemType)), + }; if (contentType == null) throw new Exception($"ContentTypeService failed to find a {itemType.ToString().ToLower()} type with id {id}."); @@ -259,6 +294,7 @@ namespace Umbraco.Web.PublishedCache } // for unit tests - changing the callback must reset the cache obviously + // TODO: Why does this even exist? For testing you'd pass in a mocked service to get by id private Func _getPublishedContentTypeByAlias; internal Func GetPublishedContentTypeByAlias { @@ -282,6 +318,7 @@ namespace Umbraco.Web.PublishedCache } // for unit tests - changing the callback must reset the cache obviously + // TODO: Why does this even exist? For testing you'd pass in a mocked service to get by id private Func _getPublishedContentTypeById; internal Func GetPublishedContentTypeById { diff --git a/src/umbraco.sln b/src/umbraco.sln index 63fb856b5d..78ff0ef12d 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -58,8 +58,24 @@ Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Web.UI.Client", "ht StartServerOnDebug = "false" EndProjectSection EndProject -Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Tests.AcceptanceTest\", "Umbraco.Tests.AcceptanceTest\", "{9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}" +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Tests.AcceptanceTest", "Umbraco.Tests.AcceptanceTest\", "{9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}" ProjectSection(WebsiteProperties) = preProject + TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0" + Debug.AspNetCompiler.VirtualPath = "/localhost_62926" + Debug.AspNetCompiler.PhysicalPath = "Umbraco.Tests.AcceptanceTest\" + Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_62926\" + Debug.AspNetCompiler.Updateable = "true" + Debug.AspNetCompiler.ForceOverwrite = "true" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.VirtualPath = "/localhost_62926" + Release.AspNetCompiler.PhysicalPath = "Umbraco.Tests.AcceptanceTest\" + Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_62926\" + Release.AspNetCompiler.Updateable = "true" + Release.AspNetCompiler.ForceOverwrite = "true" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.Debug = "False" + VWDPort = "62926" SlnRelativePath = "Umbraco.Tests.AcceptanceTest\" EndProjectSection EndProject @@ -123,6 +139,9 @@ Global {4C4C194C-B5E4-4991-8F87-4373E24CC19F}.Release|Any CPU.Build.0 = Release|Any CPU {3819A550-DCEC-4153-91B4-8BA9F7F0B9B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3819A550-DCEC-4153-91B4-8BA9F7F0B9B4}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}.Release|Any CPU.Build.0 = Debug|Any CPU {651E1350-91B6-44B7-BD60-7207006D7003}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {651E1350-91B6-44B7-BD60-7207006D7003}.Debug|Any CPU.Build.0 = Debug|Any CPU {651E1350-91B6-44B7-BD60-7207006D7003}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -157,6 +176,7 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {227C3B55-80E5-4E7E-A802-BE16C5128B9D} = {2849E9D4-3B4E-40A3-A309-F3CB4F0E125F} + {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {5D3B8245-ADA6-453F-A008-50ED04BFE770} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {E3F9F378-AFE1-40A5-90BD-82833375DBFE} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D} {5B03EF4E-E0AC-4905-861B-8C3EC1A0D458} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D} @@ -164,7 +184,6 @@ Global {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {C7311C00-2184-409B-B506-52A5FAEA8736} = {FD962632-184C-4005-A5F3-E705D92FC645} {FB5676ED-7A69-492C-B802-E7B24144C0FC} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} - {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7A0F2E34-D2AF-4DAB-86A0-7D7764B3D0EC}