Umbraco 8 Content Apps

Heads Up!

This article is several years old now, and much has happened since then, so please keep that in mind while reading it.

What are content apps?

According to Umbraco HQ, content apps are "...companions to the editing experience when working with content or media in the Umbraco backoffice.".

There, does that answer the question? What do you mean “no”? Alright, let me explain a little further… content apps are a feature introduced in Umbraco 8, they're similar to custom dashboards but with the main difference being that they are added onto content nodes, media nodes, member nodes and doctypes; and are intended to provide contextual information to your content editors about those nodes. They appear as tabs at the top of the node in the backoffice, right next to the Content and Info tabs! In fact, the info tab is a great example of a Content App, as that’s exactly what it is, just a built-in one!

 

What could I do with them?

Anything! It’s AngularJS, but with the added benefit of being able to access all of the properties of the content node it’s currently living on; thus making them perfect for displaying contextual information about the current node to your content editors. What kind of contextual information, do I hear you ask? Well, you could display information like word counts, reading ease scores, you could also list the locations that reusable element types are used (useful for reusable Call To Action libraries). For member nodes, you could have a usage analytics style Content App that tracks log in/outs and displays what pages members use the most. But wait, what if you don’t want your content editors to view that kind of information? Thankfully you do have the ability to restrict that content app to only show for users in the Admin user group!

 

Community Content App Spotlight

There are already some really great content apps available to download right now, three of my current favourites being:

Screenshot of the Skybrud Analytics content app

Screenshot of the Preflight content app

  • Image Filter (Paul Seal & Nik Rimington)
    • Allows content editors to apply various filters, flip and rotate images directly from Media nodes!

Screenshot of the Image Filter content app

My experience creating my first content app

The first Content App I created was a readability checker for one of my company’s clients. The requirement was that their content editors needed to be able to view a readability score (using the Flesch reading ease test), a word count and an estimated reading time for each piece of textual content.

Overall I found the process quite straight forward, this is mostly thanks to the excellent introductory guide, that Umbraco HQ created; which takes you through step by step on how to create a basic content app https://our.umbraco.com/documentation/extending/Content-Apps/ (I highly recommend looking at this, it takes 20-30 minutes, so maybe something to do during your lunch break?) 

The next step I took was looking into how other content apps worked, what methods/techniques other Umbraco community members employed when creating theirs and this is what I came up with:

Screenshot of the Readability Content App

The content app lists all of the RichText, TextArea and TextString properties that the content node has and displays their word count, reading time estimate and Flesch reading ease score. Originally it only listed RTEs and TextAreas as getting a word count and reading time on TextStrings seemed a bit pointless, but the client wanted them too, so... ¯\_(ツ)_/¯

I also included an extract from Wikipedia describing exactly what the Flesch reading ease score is and what the numbers mean, this should help their content editors understand the numbers.

Screenshot of the readability content app showing the wikipeda extract describing the Flesch reading ease score

So, how does it work? Let’s have a look at the angular controller:

Download the full controller js here: ReadabilityCalculator.controller.js

On line 18 I’m injecting a few different services into the angular controller:

(function () {
    function mainController($scope, editorState, notificationsService, fleschReadingEaseScore, umbPropertyTypeAlias) {
        var vm = this;

        //...code stuff...
    }
})();

The various services being injected into the Content App's controller

  • $scope 
    • If you’re at all familiar with AngularJS you would have seen this before.
  • editorState 
    • This is the “magical” Umbraco service that gives us access to the current content node, its variants and all of its properties.
  • notificationsService 
    • The Umbraco notification service allows us to trigger notification pop-ups to inform the content editor when things have gone good, or bad. The same green bar that pops up when you save a content node!
  • fleschReadingEaseScore 
    • This is a custom angular service that works out a reading ease score using the Flesch Reading Ease equation.
  • umbPropertyTypeAlias 
    • This is a custom Angular constant that simply lists property aliases.

Honourable mention - we could also inject the userService, which would allow us to get information on the current user, we could then display a nice friendly personal greeting e.g. “Good afternoon username!”.

I then use the editorState service (line 50) to get the current variant; what this will do is it will load the properties for the variant of the node that the content editor was last looking at.

var currentVariant = editorState.current.variants.find(x => x.active === true);
var tabs = currentVariant.tabs;

for (var tabIndex = 0; tabIndex < tabs.length; tabIndex++) {
    var properties = tabs[tabIndex].properties;

    for (var index = 0; index < properties.length; index++) {
        let currentProperty = properties[index];
        var propertyTypeAlias = currentProperty.view;

        if (isValidProperty(propertyTypeAlias)) {
            //Generate a report
            var propertyReport = generateReadabilityReport(currentProperty);
            var readingAge = propertyReport.readingAge;

            //Add to reports list
            vm.readabilityReports.push(propertyReport);

            if (readingAge < 60 && readingAge > 50) {
                //warning
                vm.warningCount++;
            } else if (readingAge < 50) {
                //fail
                vm.failCount++;
            }
        }
    }
}

Getting the current variant of the node we're on

At this point, where you could determine what language variant your editor is looking at, you could change the behaviour of the content app accordingly. To determine the variant language, you would access it like this:

vm.currentContentCulture = currentVariant.language.culture; //e.g. ‘cy-GB

vm.currentContentCultureLong = currentVariant.language.name; //e.g. ‘Welsh (United Kingdom)

Note: I removed this functionality from my demo code to keep it simple.

You can then loop through the Variant’s Tabs and subsequently loop through the individual Properties on each Tab; here you would then do your fancy logic on the properties. In the case of the Readability app, I first check if it’s the correct property type and if it is, I work out its reading ease score, word count, reading time and then add it to the list of reports.

But wait, what’s a Tab I hear you say? Well, it’s a different name for a property group, I’m not sure why they’re called tabs here, although I suspect it’s a leftover from Umbraco 7 (when we still had tabs).

/* Show an orange warning notification */
function showWarningNotification() {
    var message = "1 property has a readability check warning";

    if (vm.warningCount > 1) {
        message = vm.warningCount + " properties have readability check warnings";
    }

    notificationsService.warning("Readability", message);
}

/* Show a red fail notification  */
function showFailNotification() {
    var message = "1 property has failed the readability check";

    if (vm.failCount > 1) {
        message = vm.failCount + " properties have failed the readability checks";
    }

    if (vm.warningCount === 1) {
        message += " and 1 property has a warning";
    }
    else if (vm.warningCount > 1) {
        message += " and " + vm.warningCount + " properties have warnings";
    }

    notificationsService.error("Readability", message);
}

How to show different coloured notification popups to the content editor

After checking all of the properties and building our list of scores, we can then use the notificationsService to send a notification to the user to notify them of any readability score failures; we could also add a badge to the function app icon using this:

/* Display an Umbraco notification badge on the Content App icon */
function updateBadge() {
    if (vm.failCount > 0) {
        //display fail badge
        $scope.model.badge = {
            count: vm.totalFailsCount(),
            type: "alert"
        };
    }
    else if (vm.warningCount > 0) {
        //display warning badge
        $scope.model.badge = {
            count: vm.totalFailsCount(),
            type: "warning"
        };
    } else {
        //display success badge
        $scope.model.badge = {
            type: "success icon-"
        };
    }
    return;
}

How to add a Badge notification to the Content App icon

The count property adds a number into the middle of the badge, and the type property simply adds a CSS class to it, by default alert gives you a red badge, warning a yellow badge, success a green badge and info a blue badge. If you want to do some custom styling e.g. show a tick instead of a number, then you can apply a custom class by adding it to the type property, like this: 

Screenshot of a green success badge on the content appScreenshot of a yellow warning badge on the content appScreenshot of a red alert badge on the content app

$scope.model.badge = { type: "success my-class" };

A badge adds a nice visual cue for the Content Editors, so that they can easily, at a glance, see that something in the content app requires their attention.

And finally, we come to the view; and, you guessed it, it’s a regular old AngularJs view, nothing fancy here!

Download the full view here: ReadabilityCalculator.html

I’ve made use of the nice built-in Umbraco Directives to build up the main structure of my view, this means I have less styling to do and also keeps it in line with the look of the rest of the backoffice. Double win!

I also created a post save event interceptor, that would call the calculateReadability() function of the readability app controller, this means Content Editors then get instant feedback when they save their content nodes!

//Intercept save content api call
(function () {
    var postSaveUrl = '/umbracoapi/content/postsave';

    angular.module('umbraco.services').config([
        '$httpProvider',
        function ($httpProvider) {

            $httpProvider.interceptors.push(function ($q) {
                return {
                    'response': function (response) {
                        if (response.config.url.toLowerCase().indexOf(postSaveUrl) !== -1) {
                            try {
                                if (document.getElementById('readability-app') !== null) {
                                    angular.element(document.getElementById('readability-app')).scope().calculateReadability();
                                }
                            }
                            catch (error) {
                                console.log("Readability save intercept error: " + error);
                            }
                        }
                        return response;
                    }
                };
            });

        }]);
})();

This code intercepts the "Post Save" event and triggers our content app

Can I do more than just show information?

The content apps that we’ve talked about currently all just display information, they don’t actually do anything too fancy (apart from Image Filter), so is it possible to perform more complex functionality with content apps? Yes, yes you can. 

As I said earlier, it’s AngluarJS; you can pretty much do whatever you want; want to make a backoffice API that you interact with via a content app? Go for it! In fact, that’s exactly what I did for another one of my Company’s clients.

One of the requirements for our client, the Welsh Parliament, was the ability to manage a list of “People” nodes that could contain public profiles on people in the Welsh Parliament and Members of the Senedd. Some of the people nodes would be created in the Umbraco backoffice and managed there, and the MS profiles would need to be imported and kept up to date from their central servers (with additional data added in via Umbraco).

So, what did I come up with? Well, first I started with a classic List node (People List, inventive name eh?) that could hold People nodes underneath. I then created an Umbraco Authorized API that would automatically import and update MS profile nodes and select People nodes, the importer actually gets run on a schedule (Umbraco scheduled task), but they also needed a way to manually trigger an import and check the status of the automated import.

My first thought was maybe a custom dashboard; that probably would have worked if there was only one People List node, but as this is a multi-site installation there can be multiple People List nodes that are managed by different content editors. Therefore I decided on a Content App that would live on the People List node itself, this made it really easy for the content editors to find and manage.

Here's a video of it in action:

There is some debate on whether or not Content Apps should be used for manipulating data or only used for displaying data; personally, I can see pros and cons for both sides and I don’t think there’s really that many use cases for a Content App that actually manipulates data. But, a Content App that manages the content on List nodes (like my Senedd importer) is a really good use case, and can greatly increase the editing experience for your content editors.

 

Why are they called "Content" Apps if they can also go on DocTypes, Media and Member nodes?

When they were first introduced in Umbraco 8, they were originally only available on Content nodes, but since then, Content apps have expanded to also work on Media nodes, Member nodes and DocTypes.

There’s currently an argument discussion going on (on GitHub) on whether or not Content Apps should be renamed. Personally, I think “Context Apps” would be a good fit as it describes their purpose perfectly, but if you have a better suggestion, then chip in here: https://github.com/umbraco/Umbraco-CMS/issues/8863

So what do you think of Content Apps? Have you made any custom Content Apps before? used any community-created ones? Or do you have any ideas for Content Apps you would love to create? Let us know on Twitter!

Owain Jones

Owain is on Twitter as