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

The new way to do a 404 Umbraco Handler

Hello all,
As you may or may not be aware I have been building a new Umbraco starter kit called CWS Start. In this package I wanted to have a 404 page setup for the site. Currently the way to setup a 404 page is to put a node ID in the umbracosettings.config file. My only concern with this approach is that if you delete the node and recreate the node then the 404 will stop working.

So I decided to ask the Umbraco community how they do 404’s and I got a fantastic response from the community.
http://our.umbraco.org/forum/developers/extending-umbraco/43866-Alternatives-to-404-in-umbracosettingsconfig

Stefan & Lee K gave me some fantastic answers and filled me on the new Request pipeline in Umbraco and a way on how to add a ContentFinder to Umbraco, aka the new way of doing 404 handlers in Umbraco 6.1 and any other type of content finders.

Show me the code!

    public class _404iLastChanceFinder : IContentFinder
    {
        public bool TryFindContent(PublishedContentRequest contentRequest)
        {
            //Check request is a 404
            if (contentRequest.Is404)
            {
                //Get the home node
                var home = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetAtRoot().Single(x => x.DocumentTypeAlias == "CWS-Home");

                //Get the 404 node
                var notFoundNode = home.Children.Single(x => x.DocumentTypeAlias == "CWS-404");

                //Set Response Status to be HTTP 404
                contentRequest.SetResponseStatus(404, "404 Page Not Found");

                //Set the node to be the not found node
                contentRequest.PublishedContent = notFoundNode;
            }

            //Not sure about this line - copied from Lee K's GIST
            //https://gist.github.com/leekelleher/5966488
            return contentRequest.PublishedContent != null;
        }
    }

And then we need to register this in Umbraco on App Startup like so:

        protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            //On application starting event...
            //Add to the ContentFinder resolver collection our custom 404 Content Finder resolver
            ContentLastChanceFinderResolver.Current.SetFinder(new _404iLastChanceFinder());
        }

There are other use cases for IContentFinders and Lee K has a great little example he posted to Gist

How to compile Sass, Less & CoffeeScript on your build server with TeamCity

Hiya this post will be relatively quick & brief and will show you how I wrote a PowerShell file to help us dynamically compile Sass, Less & CoffeeScript files from our web projects, so that we never have to compile them locally on our machines and commit the ouput of the file to source control, as this has caused a few issues before with the team where the files have strangely got out of sync.

Continue reading “How to compile Sass, Less & CoffeeScript on your build server with TeamCity”