It's no secret that I'm a fan of Content Apps. I've created a few of them since the documentation page was published on how to create them.

My favourite one that I've written so far is the one I co-wrote with Nik Rimington, called ImageFilter.

One thing I don't like is how the decision on who gets to see the Content App is made by the package owner, in that they can code it in to only allow for admin roles etc.

Also there is not much room for too many Content Apps so if we can manage who gets to see them ourselves then it will be a real help.

So I've made a way to allow you to be able to control which groups can see a specific Content App.

The idea is, you add this code to your site, or install the NuGet package when I publish it and then you configure the permissions in a single app setting value.

If we take my Image Filter content app as an example, I can add an app setting which says only show the content app titled 'Filter' to people who are in a group called 'Filter' or 'Administrators'.

<!-- Enter the Content app and allowed groups in this format: 
  ContentApp1Name[AllowedGroup1|AllowedGroup2],ContentApp2Name[AllowedGroup1] -->

<add key="ContentAppsRestrictedByGroup" value="Filter[Filter|Administrators]"/>

I can then create a Group called 'Filter' and add the relevant users to that group.

Now only users who are in the group 'Filter' or 'Administrators' can see the Content App.

Here is the code:

using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web.Http.Filters;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Models.ContentEditing;
using Umbraco.Core.Models.Membership;
using Umbraco.Web.Editors;
using Umbraco.Web.Models.ContentEditing;

namespace Our.Umbraco.ContentAppGroupPermissions.Core.Compose
{
    [RuntimeLevel(MinLevel = RuntimeLevel.Run)]
    public class ContentAppGroupPermissionsComposer : ComponentComposer<ContentAppGroupPermissionsComponent>
    {
    }

    public class ContentAppGroupPermissionsComponent : IComponent
    {
        public void Initialize()
        {
            EditorModelEventManager.SendingMediaModel += EditorModelEventManager_SendingMediaModel;
            EditorModelEventManager.SendingContentModel += EditorModelEventManager_SendingContentModel;
        }

        public void Terminate()
        {
        }

        private void EditorModelEventManager_SendingMediaModel(HttpActionExecutedContext sender, EditorModelEventArgs<MediaItemDisplay> e)
        {
            e.Model.ContentApps = GetAllowedContentApps(e.UmbracoContext.Security.CurrentUser, e.Model.ContentApps);
        }

        private void EditorModelEventManager_SendingContentModel(HttpActionExecutedContext sender, EditorModelEventArgs<ContentItemDisplay> e)
        {
            e.Model.ContentApps = GetAllowedContentApps(e.UmbracoContext.Security.CurrentUser, e.Model.ContentApps);
        }

        private IEnumerable<ContentApp> GetAllowedContentApps(IUser user, IEnumerable<ContentApp> contentApps)
        {
            // Read the app setting for restricting content apps
            var contentAppsRestrictedByGroups = ConfigurationManager.AppSettings["ContentAppsRestrictedByGroup"]?.Split(',');

            if (contentAppsRestrictedByGroups != null)
            {
                // Loop through the content app settings to find the content app name and allowed group list
                foreach (var contentAppSetting in contentAppsRestrictedByGroups.Where(x => !string.IsNullOrWhiteSpace(x)))
                {
                    var contentAppName = contentAppSetting.Split('[').FirstOrDefault();
                    var allowedGroupList = GetAllowedGroupList(contentAppSetting);
                    if (!string.IsNullOrWhiteSpace(contentAppName) && allowedGroupList != null)
                    {
                        // check if the user is in any of the allowed groups
                        // if they are not, then remove the content app from the list
                        contentApps = UserIsInAnyOfTheseGroups(user, allowedGroupList)
                            ? contentApps
                            : contentApps.Where(x => x.Name != contentAppName);
                    }
                }
            }

            return contentApps;
        }

        // Reads the app setting and returns a list of group names that are allowed to use this content app
        private static string[] GetAllowedGroupList(string contentAppSetting)
        {
            var groupNamesStart = contentAppSetting.IndexOf('[');
            var groupNamesEnd = contentAppSetting.IndexOf(']');
            if (groupNamesStart != -1 && groupNamesEnd != -1)
            {
                var allowedGroups = contentAppSetting.Substring(groupNamesStart + 1, (groupNamesEnd - groupNamesStart) - 1);
                var allowedGroupList = allowedGroups.Split('|');
                return allowedGroupList;
            }

            return null;
        }

        private static bool UserIsInAnyOfTheseGroups(IUser user, IEnumerable<string> groupNames)
        {
            return user?.Groups?.Any(x => groupNames.Contains(x.Name)) ?? false;
        }
    }
}

As you can see, the idea is that if the user is not a member of any of the groups in the app setting for the Content App then it gets removed from the list.

In theory, the Content tab and Info tab are both Content Apps, so you can use this to restrict them too

<!-- Enter the Content app and allowed groups in this format: 
  ContentApp1Name[AllowedGroup1|AllowedGroup2],ContentApp2Name[AllowedGroup1] -->
<add key="ContentAppsRestrictedByGroup" value="Filter[Filter|Administrators],Content[Administrators],Info[Sensitive data]"/>

Want to thank me?

If I've helped you out and you want to thank me, why not buy me a coffee?

Buy me a coffee

About the author

Paul Seal

Umbraco MVP and .NET Web Developer from Derby (UK) who specialises in building Content Management System (CMS) websites using MVC with Umbraco as a framework. Paul is passionate about web development and programming as a whole. Apart from when he's with his wife and son, if he's not writing code, he's thinking about it or listening to a podcast about it.

Related Posts

How to solve the error assets file project.assets.json not found in Visual Studio

This post tells you how to solve the error assets file project.assets.json not found in Visual Studi…

Read Post

Code to help you debug an umbraco issue on a remote site

This post gives you some razor code to help you see the values of the IPublishedContent item's prope…

Read Post

How to fix the 403 error on preview in Umbraco after upgrading

This post shows you how to fix the 403 error on preview after you have upgraded it.

Read Post

How to create a dynamic image from multiple images using ImageProcessor

This post shows you how I created a handler to create dynamic header images for my umbraco website u…

Read Post