Mark Hall
May 16, 2018
  3777
(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
Implementing EmbeddedLocalization in Optimizely CMS 12

My previous post on translation (Translating Optimizely CMS 12 UI components) gives an overview of how to implement the FileXmlLocalizationProvider...

Eric Herlitz | Jan 27, 2023 | Syndicated blog

Breaking changes in EPiServer.CMS.TinyMce 4.0.0

After upgrading to the latest version of EPiServer.CMS.TinyMce, the dropdown with formats disappears. Learn how to get it back!

Tomas Hensrud Gulla | Jan 27, 2023 | Syndicated blog

Translating Optimizely CMS 12 UI components

Optimizely CMS 12 have been out for a while now, but still some elements haven't been properly translated resulting in a GUI defaulting to english....

Eric Herlitz | Jan 26, 2023 | Syndicated blog

Image preview in Optimizely CMS12 all properties view

With these simple steps, you can now see an Image and its Metadata, including size and dimensions, when editing an Image property in Optimizely...

Tomas Hensrud Gulla | Jan 26, 2023 | Syndicated blog