Skip to content
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

Allow for filtering of document type allowed children and allowed at root when creating new content. #18029

Merged
merged 2 commits into from
Jan 20, 2025

Conversation

AndyButland
Copy link
Contributor

@AndyButland AndyButland commented Jan 20, 2025

Prerequisites

  • I have added steps to test this contribution in the description below

This addresses part of the discussion in #16329 relating to the removal of SendingXNotifications in 14+, to provide an alternative for the SendingAllowedChildrenNotification.

Description

This PR implements an alternative to the SendingAllowedChildrenNotification that isn't available in Umbraco 14+ but is documented as a feature.

A filter is defined by the interface IContentTypeFilter. Methods are asynchronous. My guess is that most implementations won't need to use asynchronous methods, but given they might, it seemed better to define them this way.

Originally I registered a single instance of this with a "no-op" implementation, but then realised it would be better as a collection. That way there could be more than one filter (perhaps one added by a package, and another explicitly by the implementor).

The registered filters are called in the ContentTypeServiceBase methods used for retrieving the "allowed children" and the "allowed at root" content types. So we get whatever we get from the Umbraco schema, and then run the results through the appropriate method on the filters, before returning the data to the front-end for rendering.

An implementor could register their own filter that implements the features that this notification was used for - e.g. restricting the ability to create a node of a particular type if it already exists.

To Test:

  • First, just make sure you can create content, media and members at the root and as child items as expected.
  • Register filters and verify that the expected items are removed. You can use a composer like the below.

This sample shows a couple of filters:

  • An illustrative one that allows only those with an alias beginning with "t".
  • A more real-world one that ensures you can only create the home page once at the root level.
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Filters;

public class TestingComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
    {
        builder.ContentTypeFilters()
            .Append<MyContentTypeFilterService>()
            .Append<MyContentTypeFilterService2>();
    }
}

internal class MyContentTypeFilterService : IContentTypeFilter
{
    public Task<IEnumerable<TItem>> FilterAllowedAtRootAsync<TItem>(IEnumerable<TItem> contentTypes)
        where TItem : IContentTypeComposition
        => Task.FromResult(contentTypes);

    public Task<IEnumerable<ContentTypeSort>> FilterAllowedChildrenAsync(IEnumerable<ContentTypeSort> contentTypes, Guid parentKey)
        => Task.FromResult(contentTypes
            .Where(x => x.Alias.StartsWith("t")));
}

internal class MyContentTypeFilterService2 : IContentTypeFilter
{
    private readonly IContentService _contentService;

    public MyContentTypeFilterService2(IContentService contentService) => _contentService = contentService;

    public Task<IEnumerable<TItem>> FilterAllowedAtRootAsync<TItem>(IEnumerable<TItem> contentTypes)
        where TItem : IContentTypeComposition
    {
        var docTypeAliasesToExclude = new List<string>();

        const string HomePageDocTypeAlias = "homePage1";
        var docTypeAliasesAtRoot = _contentService.GetRootContent().Select(x => x.ContentType.Alias).Distinct().ToList();
        if (docTypeAliasesAtRoot.Contains(HomePageDocTypeAlias))
        {
            docTypeAliasesToExclude.Add(HomePageDocTypeAlias);
        }

        return Task.FromResult(contentTypes
            .Where(x => docTypeAliasesToExclude.Contains(x.Alias) is false));
    }

    public Task<IEnumerable<ContentTypeSort>> FilterAllowedChildrenAsync(IEnumerable<ContentTypeSort> contentTypes, Guid parentKey)
        => Task.FromResult(contentTypes);
}

Copy link
Member

@Zeegaan Zeegaan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, tests good 🚀

@Zeegaan Zeegaan merged commit 171ada2 into v15/dev Jan 20, 2025
27 of 28 checks passed
@Zeegaan Zeegaan deleted the v15/feature/filter-allowed-children branch January 20, 2025 11:26
@AndyButland
Copy link
Contributor Author

Docs PR is here: umbraco/UmbracoDocs#6808

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants