Skip to content

Forward merge #19720 to V16 #19735

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,8 +26,9 @@ public ContentTypeRepository(
ILogger<ContentTypeRepository> 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)
{
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Data;

Check notice on line 1 in src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

✅ Getting better: Primitive Obsession

The ratio of primitive types in function arguments decreases from 32.08% to 31.48%, threshold = 30.0%. The functions in this file have too many primitive types (e.g. int, double, float) in their function argument lists. Using many primitive types lead to the code smell Primitive Obsession. Avoid adding more primitive arguments.
using System.Globalization;
using System.Linq.Expressions;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -29,15 +29,22 @@
where TEntity : class, IContentTypeComposition
{
private readonly IShortStringHelper _shortStringHelper;

protected ContentTypeRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache,
ILogger<ContentTypeRepositoryBase<TEntity>> logger, IContentTypeCommonRepository commonRepository,
ILanguageRepository languageRepository, IShortStringHelper shortStringHelper)
private readonly IIdKeyMap _idKeyMap;

protected ContentTypeRepositoryBase(
IScopeAccessor scopeAccessor,
AppCaches cache,
ILogger<ContentTypeRepositoryBase<TEntity>> 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; }
Expand Down Expand Up @@ -291,7 +298,7 @@
// 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 =
Expand Down Expand Up @@ -592,7 +599,7 @@
// 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
Expand Down Expand Up @@ -1434,37 +1441,59 @@
protected abstract TEntity? PerformGet(Guid id);

/// <summary>
/// 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.
/// </summary>
/// <param name="propertyType"></param>
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<ISqlContext> sql = Sql()
.Select<DataTypeDto>(dt => dt.Select(x => x.NodeDto))
.From<DataTypeDto>()
.InnerJoin<NodeDto>().On<DataTypeDto, NodeDto>((dt, n) => dt.NodeId == n.NodeId)
.Where(
"propertyEditorAlias = @propertyEditorAlias",
new { propertyEditorAlias = propertyType.PropertyEditorAlias })
.OrderBy<DataTypeDto>(typeDto => typeDto.NodeId);
DataTypeDto? datatype = Database.FirstOrDefault<DataTypeDto>(sql);

// we cannot assign a data type if one was not found
if (datatype != null)
Attempt<int> 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<ISqlContext> sql = Sql()
.Select<DataTypeDto>(dt => dt.Select(x => x.NodeDto))
.From<DataTypeDto>()
.InnerJoin<NodeDto>().On<DataTypeDto, NodeDto>((dt, n) => dt.NodeId == n.NodeId)
.Where(
"propertyEditorAlias = @propertyEditorAlias",
new { propertyEditorAlias = propertyType.PropertyEditorAlias })
.OrderBy<DataTypeDto>(typeDto => typeDto.NodeId);
DataTypeDto? datatype = Database.FirstOrDefault<DataTypeDto>(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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,8 +25,9 @@ public MediaTypeRepository(
ILogger<MediaTypeRepository> 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)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,9 +28,10 @@ public MemberTypeRepository(
ILogger<MemberTypeRepository> 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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public abstract class UmbracoIntegrationTest : UmbracoIntegrationTestBase

protected IShortStringHelper ShortStringHelper => Services.GetRequiredService<IShortStringHelper>();

protected IIdKeyMap IdKeyMap => Services.GetRequiredService<IIdKeyMap>();

protected GlobalSettings GlobalSettings => Services.GetRequiredService<IOptions<GlobalSettings>>().Value;

protected IMapperCollection Mappers => Services.GetRequiredService<IMapperCollection>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<LanguageRepository>());
contentTypeRepository = new ContentTypeRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger<ContentTypeRepository>(), commonRepository, languageRepository, ShortStringHelper);
contentTypeRepository = new ContentTypeRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger<ContentTypeRepository>(), commonRepository, languageRepository, ShortStringHelper, IdKeyMap);
var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger<RelationTypeRepository>());
var entityRepository = new EntityRepository(scopeAccessor, AppCaches.Disabled);
var relationRepository = new RelationRepository(scopeAccessor, LoggerFactory.CreateLogger<RelationRepository>(), relationTypeRepository, entityRepository);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<LanguageRepository>());
mediaTypeRepository = new MediaTypeRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger<MediaTypeRepository>(), commonRepository, languageRepository, ShortStringHelper);
mediaTypeRepository = new MediaTypeRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger<MediaTypeRepository>(), commonRepository, languageRepository, ShortStringHelper, IdKeyMap);
var tagRepository = new TagRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger<TagRepository>());
var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger<RelationTypeRepository>());
var entityRepository = new EntityRepository(scopeAccessor, AppCaches.Disabled);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -411,7 +412,7 @@ public void Can_Verify_PropertyTypes_On_File_MediaType()
}

private MediaTypeRepository CreateRepository(IScopeProvider provider) =>
new((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger<MediaTypeRepository>(), CommonRepository, LanguageRepository, ShortStringHelper);
new((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger<MediaTypeRepository>(), CommonRepository, LanguageRepository, ShortStringHelper, IdKeyMap);

private EntityContainerRepository CreateContainerRepository(IScopeProvider provider) =>
new((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger<EntityContainerRepository>(), Constants.ObjectTypes.MediaTypeContainer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,7 +27,7 @@ private MemberTypeRepository CreateRepository(IScopeProvider provider)
{
var commonRepository = GetRequiredService<IContentTypeCommonRepository>();
var languageRepository = GetRequiredService<ILanguageRepository>();
return new MemberTypeRepository((IScopeAccessor)provider, AppCaches.Disabled, Mock.Of<ILogger<MemberTypeRepository>>(), commonRepository, languageRepository, ShortStringHelper);
return new MemberTypeRepository((IScopeAccessor)provider, AppCaches.Disabled, Mock.Of<ILogger<MemberTypeRepository>>(), commonRepository, languageRepository, ShortStringHelper, IdKeyMap);
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<LanguageRepository>());
var contentTypeRepository = new ContentTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger<ContentTypeRepository>(), commonRepository, languageRepository, ShortStringHelper);
var contentTypeRepository = new ContentTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger<ContentTypeRepository>(), commonRepository, languageRepository, ShortStringHelper, IdKeyMap);
var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger<RelationTypeRepository>());
var entityRepository = new EntityRepository(scopeAccessor, AppCaches.Disabled);
var relationRepository = new RelationRepository(scopeAccessor, LoggerFactory.CreateLogger<RelationRepository>(), relationTypeRepository, entityRepository);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading