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

Added option for filtering the content types allowed as children based on the parent node #18380

Conversation

AndyButland
Copy link
Contributor

@AndyButland AndyButland commented Feb 19, 2025

Prerequisites

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

Resolves issue raised in discussion: #16329 (reply in thread)

Description

We discussed and introduced a server-side replacement for "sending allowed children notification" in 15.2, via this PR, but it had a bit of a design flaw for use for a typical use case where you want to allow only a certain number of items of content of a given type to be created.

We made available the parent document type to the filter, but not the parent content item. So when filtering allowed types under a given item, you didn't have the details of that item available.

I've resolved with this PR but it needed a bit more update than the previous, as we need to pass the detail of which item we are creating under up to the method that retrieves the allowed children, and where the filtering occurs.

To Test:

I've used and registered the following example to verify and test this, that can be adapted for other use cases:

using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Filters;

namespace Umbraco.Cms.Web.UI.Composers;

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

internal class MyContentTypeFilterService : IContentTypeFilter
{
    private readonly IContentService _contentService;
    private readonly IContentTypeService _contentTypeService;
    private readonly IIdKeyMap _idKeyMap;

    public MyContentTypeFilterService(IContentService contentService, IContentTypeService contentTypeService, IIdKeyMap idKeyMap)
    {
        _contentService = contentService;
        _contentTypeService = contentTypeService;
        _idKeyMap = idKeyMap;
    }

    public Task<IEnumerable<TItem>> FilterAllowedAtRootAsync<TItem>(IEnumerable<TItem> contentTypes)
        where TItem : IContentTypeComposition
    {
        // Only allow one home page at the root.
        var docTypeAliasesToExclude = new List<string>();

        const string HomePageDocTypeAlias = "homePage";
        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 parentContentTypeKey, Guid? parentContentKey)
    {
        // Only allow one landing page when under a home page.
        const string HomePageDocTypeAlias = "homePage";
        const string LandingPageDocTypeAlias = "landingPage";

        if (parentContentKey.HasValue is false)
        {
            return Task.FromResult(contentTypes);
        }

        IContentType? docType = _contentTypeService.Get(parentContentTypeKey);
        if (docType is null || docType.Alias != HomePageDocTypeAlias)
        {
            return Task.FromResult(contentTypes);
        }

        var docTypeAliasesToExclude = new List<string>();

        Attempt<int> parentContentIdAttempt = _idKeyMap.GetIdForKey(parentContentKey.Value, UmbracoObjectTypes.Document);
        if (parentContentIdAttempt.Success is false)
        {
            return Task.FromResult(contentTypes);
        }

        var docTypeAliasesAtFolder = _contentService.GetPagedChildren(parentContentIdAttempt.Result, 0, int.MaxValue, out _)
            .Select(x => x.ContentType.Alias)
            .Distinct()
            .ToList();
        if (docTypeAliasesAtFolder.Contains(LandingPageDocTypeAlias))
        {
            docTypeAliasesToExclude.Add(LandingPageDocTypeAlias);
        }

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

@AndyButland AndyButland requested a review from Migaroez February 19, 2025 09:27
Copy link
Contributor

@Migaroez Migaroez left a comment

Choose a reason for hiding this comment

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

Minor comments about obsoletion messages.
Its best to put the version at the end of the message and used the wording Scheduled for removal or Scheduled to be removed as we have had complaints about things that were obsoleted with stricter removal indications not actually being removed.

I will test the implementation soon™

@Migaroez Migaroez merged commit 2826678 into v15/dev Mar 4, 2025
22 of 23 checks passed
@Migaroez Migaroez deleted the v15/bugfix/support-access-to-parent-content-item-when-filtering-allowed-children branch March 4, 2025 08:34
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