Quick Snippet: Toggle Tree Navigation in Umbraco Backoffice using JavaScript

Hello,
Here is a very quick blog post on how to toggle the tree navigation in the Umbraco 7 (aka Belle) back office using JavaScript with some AngularJS services that the Umbraco core team has given to us to use.

First of all you may be wondering why you would want to toggle the display of the tree navigation, considering that this is the main & primary navigation when using the Umbraco backoffice, however on a project I am doing at work a new property editor that is being developed requires more screen real estate and toggling the tree navigation allows the content editor to see & use this property editor a lot easier, as it involves plotting objects on a canvas. So as much space as possible is useful in this case.

So enough of the why and I will get straight down to the very simple JavaScript code snippet that allows us to do this:

angular.module('umbraco').controller('demo.toggler', function($scope, appState, eventsService) {
  //Create an Angular JS controller and inject Umbraco services appState & eventsService

  //Button Click - ToggleUmbracoNavigation
  $scope.toggleUmbracoNavigation = function() {

    //Get the current state of showNavigation
    var currentNavigationState = appState.getGlobalState('showNavigation');

    //console.log("currentNavigationState", currentNavigationState);
    //console.log("Inverse of currentNavigationState", !currentNavigationState);

    //Toggle the tree visibility
    appState.setGlobalState("showNavigation", !currentNavigationState);
  }

  //The eventService allows us to easily listen for any events that the Umbraco applciation fires
  //Let's listen for globalState changes...
  eventsService.on("appState.globalState.changed", function (e, args) {
    //console.log("appState.globalState.changed (args)", args);

    if (args.key === "showNavigation") {
      //console.log("showNavigation value", args.key, args.value);

      //If false (So hiding navigation)
      if(!args.value) {
        //Set css left position to 80px (width of appBar)
        document.getElementById("contentwrapper").style.left = "80px";
      }
      else {
        //Remove the CSS we set so default CSS of Umbraco kicks in
        document.getElementById("contentwrapper").style.left = "";
      }
    }
  });

});

Hopefully the JavaScript is commented enough and is easy to follow, but the ensence of this is that we have a button or anchor link in our view with the Angular directive of ng-click applied to it calling out method, i.e ng-click=”toggleUmbracoNavigation()” this toggles the value stored in Umbraco’s globabl settings in this case these values & settings are stored in an AngularJS service called appState, that allows us to get & set values stored in here. I recommend you look at the source code on GitHub to see what other values are stored in appState for you to use in your customisations to the Umbraco back office.

I would be interested to hear what other people think of this and should this tree toggle become part of the core by doing a Pull Request and if was to become part of the main UI, where should the button go or by double clicking the grey area with the application/section icons toggle this action?

Well let me know by leaving your thoughts in the comments.

Thanks,
Warren 🙂

Umbraco 7 – Creating a generic Settings editor with WebAPI & AngularJS

Hello all,
So my adventure for creating a new Umbraco 7 package for viewing Google Analytics information. I have created a generic way to edit settings from an XML file for this new Analytic section inside Umbraco.

So if I add any new elements to my XML config file, they will automatically appear in the Umbraco backoffice for it to be edited.

So I will run you through the parts such as the WebAPI Controller, and Angular to wire this all up nicely.

Firstly here is the XML config file I want to edit & if I add any new nodes it will automatically show in Umbraco to be edited:

<?xml version="1.0" encoding="utf-8" ?>
<Analytics>
  <ClientId label="Client ID" description="This is the Client ID key from the Google API">1234567</ClientId>
  <Username label="Username" description="The username for the Google account">warren</Username>
</Analytics>

So let’s take a look at the WebAPI controller I created to Get & Post the settings to and from the xml.config file.

using System.Collections.Generic;
using System.Linq;
using System.Web.Hosting;
using System.Xml;
using Analytics.Models;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;

namespace Analytics.Controllers
{
    /// <summary>
    /// Web API Controller for Fetching & Saving values from settings.config file
    /// 
    /// Example route URL to API
    /// http://localhost:62315/umbraco/Analytics/SettingsApi/GetSettings
    /// </summary>
    [PluginController("Analytics")]
    public class SettingsApiController : UmbracoAuthorizedApiController
    {
        /// <summary>
        /// Does what it says on the tin - the path to the settings.config file
        /// </summary>
        private const string ConfigPath = "~/App_Plugins/Analytics/settings.config";

        /// <summary>
        /// Gets Settings from the XML settings.config
        /// </summary>
        /// <returns>Serializes settings from XML file into a nice list of objects</returns>
        public List<SettingsValue> GetSettings()
        {
            //A list to store our settings
            var settings = new List<SettingsValue>();

            //Path to the settings.config file
            var configPath = HostingEnvironment.MapPath(ConfigPath);

            //Load settings.config XML file
            XmlDocument settingsXml = new XmlDocument();
            settingsXml.Load(configPath);

            //Get all child nodes of <Analytics> node
            XmlNodeList analayticsNode = settingsXml.SelectNodes("//Analytics");

            //Ensure we found the <Analytics> node in the config
            if (analayticsNode != null)
            {
                //Loop over child nodes inside <Analytics> node
                foreach (XmlNode settingNode in analayticsNode)
                {
                    foreach (XmlNode setting in settingNode.ChildNodes)
                    {
                        //Go & populate our model from each item in the XML file
                        var settingToAdd            = new SettingsValue();
                        settingToAdd.Key            = setting.Name;
                        settingToAdd.Label          = setting.Attributes["label"].Value;
                        settingToAdd.Description    = setting.Attributes["description"].Value;
                        settingToAdd.Value          = setting.InnerText;

                        //Add the item to the list
                        settings.Add(settingToAdd);
                    }
                }
            }

            //Return the list
            return settings;
        }

        public List<SettingsValue> PostSettings(List<SettingsValue> settings)
        {
            //Update the XML config file on disk

            //Path to the settings.config file
            var configPath = HostingEnvironment.MapPath(ConfigPath);

            //Load settings.config XML file
            XmlDocument settingsXml = new XmlDocument();
            settingsXml.Load(configPath);

            //Get all child nodes of <Analytics> node
            XmlNodeList analayticsNode = settingsXml.SelectNodes("//Analytics");

            //Loop over child nodes inside <Analytics> node
            foreach (XmlNode settingNode in analayticsNode)
            {
                foreach (XmlNode setting in settingNode.ChildNodes)
                {
                    //Go & populate our model from each item in the XML file
                    setting.InnerText = settings.SingleOrDefault(x => x.Key == setting.Name).Value;
                }
            }

            //Save the XML file back down to disk
            settingsXml.Save(configPath);

            //Return the same settings that passed in
            return settings;
        }
    }
}

You can see the controller is nicely commented and should be easy to follow along in what it is doing, you will notice I have a custom model/object to store the list of items so it can be serialised easily to JSON for the GET & POST requests. The model is as follows:

namespace Analytics.Models
{
    public class SettingsValue
    {
        public string Key { get; set; }

        public string Label { get; set; }

        public string Description { get; set; }

        public string Value { get; set; }
    }
}

So with these parts in place we have a way with a WebAPI to fetch & post values to our XML config file. Next we need to create an Angular View, Angular Controller & Angular Resource file to consume this WebAPI.

<div ng-controller="Analytics.SettingsController">
    <div class="umb-pane">
        <umb-control-group ng-repeat="setting in settings" label="{{ setting.Label }}" description="{{ setting.Description }}">
            <input type="text" name="{{ setting.Key }}" id="{{ setting.Key }}" value="{{ setting.Value }}" ng-model="setting.Value" />
        </umb-control-group>

        <div class="umb-tab-buttons">
            <div class="btn-group">
                <button ng-click="save(settings)" data-hotkey="ctrl+s" class="btn btn-success">
                    <localize key="buttons_save">Save</localize>
                </button>
            </div>
        </div>

    </div>
</div>

This Angular HTML view loops over the settings JSON object and repeats the markup & input field for each setting to edit within the XML file with the ng-repeat Angular data attribute.

The next part is to look at the Angular Controller file that is wired up to this view.

angular.module("umbraco").controller("Analytics.SettingsController",

function($scope, settingsResource, notificationsService) {

    //Get all settings via settingsResource - does WebAPI GET call
    settingsResource.getall().then(function(response) {
        $scope.settings = response.data;
    });

    //Save - click...
    $scope.save = function(settings) {

        //Save settings resource - does a WebAPI POST call
        settingsResource.save(settings).then(function(response) {
            $scope.settings = response.data;

            //Display Success message
            notificationsService.success("Success settings have been saved");
        });
    };

});

You can see we have created a Resource to get the settings and to save the settings, which the resource file handles the GET & POST to the WebAPI controller. When the save button is clicked it POSTs the settings to the WebAPI controller and shows a nice success message with the notification service.

angular.module("umbraco.resources")
    .factory("settingsResource", function($http) {
    return {

        getall: function() {
            return $http.get("Analytics/SettingsApi/GetSettings");
        },

        save: function(settings) {
            return $http.post("Analytics/SettingsApi/PostSettings", angular.toJson(settings));
        }
    };
}); 

With all these parts in place we now have a nice generic way of displaying & saving values to an XML config file and allowing easy update to the UI if we decide to add in more settings at a later date.

I hope someone finds this useful & can take something away to learn from it.

Cheers,
Warren 🙂

The wonderful assistant

Today I helped out ex colleague and good friend Per Ploug with his first LIVE Google Hangout broadcast to YouTube to help direct some questions and interject with some questions of my own.

So if you missed Per giving a fantastic demo & introduction on how to build a Markdown Property Editor for Umbraco Belle, then I highly recommend you watch the video from the hangout, in order to catch up.

How I built my first property editor for Umbraco Belle

After recently building my first AngularJS project, Diagnostics for Umbraco so I can learn the framework as Umbraco Belle is built with Angular.

So my next step was to learn how to build an Umbraco property editor for Belle, before me and my colleagues at The CogWorks along with lead Belle developer Per Ploug to host a Belle hands on workshop.

Currently Per has written a fantastic tutorial for creating your first property editor in where you create a markdown property editor which is a great tutorial that I followed.

But for me to ensure I learnt the skills properly then I decided to create my first property editor by myself.

If you remember when I was working at the HQ I wrote a blog tutorial on creating a property editor which was a RGBA colour picker. In this tutorial I will be recreating the same property editor for Belle.

So first thing first is to create a folder called RGBAEditor in App_Plugins in an Umbraco Belle installation. This is where we store the HTML, JS and package manifest file for our property editor.

{
    propertyEditors: [      
        {
            id: "B2FE9CFE-50FC-42A5-9DFE-35DE84953C67",
            name: "(CWS) RGBA Colour Picker",
            editor: {
                view: "~/App_Plugins/RGBAEditor/rgba-editor.html"
            }
        }
    ]
    ,
    javascript: [
        '~/App_Plugins/RGBAEditor/rgba.controller.js'
    ]
}

In this JSON, we give our property a unique GUID, a friendly name for our property editor so we can choose it from the back office and then we define the HTML Angular JS view for our property editor in this case it’s rgba-editor.html and finally we define our Javascript we need to load for this property editor which in this case is our AngularJS controller file – rgba.controller.js

Next we need to our HTML for our property editor, in this case it will contain three sliders and an element to show a live preview of the RGBA colour. So in rgba-editor.html file we need the following.

<div ng-controller="CWS.RGBAController">

    <div class="row">
        <!-- SLIDERS -->
        <div class="sliders span8">
            <!-- RED -->
            <div class="control-group">
                 <label class="control-label" for="rgba-red">Red</label>
                <div class="controls">
                     <input type="text" name="rgba-red" ng-model="renderModel.red" class="slider red" data-slider-min="0" data-slider-max="255" data-slider-step="1" data-slider-value="255" data-slider-orientation="horizontal" data-slider-selection="after" data-slider-tooltip="show"/>
                </div>
            </div>

            <!-- GREEN -->
            <div class="control-group">
                 <label class="control-label" for="rgba-green">Green</label>
                <div class="controls">
                     <input type="text" name="rgba-green" ng-model="renderModel.green" class="slider green" data-slider-min="0" data-slider-max="255" data-slider-step="1" data-slider-value="0" data-slider-orientation="horizontal" data-slider-selection="after" data-slider-tooltip="show"/>
                </div>
            </div>

            <!-- BLUE -->
            <div class="control-group">
                 <label class="control-label" for="rgba-blue">Blue</label>
                <div class="controls">
                     <input type="text" name="rgba-blue" ng-model="renderModel.blue" class="slider blue" data-slider-min="0" data-slider-max="255" data-slider-step="1" data-slider-value="0" data-slider-orientation="horizontal" data-slider-selection="after" data-slider-tooltip="show"/>
                </div>
            </div>

            <!-- ALPHA -->
            <div class="control-group">
                 <label class="control-label" for="rgba-alpha">Alpha (%)</label>
                <div class="controls">
                     <input type="text" name="rgba-alpha" ng-model="renderModel.alpha" class="slider alpha" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="100" data-slider-orientation="horizontal" data-slider-selection="after" data-slider-tooltip="show"/>
                </div>
            </div>
        </div>

        <!-- PREVIEW -->
        <div class="preview span4">
            <!-- This will show a live preview of the RGBA colour based on jQuery UI sliders below -->
            <div id="rgba-preview-{{model.alias }}"><span ng-style="{background: preview}"></span></div>
            {{ renderModel.red }} {{ renderModel.green }} {{ renderModel.blue }} {{ renderModel.alpha }}
        </div>
    </div>

    <hr/>

    <h6>Debug JSON</h6>
    {{ model | json }}

</div>

In this HTML I am using the Bootstrap CSS styles to help create a simple layout as Belle uses Twitter Bootstrap for it’s styling. The ng-controller attribute on the opening div is used to map our AngularJS controller to our view. We then have lots of data attributes on our inputs, this is because we are using a jQuery plugin for Bootstrap styled sliders that require these attributes to be present on the text input field.

The slider plugin that I am using for this is the Eyecon.ro Bootstrap Slider. In addition in this view you will see we use some custom properties that we store on the scope such as red, green, blue etc so that when the slider is updated our AngularJS controller knows about it and finally we use the ng-style attribute to update the CSS background property on the element from the concatenated value of preview stored on our scope in our controller.

Lets now move onto our JS controller to see the final piece of the puzzle.

angular.module("umbraco").controller("CWS.RGBAController", function ($scope, $log, assetsService) {

    //tell the assetsService to load the bootstrap slider
    //libs from the plugin folder
    assetsService
        .loadJs("/app_plugins/RGBAEditor/lib/bootstrap-slider/bootstrap-slider.js")
        .then(function () {

            //Initiate sliders
            $('.slider').slider();

            //Update the preview DIV function...
            var RGBChange = function () {
                //Log...
                $log.log("Fired RGBCHange()");

                $scope.$apply(function () {
                    //Divide by 100 to get decimal needed for RGBA css
                    var alpha = a.getValue() / 100;

                    //Store the value as a CSV
                    $scope.model.value = [r.getValue(), g.getValue(), b.getValue(), alpha].join();

                    //Set the preview value, so we can update the view
                    $scope.preview = 'rgba(' + $scope.model.value + ')';
                });
            };

            //Slider values stored in data object
            var r = $('.red').slider().on('slideStop', RGBChange).data('slider');
            var g = $('.green').slider().on('slideStop', RGBChange).data('slider');
            var b = $('.blue').slider().on('slideStop', RGBChange).data('slider');
            var a = $('.alpha').slider().on('slideStop', RGBChange).data('slider');

            //this will be comma delimited 'r,g,b,a'
            if ($scope.model.value && (typeof $scope.model.value === "string")) {
                var splitVals = $scope.model.value.split(",");

                //Get values from split and set the sliders
                r.setValue(splitVals[0].trim());
                g.setValue(splitVals[1].trim());
                b.setValue(splitVals[2].trim());

                //Alpha convert back to full %
                var alphaConvert = splitVals[3].trim() * 100;
                a.setValue(alphaConvert);

                //set the preview background value
                $scope.preview = 'rgba(' + $scope.model.value + ')';
            }
        });

    //load the seperate css for the editor to avoid it blocking our js loading
    assetsService.loadCss("/app_plugins/RGBAEditor/lib/CWS-rgba.css");
    assetsService.loadCss("/app_plugins/RGBAEditor/lib/bootstrap-slider/slider.css");

});

The code is heavily commented above and should make sense, but I will go over some key points in it. First we define our controller CWS.RGBAController which is the same as the ng-controller attribute in our view. Next key point is the assetsService which the Umbraco guys have made that allows us to load in external Javascript dependencies. In this case I am loading in the Eyecon.ro Bootstrap Slider library so that I can use it. Once it’s loaded we can work with it inside the .then function.

In here we initiate our sliders and then have a function for every time one of the sliders is updated we update our scope values, the most important being $scope.model.value as this is what will be saved back into Umbraco when the user clicks save on the document and in addition we update the preview as well on the scope so that we can allow the colour preview to be updated in the HTML view. We store the RGBA value in Umbraco as a CSV string r,g,b,a.

The final part to this is to get the values when the property editor is initially loaded and set the sliders to those values. Which we will need to do for when we come back to the node when the values have been saved by doing a simple split of the values and then setting the sliders back to those values from the CSV string stored in Umbraco.

It’s as easy as that to create a property editor for Umbraco Belle, no Visual Studio needed. No compiling down to DLLs and writing C# code. This is so easy to do that your front-end developer can start writing property editors for Umbraco. Which is fantastic news 🙂

Here’s to a bright and happy future with Umbraco Belle.

Diagnostics for Umbraco

Well after a short weeks holiday I have come back and released a new must have package for any Umbraco 6.1 site or newer. This handy little package is called Diagnostics and it does exactly what it says on the tin. It shows you diagnostical information about your Umbraco install.

Features

This package was a bit of an experiment for me in order to learn how to write an AngularJS application. So later on this post I will cover some of the basics of what I wrote and show you some parts of the code.

  • Umbraco Version, assembly & release notes
  • Database type & connection string
  • Server information, ASP.NET version etc…
  • Assemblies along with MD5 & SHA1 Checksums
  • List the packages installed
  • List the Umbraco backoffice users
  • List the domains in use for the site
  • List all the folders and their current permissions applied to them
  • List all the Umbraco events and what is attached to those events
  • List the MVC Routes used in the site
  • List all the Trees used within the Umbraco backoffice

Let’s see it in action

Please note this video is ever so slightly out of date and the UI has been given a little bit love rather than messy UL list’s

http://screenr.com/0iRH

Download it

You can download the Diagnostics package from the Our Umbraco project page and the source code is publicly available on GitHub where you can view the source and log any issues along with any feature requests.

Show me the code!

OK so this was my first experiment into the world of AngularJS and I wanted to build a small app using it, as the new Umbraco user interface is getting overhauled in version 7 aka Belle and will be using AngularJS as well. So any head start on this would be highly beneficial.

Firstly I looked at many videos and resources before diving in. But the video I highly recommend you watch is this one from Dan Wahlin

Now you have watched that I can go over some parts of the code.

First let’s start with the index.html page found in /App_Plugins/Diagnostics.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Umbraco Diagnostics</title>

    <!-- Latest compiled and minified CSS -->
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css">
</head>
<body ng-app="umbracoDiagnosticsApp">
    
    <h1>Diagnostics</h1>
    <!-- Nav -->
    <ul class="nav nav-tabs">
        <li ng-class="{active: locationUrl == '/'}">
            <a href="#/">General</a>
        </li>
        <li ng-class="{active: locationUrl == '/packages'}">
            <a href="#/packages">Packages</a>
        </li>
        <li ng-class="{active: locationUrl == '/users'}">
            <a href="#/users">Users</a>
        </li>
        <li ng-class="{active: locationUrl == '/domains'}">
            <a href="#/domains">Domains</a>
        </li>
        <li ng-class="{active: locationUrl == '/assemblies'}">
            <a href="#/assemblies">Assemblies</a>
        </li>
        <li ng-class="{active: locationUrl == '/permissions'}">
            <a href="#/permissions">Permissions</a>
        </li>
        <li ng-class="{active: locationUrl == '/events'}">
            <a href="#/events">Events</a>
        </li>
        <li ng-class="{active: locationUrl == '/routes'}">
            <a href="#/routes">MVC Routes</a>
        </li>
        <li ng-class="{active: locationUrl == '/trees'}">
            <a href="#/trees">Trees</a>
        </li>
    </ul>

    <!-- Placeholder for views -->
    <div ng-view=""></div>
    
    <!-- JS -->
    <script type="text/javascript" src="scripts/angular1.0.7.min.js"></script>
    <script type="text/javascript" src="scripts/app.js"></script>

    <!-- Latest compiled and minified JavaScript -->
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/js/bootstrap.min.js"></script>
</body>
</html>

The things to note is the ng-app attribute on the body tag, the ng-class attributes on the LI elements and the ng-view on the div.

I will cover what these items do a bit later on.

Next we need to take a look at the app.js file in /App_plugins/Diagnositcs/scritps/app.js

var umbracoDiagnosticsApp = angular.module('umbracoDiagnosticsApp', []);

umbracoDiagnosticsApp.config(function($routeProvider) {
    $routeProvider
        .when('/',
            {
                controller: 'GeneralController',
                templateUrl: 'partials/general.html'
            })
        .when('/packages',
            {
                controller: 'PackagesController',
                templateUrl: 'partials/packages.html'
            })        
        .otherwise({ redirectTo: '/' });
});


/*
=====================================
CONTROLLERS
=====================================
*/

//General Controller
umbracoDiagnosticsApp.controller('GeneralController', function ($scope, $http, $rootScope, $location) {
    $http.get('/Umbraco/Api/DiagnosticsApi/GetVersion').success(function (data) {
        $scope.version = data;
    });

    $http.get('/Umbraco/Api/DiagnosticsApi/GetVersionAssembly').success(function (data) {
        $scope.assembly = data;
    });
    
    $http.get('/Umbraco/Api/DiagnosticsApi/GetVersionComment').success(function (data) {
        $scope.comment = data;
    });
    
    $http.get('/Umbraco/Api/DiagnosticsApi/GetServerInfo').success(function (data) {
        $scope.server = data;
    });
    
    $http.get('/Umbraco/Api/DiagnosticsApi/GetDBInfo').success(function (data) {
        $scope.db = data;
    });
    
    //Pass location url value into an item on our scope object
    $rootScope.locationUrl = $location.$$url;
});

//Packages Controller
umbracoDiagnosticsApp.controller('PackagesController', function ($scope, $http, $rootScope, $location) {
    $http.get('/Umbraco/Api/DiagnosticsApi/GetPackages').success(function (data) {
        $scope.packages = data;
    });
    
    //Pass location url value into an item on our scope object
    $rootScope.locationUrl = $location.$$url;
});

The very first line in this app.js defines our AngularJS module and in this case it’s called umbracoDiagnosticsApp and this needs to be in the ng-app attribute in the HTML document as shown above.

The next part of this file is to wire up the URL routes, so when the link/URL changes we can load in a different view and controller into our div with the attribute of ng-view.

So when we click on the different tabs such as Packages, it loads the URL #/packages and then maps the view /partials/packages.html and to use the packages controller.

In this controller for packages we do a HTTP WebAPI call to /Umbraco/Api/DiagnosticsApi/GetPackages fetch some JSON and then we store the results of the JSON into an object called packages which we then use in our view.

So lets take a look at the partial view for packages to see how we display the package information.

<table class="table table-striped table-hover">
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Version</th>
            <th>Author</th>
            <th>Has Update?</th>
        </tr>
    </thead>
    <tbody>
        <tr ng-repeat="package in packages">
            <td>{{ package.Id }}</td>
            <td>{{ package.Name }}</td>
            <td>{{ package.Version }}</td>
            <td>{{ package.Author }}</td>
            <td>{{ package.HasUpdate }}</td>
        </tr>
    </tbody>
</table>

So we use mustache in our partial view to output the properties stored in our packages JSON we received from our WebAPI call and with the ng-repeat attribute we can easily loop over each item in the collection and output the properties stored in the JSON.

So the final piece of the puzzle is to have a WebAPI setup in Umbraco to get our information back as JSON.

using umbraco.cms.businesslogic.web;
using Umbraco.Core.Configuration;
using umbraco.interfaces;
using Umbraco.Web.WebApi;
using umbraco.cms.presentation.Trees;
using Package = umbraco.cms.businesslogic.packager.repositories.Package;

namespace CWS.UmbracoDiagnostics.Web.Controllers
{
    public class DiagnosticsAPIController : UmbracoAuthorizedApiController
    {

        public List<PackageInstance> GetPackages()
        {
            var allPackages = new List<PackageInstance>();

            //Get packages
            var packages = InstalledPackage.GetAllInstalledPackages();

            //loop over them
            foreach (var item in packages)
            {
                allPackages.Add(item.Data);
            }

            //Return the list
            return allPackages;
        }        
    }
}

So in this WebAPI controller class I inherit from UmbracoAuthorizedApiController which only allows these requests to be made to the API if you are logged into the Umbraco backoffice to ensure this information is not publicly available.

So in a nutshell that is all the components that makes up this AngularJS Umbraco Diagnostics application.

Any questions or improvements to this then please let me know.

Thanks,
Warren