Category Archives: Web Development

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.

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

Prototyping with Axure

Prototyping with AxureRecently, I had a chance to try my hand at prototyping with Axure (read: ak shûr). Axure is a software tool used to create interactive HTML prototypes of your apps or websites.

I don’t think that I need to tell you how important and useful prototyping is. But to quickly sum it up, prototyping can be used to:

  • iron out UI, UX and IA design
  • reach an understanding between you and the client about what it is exactly that the software/website is supposed to do
  • exchange feedback between you and your client
  • user test at the early stages
  • estimate development costs
  • bring large teams together by providing a reference points

Of course all these benefits exist only if the prototype can be clear enough and can be built and updated quickly.  And here is where Axure comes in.

How Does Axure Work?

To start using Axure, download the software from their website and follow the installation instructions. You can start with 30 days trial version to familiarize yourself with the product.

Once installed, Axure presents an extended drag-and-drop interface.

Axure drag-and-drop interface

The left-side panel shows the website structure and the website elements/widgets that you can drag-and-drop to the right-side panel, the website’s or app’s body.

Once the widget is in the body panel you can manipulate it by rearranging, aligning, adding text. And because Axure allows you to create interactive prototypes, you can also add behaviors to elements. For example, you can create drop down lists, add list items and show or hide other elements depending on which list item is chosen. Buttons can link to other pages, menus can show sub-menus on hover etc. Very neat.

Once your website or application is ready you can “build it” which basically creates the html code behind the prototype. Now you can open the website in your browser or send the code to other people involved in the project.

Good Things About Prototyping with Axure

The 3 best things about prototyping with Axure are:

  1. Simple and intuitive interface makes it really easy to use the product
  2. You can build powerful and complex interfaces really fast
  3. If you ever need support, Axure offers comprehensive training on their website

Bad Things About Prototyping with Axure

The 3 worst things about working with Axure are…well it’s hard to find bad things really. I guess the interface is designed to speak best to web professionals and designers so if you are nether you might need some help from the training.

And the second drag for me is the price of the software. Most companies can easily afford it but if you are a freelancer you might be hard pressed to gain access to this great software.

Conclusion

Prototyping is an important part of building a digital product, be it a desktop application or a website. And prototyping with Axure is a fast, professional, and powerful way to build complex prototypes. Ergo, if you are a web professional or a programmer and you can afford it, Axure should be in your toolbox.

Beginning with ASP.NET MVC 3

ASP.NET MVC 3 logo

If you’re like me and constantly try to learn something new, you surely realized by now that the best way to learn a new, complicated topic is by explaining it to someone else.

For the last couple of weeks I’ve been reading up and trying my hand in C# and ASP.NET MVC. It’s going ok but there are still some things that feel a bit shaky in my mind. This is why I’m writing this post. This is a beginner’s understanding of  ASP.NET MVC.

What is ASP.NET MVC?

What is .NET Framework and ASP.NET?

In short .NET Framework is a collection of technologies designed to help developers build a variety of applications such as rich Windows applications, command line tools or web applications. ASP.NET is one part of .NET Framework used to build web applications. It is an engine that hosts said web applications and a high-level development platform designed as a server-side technology, which means that all code executes on the server.

What is MVC?

MVC is a design (aka. architecture) pattern which can be used to design applications with any programming language. It is based on a principle of separation of concerns. This means that the business logic of the application is separate from the input logic (or interaction) and the presentation logic (or the UI).

Before MVC was introduced as the leading design pattern for ASP.NET applications, it used Web Forms. While Web Forms are still supported MVC is the future of ASP.NET-based applications. The two main advantages of MVC over Web Forms are:

  1. Web Forms use View State and postbacks to create the effect of statefulness. While this make some coding faster (and easier to learn) it also means that every time a page is requested all input field states are send to the server and back. This can result in slow applications (even if they are rather small).
  2. Thanks to the way it is built, MVC gives the developer more control over the code (including the final html markup) and the application’s behavior.
If you want to read more on the topic of differences between the two patterns have a look at the official Microsoft ASP.NET website.

The abbreviation MVC stands for Model, View and Controller.

M is for Model

Model, or domain model, is the heart of the application. Domain is the real-world entities, operations and rules that exist in the industry, company or activity that the application will support. The domain model is the software representation of the domain.  In practice that means that the model part of the application contains domain objects (classes) and methods that store and operate on data.

C is for Controllers

Controllers have .cs extension and their job is to receive and handle incoming http requests, manipulate data in the model and choose the view in which this data should be rendered to the user.  Public methods in controllers are called action methods and these methods can be invoked from the browser through an URL. In MVC URLs do not correspond to physical files. Each action method has its own URL. This means that when user opens /, /Home, or /Home/Index, the Index() action method from HomeController.cs is called.

Controllers also return views by calling the View() method (return View(); – returns default view) which results in creating ViewResult object. This object instructs the application which view should be used to render the data.

V is for View

Views are files with the .cshtml extension. They are used to render html. Views are associated with action methods by naming conventions. Index action method will by default point to index view in the folder named after the controller file in which Index action method is placed.