Umbraco: Displaying Content Tree with AngularJS Directive

It seems that more and more CMS-based websites implement frontend editing. Sitecore gives you this possibility by default, but in Umbraco, the feature has to be custom made for every solution.

When creating new content from front-end, one of the challenges encountered most often is placing the new content in the content tree.

Here is a AngularJS directive based solution to the problem. It is easy to port from solution to solution and  customize in the front-end. When the relevant files are added to any given solution, all that a front-end developer needs to do, to display the content tree is include <content-tree> directive in her html.

The directive can be customized and show only certain document types or only certain levels. The content nodes that do not belong to the selection can either be removed from the selection all together or disabled.

The following demo displays the nodeId of the selected node. In a solution that uses content tree  to place newly created content, the nodeId would be saved in a hidden input field and submited to the controller together with the new content.

Solution requires AngularJS and it was made for Umbraco 7 with MVC.

Solutions consists of 4 files:

  • \frontend\js\QApp\controllers\contentTreeController.js

This controller makes an ajax call to surface controller and passes on the filters from directive. It then receives the Node list and exposes it on the scope.

gameQApp.controller('TreeController', ['$scope', '$http', function ($scope, $http) {
    var treeDirective      = angular.element(document.querySelector('content-tree')),
        docTypes           = treeDirective.attr('documenttypes'),
        excludedNodes      = treeDirective.attr('excludednodes'),
        levelRange         = treeDirective.attr('levelrange');

    $http({
      url: 'umbraco/surface/ContentTreeSurface/GetNodes',
      data: { 'docTypes' : docTypes, 'excludedNodes' : excludedNodes, 'levelRange' : levelRange},
      method: 'POST'
    }).success(function(data){
        $scope.nodes = data.slice().reverse();
    });

}]);
  • \Controllers\SurfaceControllers\ContentTreeSurfaceController.cs

If like me, you are coming from a front-end development background, this code might look a bit daunting. What it basically does is crawling down the node tree in Umbraco and creating a flat list of IContent objects while applying the various filters.

Once the flat list (called _result) is ready, the code runs through it and creates structured list (starting from foreach (IContent item in _result) line). The loop, creates a Node object and checks if it’s parent is already on the list. If the parent is on the list, the new Node is added to its children list.

The structured list is returned to the angular controller.

using GameQ.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using umbraco;
using umbraco.cms.businesslogic.web;
using Umbraco.Core.Models;
using Umbraco.Web.Mvc;

namespace GameQ.Controllers.SurfaceControllers
{
    public class ContentTreeSurfaceController : SurfaceController
    {
        private List _result = new List();

        public JsonResult GetNodes(string docTypes = "all", string excludedNodes = "hide", string levelRange = "all")
        {

            // content and type services
            var cs = Services.ContentService;

            //get all nodes
            List level1 = cs.GetRootContent().ToList();
            foreach (IContent item in level1)
            {
                ProcessAllItems(item, docTypes, excludedNodes, levelRange);
            }

            //get filtered nodes
            _result = _result.OrderBy(x => x.Level).ToList();

            List nodes = new List();

            //build a structured list
            foreach (IContent item in _result)
            {
                //create Node object
                Node nItem = new Node();
                nItem.nodeId = item.Id;
                nItem.name = item.Name;
                nItem.children = new List();
                nItem.enabled = true;
                nItem.level = item.Level;

                //check if the node should be disabled
                if (item.WriterId == 999999999)
                {
                    nItem.enabled = false;
                }

                //put the node in the list
                if ((nodes.FirstOrDefault(x => x.nodeId == item.Id)) == null)
                {
                    //find if parent exists
                    Node parent = null;
                    parent = FindParent(nodes, item.ParentId, parent);

                    if (parent != null)
                    {
                        parent.children.Add(nItem);
                    }
                    else
                    {
                        nodes.Add(nItem);
                    }
                }
            }

            return Json(nodes, JsonRequestBehavior.AllowGet);
        }

        // building a flat list of all content
        private void ProcessAllItems(IContent n, string docTypes, string excludedNodes, string levelRange)
        {
            ProcessItem(n, docTypes, excludedNodes, levelRange);
            foreach (var item in n.Children().ToList())
            {
                ProcessAllItems(item, docTypes, excludedNodes, levelRange);
            }
        }

        //add node to flat list while applying the relevant filters
        private void ProcessItem(IContent node, string docTypes, string excludedNodes, string levelRange)
        {
            string[] docTypesList   = docTypes.Split('-');
            string[] levels = levelRange.Split('-');

            bool toAdd = false;

            if (docTypes == "all")
            {
                toAdd = true;
            }
            else
            {
                foreach (var type in docTypesList)
                {
                    if (node.ContentType.Alias == type)
                    {
                        toAdd = true;
                    }
                }
            }

            if (levelRange == "all")
            {
                if (toAdd == true)
                {
                    toAdd = true;
                }
            }
            else
            {
                if (levelRange.Contains("-"))
                {
                    if (node.Level >= Int32.Parse(levels[0]) && node.Level <= Int32.Parse(levels[1]) && toAdd == true)                     {                         toAdd = true;                     }                     else                     {                         toAdd = false;                     }                 }                 else                 {                     if (node.Level >= Int32.Parse(levelRange) && toAdd == true)
                    {
                        toAdd = true;
                    }
                    else
                    {
                        toAdd = false;
                    }
                }
            }

            if (toAdd)
            {
                _result.Add(node);
            }
            else if (toAdd == false && excludedNodes == "show")
            {
                node.WriterId = 999999999;
                _result.Add(node);

            }
        }

        //retriving parent to build a structured list
        public Node FindParent(List nodes, int ParentId, Node parent)
        {

            foreach (var node in nodes)
            {
                if (node.nodeId == ParentId)
                {
                    parent = node;
                    break;
                }
                else
                {
                    if (node.children.Any())
                    {
                       parent = FindParent(node.children, ParentId, parent);
                       if (parent != null)
                       {
                           break;
                       }

                    }
                }
            }

            return parent;
        }

    }
}
  • \Models\ContentTreeModel.cs

This is a very simple model that describes Node object. This limits the amount of data sent back to frontend and allows for children property (used for the structured list)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Umbraco.Core.Models;

namespace GameQ.Models
{
    public class Node
    {
        public int nodeId { get; set; }
        public string name { get; set; }
        public List children { get; set; }
        public bool enabled { get; set; }
        public int level { get; set; }
    }
}
  • \frontend\js\QApp\directives\contentTree.js

The directive file, contains two elements: the factory – RecursionHelper and the directive – contentTree. RecursionHelper allows the directive to read the nested list of nodes and display the template in a nested fashion. RecursionHelper was written by Mark Lagendijk and you can see it here.

The directive part describes the…directive (sic!), its template, and two functions. OnClick exposes the nodeId of the clicked node on the rootscope and showChildren is just a UI detail that allows for unfolding of the content tree.

gameQApp.factory('RecursionHelper', ['$compile', function($compile){
    return {
        /**
         * Manually compiles the element, fixing the recursion loop.
         * @param element
         * @param [link] A post-link function, or an object with function(s) registered via pre and post properties.
         * @returns An object containing the linking functions.
         */
        compile: function(element, link){
            // Normalize the link parameter
            if(angular.isFunction(link)){
                link = { post: link };
            }

            // Break the recursion loop by removing the contents
            var contents = element.contents().remove();
            var compiledContents;
            return {
                pre: (link && link.pre) ? link.pre : null,
                /**
                 * Compiles and re-adds the contents
                 */
                post: function(scope, element){
                    // Compile the contents
                    if(!compiledContents){
                        compiledContents = $compile(contents);
                    }
                    // Re-add the compiled contents to the element
                    compiledContents(scope, function(clone){
                        element.append(clone);
                    });

                    // Call the post-linking function, if any
                    if(link && link.post){
                        link.post.apply(null, arguments);
                    }
                }
            };
        }
    };
}]);

gameQApp.directive('contentTree', function(RecursionHelper) {
  return {
    restrict: 'E',
    template:
        '<ul>' + 
               '<li ng-repeat="node in nodes" data-nodeId="{{node.nodeId}}" id="{{node.nodeId}}">' + 
                   '<span ng-click="showChildren(node.nodeId)"><span class="glyphicon glyphicon-triangle-right" aria-hidden="true" ng-show="node.children.length"></span><span class="glyphicon glyphicon-minus" aria-hidden="true" ng-show="!(node.children.length)"></span> {{node.name}}</span>' +
                   ' <a ng-click="onClick(node.nodeId)" ng-if="node.enabled">Choose node</a>' +
                   '<content-tree nodes="node.children"></content-tree>' +                    
               '</li>' +
          '</ul>',
    scope: {
      nodes: '=',
      choosenode: '&',
      documenttypes: "="
    },

    compile: function(element, $scope, attr) {
      return RecursionHelper.compile(element, function(scope, iElement, iAttrs, controller, transcludeFn){
      });
    },
    controller: function($scope, $attrs) {
     var scope = angular.element(document.querySelector('[ng-app]')).scope();
      $scope.onClick = function(nodeId) {
        scope.chosenNodeId = nodeId;
      }

      $scope.showChildren = function(nodeId) {
        var id            = nodeId,
            listElement   = $.find('#' + id),
            subList       = $(listElement[0]).find('>content-tree').find('>ul'),
            icon          = $(listElement[0]).find('>span').find('.glyphicon');

            subList.toggleClass('showList');
            icon.toggleClass('glyphicon-triangle-right');
            icon.toggleClass('glyphicon-triangle-bottom');
      }
    }
  };
});

Once all these files are in place, all that is left is adding the directive element to the html:

<div ng-controller="TreeController">
	<content-tree nodes="nodes" choosenode="choosenode()" excludednodes="show" documenttypes="Home-Games-Game">
	</content-tree>
	<p>Chosen node: <span ng-bind="chosenNodeId"></span></p>
</div>

The three configuration parameters are documenttypes, levelrange and exludednodes.

  • if no configuration params are sent, the tree will contain all the nodes. See http://marialind.dk/#/search
  • documenttypes – lists the documenttypes that should be included (ex: “Frontpage-Article-List”). See http://marialind.dk/#/bydoctype
  • levelrange – either a starting level for the tree or a level range (ex: “2″ or “2-3″). “2-2″ will list only level 2. See http://marialind.dk/#/bylevel
  • excludednodes – if set to “show”, it will cause the excluded nodes to show on the list but without the “choose node” link. See http://marialind.dk/#/bydoctypedisable

The directive can be added on any number of pages on a website but, as of now, it cannot be added several times on the same page. At least not without modification of the directive and the controller.

The other weak point of the directive is the way I mark disabled nodes in .cs controller. There must be a better way then changing the WriterId to 999999999 if the node should be added to the list but disabled.

Filtering and ordering lists with AngularJS

angularJS logo

Recently, I have been spending quite a bit of time playing around with AngularJS. I mostly like it although occasionally “the magic” doesn’t happen and then it is very hard to figure out what went wrong because everything is just so “magical”. By that I mostly mean that bindings between controllers and views are so very automatic that it’s hard to see where they fail, when they fail. Luckily, that doesn’t happen so often and the basic techniques are really easy to pick up.

However, what I especially like about AngularJS is how easy it is to achieve pretty smooth interface interactions. What follows is a small description of  using AngularJS to show a number of records and filter them based on some of their values.

The (much simplified) app I am building here will display a list of books in an imaginary collection, including their title, author, publish year, genre and their rating. The hypothetical visitors to the app will also be able to vote on books to create a top 10 list. They will also be able to order the list by author, title, year, or number of points. And finally, it will be possible to display books from only one genre. You can see a demo of this app here.

Here is what I am using in this project:

  1. AngularJS script and web-server.js that you can find in the seed project (not necessary but it will make your life easier). You can read here how to install node.js and run the server script.
  2. Bootstrap (just for the css)

My apps structure looks like this:

BookCollection file structure

For this tutorial fonts and img folders are irrelevant. The scripts folder holds angular.js and web-server.js file.

In a real application the data for the website would come from a database, but for simplicity’s sake, I will just write an array of objects and get data from there.

Step 1: AngularJS Module

The first thing to do when writing an AngularJS app is to create the module. This is done in the js/app.js file:

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

All this code does is creating a variable for your angular module. You will use this variable to refer to this module. In a more complex application you might use this file to inject dependencies to your module but in this case this is not necessary.

Step 2: Data

The second piece of code, you want to write is the data factory (js/services/BooksData.js):

BookCollectionApp.factory('BooksData', function(){
	return [
		{title: "To Kill the Mocking Bird", author: "Harper Lee", year: "1960", genre: "Southern Gothic", points: 0},
		{title: "Feet of Clay", author: "Terry Pratchett", year: "1996", genre: "Fantasy", points: 0},
		{title: "Solaris", author: "Stanisław Lem", year: "1961", genre: "SciFi", points: 0},
		{title: "Neverwhere", author: "Neil Gaiman", year: "1996", genre: "Fantasy", points: 0},
		{title: "The Wind-up Bird Chronicle", author: "Haruki Murakami", year: "1997", genre: "Who the hell knows?!", points: 0}
	]
});

In this file I register a factory service called BookData on my app (BookCollectionApp). It will return an array of objects. Each object is a single book bascwith title, author, year, genre and a default point value.

Step 3: AngularJS Controller

Now, it is time to create a controller in the js/controllers/BookCollectionController.js that will be registered on the BookCollectionApp module and that will expose the factory data to potential views:

BookCollectionApp.controller('BookCollectionController', function($scope, BooksData){
	$scope.Books = BooksData;
});

This is a very basic controller file. We will expand it a little bit later but for now this is all that is needed. I register my controller on the BookCollectionApp and I give the controller “BookCollectionController” name. In the controller function I pass scope and BooksData factory and create “Books” variable in my scope giving it the value of what the BooksData factory returns (the array of book objects);

Step 4: View file

With this basic setup, I can move on to building a view that will display books as a list. A View is basically a regular HTML template with data injected through AngularJS. In this case we are talking about index.html file:

<!doctype html>

<meta charset="UTF-8" />
Document
		<link href="css/bootstrap.min.css" rel="stylesheet" />
		<link href="css/style.css" rel="stylesheet" />

On the html tag, I use the ng-app directive to tell my view which module it belongs to.

<body class="container">
	<h1>Book collection</h1>
	<div ng-controller="BookCollectionController" class="row col-md-12">
		<ul class="row col-md-12 books">
			<li ng-repeat="book in Books" class="row col-md-12">
				<div>
					<h3 class="title">{{book.title}}</h3>
					<span class="author">{{book.author}}</span>
					<span class="year">{{book.year}}</span>
					<span class="genre">{{book.genre}}</span>
				</div>
			</li>
		</ul>
	</div>
	<script src="scripts/angular.js"></script>
	<script src="js/app.js"></script>
	<script src="js/services/BooksData.js"></script>
	<script src="js/controllers/BookCollectionController.js"></script>
	<script src="js/bootstrap.min.js"></script>
</body>
</html>

The important bits here are these 3 directives the:

  1. ng-controller – tells the controller on which html elements it should operate.
  2. ng-repeat – repeats the content of the tag for every item in the selection. In this case, it tells the li and everything inside to repeat for every book in the Books variable we created and exposed in the controller
  3. curly braces notation – tells AngularJS to replace the object property with the value of that property. So the book.title will be replaced with the title of each book that the code iterates through

All the necessary scripts are referenced at the end of the file. The result of this code is a simple rendering of the book collection. Now we can move on to more fun properties of AngularJS.

Step 5: Voting functionality

I want to allow visitors to the website to vote on the books, so that I can make a top 10 list. In order to do that, I need to amend 2 files. First, I have to edit the view file so that it has the interface  to display points and vote. I also need to bind it the interface elements to the controller. Then I need to adjust the controller itself so it can handle the voting.

In the index.html, I will adjust the li element like so:

<li ng-repeat="book in Books" class="row col-md-12">
	<div>
		<h3 class="title">{{book.title}}</h3>
		<span class="author">{{book.author}}</span>
		<span class="year">{{book.year}}</span>
		<span class="genre">{{book.genre}}</span>
		<div class="row col-md-12 votes">
			<a class="voteUp" ng-click="voteUp(book)">+</a>
			<span class="points">{{book.points}}</span>
			<a class="voteDown" ng-click="voteDown(book)">-</a>
		</div>
	</div>
</li>

The extra div contains two links and a span. The span simply displays current amount of points. The links have ng-click directive which listens to the click directive on these elements and calls voteUp or voteDown functions. The functions (that we will write in the controller) have the book object passed to them. That means that for every li element, the voteUp function will have access to the correct book object and will be able to change the amount of votes.

And here comes the controller that takes care of the functions.

BookCollectionApp.controller('BookCollectionController', function($scope, BooksData){
	$scope.Books = BooksData;

	$scope.voteUp = function (book) {
		book.points++;
	}

	$scope.voteDown = function (book) {
		book.points--;
	}
});

In this controller, I create voteUp and voteDown functions on the scope (so that the view actually has access to them). The voteUp function adds one to book.points property value and voteDown, surprisingly, detracts one point from the book.points value.

Thanks to two-way binding, every time a user clicks on a plus or minus sign, the object will be edited and the new value will be displayed. Of course this application is not actually using any storage, so the edits will be lost between page reloads.

Step 6: Filtering with AngularJS

And now the moment we’ve all been waiting for. The super easy filtering in AngularJS. So in this app I want to be able to order the list according to some properties on the objects. I also want to display objects of only one type. To do that, I need to start by creating the interface to choose the filter/order by principle:

Order by:
<select ng-model="sortorder">
	<option value="default" selected>None</option>
	<option value="title">Title</option>
	<option value="author">Author</option>
	<option value="-points">Points</option>
</select>
Show:
<select ng-model="showGenre">
	<option value=" " selected>All</option>
	<option ng-repeat="genre in genres" value="{{genre}}">{{genre}}</option>
</select>

This code is added just under the div ng-controller directive. It creates two dropdown lists but I could just as well have lists with links instead or radiobuttons etc. What actually matters is the ng-model directive (you can call it what you like). I’ll use it in the list of books to inform it where it should look for the filter criteria.

Notice that value for points has a “-” sign in front of it. This is to ensure that items are ordered by points from the highest to lowest value and not the opposite (as is the default).

Because, I don’t know in advance what genres I might have in my collection, I create the list dynamically based on the books in the collection. The easiest way would be to simply type:

<option ng-repeat="book in Books" value="{{book.genre}}">{{book.genre}}</option>

This of course would mean that if I had two books of the same genre, the genre will be shown twice in the dropdown. So instead in my controller I will create a new array by pushing none-repeating genres. Here is how the piece of code looks:

$scope.genres = []
	angular.forEach($scope.Books, function(value, genre){
		if($scope.genres.indexOf(value.genre) == -1)
		{
			$scope.genres.push(value.genre);
		}
	});

Now in my view, I can iterate through genres knowing that in the controllers I took care to only store the relevant items.

When the interface is in place, I can take care of actually reacting to the filter parameters that the user has chosen. And here is where the real genius of AngularJS shows. All I need to do is amend this line of code in my view:

<li ng-repeat="book in Books | orderBy:sortorder | filter:showGenre" class="row col-md-12">

Now I told AngularJS to pay attention to sortorder and showGenre models and react to changes on them by applying respective sort orders and filters.

Et voila! Application is ready.

Improving UX with Umbraco 7

Umbraco 7 logoOn November 21st Niels Hartvig announced the release of long awaited Umbraco 7 (codename Belle). The new version runs on the same engine as Umbraco 6 but sports a completely new backoffice. Here are my initial thoughts on the topic + a little introductory tutorial.

In this post I’ll:

  1. be really excited about the new look of Umbraco 7 backoffice (I mean, have you seen the skin on that?!)
  2. explain a bit more seriously why I think Umbraco 7 will be a revelation for editors and will take this CMS to a whole new level
  3. show you a quick tutorial of how to build Umbraco 7 custom property editor and leverage the new secret powers to improve editors’ user experience

Umbraco 7, codename Belle indeed

So, have you had a look at the sparkling beauty that is Umbraco 7′s interface yet? No more feeling like a bum when working on your Umbraco installation in a crowded hipster coffee shop, right? Ok, that really isn’t the point. The point is that the new backoffice, while looking nicely minimal, also follows many of the basic interface design patterns that the old version gleefully ignored.

Some examples include improved workflows with “save and publish button at the bottom of the page; animation and self-healing pattern on deleting items from content tree; or additional information appearing on hover. When adding media items to RTE, the editor will be able to use drag-and-drop functionality or upload new files in the same tab sliding from the right which makes the process faster and more intuitive.

The new interface is also much faster than the old one, which makes a great difference when you work long hours curating content.

Improving editors’ experience with Umbraco 7

However, the best feature of Umbraco’s new backoffice is that it is built with HTML5 and AngularJS. This means that it is relatively easy for developers to create custom property editors. Why is this relevant? Don’t we already have all the properties we might want? Well, kind of. But with Umbraco 7 we can combine properties to create new ones or slightly adjust them to fit better with editors’ needs. An example that springs to mind is a property editor that allows editors to arrange content by dragging-and-dropping content boxes.

But mainly I see Umbraco 7′s advantage over older versions and other CMSs in that it allows developers to adjust interface to improve user experience. Here is what I mean with this:

Imagine you are building a website for an airport. It is a large and complex site with many content items and functionalities. On the frontpage of the website there is a small box that displays the status of the airport (“All is well”, “There are long queues, make sure to come in advance”, “We are snowed in and all flights are cancelled” etc.).  The editor would also like to be able to choose whether the message is “not important”, “important”, or “critical”. The status is used to decide two things: whether the message should be emailed to employees and how it is displayed on the website (i.e the css styling). The property editors are a textbox and a radio button list with labels denoting the importance of the status.

In a complex, large installation it is easy to imagine that editors forget exactly what each status means in practice. Of course I am choosing a “very important” for a status about longer queues but does it mean that the text is displayed in red? And will the employees be emailed? Any developer worth his money will instantly describe all the options in the property editor’s description field. Fair enough, but think of the volume of tiny text that you need to use to describe in details just these 3 options. And what if there are 5 or 10 of them? Scanning through this text is simply not optimal when the editor wants to perform a simple task of choosing the importance of his content.

Enters, Umbraco 7. With HTML5 and AngularJS, it is a question of about 30 minutes to build a new property editor that will deal with this clatter in the best UX style. Instead of having one long text, the developer can ensure that hovering over each option will cause a hint box to appear, describing the consequences of this particular choice. No clatter, only the information the editor needs, delivered in relevant small chunks.

This way of delivering content is in keeping with hover-reveal contextual content UX pattern. It reduced the noise and delivers only relevant information. Keep in mind though that for mobile devices this interaction would have to happen on click and not hover!

Building your first custom property editor with Umbraco 7

Disclaimer: This mini tutorial is based on and extends this tutorial so you might want to have a look at it as well.

Required files

To begin with, in your App_Plugin folder you will need to create a folder for your property (RadioListEditor in my case). The new folder will include the following files:

  • package.manifest
  • radioList.controller.js
  • radioList.html
  • toolTipsStyle.css

package.manifest is a json file that describes the property editor and binds all required files together.

{
    propertyEditors: [
        {
            /*unique alias*/
            alias: "My.RadioListEditor",
            /*name*/
            name: "Radio List Editor",
            /*html file (view) to render the editor*/
            editor: {
                view: "~/App_Plugins/RadioListEditor/radioList.html"
            }
        }
    ]
    ,
    //array of files we want to inject into the application on app_start.
    //In this case a controller (necessary) and stylesheet (optional)
    javascript: [
        '~/App_Plugins/RadioListEditor/radioList.controller.js'
    ]
	,
	css: [
		 '~/App_Plugins/RadioListEditor/tooltipsStyle.css'
	]
}

radioList.controller.js is, as the name suggests, your AngularJS controller that does all the heavy lifting of passing data there and back. In our case the controller doesn’t actually need to do much other than pass data to the model so it will be limited to a bare minimum:

angular.module("umbraco")
    .controller("My.RadioListController",
        function (){}
    );

It is not important that the function is empty, but it still have to be there.

The last required file is radioList.html which is an html snippet used to render the property editor in backoffice (a.k.a your view)

<div>
    <ul ng-controller="My.RadioListController">
        <li class="radio-option">
            <input type="radio" value="1" ng-model="model.value">
            <label>Not important</label>
            <div class="radio-tooltip">The color of the status message will be black</div>
        </li>
        <li class="radio-option">
            <input type="radio" value="2" ng-model="model.value"/>
            <label>Important</label>
            <div class="radio-tooltip">The color of the status message will be green</div>
        </li>
        <li class="radio-option">
            <input type="radio" value="3" ng-model="model.value"/>
            <label>Critical</label>
            <div class="radio-tooltip">The color of the status message will be red</div>
        </li>
    </ul>
</div>

As you can see there is not much magic here either, except for ng-model=”model.value”. This is an AngularJS directive (easily recognized by the ng- prefix) that takes care of passing the value your editor chooses to the model that handles content in Umbraco. There is probably a whole other article to write about how you use model.value and what it actually does. For now, just know that you need it in the input fields so that the editor’s input is saved and accessible when you want to display it.

And lastly, an optional file that takes care of displaying div tooltips on hover – the css file

.radio-tooltip {
  display: none; }

.radio-option:hover .radio-tooltip {
  display: block;
  width: 150px;
  margin-top: -22px;
  padding: 10px;
  border: 1px solid #fff;
  background: #F2E994;
  position: absolute;
  left: 410px;
  z-index: 9999; }

Note that if you want this functionality to be available for mobile-device users, you might want to cause the behavior with JavaScript or do some media-query/wurfl magic instead. If you choose to use JavaScript you will need to include your script in the same way you included the .css file in package.manifest

Once all your files are created all you need to do is restart your application to make sure that the new property editor is registered (you might need to restart the application every time you change something in the controller code).

Next, go to the developer section of your backoffice and click on the 3 dots next to the Data Types option. Create a new Data Type and give it a name of your choosing. From the dropdown list choose the Radio List Editor (or whatever name you gave your property editor in package.manifest) and hit save. Now the property editor is available to your document types as per usual. Add it to your document type, create content node of that type and see the magic.

tooltip on hover - screenshot

This is a very simple example of how Umbraco 7 gives much greater control over backoffice interface to developers, and how this control can improve editors’ experience. There is much to be gained from this control and I am looking forward to exploring further.

Widget Builder for Umbraco

One of my absolutely favorite packages for Umbraco in the recent months has to be Kevin Giszewski’s Widget Builder. I do not know why allowing editors to repeatedly add input fields isn’t a core functionality of Umbraco yet, but until it is, Widget Builder provides a handy solution.

So what is really the problem?

Imagine a simple (and simplified) scenario: A local cafe, for which you are building an Umbraco website, likes to entertain their guests with music performances. They would like you to include a page in their website that sports a simple list of date/time, performer and title of the performance. As simple as that.

With current Umbraco capabilities your choices are:

  1. Providing limited number of time/performer/title groups so that the website will only display a set number of them at a time – I think we all know that this is a very bad UX practice.
  2. Setting the input field as a TinyMCE and letting your editors enter the information in any form they choose – we’ve all been there and we’ve all seen how quickly this can go bad. I firmly believe that RTE should only be available to editors if what they want to enter is body text. In all other cases, the more structure we can make it, the less chances for mistakes and the more clear and uniform the website
  3. Creating a docType for the entertainment page with the list AND a docType for a list item so that every time the editor wants to add an item to the list, she has to create new content item as a child to the entertainment page. – with a simple macro, this solution will of course work. But it seems like we’re adding extra work for the editor. Adding a new piece of content only to display a line with three information pieces on the website, just doesn’t seem right.

So what is an Umbraco developer to do?

Enter Widget Builder

Kevin writes in his documentation:

Widget Builder is a data type designed to be repeatable within a property.

What it means is that this package solves our problem with the entertainment list by allowing the editor to repeat the set of input fields, we had defined, as many times as is needed within one content node. Here is how to do it:

  1. Install package as per usual
  2. Go to Developer and add new dataType. Choose Widget Builder as Property Editor and save.
  3. Define your set of properties that should be repeatable:

Umbraco widget builder screenshot In the case of our cafe, we want to add three simple textbox fields Date and Time, Performance Title and Performer. Once the widget is saved we can move on to docTypes.

  1. Create a doctype that will display the list of performances: Entertainment List
  2. The doctype should include one property: Entertainment. Choose your freshly created widget builder as a type for this property. In my case this is also called Entertainment.

Now you can move on creating content.

  1. Create page with the docType you’ve just created.
  2. Add as many sets of input field sets as you want by clicking on the green plus icon on the left. Remove sets with the red icon and move them around by dragging and dropping.

Using widget builder - screenshot

Voila! Well, not really. You still need to be able to display the input from the widget and you certainly cannot do it with @Umbraco.Field(“Entertainment”). Here is where the fun begins. To access the editor’s input you will need a macro. In my case we will use a Razor macro:

<ul>
  @foreach (var performance in Model.entertainment)
  {
     <li>
       <span class="performanceDateTime">@performance[0].InnerText</span>
       <span class="performanceTitle">@performance[1].InnerText</span>
       <span class="performancePerformer">@performance[2].InnerText</span>
     </li>
  }
</ul>

Read this macro as follows:

  • Model – current page
  • Entertainment – The property that stores all the sets of input fields
  • Performance – a set of input fields
  • Performance[0] – the first input field in the set etc.

And voila! If you add this macro to your page’s template, it will happily display all the sets as list items. You can of course style them as you want and display them as divs, paragraphs or whatever else your imagination dictates.

What if I want to use different dataTypes?

The last thing worth mentioning is that, as you might have noticed when creating the set of input fields in development section, you can use different input types. This makes the macro you need to write a little bit more complicated.

So let’s say that our cafe owning client would like to display a list of performers for each performance. After all there is often more than one. So instead of a textbox, we want to use a list for the performer field. Head back to the developer section, delete the last widgetTextBox and add a widgetList. For simplicity’s sake let’s keep the name Performer. With this input type we can choose to allow more than one indent level, but this really isn’t necessary in our case. Save the dataType and move on to the content.This time to add new items in the list but not a new input fields set, click the green icon on the right.

If you look at the page now, the macro that we have created will in fact work but not perfectly. All names you’ve added to a list will be displayed together as a string, without so much as a space between words. To fetch list items as separate strings, we need to add to our macro:

<ul>
    @foreach (var performance in Model.entertainment)
    {
        <li>
            <span class="performanceDateTime">@performance[0].InnerText</span>
            <span class="performanceTitle">@performance[1].InnerText</span>
            <ul class="performancePerformer">
                @foreach (var item in performance[2])
                {
                    foreach (var name in item)
                    { 
                       <li>@name.InnerText</li>
                    }
                }
            </ul>
        </li>              
    }
</ul>

As counterintuitive as it may seem, you need to add two foreach loops in order to achieve the intended result.
In this case:

  1. performance[2] – the property that holds list
  2. item – the particular list that holds list items
  3. name – each list item we want to display

Now it it finished and all ready to be styled to your heart’s content.

I know it feels like the macro gets a bit complicated when you try to access input from special dataTypes but providing this simple functionality for your editors will make their lives so much easier and their pages so much more structured. The only thing to wish for is that the Widget Builder will become a core functionality of Umbraco one day. Maybe in Umbraco 7? Who knows?

Building News Module with Umbraco v6

This is a small tutorial describing the process of building news module and recycling content in Umbraco v6. I go through the entire process of building a simple Umbraco website that includes a calendar with events, news page with news articles, home page that displays a selection of news and a calendar module that can be rendered on any page within the website.


Update: thanks to a hint from Jeroen Breuer I improved this tutorial a bit. You can do this tutorial as it is or have a look at the end of this post for an update and explanation.

Inaboxdesign.dk: Home screenshot

I am using Umbraco 6.0.3 with Razor and Twitter Bootstrap so that I don’t have to think much about the presentation. I hope you will find it useful and well described but as always with this blog, any and all feedback is very much welcome.

Set up

Start by creating an empty MVC project in Visual Studio. Go to Tools -> library package manager -> Manage NuGet Packages and install Umbraco CMS. You can also use WebMatrix instead. Once Umbraco is installed, go to config folder -> umbracoSettings.config in your solution explorer and find this snippet:

<!-- To switch the default rendering engine to MVC, change this value from WebForms to Mvc -->
<!-- Do not set useAspNetMasterPages to false, it is not relevant to MVC usage -->
WebForms

Replace it “WebForms” with “Mvc”. This will make sure that we are using MVC architecture and Razor engine in our templates and macros.

Run the solution and complete the installation with the database options of your choice. In the next step choose an empty installation

DocTypes

Any good Umbraco website starts with planning – so plan your docTypes carefully.In our case we will have the following docTypes and properties

  • FrontPage (properties: title – textstring)
  • NewsContainer (properties: title – textstring)
  • Article (properties: title – textstring, img – mediaUploader, snippet – RTE, showSnippet – true/false, content -RTE)
  • Calendar (properties: title – textstring)
  • Event (properties: title – textstring, date – Date Picker, img – mediaUploader, snippet – RTE, showSnippet – - true/false, content -RTE)
  • FrontPage doctype will be used to create the Home page at the top of the content tree. The editors will only create the title for this page. The rest of content will be pulled in from the Article and Event docType pages.
  • NewsContainer is not necessary but will make the content tree look much cleaner. It will also help to guide the editors when they create new items on the content tree. Same function is fulfilled by the Calendar.

The properties and docTypes should be fairly self-explanatory so let me just explain that the snippet property will serve to give the user control over what should be displayed on the Home page. If the showSnippet checkbox is checked the snippet will also show as the first paragraph of the Event or Article page.

You might have also noticed that Event and Article are almost identical when it comes to properties. The only difference is that Event docType includes date property. This means that we can have Event docType inherit from Article docType instead of creating all the properties twice.

InaBoxDesign.dk: docType tree

Next step is to actually create the docTypes. Start by creating all the docTypes and their properties. Make sure you create tabs to organize properties into groups. All docTypes should have matching templates.

Once all the docTypes are created and have their properties, you can build the structure of your website. In our small website the structure is fairly simple:

  • FrontPage is allowed at the root and has only two types of allowed children nodes: NewsContainer and Calendar
  • NewsContainer allows Article children
  • Calendar allows Event children

Now you can go ahead and create your basic content. Hint: install FamFamFam Icon package to make it easier for your future editors to discern between docTypes.

Content Tree

Templates

Next step is to present the content. I will use Twitter Bootstrap to make my site shiny but it is up to you if you want to do the same or build your own CSS.

To present the content we need to build templates. I start with Master, to make my life easier. All the other templates will be partials under Master and they will inherit the basic structure of the website. This way I don’t need to repeat the same markup.

Master

All the Master really does is to establish html structure of our website, load scripts and stylesheets and render the content of templates that inherit from it (@RenderBody()). It also renders the navigation macro (@Umbraco.RenderMacro(“Navigation”)) so that navigation will appear on all pages without the need to repeat the code. Isn’t this just neat?

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
    Layout = null;
}
<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>@Umbraco.Field("pageName")</title>
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<meta name="description" content="">

	<link href="/css/bootstrap.css" rel="stylesheet"/>
    	<link href="/css/bootstrap-responsive.css" rel="stylesheet"/>
	<link href="/css/style.css" rel="stylesheet"/>

	<script src="/scripts/jquery.js"></script>
	<script src="/scripts/bootstrap.js"></script>
</head>
<body>
    @Umbraco.RenderMacro("Navigation")
    <div class="container">
        <div class="hero-unit">
            <h1>Hello, world!</h1>
            <p>This is a community website with news and calendar modules</p>
        </div>
	    @RenderBody()
    	 </div>
    <footer>
        <p>&copy; InaBox 2013</p>
    </footer>
</body>
</html>

Article

This template simply presents the content of any node with docType Article. It renders 3 macros: Image, Snippet and SideCalendar. They will be described further down in this post.

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
    Layout = "Master.cshtml";
}
    <div class="row">
    <div class="span8">
        <h2>@Umbraco.Field("title")</h2>
        @Umbraco.RenderMacro("Image")
	 @Umbraco.RenderMacro("Snippet")
        @Umbraco.Field("content")
    </div>
    <div class="span4">
        <h2>Upcoming Events</h2>
        @Umbraco.RenderMacro("SideCalendar")
    </div>
    </div>

InaBoxDesign.dk:Article Page

Event

This template presents the content of any node with docType Event. It renders 4 macros: Date, Image, Snippet and SideCalendar. And again, keep reading to find more about them.

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
    Layout = "Master.cshtml";
}

  <div class="row">
    <div class="span8">
        <h2>@Umbraco.Field("title")</h2>
	 <h4>@Umbraco.RenderMacro("Date")</h4>
        @Umbraco.RenderMacro("Image")
	 @Umbraco.RenderMacro("Snippet")
        @Umbraco.Field("content")
    </div>
	<div class="span4">
		<h2>Upcoming Events</h2>
		@Umbraco.RenderMacro("SideCalendar")
    	</div>
    </div>

InaBoxDesign.dk: Event page, Umbraco News Module

FrontPage

This template presents the content of the FrontPage node, our Home page. It renders only 2 macros: NewsModule and SideCalendar. These two macros provide most of the functionality in our website and they will be explained in detail.

	@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
    Layout = "Master.cshtml";
}
    <div class="row">
    <div class="span8">
        <h2>@Umbraco.Field("title")</h2>
        @Umbraco.RenderMacro("NewsModule")
    </div>
    <div class="span4">
        <h2>Upcoming Events</h2>
        @Umbraco.RenderMacro("SideCalendar")
    </div>
    </div>

NewsContainer

This template is used by…surprise, surprise…NewsContainer docType. All it does, is to render NewsPage and SideCalendar macros. The first one pulls the content from child nodes of the the node (events under calendar page), the latter displays the next 3 upcoming events.

	@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
    Layout = "Master.cshtml";
}

   <div class="row">
		<div class="span8">
			<h2>@Umbraco.Field("title")</h2>
			@Umbraco.RenderMacro("NewsPage")
			<p><a class="btn" href="#">View details &raquo;</a></p>
		</div>
		<div class="span4">
        	<h2>Upcoming Events</h2>
        	@Umbraco.RenderMacro("SideCalendar")
    	</div>
    </div>

InaBoxDesign: Umbraco screenshot: NewsContainer

Calendar

And lastly, the very succinct Calendar template renders the tilte of the page and CalendarPage macro. Most of the content on this page will be pulled in from its children – the events.

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
    Layout = "Master.cshtml";
}
<h2>@Umbraco.Field("title")</h2>
	@Umbraco.RenderMacro("CalendarPage")

InaBoxDesign.dk:Umbraco Screenshot Calendar

Macros

Now comes the lifeblood of the website and the best part: functionality. It will be handled with macro scripts written with Razor.

I’ll start with the three simplest scripts: Date, Image and Snippet. Then I’ll move on to navigation and lastly, I’ll pull the content from Article and Event docTypes to display it wherever I want.

Date

The only reason to have this macro instead of a regular umbraco field is so that we will be able to convert Date property from Event docType into a string and display it in a format that makes us happy campers. In my case the format will be 07/05/2013. If you want to choose another way of displaying your date, have a look at this string format codes.

@inherits umbraco.MacroEngines.DynamicNodeContext

@Model.Date.ToString("dd/MM/yyyy")

Image

Again, this macro is not really necessary but it opens up future possibilities for manipulating the content. I find it most intuitive to only use templates for displaying the most basic umbraco fields and render everything else through macros.

@inherits umbraco.MacroEngines.DynamicNodeContext

<img alt="" src="@Model.Image" />

Snippet

This is another simple macro. You might remember that we gave our editor the option to choose whether they want to display the snippet on the article and event pages or if it should be hidden and only used on the front page as a teaser. In the Snippet macro we simply check if the ShowSnippet checkbox was checked. If yes, then we should display the snippet, if no…well, nothing to see here, move on.

@inherits umbraco.MacroEngines.DynamicNodeContext
@{
	if(@Model.ShowSnippet)
	{
		<div class="snippet">@Model.Snippet</div>
	}
}

Navigation

As the name suggests, this script renders the navigation by iterating through child nodes of the Home page. First we define the root node (var root = Model.AncestorsOrSelf(1)) and then we iterate through its children nodes (@foreach…) and wrap it all up in html markup from Bootstrap – no magic included.

@inherits umbraco.MacroEngines.DynamicNodeContext
@{ 
    var root = Model.AncestorOrSelf(1);
}

<div class="navbar navbar-inverse navbar-fixed-top">
    <div class="navbar-inner">
        <div class="container">
            <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="brand" href="#">Our Community Website</a>
            <div class="nav-collapse collapse">
                <ul class="nav">
                    <li><a href="@root.Url">@root.Name</a></li>
                    @foreach (var page in root.Children.Where("Visible"))
                        { 
                            <li class="@page.IsAncestorOrSelf(Model, "active", "")">
                                <a href="@page.Url">@page.Name</a>
                            </li>
                        }
                </ul>
            </div>
        </div>
    </div>
</div>

CalendarPage

Similarly to NewsPage, the CalendarPage is a macro that pulls in the content from child pages to display it on Calendar (and Community News). To do this, it starts by checking if the current node has any children and then iterates through them with foreach statement, displaying its properties.

@inherits umbraco.MacroEngines.DynamicNodeContext

@if (Model.Children.Any())
{            
	foreach (var childPage in Model.Children)
	{
		<div class="row">
		    <div class="span12">
				<h4><a href="@childPage.Url">@childPage.Title</a></h4>
					@childPage.Snippet
			</div>	
		</div>
	}
}

NewsPage

This macro does exactly the same as CalendarPage but for NewsContainer docType.

@inherits umbraco.MacroEngines.DynamicNodeContext

@if (Model.Children.Any())
{            
	foreach (var childPage in Model.Children)
	{
		<div class="row">
		    <div class="span8">
				<h4><a href="@childPage.Url">@childPage.Title</a></h4>
				@childPage.Snippet
			</div>	
		</div>
	}
}

NewsModule

News Module is the juicy bit in our website (and the whole reason for this entire article). It is rendered on the front page and it pulls in the content from the 3 topmost Article docType nodes under the Community News node. It simply displays the community news on the Home page.

In the case of this macro we know that our Model (so our current page) refers to Home page. This is because of the structure we’ve established with docTypes. NewsContainer simply cannot be created anywhere else in the node tree.

Therefore we can start by checking if our Model has descendants of type NewsContainer and if NewsContainer has any children. If both of the conditions are satisfied we move on to…yup, you guessed it, iterating through NewsContainer’s children (a.k.a Articles) and display their title and snippet. Notice that we don’t want all the articles so we .Take(3) from the collection.

@inherits umbraco.MacroEngines.DynamicNodeContext
@if (Model.Descendants("NewsContainer").Any())
{   
	if(Model.Descendants("NewsContainer").First().Children.Any())
	{

		foreach (var item in Model.Descendants("NewsContainer").First().Children.Take(3))
		{
			<div class="row">
				<div class="span8">
					<h4>@item.Title</h4>
					@item.Snippet
					<p><a class="btn" href="@item.Url">View details &raquo;</a></p>
				</div>	
			</div>
		}
	}
}	

SideCalendar

This is a bit more complicated version of the previous macro that built news module. It also pulls in the content of 3 nodes of a certain type, events from under the Calendar page. However there are 2 major differences here. The first one is that the 3 nodes are ordered according to the date property.

The second difference stems from the fact that the macro will be rendered on multiple nodes of different types. We want to have the side calendar on the FrontPage, NewsContainer, Article and Event docTypes. This means that we don’t know what will be the exact relation of the node that renders the macro and the nodes that should provide the content (events). To deal with this problem is dealt with by defining the root node and finding Event nodes by crawling down from it.

The first thing we need to do is define the root node as the ultimate ancestor of Model. No matter where in the structure you are, the root is always either your model (if you are on the Home page) or an ancestor of your model (since all pages are created under the Home page).

The second step is to check if root has any children of the type Calendar. If it does, we create a variable that stores the first calendar node (we only have one so this simply means that its not a collection but rather a single node). Then we move on to checking if this calendar has any children (Have we created any events yet?). If it does, we organize them by date (OrderBy(“Date”) ), take the 3 topmost and store them in events variable.

And I think at this point we can all guess what happens next. Yup, it seems like Umbraco is all about iterating.

@inherits umbraco.MacroEngines.DynamicNodeContext

@{
    var root = Model.AncestorOrSelf();
	
	if(root.Descendants("Calendar").Any())
	{
		var calendar = root.Descendants("Calendar").First();
		
		if(calendar.Children.Any())
		{
			var events = calendar.Children.OrderBy("Date").Take(3);
		
			foreach(var item in events)
			{
				 <div class="row">
						<div class="span4">
							<h4>@item.Title</h4>
							<span>@item.Date.ToString("dd/MM/yyyy")</span>
							@item.Snippet
							<p><a class="btn" href="@item.Url">View details &raquo;</a></p>
						</div>	
					</div>
			   
			}
		}
	}
   
}

Update

So as mentioned at the beginning of the post, Jeroen asked the very good question of why using macros for date and image when it can be done in templates instead and he pointed me in the right direction for solutions.

It turns out the the syntax one uses in the macros is not exactly the same as in templates. It seems it has to do with different versions of Razor but let us not get into the topic right now

The point is that you can delete Date and Image macros and enter the following lines of code instead of the respective references to macros in your templates

  • Replace @Umbraco.RenderMacro(“Date”) with
@Umbraco.Field("date", formatAsDate:true)
  • Replace @Umbraco.RenderMacro(“Image”) with
<img src="@Model.Content.GetPropertyValue("Image")" alt=""/>

Summary

This write up should have taken you through all the necessary steps of creating a website that recycles its content with news module and calendar module. Hopefully, the explanations were clear and you will now be able to take this general tool and use it on your various projects.

I am looking forward to hearing about your experiences!

Umbraco DK Festival 2013

Umbraco DK Festival 2013 logo

photo courtesy of Douglas Robar

A little over a week ago I had the chance to participate in one of the many Umbraco community events, Umbraco DK Festival. The event is organized yearly by Kraftværk  and takes place in Aarhus. This year’s edition gathered 165 Umbracians which is over twice as much as last year.

In itself, the festival is  great place to meet local, like-minded, industry folk and strike up great conversations. But even though socializing is a vital part of the gathering, knowledge sharing is perhaps even more important.

The program for this year promised a full day of talks from Umbraco HQ, Kraftværk and a few other Umbraco-minded professionals  And it was not a disappointment. Out of all the talks I listened to 3 were especially interesting: Per Ploug’s presentation of Project Belle, Flemming Rubak’s “The responsive way”, and Douglas Robar’s ”How to become a superhero”. Each of the presentations was interesting for a different reason.

Project Belle

Project Belle is a long time coming update on the Umbraco backoffice interface. It’s built with AngularJS, RequireJS and Twitter Bootstrap. The changes concern not only the look and feel (no more Windows 95 style boxes) but also the way the interface is built and can be modified. You can download current prototype of Belle from this github. Slides from the presentation are also available here and though they are rather limited, you can still catch a glimpse of the beauty, that is the new backoffice. Currently there is no release date yet but my hopes are up because I think this will actually change a lot in terms of how we interact with Umbraco and the experience of our clients in their everyday operations.

The Responsive Way

Flemming Rubak from Kraftværk spoke about the topic that, while not being directly Umbraco related, concerns every webmaker out there, responsive websites. Much has been said and written on the topic and there are many tools one can employ. And while there certainly are some best practices and tried solutions, it is great to hear how an experienced developer puts together the different tools to create outstanding solutions.

Flemming Rubak did exactly that: explained his way of creating responsive websites with the use of, among others, WurflZen Grids and Sass. The presentation was long and elaborate and I won’t presume to repeat it all here. Suffice to say that a couple of really good tips stuck in my head and I’m looking forward to when the presentation slides will be published to look into the many resources mentioned.

How to become a superhero?

And lastly, Douglas Robar spoke of how to become a superhero to your clients by providing them with the best possible editor’s experience and limiting their possibilities for making errors. The presentation revolved around the KISS (keep it simple, stupid) principle of interface design but what made it special, was that it referred back to practical tips for building sites with Umbraco. Anyone who has ever worked with interface design must surely have heard about the rules of only providing relevant choices, hiding elements that would be distracting, or using enclosure and visual groupings to provide context. However how to bring these rules into life when setting up Umbraco’s backoffice for your editors is a whole other questions.  One, which in great part was answered by Doug Robar during his presentation.

All in all, Umbraco DK Festival 2013 was a great experience.  Both as an opportunity to meet active community members and socialize over a beer or two, but also a chance to share knowledge and be inspired to learn more and build better Umbraco websites.

Certified Umbraco Developer

Certified Umbraco Developer Badge

I am proud to say that, as of yesterday morning, I am Level 1 and 2 Certified Umbraco Developer.

You might have read some of my posts on Umbraco on this blog. I’ve met Umbraco for the first time about a year ago and I’ve been learning to use it and working with it ever since (although not all the time).

The culmination of this period was a great training that Umbraco offered to Business Academy‘s students, me included. Last week 30 students sat down for 3 intensive days to cram Level 1 and Level 2 material , which usually takes 4 days. It was a marathon but totally worth it.

Cake

The course was really well taught by Per Ploug of Umbraco HQ. He mixed short theoretical parts with longer practical exercises which kept us all wide awake and attentive. We were also supplied with pdf workbooks and pre-made Umbraco installations so that we could practice more advance techniques. The tempo of the class was also good. Due to some problems we encountered with MVC and Visual Studio along the way, there was a downtime during the second day. At that time the course would have greatly profited from a second teacher who could help handle the particular problems. However, thanks to the workbooks, the course didn’t grind to a total halt because students without the problems could continue exploring on their own.

And as a bonus, when it turned out there were some typos and small errors in the course materials, we were treated to an “I’m sorry cake” from Per. And I mean 30-people-worth-of-cake!

All in all, the experience was really good and worth the effort.  And it seems that it was the case for both sides. Today morning, I took the tests that belong with the course and I am now officially certified Level 1 and 2 Umbraco developer. And to anyone who wonders if they should do it or not, I say “Go for it”.

Responsive Umbraco Blog with Twitter Bootstrap

Umbraco 4.9 logo

So the semester assignments are finally turned in and the exam is still a way off so the time has finally come for me to look into Umbraco 4.9.
For the first little project to get me started, I’ve decided to try and put Twitter Bootstrap to work. Below you can find a relatively basic tutorial of how to make one of the starterkit blogs responsive.

The starterkit I am using is called SweetAs and is available during Umbraco 4.9 installation. We will start by stripping it down and then we will make it responsive with Bootstrap.

I am using Visual Studio to manipulate the files, but you might just as well do it from within your backoffice.


Setting Up Umbraco and Bootstrap Files

  1. Install Umbraco 4.9 with blog starterkit. Choose SweetAs.
  2. Download Bootstrap and add the files to proper folders in your Umbraco project (.js files to scripts/js, .css files to css folder)
  3. Remember to download new version of jquery (1.8 or newer) and substitute it for blog starterkit version (right now it’s 1.4 and it doesn’t work)
  4. Comment out references to all css files in masterpages/umbMaster.master and add reference to bootstrap.css and bootstrap-responsive.css, bootstrap.js. If you are sure you will use no styles from the existing css files, feel free to remove the links and the corresponding files. You can also remove reset.css – this is included in your bootstrap.css file anyway.
  5. In the same file replace the meta viewport tag with this tag:
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    

    The existing one with “;” causes Chrome to throw errors.

  6. Update: In order to make sure that the navigation also works from within the posts, you need to change the links to scripts. Go to umbBlogpost.master file and change the cp_head content placeholder to look like this:
    <asp:content contentplaceholderid="cp_head" runat="server">
      <script type="text/javascript" src="../../../scripts/js/jquery.min.js"></script>
      <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.5.5/jquery.validate.min.js></script>
      <script type="text/javascript" src="../../../scripts/js/bootstrap.js"></script>
    </asp:content>
    

Changing the Umbraco Code

  1. In masterpages/umbMaster.master: add class=”container-fluid” on div id=”main” and div id=”footer”
  2. In masterpages/umbHomepage.master: add “row-fluid” to the div id=”content” class. Now that we have the row we need to divide it into spans.
     <div class="bodyText span7">
    

    and

    <div id="sidebar" class="umbModuleContainer span4 offset1">
    

    You need to do that because the homepage document type contains two columns and we want to make sure that the layout responds nicely to screen sizes.

    If you want to make sure that the bodyText is on the left, make sure that it comes first in the code.

    To learn more about the fluid layout and the grid , take a look at the Bootstrap page.

  3. In xslt/umbTopNavigation.xslt: change the code within template tags so that it looks like the following:
    <xsl:template match="/">
    
          <div class="navbar">
            <div class="navbar-inner">
              <div class="container-fluid">
                <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
                <span class="icon-bar">.</span>
                <span class="icon-bar">.</span>
                <span class="icon-bar">.</span>
              </a>
                <div class="nav-collapse collapse">
                  <ul id="topNavigation" class="nav">
                    <li class="home">
                      <xsl:if test="$currentPage/@id = $currentPage/ancestor-or-self::* [@level=$level]/@id">
                        <xsl:attribute name="class">home current</xsl:attribute>
                      </xsl:if>
                      <a href="/">Home</a>
                    </li>
                    <xsl:for-each select="$currentPage/ancestor-or-self::* [@level=$level]/* [@isDoc and string(umbracoNaviHide) != '1']">
                      <li>
                        <xsl:if test="@id = $currentPage/@id">
                          <xsl:attribute name="class">current</xsl:attribute>
                        </xsl:if>
                        <a class="navigation" href="{umbraco.library:NiceUrl(@id)}">
                          <span>
                            <xsl:value-of select="@nodeName"/>
                          </span>
                        </a>
                      </li>
    
    
                    </xsl:for-each>
                  </ul>
                </div>
              </div>
            </div>
          </div>
    
        </xsl:template>
    

    It might look complicated but all you really do is adding the markup and classes to tell Bootstrap what to with the navigation when screen size changes.

    You can read more about what the different elements do on bootstrap’s page.

Final Thoughts

And done. Wasn’t so hard now, was it? Your blog is now responsive. Resize the screen to your heart’s content.

This, of course, is not the end of your work. Your blog is stripped bare so you will need to add styles and graphics. But all you need to do is dive into bootstrap.css and modify it as per usual. Enjoy and let me know how it went.

Switching your webshop to Quickpay

Quickpay Logo

Quickpay is a fast growing Danish payment gateway. They operate both in Denmark and abroad and it’s a good idea to have their system in your toolbox.

Quickpay offers modules that integrate with many existing shop-systems but just in case your shop-system is hand-crafted have a look at this quick overview of how to integrate secure payment window into any system.

Note: this overview describes integrating Quickpay into php-based website but the general remarks should be useful for .NET users as well.

3 Steps To Quickpay

  • If you don’t have an account yet, log in to the demo manager
  • Under Tools (Værktøjer) in the menu you will find generate code options
  • Get your PHP or ASP.NET code. In the simplest, php-version the folder you download contains 4 files: call-quickpay.php, error.php, form-quickpay.php and ok.php. You are most interested in form-quickpay.php. This is the form that needs to be posted to Quickpay. You place it on the page that stores the hidden details of the transaction, just before the page you want to make people pay.  For example if your shop system has the following flow: Your shopping cart – Enter your shipping details – Review Order – Pay, you want to place your form on the Review Order page and the pay button should post the form.

The Code Explained

<?php

	$protocol='6';
	$msgtype='authorize';
	$merchant='89898978';
	$language='en';
	$ordernumber = time();
	$amount='100';
	$currency='DKK';
	$continueurl='http://www.dyrernesnetsalg.dk/ok.php';
	$cancelurl='http://www.dyrernesnetsalg.dk/error.php';
	$callbackurl='http://www.dyrernesnetsalg.dk/callback.php'; //see http://quickpay.dk/clients/callback-quickpay.php.txt
	$md5secret ='29p61DveBZ79c3144LW61lVz1qrwk2gfAFCxPyi5sn49m3Y3IRK5M6SN5d8a68u7';

    $md5check = md5($protocol.$msgtype.$merchant.$language.$ordernumber.$amount.$currency.$continueurl.$cancelurl.$callbackurl.$md5secret);

?>

<form action="https://secure.quickpay.dk/form/" method="post">
    <input type="hidden" name="protocol" value="<?php echo $protocol ?>" />
    <input type="hidden" name="msgtype" value="<?php echo $msgtype ?>" />
    <input type="hidden" name="merchant" value="<?php echo $merchant ?>" />
    <input type="hidden" name="language" value="<?php echo $language ?>" />
    <input type="hidden" name="ordernumber" value="<?php echo $ordernumber ?>" />
    <input type="hidden" name="amount" value="<?php echo $amount ?>" />
    <input type="hidden" name="currency" value="<?php echo $currency ?>" />
    <input type="hidden" name="continueurl" value="<?php echo $continueurl ?>" />
    <input type="hidden" name="cancelurl" value="<?php echo $cancelurl ?>" />
    <input type="hidden" name="callbackurl" value="<?php echo $callbackurl ?>" />
    <input type="hidden" name="md5check" value="<?php echo $md5check ?>" />
    <input type="submit" value="Pay" />
</form>

The form code is fairly straightforward. The less obvious settings are:

  • merchant – this is a number you will receive from Quickpay once you order an account. (Settings->Merchant->QuickpayId
  • continueurl – Quickpay will redirect to this URL after the payment has been completed and authorized. This is basically your receipt page.
  • cancelurl – similarly, this is the URL that gets called if the payment wasn’t completed or wasn’t completed sucessfully
  • callbackurl – this one is a bit tricky. According to the QuickPay’s own documentation, Quickpay will make a call back to this URL with the result of the transaction. You can read more about it on Quickpay’s website. Callbackurl is not required but it provides an extra measure of security so it is advisable to use it.
  • $md5check – is again a value you will find in your Quickpay manager under Settings -> MD5Secret

Setting Up Quickpay Manager

Once you have the code running you need to enter your acquirer’s details under Settings->Acquirers. The information come from the company that actually handles the payments. It is up to you to get an agreement with one of the acquirers. You can read a bit more about this on Quickpay’s website.

While you are still testing your shop your gateway mode should be set to test (Settings->Gateway Mode). That means that no money will change hands until you are ready to move to production.

To make sure that everything is going just fine, have a look at the top-right corner of your dashboard (Merchant Status). If there is anything you forgot, this box should give you a warning. Clicking on the link will take you to the settings that need to be edited.

You can also use Testsuit (Tools->Testsuite) to test your setup.

Useful Links

  1. Technical Documentation
  2. Features
  3. Quickpay user guide

5 Impressive Umbraco Websites

Very recently I had the chance to see Per Ploug introduce Umbraco 4.9 and it made me want to look back into the CMS. For inspirations, I looked into existing Umbraco sites and here is the most interesting 5 I found.

1. Confio Software

Confio’s website is brilliantly interactive. And despite its complexity, it is easy to navigate.

Confio Software Website

2. Hasselblad

Another very complex and very well handled website.

Hasselblad website

3. FXUK

FXUK is an interesting example of Umbraco in use. Almost the entire content on the website consists of images and videos.

FXUK website

4. Education Impact

This page caught my attention because of the way it displays the news. The front-page grid layout attracts users and makes the news prominent.

Education Impact front page

5. Kiers.me

This last website constitutes an interesting counterweight to the previous 4. It is a personal website and a blog by Stephen Kiers and it shows that Umbraco can serve well in any size.

Stephen Kiers Blog