Content Apps: Keeping Content Company Since February 2019

In case you missed it, Umbraco 8 dropped almost by surprise in the last week of February.

Along with a massive refactoring of lots of code of which I have absolutely no understanding, v8 introduces something I do understand - Content Apps.

Since you're reading Skrift, I'm hoping you're up to speed with the latest and greatest from Umbraco HQ, and content apps (or the term, at least), aren't an entirely new concept. If you're reading this and thinking 'Content Whats?', duck over to the Umbraco documentation and read up on the shiny new feature.

Content apps are beautifully described in that documentation as 'companion read-only information relating to the current content item in the Umbraco backoffice'.

Note the emphasis on companion and read-only. Content apps are not an interface for updating a node, but for supplying additional information for making our editors' jobs more pleasant.

Version 7, in a roundabout, technically correct-ish kind of way already ships with a content app - the info tab.

The info tab is not part of the content model, it's loaded into the backoffice separately from the content, doesn't modify the content, but does provide a heap of useful data points related to the content.

In v8, the info tab becomes a true content app.

Why

Couldn't you just do this with a regular, familiar, v7-style package?

Of course you could. You can also use a shoe to open a bottle of wine, but that doesn't mean your filthy old size-11 is the right tool for the job.

To achieve similar in v7, in the past I've done the following:

  • Create a package defining a property editor
  • Create an instance of that editor in the backoffice
  • Add it to a content type, on its own tab
  • Profit (ha!)

That works fine. It gets the view I need onto the content type, it displays in the backoffice and shows the data I need to show.

It works fine, but compared to creating a content app, it feels positively filthy.

Content apps provide clear contextual separation for the editor - editing their content actually happens in the Content content app. Other activities require a change of context (via the tabs) to flip into a new app and therefore a new activity.

How

If you've built an Umbraco package, you've all-but built a content app. If you haven't, and would like to start, the Umbraco documentation has got you, fam.

The fundamentals are identical - define some bits in a package.manifest file (or in C# code), create those bits, package 'em up and install. Obviously that's simplify the gory bits, but the point stands that the processes for building packages or content apps are super similar.

Content apps though provide some nifty out-of-the-box features not available to standard packages, most of which are defined via the manifest.

Defining a new app

You've been struck by inspo-lightning, and have a cracking idea for a new app. You've got an Umbraco dev environment up and running, and have created an app_plugins folder for your app.

In that folder, you've created package.manifest. All you need to spin up an empty app is the following:

{
    "contentApps": [
        {
            "name": "My Sweet App",
            "alias": "mySweetApp",
            "weight": 0,
            "icon": "icon-smiley",
            "view": "~/App_Plugins/MySweetApp/view.html"
        }
    ]
}

Easy as that. It's not going to do anything amazing just yet, but when you fire up Umbraco, your new app will appear in the header/nav/toolbar for all content and media items.

Based on the values in the manifest, the app link will be labelled 'My Sweet App', with a happy smiley icon. Clicking it will load the view from the MySweetApp folder in /App_Plugins.

Done. Content app created. Whatever you want to have happen in the view is entirely up to you - it is going to need at least a sprinkle of AngularJs, so you'll need an entry point in your manifest:

{
    "contentApps": [
    // content app definition removed for brevity
    ],
    "javascript": [
        "~/App_Plugins/MySweetApp/app.controller.js"
    ]
}

Within that controller we are essentially operating in familiar Umbraco package development space. All the usual backoffice APIs are available, all the usual directives and helpful bits and pieces too.

Fundamentally, the execution is the same. An app is a package. A package can become an app. It's the context that makes the big difference and makes apps really useful.

Worth noting too is that the contentApps object in the manifest accepts an array of app definitions - one manifest can therefore define multiple apps.

Why is that noteworthy? Because we can set conditions for displaying apps by section, content type or user roles.

That means that MySweetApp can show one thing for administrators, something different in the media section, and only on particular content types, but still leverage the same underlying code.

For example, let's imagine MySweetApp was actually MySweetAnalyticsApp.

With appropriate configuration, we could do the following:

  • show admin users a full set of data
  • show editors page views, time on page, and a device breakdown
  • hide it completely in the media section, except for file content items
  • show on all content types, other than our two config content types

We could achieve that two ways - with a single app definition and some extra logic in our view and controller, or with two app definitions, and a cleaner view and controller.

First, the single definition:

{
   "contentApps": [
       {
           "name": "My Sweet App",
           "alias": "mySweetApp",
           "weight": 0,
           "icon": "icon-smiley",
           "view": "~/App_Plugins/MySweetApp/view.html",
           "show": [ 
               "-content/siteconfig", // hide for content type 'siteconfig'
               "-content/navconfig", // hide for content type 'navconfig
               "+content/*", // show for all other content
               "-media/*" // hide on all media
           ]
       }
   ]
}

We'd still need to add some logic in our controller to distinguish between admin and editor users, then show them the correct content. Much better if we could remove that requirement, and serve a completely different view per user.

How? By defining two different apps in our manifest, serving each to the correct user type:

{
    "contentApps": [
        {
            // removed for brevity
            "view": "~/App_Plugins/MySweetApp/admin-view.html",
            "show": [
                "+role/admin" // only show it to admin group
            ]
        },
        {
            // removed for brevity
            "view": "~/App_Plugins/MySweetApp/editor-view.html",
            "show": [
                "+role/editor" // only show it to editor group
            ]
        }
    ]
}

Now we have two separate instances of the same app, with a different entry point based on the user's group membership. Given that we hit a different view, we can load a different controller too, but still leverage the same code for requesting data.

Does one work better than the other? Not really, it's more a matter of personal preference - I know I'd rather the second approach, as it means when I get to actually building the app functionality, I don't need to check the current section or user/content type, as Umbraco has managed this for me.

Note: It's not possible to set both a role and section restriction on the same app - we can't (unfortunately) show an app to admin on media only, at least not through the manifest alone. For cases like this, we'd need to use a single definition and check the current user type/roles in our controller, then show the appropriate UI.

Other cool stuff

Badges

In the mountain of PRs recently created by all-round legend Kenn Jacobsen was an absolute gem allowing notification badges on content app icons.

It's as easy as defining $scope.model.badge in our controller, where badge has two optional properties - count and type:

$scope.model.badge = {
    count: 3,
    type: 'warning' // or 'alert' 
}

I've used this to (I feel) pretty good effect in my app Preflight, which runs a suite of content health checks. Since each test returns its results via SignalR, I can increment the counter as failed tests return.

It's a slick little UI feature that draws attention to the app, and hopefully encourages users to click across into the new context and check out their results.

Another note: somewhere between the v8 nightlies and v8 proper, the badges broke. They were fixed at the Umbraco Down Under Festival (go UDUF!), and will be workin again in 8.0.1.

Dashboards

Not really a content app feature, but certainly something you might be wanting to leverage as part of a new app (think dashboard for app config/settings over in the settings section).

As of v8, it is be possible (and wonderfully simple) to create dashboards and custom sections via manifest declarations. Dashboard.config is on the scrap heap, meaning no more XML editing/updating/merging.

To create a dashboard or section using the package manifest, simply include the appropriate directive:

{
    "dashboards": [
        {
            "name": "MySweetApp Settings",
            "alias": "mySweetAppSettings",
            "view": "~/app_plugins/mysweetapp/backoffice/dashboards/settings.html",
            "sections": [ 
                "settings" 
            ]
        }
  ],
  "sections": [
      {
          "alias": "customSection",
          "name":  "Custom section"
      }
  ]
}

One small potential gotcha with dashboards - you'll need to include a dashboardTabs area in your language files, with a translation for the dashboard name, else it will be rendered wrapped in square brackets (aka ugly).

When

When would you choose to build a content app over a 'normal' property editor? That should be reasonably obvious given the naming of the latter - property editors are for editing. If you need to make changes to the content, or provide editors a means to do the same, build a property editor.

If you want to deliver contextual information related to the current node, build a content app. The distinction is that simple.

Remember, content Apps are a v8 feature and simply won't work in v7.

I'm really looking forward to seeing the ideas the community comes up with in the next few months, once new content app ideas start brewing.

If you're new to package development (and we're all new to content app development), don't be put off by the challenge - grab it with both hands and squeeze! Guaranteed you'll learn a heap, and that's a reward in itself.

About the Author

Nathan has been working with Umbraco since v4, and started poking around v8 package and content app development before it was really sensible to be doing so. He works at the University of the Sunshine Coast in the glorious south-east corner of Queensland, Australia, where he lives with his wife, two kids and a dog who thinks she's a person. Find him on Twitter at @nathanwoulfe.

comments powered by Disqus