Tag Archives: umbraco

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.

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.

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

Umbraco: The King is Dead, Long Live v4

Codegarden 2012Codegarden 2012 (official Umbraco conference) is now over and it is time to sum things up. As many, if not all of you, have already heard this years conference was all about the big announcement to pull the plug on Umbraco v5. The announcement was delivered by Niels Hartvig, the founder of Umbraco, in his keynote address you can see online.

Umbraco v5 was released earlier this year and was trumpeted as the best thing that ever happened to humanity since sliced bread. But, as it now turns out, the many performance issues were partly caused by the complexity of the code. This complexity made it impossible for Umbraco core developers to quickly solve problems or to have the community help out (which was the practice in v4). And therefore the surely difficult decision was made to revert focus to v4. Resources will now be spent on improving v4 by rolling out new features, also these intended primarily for v5. Umbraco HQ is hoping to enlist the help of the community and work in short development cycles to show the progress.

The announcement was met with mixed reactions from the community.  Developers and web agencies who already invested time and money are deeply disappointed, hurt and angry about the decision. They stand to loose some business over this unfortunate turn of events. Others, and they seem to be more vocal, support the decision and are happy to see the resources brought back to working on v4, a mature and stable platform. 

Codegarden 2012

Now it seems that the future of Umbraco rides on how fast and how well can v4 be developed. Some people have certainly lost confidence in Umbraco and need to be persuaded

that they can trust the same team with the future of their businesses. However it doesn’t seem like the “hardcore” supporters and developers are ready to give up on the project just yet. The next couple of months will show if the brand can still stand for quality and be a respected as a business solution.

If you are interested to read more about the topic and people’s reaction, check out our.umbraco.com blog.

Installing Umbraco 5.1

Umbraco 5.1 the friendly CMS

This is another post from the basic Umbraco series. For the tutorial on how to create your first Umbraco website check this category.

This post is even more basic and it describes how to get Umbraco 5.1 installed on your computer. It assumes that you already have Microsoft WebMatrix and Microsoft SQL Server running.

If you look into WebMatrix Site Gallery you will see that there are two Umbraco installations available: the first one is Umbraco 4 and the other is Umbraco 5.01. But if you want to install Umbraco 5.1 you need to begin with downloading it.

1. Go to this Codeplex page and choose Umbraco 5.1 CMS Web Application download link.

2. Unzip the downloaded folder to where your websites reside. Documents>My Web Sites is always a good place if you don’t have anything in mind. Call the website folder something descriptive. I called mine Umbraco 5.1 (since it is only going to be used for this post).

3. In WebMatrix choose Site from Folder and navigate to your Umbraco 5.1 folder. Click Select Folder

Web Matrix screenshot

4. Choose “Site”. On the new screen you will see two links. One is the URL to your website and one is the Path to your website on your disk. Click URL to start the installation process in your browser. In the future, once your website is installed, this is how you will be able to navigate to your website and the backoffice in your browser.

Webmatrix screenshot

5. Now your browser opens the first installation screen and all you need to do here is to click on the Install Umbraco 5.1 link:

Umbraco installation screen 1

6. This takes you to the second screen and this time you only need to click on the big “Let’s get started” button.

Umbraco 5.1 installation screen 2

7. The third screen is where you set up your database. If you have no experience doing that choose “I want to use SQL CE 4, a free, quick-and-simple embedded database”. These option will take care of everything for you. Then click “Install”.

Installing Umbraco 5.1 screen 3

8. The next screen is the happy screen that shows you you’re done installing and everything went fine. Click “Continue” to continue (sic!).

Umbraco 5.1 installation screen

9. On this screen you need to set up your umbraco user. Fill in the small form and make sure you remember the password or you won’t be able to log into your Umbraco back office.

Umbraco 5.1 installation screen

10. Next, you can choose to install a starter kit which is a basically a filled website. If you are new to Umbraco this might be a good way to learn your way around the back office. Click on the icon to install the kit or on the “skip” button to move on.

Umbraco 5.1 installation screen

11. And your done. Now you can move on to creating your website. Only thing that stands between you and your website is clicking the “Set up your new website” button and logging in to your  back office (using the login and password you set up in step 9).

Umbraco 5.1 installation screen

Building first Umbraco 5 website: Partials

The post you are reading now is the last one in a series of four blog posts describing my experience with creating a relatively simple Umbraco 5-based website for a made-up coffee shop.

  1. Building first Umbraco 5 website: Document Types
  2. Building first Umbraco 5 website: Nodes
  3. Building first Umbraco 5 website: Templates
  4. Building first Umbraco 5 website: Partials

The posts are written in a form of beginners’ tutorial and will take you through all the steps necessary for creating the website. Disclaimer: However, please keep in mind that I am in no way an expert or professional in the field. Therefore I submit to you a description of a learning process rather than an expert tutorial and I ask you to read it as such. More than that, I encourage you to commen,t to point out mistakes or suggest better practices.In the first three posts I:

  1. Explained the concept of document types
  2. Analyzed my website from the perspective of document types
  3. Created document types necessary for the website
  4. Explained the concept of nodes and explained how they are created
  5. Build the document structure for the website we are creating
  6. Showed you how to display content on your website using templates
  7. Explained the way to attach external stylesheets and JavaScript files to your website

In this post I will talk about partials and how to use them in your website. I will also show you the code used to complete the website we were working on. I will explain what the partials do in broad strokes but I will not go into much details. After you go through all the steps in this post, your website will be ready to post online.

Step 1: Partials in Theory

Partials are the life blood of Umbraco. They make sure that your page is dynamic and not everything has to be done manually. They are basically snippets of code written with Razor, which is an ASP.NET programming syntax used to create websites with the C#. For example you might remember that we didn’t want to hard-code the navigation in the header. We didn’t want it because that would mean that with every new page created by the administrator of our page, we would have to go into the code and add a new <li> to our <nav> list.  Partials are exactly the thing that will save us from doing that. We will add a piece of code to our website that will check for all child pages of the root and display them as navigation.

Creating New Partials

  1. In back office go to settings
  2. Right-click on Partials and choose create
  3. Give your partial a recognizable name
  4. Remember to save after editing
Creating New Partials in Umbraco 5

Using Partials in your templates

  1. Go to your template
  2. Place your cursor in the code where the partial should be executed
  3. Choose Insert Partial View from top menu
  4. Choose your partial from the list
  5. Click insert
Inserting Partials in Umbraco 5

Alternatively you can also type: @Html.Partial(“yourPartialsName”)in the templates code.

Step 2: Partials in Practice

To get our website to work we need 4 partials:

  1. For displaying images
  2. For top navigation
  3. For listing events on events page
  4. For displaying content on Menu page

The image partial (called: image) is this:

@inherits RenderViewPage
@using Umbraco.Cms.Web;</pre>
<img src="@Umbraco.GetMediaUrl(@Model.Field(" alt="" />
<pre>

This partial is really just one line of code and you could use it directly in your templates. You can go ahead and replace all @Umbraco.Field(“image”) in your templates with img src=”@Umbraco.GetMediaUrl(@Model.Field(“image”).ToString())”. Placing it in a separate partial introduces more order and organization to your code. Note that if you don’t have an image picked in your content section, this partial will actually cause an error. A more complicated partial that checks if the image hiveId exists is necessary to avoid this error.

This is top navigation (called: navigation):

@inherits RenderViewPage
@using Umbraco.Cms.Web
@using Umbraco.Cms.Web.Macros
@using Umbraco.Framework

@{
    @* Walks up the nodes tree from the current page to the rootNode *@
    var rootNode = DynamicModel.AncestorsOrSelf.Last();
}

@* Checks if the rootNode has children *@
@if (rootNode.Children.Any())
{</pre>
<ul>
	<li>@* Adds the rootNode to the navigation *@ <a href="@rootNode.Url">@rootNode.Name</a></li>
@* For each childPage of the rootNode add the child to the navigation as list item *@ @foreach(var childPage in rootNode.Children) {
	<li><a href="@childPage.Url">@childPage.Name</a></li>
}</ul>
<pre>}
  1. It starts by defining what rootNode is
  2. Then it checks if rootNode has children
  3. If there are children it starts by listing the rootNode (in our case the Home page)
  4. Then it lists the page name and makes it a link

Notice that we add HTML elements that should be created. For example the navigation items are enveloped in an unordered list and list item tags.

Listing events on events page (called: Sub Navigation):

@inherits RenderViewPage
@using Umbraco.Cms.Web
@using Umbraco.Cms.Web.Macros
@using Umbraco.Framework

@* Checks if the rootNode has children*@
@if(DynamicModel.Children.Any())
{</pre>
<div class="lable"><nav id="subnavi">
<ul>@* For each childPage of the rootNode *@ @foreach (var childPage in DynamicModel.Children) {
	<li><a href="@childPage.Url"> @childPage.Name
@childPage.Date
 </a></li>
}</ul>
</nav></div>
<pre>}

This partial basically does the same thing as the previous one, except it only lists the descendants of the page on which the partial is placed. In our case it will list all the particular event pages on the Events page.

Displaying content on Menu page (called: Inherit Content):

@inherits RenderViewPage
@using Umbraco.Cms.Web
@using Umbraco.Cms.Web.Macros
@using Umbraco.Framework

@* Check if rootNode has children*@
@if(DynamicModel.Children.Any())
{
    foreach (var childPage in DynamicModel.Children)
    {
    <ul class="menu">
        <li>
           <h1 class="section">@childPage.heading</h1>
           @if(childPage.Children.Any())
            {
                foreach (var grandchildPage in childPage.Children)
                {
                    <li>
                      <h3 class="item">@grandchildPage.menuItem</h3>
                      <span class="price">@grandchildPage.price</span>
                    </li>
                }
            }
        </li>
     </ul>
    }
}
  1. It checks if there are any children from the page on which this partial is used (these are menu sections)
  2. It lists the heading of the child page
  3. It checks if the childPage has any children (these are menu items)
  4. It lists the childPage child’s menuItem property value and price property value

Linking partials to templates:

  1. In your templates insert image partial everywhere you have @Umbraco.Field(“image”)
  2. Insert Inherit Content at the bottom of Menu template
  3. Insert Navigation into the Layout template between the <nav></nav> tags
  4. Insert the Sub Navigation partial into Events template just before the last </div> tag

The partials used in this website are all variations of the partials that you can find in the Umbraco 5 documentation on GitHub. You can learn much more by browsing this page.