Configuring Umbraco 7 for MiniProfiler

This post is based on the following Umbraco setup.
  • Umbraco v7.2
  • Azure Sql Server – Web Edition

Why create this post.

During a previous v7.2 Umbraco project I tried several times and failed to configure Umbraco to use Mini Profiler. This seemed the simplest route to debugging some performance problems I was having with the site. However, there was no definitive guide out there on how to configure Mini Profiler that worked for me. So, to try and create that guide, and to save me searching across multiple blog posts and forums each time, I’ve created this,

What is Mini Profiler

Mini Profiler is a component from those clever bods over at Stack Exchange, and has been part of Umbraco since vx.xx When enabled, Mini Profiler provides diagnostics about your application in a

How to configure in Umbraco 7.

1. Add the following piece of Razor to the views you want to debug.
@MiniProfiler.RenderIncludes()
2. Add the following using statement to the top of all views
using StackExchange.Profilling

3. Ensure the debug attribute of the compilation element in the web.config in the root is set to true.

4. Open the page you want to debug in the a web browser and append ?umbDebug=true to the query string.

And that is it!

Filtering nodes in the Umbraco back office

This post is based on the following Umbraco setup.

  • Umbraco v7.2
  • Azure Sql Server – Web Edition

Question

Is there a way in the Umbraco back office to selectively filter tree nodes based on a property value on the user?

Answer

Sadly, no. There is currently nothing natively in the back office which will allow the conditional display of tree nodes. However using Umbraco’s event model and API, it is fairly simple to add this in.

Requirement

In this particular Umbraco installation, each back office user will be assigned to a country. In one area of the content tree, each node has a country property. The tree structure looked like this;

Answers
– German Answer 1
– French Answer 1
– USA Answer 1
– Australia answer 1
– French answer 1

If a French back office user logged in to the back office he should only see this;

Answers
– French answer 1
– French answer 2

The Solution

Of course I can take no real credit for this solution. Once again the excellent resource that is Our Umbraco provided the answers. I was aware that Umbraco used an event model, so the first piece of the puzzle was understanding which event to hook on to. The answer event I was looking for was;

TreeControllerBase.TreeNodesRendering

I created a TreeEvents class which inherited from ApplicationEventHandler. I then overrode the ApplicationStarted method to which I added the following event handler;

protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) 
{ 
 TreeControllerBase.TreeNodesRendering += FilterCountryNodes; 
}

The handler looked like this;

public static void FilterCountryNodes(TreeControllerBase sender, TreeNodesRenderingEventArgs eventArguments)
{
  if (sender.TreeAlias == Constants.UmbracoNative.CmsSections.ContentAlias)
  {
    var currentUserLevel = sender.Security.CurrentUser.UserType.Alias;
    if (currentUserLevel == Constants.ContentWorkflow.UserTypes.LowLevel)
    {
      var answerNodeIds = eventArguments.Nodes.Select(n => Int32.Parse(n.Id.ToString()));
      var umbracoHelper = new Umbraco.Web.UmbracoHelper(Umbraco.Web.UmbracoContext.Current);
      var allCountries = umbracoHelper.TypedContentAtXPath(Constants.XPaths.GlobeSearchCountries);

      var countriesUserCanEdit = TreeHelper.GetCountryIdsUserCanEdit(allCountries, sender.Security.CurrentUser.Id);

      var answers = sender.ApplicationContext.Services.ContentService.GetByIds(answerNodeIds);
      var nodesToRemove = TreeHelper.GetNodesToRemove(answers, countriesUserCanEdit);
      eventArguments.Nodes.RemoveAll(node => nodesToRemove.Contains(Int32.Parse(node.Id.ToString())));
    }
  }
}

To break this down a litle, the method firstly only acts if the type of Umbraco tree we are receiving the event from is the Content section. A tree in this case is considered to be any one of the Cms sections eg Content, Media, etc, including any custom sections you may have added. We are also only performing this filtering for a particular back office user type (LowLevel), so we inspect the user type alias property to make sure the logged in user is of the correct type.

The final piece of the puzzle was trying to associate a user to a country, and filtering the answers by the user’s countries. I started by trying to add countries to a back office user. However, unlike all other areas of Umbraco, the Users section and Users doesn’t appear to be extensible in any way, so another forum post in “Our” helped turn the problem on its head.

Adding users to a country

In Umbraco I have a Country document type defined. To this I added a new multi user picker property. This allowed me to associate multiple Umbraco back office users with a particular country.

In my event handler I check the logged in user has been added to the new property for each country.

var allCountries = umbracoHelper.TypedContentAtXPath(Constants.XPaths.SearchCountries);
var countriesUserCanEdit = TreeHelper.GetCountryIdsUserCanEdit(allCountries, sender.Security.CurrentUser.Id);

Once we have a list of the logged in user’s countries, we then retrieve the answers for all countries. We then find any answers which don’t contain at least one of the users countries – a user can be associated with multiple countries. Finally once we have a list of answers which the logged in user shouldn’t be able to view, we call the Umbraco API method RemoveAll()

var answers = sender.ApplicationContext.Services.ContentService.GetByIds(answerNodeIds); 
var nodesToRemove = TreeHelper.GetNodesToRemove(answers, countriesUserCanEdit); 
eventArguments.Nodes.RemoveAll(node => nodesToRemove.Contains(Int32.Parse(node.Id.ToString())));

And that’s it!

Drawbacks

My only reservation with this approach is that it may take some explaining to content editors. Restricting access to all other areas of Umbraco is controlled via the Members section. So this muddies the waters in terms of which areas of the back office perform which function. However I was unable to find another way. If anyone has any pointers to improve this, then please comment below.