diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs index f437c0cd0d1d..e1672beda982 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.Querying; @@ -25,8 +26,9 @@ public ContentTypeRepository( ILogger logger, IContentTypeCommonRepository commonRepository, ILanguageRepository languageRepository, - IShortStringHelper shortStringHelper) - : base(scopeAccessor, cache, logger, commonRepository, languageRepository, shortStringHelper) + IShortStringHelper shortStringHelper, + IIdKeyMap idKeyMap) + : base(scopeAccessor, cache, logger, commonRepository, languageRepository, shortStringHelper, idKeyMap) { } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 0d68cd39bf04..ae843ddfadbd 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -29,15 +29,22 @@ internal abstract class ContentTypeRepositoryBase : EntityRepositoryBas where TEntity : class, IContentTypeComposition { private readonly IShortStringHelper _shortStringHelper; - - protected ContentTypeRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, - ILogger> logger, IContentTypeCommonRepository commonRepository, - ILanguageRepository languageRepository, IShortStringHelper shortStringHelper) + private readonly IIdKeyMap _idKeyMap; + + protected ContentTypeRepositoryBase( + IScopeAccessor scopeAccessor, + AppCaches cache, + ILogger> logger, + IContentTypeCommonRepository commonRepository, + ILanguageRepository languageRepository, + IShortStringHelper shortStringHelper, + IIdKeyMap idKeyMap) : base(scopeAccessor, cache, logger) { _shortStringHelper = shortStringHelper; CommonRepository = commonRepository; LanguageRepository = languageRepository; + _idKeyMap = idKeyMap; } protected IContentTypeCommonRepository CommonRepository { get; } @@ -291,7 +298,7 @@ protected void PersistNewBaseContentType(IContentTypeComposition entity) // If the Id of the DataType is not set, we resolve it from the db by its PropertyEditorAlias if (propertyType.DataTypeId == 0 || propertyType.DataTypeId == default) { - AssignDataTypeFromPropertyEditor(propertyType); + AssignDataTypeIdFromProvidedKeyOrPropertyEditor(propertyType); } PropertyTypeDto propertyTypeDto = @@ -592,7 +599,7 @@ protected void PersistUpdatedBaseContentType(IContentTypeComposition entity) // if the Id of the DataType is not set, we resolve it from the db by its PropertyEditorAlias if (propertyType.DataTypeId == 0 || propertyType.DataTypeId == default) { - AssignDataTypeFromPropertyEditor(propertyType); + AssignDataTypeIdFromProvidedKeyOrPropertyEditor(propertyType); } // validate the alias @@ -1434,37 +1441,59 @@ protected void ValidateAlias(TEntity entity) protected abstract TEntity? PerformGet(Guid id); /// - /// Try to set the data type id based on its ControlId + /// Try to set the data type Id based on the provided key or property editor alias. /// /// - private void AssignDataTypeFromPropertyEditor(IPropertyType propertyType) + private void AssignDataTypeIdFromProvidedKeyOrPropertyEditor(IPropertyType propertyType) { - // we cannot try to assign a data type of it's empty - if (propertyType.PropertyEditorAlias.IsNullOrWhiteSpace() == false) + // If a key is provided, use that. + if (propertyType.DataTypeKey != Guid.Empty) { - Sql sql = Sql() - .Select(dt => dt.Select(x => x.NodeDto)) - .From() - .InnerJoin().On((dt, n) => dt.NodeId == n.NodeId) - .Where( - "propertyEditorAlias = @propertyEditorAlias", - new { propertyEditorAlias = propertyType.PropertyEditorAlias }) - .OrderBy(typeDto => typeDto.NodeId); - DataTypeDto? datatype = Database.FirstOrDefault(sql); - - // we cannot assign a data type if one was not found - if (datatype != null) + Attempt dataTypeIdAttempt = _idKeyMap.GetIdForKey(propertyType.DataTypeKey, UmbracoObjectTypes.DataType); + if (dataTypeIdAttempt.Success) { - propertyType.DataTypeId = datatype.NodeId; - propertyType.DataTypeKey = datatype.NodeDto.UniqueId; + propertyType.DataTypeId = dataTypeIdAttempt.Result; + return; } else { Logger.LogWarning( - "Could not assign a data type for the property type {PropertyTypeAlias} since no data type was found with a property editor {PropertyEditorAlias}", - propertyType.Alias, propertyType.PropertyEditorAlias); + "Could not assign a data type for the property type {PropertyTypeAlias} since no integer Id was found matching the key {DataTypeKey}. Falling back to look up via the property editor alias.", + propertyType.Alias, + propertyType.DataTypeKey); } } + + // Otherwise if a property editor alias is provided, try to find a data type that uses that alias. + if (propertyType.PropertyEditorAlias.IsNullOrWhiteSpace()) + { + // We cannot try to assign a data type if it's empty. + return; + } + + Sql sql = Sql() + .Select(dt => dt.Select(x => x.NodeDto)) + .From() + .InnerJoin().On((dt, n) => dt.NodeId == n.NodeId) + .Where( + "propertyEditorAlias = @propertyEditorAlias", + new { propertyEditorAlias = propertyType.PropertyEditorAlias }) + .OrderBy(typeDto => typeDto.NodeId); + DataTypeDto? datatype = Database.FirstOrDefault(sql); + + // we cannot assign a data type if one was not found + if (datatype != null) + { + propertyType.DataTypeId = datatype.NodeId; + propertyType.DataTypeKey = datatype.NodeDto.UniqueId; + } + else + { + Logger.LogWarning( + "Could not assign a data type for the property type {PropertyTypeAlias} since no data type was found with a property editor {PropertyEditorAlias}", + propertyType.Alias, + propertyType.PropertyEditorAlias); + } } protected abstract TEntity? PerformGet(string alias); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaTypeRepository.cs index 51a4c367527c..792e98438710 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaTypeRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaTypeRepository.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.Querying; @@ -24,8 +25,9 @@ public MediaTypeRepository( ILogger logger, IContentTypeCommonRepository commonRepository, ILanguageRepository languageRepository, - IShortStringHelper shortStringHelper) - : base(scopeAccessor, cache, logger, commonRepository, languageRepository, shortStringHelper) + IShortStringHelper shortStringHelper, + IIdKeyMap idKeyMap) + : base(scopeAccessor, cache, logger, commonRepository, languageRepository, shortStringHelper, idKeyMap) { } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberTypeRepository.cs index 61127b766bad..e764f54090fd 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberTypeRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberTypeRepository.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.Factories; @@ -27,9 +28,10 @@ public MemberTypeRepository( ILogger logger, IContentTypeCommonRepository commonRepository, ILanguageRepository languageRepository, - IShortStringHelper shortStringHelper) - : base(scopeAccessor, cache, logger, commonRepository, languageRepository, shortStringHelper) => - _shortStringHelper = shortStringHelper; + IShortStringHelper shortStringHelper, + IIdKeyMap idKeyMap) + : base(scopeAccessor, cache, logger, commonRepository, languageRepository, shortStringHelper, idKeyMap) + => _shortStringHelper = shortStringHelper; protected override bool SupportsPublishing => MemberType.SupportsPublishingConst; diff --git a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index d8fd5f48a498..ec92a40fe787 100644 --- a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -60,6 +60,8 @@ public abstract class UmbracoIntegrationTest : UmbracoIntegrationTestBase protected IShortStringHelper ShortStringHelper => Services.GetRequiredService(); + protected IIdKeyMap IdKeyMap => Services.GetRequiredService(); + protected GlobalSettings GlobalSettings => Services.GetRequiredService>().Value; protected IMapperCollection Mappers => Services.GetRequiredService(); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs index 94a6c1884abb..22cc35b424a8 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs @@ -123,7 +123,7 @@ private DocumentRepository CreateRepository(IScopeAccessor scopeAccessor, out Co new ContentTypeCommonRepository(scopeAccessor, templateRepository, appCaches, ShortStringHelper); var languageRepository = new LanguageRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger()); - contentTypeRepository = new ContentTypeRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger(), commonRepository, languageRepository, ShortStringHelper); + contentTypeRepository = new ContentTypeRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger(), commonRepository, languageRepository, ShortStringHelper, IdKeyMap); var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger()); var entityRepository = new EntityRepository(scopeAccessor, AppCaches.Disabled); var relationRepository = new RelationRepository(scopeAccessor, LoggerFactory.CreateLogger(), relationTypeRepository, entityRepository); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs index 3b21605d071e..1532846fc412 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs @@ -56,7 +56,7 @@ private MediaRepository CreateRepository(IScopeProvider provider, out MediaTypeR new ContentTypeCommonRepository(scopeAccessor, TemplateRepository, appCaches, ShortStringHelper); var languageRepository = new LanguageRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger()); - mediaTypeRepository = new MediaTypeRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger(), commonRepository, languageRepository, ShortStringHelper); + mediaTypeRepository = new MediaTypeRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger(), commonRepository, languageRepository, ShortStringHelper, IdKeyMap); var tagRepository = new TagRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger()); var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger()); var entityRepository = new EntityRepository(scopeAccessor, AppCaches.Disabled); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaTypeRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaTypeRepositoryTest.cs index 34a92456363b..a05d5b96c95a 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaTypeRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaTypeRepositoryTest.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence; using Umbraco.Cms.Core.Persistence.Repositories; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Cms.Tests.Common.Builders; @@ -411,7 +412,7 @@ public void Can_Verify_PropertyTypes_On_File_MediaType() } private MediaTypeRepository CreateRepository(IScopeProvider provider) => - new((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger(), CommonRepository, LanguageRepository, ShortStringHelper); + new((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger(), CommonRepository, LanguageRepository, ShortStringHelper, IdKeyMap); private EntityContainerRepository CreateContainerRepository(IScopeProvider provider) => new((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger(), Constants.ObjectTypes.MediaTypeContainer); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberTypeRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberTypeRepositoryTest.cs index 0c1f07123989..6392354a6fd9 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberTypeRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberTypeRepositoryTest.cs @@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence; using Umbraco.Cms.Core.Persistence.Repositories; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Cms.Tests.Common.Builders; @@ -26,7 +27,7 @@ private MemberTypeRepository CreateRepository(IScopeProvider provider) { var commonRepository = GetRequiredService(); var languageRepository = GetRequiredService(); - return new MemberTypeRepository((IScopeAccessor)provider, AppCaches.Disabled, Mock.Of>(), commonRepository, languageRepository, ShortStringHelper); + return new MemberTypeRepository((IScopeAccessor)provider, AppCaches.Disabled, Mock.Of>(), commonRepository, languageRepository, ShortStringHelper, IdKeyMap); } [Test] diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs index 973ea658dc98..2350b1d1f548 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs @@ -266,7 +266,7 @@ public void Can_Perform_Delete_When_Assigned_To_Doc() var commonRepository = new ContentTypeCommonRepository(scopeAccessor, templateRepository, AppCaches, ShortStringHelper); var languageRepository = new LanguageRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger()); - var contentTypeRepository = new ContentTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger(), commonRepository, languageRepository, ShortStringHelper); + var contentTypeRepository = new ContentTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger(), commonRepository, languageRepository, ShortStringHelper, IdKeyMap); var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger()); var entityRepository = new EntityRepository(scopeAccessor, AppCaches.Disabled); var relationRepository = new RelationRepository(scopeAccessor, LoggerFactory.CreateLogger(), relationTypeRepository, entityRepository); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs index 41064377a737..fd2c335112f1 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs @@ -1971,6 +1971,65 @@ public void Variations_In_Compositions() .Variations); } + [Test] + public void Can_Create_Property_Type_Based_On_DataTypeKey() + { + // Arrange + var cts = ContentTypeService; + var dtdYesNo = DataTypeService.GetDataType(-49); + IContentType ctBase = new ContentType(ShortStringHelper, -1) + { + Name = "Base", + Alias = "Base", + Icon = "folder.gif", + Thumbnail = "folder.png" + }; + ctBase.AddPropertyType(new PropertyType(ShortStringHelper, "ShouldNotMatter", ValueStorageType.Nvarchar) + { + Name = "Hide From Navigation", + Alias = Constants.Conventions.Content.NaviHide, + DataTypeKey = dtdYesNo.Key + }); + cts.Save(ctBase); + + // Assert + ctBase = cts.Get(ctBase.Key); + Assert.That(ctBase, Is.Not.Null); + Assert.That(ctBase.HasIdentity, Is.True); + Assert.That(ctBase.PropertyTypes.Count(), Is.EqualTo(1)); + Assert.That(ctBase.PropertyTypes.First().DataTypeId, Is.EqualTo(dtdYesNo.Id)); + Assert.That(ctBase.PropertyTypes.First().PropertyEditorAlias, Is.EqualTo(dtdYesNo.EditorAlias)); + } + + [Test] + public void Can_Create_Property_Type_Based_On_PropertyEditorAlias() + { + // Arrange + var cts = ContentTypeService; + var dtdYesNo = DataTypeService.GetDataType(-49); + IContentType ctBase = new ContentType(ShortStringHelper, -1) + { + Name = "Base", + Alias = "Base", + Icon = "folder.gif", + Thumbnail = "folder.png" + }; + ctBase.AddPropertyType(new PropertyType(ShortStringHelper, "Umbraco.TrueFalse", ValueStorageType.Nvarchar) + { + Name = "Hide From Navigation", + Alias = Constants.Conventions.Content.NaviHide, + }); + cts.Save(ctBase); + + // Assert + ctBase = cts.Get(ctBase.Key); + Assert.That(ctBase, Is.Not.Null); + Assert.That(ctBase.HasIdentity, Is.True); + Assert.That(ctBase.PropertyTypes.Count(), Is.EqualTo(1)); + Assert.That(ctBase.PropertyTypes.First().DataTypeId, Is.EqualTo(dtdYesNo.Id)); + Assert.That(ctBase.PropertyTypes.First().PropertyEditorAlias, Is.EqualTo(dtdYesNo.EditorAlias)); + } + private ContentType CreateComponent() { var component = new ContentType(ShortStringHelper, -1)