Improvement - Content type filters : Add Validation for allowed children and root (#19903)

* Validate content type filter restrictions when creating content at root.

* Validate content type filter restrictions when creating content as child.

* Update tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentEditingServiceTests.Create.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Andy Butland
2025-09-17 09:38:12 +02:00
committed by GitHub
parent 546849d825
commit de8545456d
7 changed files with 176 additions and 23 deletions

View File

@@ -1,12 +1,14 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Services.Filters;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Tests.Common.Builders;
using Umbraco.Cms.Tests.Integration.Attributes;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
@@ -15,6 +17,58 @@ public partial class ContentEditingServiceTests
[TestCase(true)]
[TestCase(false)]
public async Task Can_Create_At_Root(bool allowedAtRoot)
=> await Test_Can_Create_At_Root(allowedAtRoot, allowedAtRoot);
[Test]
[ConfigureBuilder(ActionName = nameof(ConfigureContentTypeFilterToAllowTextPageAtRoot))]
public async Task Can_Create_At_Root_With_Content_Type_Filter() =>
// Verifies that when allowed at root, the content can be created if not filtered out by a content type filter.
await Test_Can_Create_At_Root(true, true);
[Test]
[ConfigureBuilder(ActionName = nameof(ConfigureContentTypeFilterToDisallowTextPageAtRoot))]
public async Task Cannot_Create_At_Root_With_Content_Type_Filter() =>
// Verifies that when allowed at root, the content cannot be created if filtered out by a content type filter.
await Test_Can_Create_At_Root(true, false);
public static void ConfigureContentTypeFilterToAllowTextPageAtRoot(IUmbracoBuilder builder)
=> builder.ContentTypeFilters()
.Append<ContentTypeFilterForAllowedTextPageAtRoot>();
public static void ConfigureContentTypeFilterToDisallowTextPageAtRoot(IUmbracoBuilder builder)
=> builder.ContentTypeFilters()
.Append<ContentTypeFilterForDisallowedTextPageAtRoot>();
private class ContentTypeFilterForAllowedTextPageAtRoot : ContentTypeFilterForTextPageAtRoot
{
public ContentTypeFilterForAllowedTextPageAtRoot()
: base(true)
{
}
}
private class ContentTypeFilterForDisallowedTextPageAtRoot : ContentTypeFilterForTextPageAtRoot
{
public ContentTypeFilterForDisallowedTextPageAtRoot()
: base(false)
{
}
}
private abstract class ContentTypeFilterForTextPageAtRoot : IContentTypeFilter
{
private readonly bool _allowed;
protected ContentTypeFilterForTextPageAtRoot(bool allowed) => _allowed = allowed;
public Task<IEnumerable<TItem>> FilterAllowedAtRootAsync<TItem>(IEnumerable<TItem> contentTypes)
where TItem : IContentTypeComposition
=> Task.FromResult(contentTypes.Where(x => (_allowed && x.Alias == "textPage") || (!_allowed && x.Alias != "textPage")));
}
private async Task Test_Can_Create_At_Root(bool allowedAtRoot, bool expectSuccess)
{
var template = TemplateBuilder.CreateTextPageTemplate();
await TemplateService.CreateAsync(template, Constants.Security.SuperUserKey);
@@ -41,7 +95,7 @@ public partial class ContentEditingServiceTests
var result = await ContentEditingService.CreateAsync(createModel, Constants.Security.SuperUserKey);
if (allowedAtRoot)
if (expectSuccess)
{
Assert.IsTrue(result.Success);
Assert.AreEqual(ContentEditingOperationStatus.Success, result.Status);
@@ -72,6 +126,57 @@ public partial class ContentEditingServiceTests
[TestCase(true)]
[TestCase(false)]
public async Task Can_Create_As_Child(bool allowedAsChild)
=> await Test_Can_Create_As_Child(allowedAsChild, allowedAsChild);
[Test]
[ConfigureBuilder(ActionName = nameof(ConfigureContentTypeFilterToAllowTextPageAsChild))]
public async Task Can_Create_As_Child_With_Content_Type_Filter() =>
// Verifies that when allowed as a child, the content can be created if not filtered out by a content type filter.
await Test_Can_Create_As_Child(true, true);
[Test]
[ConfigureBuilder(ActionName = nameof(ConfigureContentTypeFilterToDisallowTextPageAsChild))]
public async Task Cannot_Create_As_Child_With_Content_Type_Filter() =>
// Verifies that when allowed as a child, the content cannot be created if filtered out by a content type filter.
await Test_Can_Create_As_Child(true, false);
public static void ConfigureContentTypeFilterToAllowTextPageAsChild(IUmbracoBuilder builder)
=> builder.ContentTypeFilters()
.Append<ContentTypeFilterForAllowedTextPageAsChild>();
public static void ConfigureContentTypeFilterToDisallowTextPageAsChild(IUmbracoBuilder builder)
=> builder.ContentTypeFilters()
.Append<ContentTypeFilterForDisallowedTextPageAsChild>();
private class ContentTypeFilterForAllowedTextPageAsChild : ContentTypeFilterForTextPageAsChild
{
public ContentTypeFilterForAllowedTextPageAsChild()
: base(true)
{
}
}
private class ContentTypeFilterForDisallowedTextPageAsChild : ContentTypeFilterForTextPageAsChild
{
public ContentTypeFilterForDisallowedTextPageAsChild()
: base(false)
{
}
}
private abstract class ContentTypeFilterForTextPageAsChild : IContentTypeFilter
{
private readonly bool _allowed;
protected ContentTypeFilterForTextPageAsChild(bool allowed) => _allowed = allowed;
public Task<IEnumerable<ContentTypeSort>> FilterAllowedChildrenAsync(IEnumerable<ContentTypeSort> contentTypes, Guid parentContentTypeKey, Guid? parentContentKey)
=> Task.FromResult(contentTypes.Where(x => (_allowed && x.Alias == "textPage") || (!_allowed && x.Alias != "textPage")));
}
private async Task Test_Can_Create_As_Child(bool allowedAsChild, bool expectSuccess)
{
var template = TemplateBuilder.CreateTextPageTemplate();
await TemplateService.CreateAsync(template, Constants.Security.SuperUserKey);
@@ -121,7 +226,7 @@ public partial class ContentEditingServiceTests
var result = await ContentEditingService.CreateAsync(createModel, Constants.Security.SuperUserKey);
if (allowedAsChild)
if (expectSuccess)
{
Assert.IsTrue(result.Success);
Assert.AreEqual(ContentEditingOperationStatus.Success, result.Status);