Mark Hall
May 16, 2018
  6628
(6 votes)

Hide Sites in Page Tree for editors without Create or Edit Access rights

Recently I was asked by a customer how they could hide sites in the page tree for users without edit access.  I thought this was easily done using access rights, but I was wroing as the content still shows but cannot be edited since everyone has read access.  After some googling I found this article which was for CMS 6R2.  I tried removing the everyone role read access, but that led to the none of the sites showing in the page tree. 

Next I tried the next forum post which led to this article.  The code below is a continuation of the code found on the blog post by Thomas Krantz.  Please note that this code only cares about pages so editors will still have access to commerce content, blocks, media, etc.  It is also required either Edit or Create access to show the tree.

using EPiServer.Cms.Shell.UI.Rest.ContentQuery;
using EPiServer.Core;
using EPiServer.Data.Dynamic;
using EPiServer.Filters;
using EPiServer.Security;
using EPiServer.ServiceLocation;
using EPiServer.Shell.ContentQuery;
using System.Collections.Generic;
using System.Linq;

namespace EPiServer.Reference.Commerce.Site.Infrastructure
{
    [ServiceConfiguration(typeof(IContentQuery))]
    public class MyGetChildrenQuery : GetChildrenQuery
    {
        private readonly IContentRepository _contentRepository;
        private readonly IContentQueryHelper _queryHelper;
        private readonly LanguageSelectorFactory _languageSelectorFactory;

        public MyGetChildrenQuery(IContentQueryHelper queryHelper, IContentRepository contentRepository, LanguageSelectorFactory languageSelectorFactory) 
            : base(queryHelper, contentRepository, languageSelectorFactory)
        {
            _contentRepository = contentRepository;
            _queryHelper = queryHelper;
            _languageSelectorFactory = languageSelectorFactory;
        }

        public override int Rank => 100; 

        protected override IEnumerable<IContent> GetContent(ContentQueryParameters parameters)
        {
            if (((parameters.References == null) || !parameters.References.Any()) && ContentReference.IsNullOrEmpty(parameters.ReferenceId))
            {
                return Enumerable.Empty<IContent>();
            }

            var selector = _languageSelectorFactory.AutoDetect(true);
            IContent content = null;

            if (!ContentReference.IsNullOrEmpty(parameters.ReferenceId))
            {
                content = _contentRepository.Get<IContent>(parameters.ReferenceId) as PageData;
                if (content == null)
                {
                    return base.GetContent(parameters);
                }
                if (content.ContentLink.ID == 1 || (FilterAccess.QueryDistinctAccessEdit(content, AccessLevel.Edit) || FilterAccess.QueryDistinctAccessEdit(content, AccessLevel.Create)))
                {
                    return GetChildren(parameters, parameters.ReferenceId, selector);
                }
                return Enumerable.Empty<IContent>();
            }

            var filteredContent = new List<IContent>();
            foreach(var link in parameters.References.ToList())
            {
                content = _contentRepository.Get<IContent>(link) as PageData;
                if (content == null)
                {
                    filteredContent.AddRange(base.GetContent(parameters));
                    continue;
                }
                if (!FilterAccess.QueryDistinctAccessEdit(content, AccessLevel.Edit) || !FilterAccess.QueryDistinctAccessEdit(content, AccessLevel.Create))
                {
                    continue;
                }
                filteredContent.AddRange(GetChildren(parameters, parameters.ReferenceId, selector));
            }
            return filteredContent;
        }

        private IEnumerable<IContent> GetChildren(ContentQueryParameters parameters, ContentReference parentId, LanguageSelector selector)
        {
            IEnumerable<IContent> children = null;

            if (parameters.TypeIdentifiers != null && parameters.TypeIdentifiers.Any())
            {
                // Get by type and concatenate into one list
                children =
                    parameters.TypeIdentifiers.Select(i => TypeResolver.GetType(i, false))
                        .Where(type => type != null)
                        .SelectMany(type => GetChildrenByType(type, parentId, selector))
                        .Distinct()
                        .Where(x => FilterAccess.QueryDistinctAccessEdit(x, AccessLevel.Edit) || FilterAccess.QueryDistinctAccessEdit(x, AccessLevel.Create));
            }
            else
            {
                children = _contentRepository.GetChildren<IContent>(parentId, selector)
                    .Where(x => FilterAccess.QueryDistinctAccessEdit(x, AccessLevel.Edit) || FilterAccess.QueryDistinctAccessEdit(x, AccessLevel.Create)); ;
            }

            return (children ?? Enumerable.Empty<IContent>());
        }
    }
}

Here is how the access rights or setup for the site in questions.

Image AccessRights.png

Here is how the user permissions has been setup.

Image UserPermissions.png

Here is the site with administrator access.

Image Adminsitator.png

Here is the site with just site1 access.

Image Site1.png

May 16, 2018

Comments

Niklas Lord
Niklas Lord May 30, 2018 10:09 AM

Nice addition to the older solution. Great job!

Though it still have a problem I've been trying to solve before. What happens if you have no creat/edit access on a page. But you have that access on a child or a child deeper down in the tree.

Example:

Start = RCU

--Page A = R

----Page B = RCU

In that case A and B should be visible and A should be read only. With this solution A and B is hidden :-S

I guess you could query access down the tree but how will that affect the performance?

Andrea Sedlacek
Andrea Sedlacek Jun 6, 2018 11:40 PM

This is great!

Please login to comment.
Latest blogs
Running 64 Sites on Headless Optimizely CMS with GraphQL

64 websites. Live. Running on headless Optimizely with GraphQL. We just wrapped a major rollout for our Rockwool Digital Experience Platform  and t...

Piotr | Mar 14, 2026

Disabling the scheduler also stops event-based indexing in Optimizely

If you disable the scheduler in Optimizely CMS, should event-based indexing stop working too? It does!

Tomas Hensrud Gulla | Mar 13, 2026 |

Meet the latest OMVPs - H1 2026 Cohort

Meet the Latest Cohort of Optimizely Most Valuable Professionals (OMVPs) Every year, the Optimizely Most Valuable Professional (OMVP) program...

Satata Satez | Mar 13, 2026

64 Websites Live on Headless Optimizely with GraphQL

64 websites running on headless Optimizely with GraphQL. A major rollout for Digital Experience Platform covering Algolia integration, Next.js...

Piotr | Mar 12, 2026 |