[V15] Align reserved property endpoints code and usage (#18319)

* Remove/enhance duplicate code

contentTypeEditing Services now use the IReservedFieldNamesService to determine invalid property alias values.

* Let Modelsbuilder define it's reserved property alias names

* Update, add tests + fix false positives

* Removed config check to register modelsbuilder required reserved properties

* Updated unittests regarding removed check of modelsbuilder mode

* Fix merge

---------

Co-authored-by: Sven Geusens <sge@umbraco.dk>
This commit is contained in:
Kenn Jacobsen
2025-03-06 09:16:00 +01:00
committed by GitHub
parent 52bb32205b
commit 7b422598f9
18 changed files with 244 additions and 71 deletions

View File

@@ -10,4 +10,6 @@ public class ContentPropertySettings
public ISet<string> ReservedFieldNames => _reservedFieldNames;
public bool AddReservedFieldName(string name) => _reservedFieldNames.Add(name);
public void AddReservedFieldNames(ISet<string> names) => _reservedFieldNames.UnionWith(names);
}

View File

@@ -10,4 +10,6 @@ public class MediaPropertySettings
public ISet<string> ReservedFieldNames => _reservedFieldNames;
public bool AddReservedFieldName(string name) => _reservedFieldNames.Add(name);
public void AddReservedFieldNames(ISet<string> names) => _reservedFieldNames.UnionWith(names);
}

View File

@@ -10,4 +10,6 @@ public class MemberPropertySettings
public ISet<string> ReservedFieldNames => _reservedFieldNames;
public bool AddReservedFieldName(string name) => _reservedFieldNames.Add(name);
public void AddReservedFieldNames(ISet<string> names) => _reservedFieldNames.UnionWith(names);
}

View File

@@ -1,4 +1,4 @@
namespace Umbraco.Cms.Core.Models.PublishedContent;
namespace Umbraco.Cms.Core.Models.PublishedContent;
public interface IPublishedMember : IPublishedContent
{

View File

@@ -3,7 +3,6 @@ using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Models.ContentTypeEditing;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Core.Strings;
using Umbraco.Extensions;
@@ -16,6 +15,7 @@ internal sealed class ContentTypeEditingService : ContentTypeEditingServiceBase<
{
private readonly ITemplateService _templateService;
private readonly IElementSwitchValidator _elementSwitchValidator;
private readonly IReservedFieldNamesService _reservedFieldNamesService;
private readonly IContentTypeService _contentTypeService;
public ContentTypeEditingService(
@@ -24,12 +24,33 @@ internal sealed class ContentTypeEditingService : ContentTypeEditingServiceBase<
IDataTypeService dataTypeService,
IEntityService entityService,
IShortStringHelper shortStringHelper,
IElementSwitchValidator elementSwitchValidator)
IElementSwitchValidator elementSwitchValidator,
IReservedFieldNamesService reservedFieldNamesService)
: base(contentTypeService, contentTypeService, dataTypeService, entityService, shortStringHelper)
{
_contentTypeService = contentTypeService;
_templateService = templateService;
_elementSwitchValidator = elementSwitchValidator;
_reservedFieldNamesService = reservedFieldNamesService;
}
[Obsolete("Use the constructor that is not marked obsolete, will be removed in v17")]
public ContentTypeEditingService(
IContentTypeService contentTypeService,
ITemplateService templateService,
IDataTypeService dataTypeService,
IEntityService entityService,
IShortStringHelper shortStringHelper,
IElementSwitchValidator elementSwitchValidator)
: this(
contentTypeService,
templateService,
dataTypeService,
entityService,
shortStringHelper,
elementSwitchValidator,
StaticServiceProvider.Instance.GetRequiredService<IReservedFieldNamesService>())
{
}
[Obsolete("Use the constructor that is not marked obsolete, will be removed in v16")]
@@ -39,11 +60,15 @@ internal sealed class ContentTypeEditingService : ContentTypeEditingServiceBase<
IDataTypeService dataTypeService,
IEntityService entityService,
IShortStringHelper shortStringHelper)
: base(contentTypeService, contentTypeService, dataTypeService, entityService, shortStringHelper)
: this(
contentTypeService,
templateService,
dataTypeService,
entityService,
shortStringHelper,
StaticServiceProvider.Instance.GetRequiredService<IElementSwitchValidator>(),
StaticServiceProvider.Instance.GetRequiredService<IReservedFieldNamesService>())
{
_contentTypeService = contentTypeService;
_templateService = templateService;
_elementSwitchValidator = StaticServiceProvider.Instance.GetRequiredService<IElementSwitchValidator>();
}
public async Task<Attempt<IContentType?, ContentTypeOperationStatus>> CreateAsync(ContentTypeCreateModel model, Guid userKey)
@@ -184,4 +209,6 @@ internal sealed class ContentTypeEditingService : ContentTypeEditingServiceBase<
protected override UmbracoObjectTypes ContentTypeObjectType => UmbracoObjectTypes.DocumentType;
protected override UmbracoObjectTypes ContainerObjectType => UmbracoObjectTypes.DocumentTypeContainer;
protected override ISet<string> GetReservedFieldNames() => _reservedFieldNamesService.GetDocumentReservedFieldNames();
}

View File

@@ -1,6 +1,5 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentTypeEditing;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Core.Strings;
using Umbraco.Extensions;
@@ -18,7 +17,6 @@ internal abstract class ContentTypeEditingServiceBase<TContentType, TContentType
private readonly IDataTypeService _dataTypeService;
private readonly IEntityService _entityService;
private readonly IShortStringHelper _shortStringHelper;
protected ContentTypeEditingServiceBase(
IContentTypeService contentTypeService,
TContentTypeService concreteContentTypeService,
@@ -419,13 +417,13 @@ internal abstract class ContentTypeEditingServiceBase<TContentType, TContentType
return reservedAliases.InvariantContains(alias);
}
protected abstract ISet<string> GetReservedFieldNames();
private bool ContainsReservedPropertyTypeAlias(ContentTypeEditingModelBase<TPropertyTypeModel, TPropertyTypeContainer> model)
{
// Because of models builder you cannot have an alias that already exists in IPublishedContent, for instance Path.
// Since MyModel.Path would conflict with IPublishedContent.Path.
var reservedPropertyTypeNames = typeof(IPublishedContent).GetPublicProperties().Select(x => x.Name)
.Union(typeof(IPublishedContent).GetPublicMethods().Select(x => x.Name))
.ToArray();
ISet<string> reservedPropertyTypeNames = GetReservedFieldNames();
return model.Properties.Any(propertyType => reservedPropertyTypeNames.InvariantContains(propertyType.Alias));
}

View File

@@ -1,4 +1,6 @@
using Umbraco.Cms.Core.Media;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Media;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentTypeEditing;
using Umbraco.Cms.Core.PropertyEditors;
@@ -13,7 +15,25 @@ internal sealed class MediaTypeEditingService : ContentTypeEditingServiceBase<IM
private readonly IMediaTypeService _mediaTypeService;
private readonly IDataTypeService _dataTypeService;
private readonly IImageUrlGenerator _imageUrlGenerator;
private readonly IReservedFieldNamesService _reservedFieldNamesService;
public MediaTypeEditingService(
IContentTypeService contentTypeService,
IMediaTypeService mediaTypeService,
IDataTypeService dataTypeService,
IEntityService entityService,
IShortStringHelper shortStringHelper,
IImageUrlGenerator imageUrlGenerator,
IReservedFieldNamesService reservedFieldNamesService)
: base(contentTypeService, mediaTypeService, dataTypeService, entityService, shortStringHelper)
{
_mediaTypeService = mediaTypeService;
_dataTypeService = dataTypeService;
_imageUrlGenerator = imageUrlGenerator;
_reservedFieldNamesService = reservedFieldNamesService;
}
[Obsolete("Use the non obsolete constructor instead. Scheduled for removal in v16")]
public MediaTypeEditingService(
IContentTypeService contentTypeService,
IMediaTypeService mediaTypeService,
@@ -26,6 +46,7 @@ internal sealed class MediaTypeEditingService : ContentTypeEditingServiceBase<IM
_mediaTypeService = mediaTypeService;
_dataTypeService = dataTypeService;
_imageUrlGenerator = imageUrlGenerator;
_reservedFieldNamesService = StaticServiceProvider.Instance.GetRequiredService<IReservedFieldNamesService>();
}
public async Task<Attempt<IMediaType?, ContentTypeOperationStatus>> CreateAsync(MediaTypeCreateModel model, Guid userKey)
@@ -145,6 +166,8 @@ internal sealed class MediaTypeEditingService : ContentTypeEditingServiceBase<IM
protected override UmbracoObjectTypes ContainerObjectType => UmbracoObjectTypes.MediaTypeContainer;
protected override ISet<string> GetReservedFieldNames() => _reservedFieldNamesService.GetMediaReservedFieldNames();
private async Task<IDictionary<IMediaType, IEnumerable<string>>> FetchAllowedFileExtensionsByMediaTypeAsync(IEnumerable<IMediaType> mediaTypes)
{
var allowedFileExtensionsByMediaType = new Dictionary<IMediaType, IEnumerable<string>>();

View File

@@ -1,4 +1,6 @@
using Umbraco.Cms.Core.Models;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentTypeEditing;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Services.OperationStatus;
@@ -10,7 +12,24 @@ internal sealed class MemberTypeEditingService : ContentTypeEditingServiceBase<I
{
private readonly IMemberTypeService _memberTypeService;
private readonly IUserService _userService;
private readonly IReservedFieldNamesService _reservedFieldNamesService;
public MemberTypeEditingService(
IContentTypeService contentTypeService,
IMemberTypeService memberTypeService,
IDataTypeService dataTypeService,
IEntityService entityService,
IShortStringHelper shortStringHelper,
IUserService userService,
IReservedFieldNamesService reservedFieldNamesService)
: base(contentTypeService, memberTypeService, dataTypeService, entityService, shortStringHelper)
{
_memberTypeService = memberTypeService;
_userService = userService;
_reservedFieldNamesService = reservedFieldNamesService;
}
[Obsolete("Use the non obsolete constructor instead. Scheduled for removal in v16")]
public MemberTypeEditingService(
IContentTypeService contentTypeService,
IMemberTypeService memberTypeService,
@@ -22,6 +41,7 @@ internal sealed class MemberTypeEditingService : ContentTypeEditingServiceBase<I
{
_memberTypeService = memberTypeService;
_userService = userService;
_reservedFieldNamesService = StaticServiceProvider.Instance.GetRequiredService<IReservedFieldNamesService>();
}
public async Task<Attempt<IMemberType?, ContentTypeOperationStatus>> CreateAsync(MemberTypeCreateModel model, Guid userKey)
@@ -79,6 +99,8 @@ internal sealed class MemberTypeEditingService : ContentTypeEditingServiceBase<I
protected override UmbracoObjectTypes ContainerObjectType => throw new NotSupportedException("Member type tree does not support containers");
protected override ISet<string> GetReservedFieldNames() => _reservedFieldNamesService.GetMemberReservedFieldNames();
private void UpdatePropertyTypeVisibility(IMemberType memberType, MemberTypeModelBase model)
{
foreach (MemberTypePropertyTypeModel propertyType in model.Properties)