Sections and Trees in Umbraco 7

If you're used to working with Umbraco, you're probably also familiar with with the various sections of Umbraco - e.g. Content and Media for the editors, or Developer and Settings for the developers. If you need something more than what Umbraco gives you by default, you can also add your own section - which is what this article is about.

To summarize this article, I have also created a GitHub repository with a Visual Studio solution and the entire code explained in this article. To login, simply build the solution, and then use skrift for the username and skrift1234 as the password.

Understanding sections and trees

To start with the basics, sections are the links/icons you see in the dark horizontal bar to the left when logging into Umbraco. A section may have one or more trees (or none at all) - if you see the screenshot below, the Settingssection has individual trees for Stylesheets, Templatesand so on.

When we move on to the code and configuration of Umbraco, it's worth noticing that a section is also sometimes referred to as an app/application. The apps of a given Umbraco installation is determined by the ~/config/applications.config file. By default it will look like the XML below (each section has an alias, a name as well as an icon and a sort order):

Even with that line in the config file, the new section will not show up by default. In order for the section to show up, you have to go to the Users section and manually grant individual users access to the section. We could also grant all users access to the section through the code:

Since the section is added during startup, there is no way to determine the Umbraco user actually installing the package, and grant him/her access to the section, so it might be best to handle this process of granting access manually.

When you have granted your self access to the new section, you will see that the name looks a bit wrong. Even though we specified a title for the section (which isn't really used), we need to specify a translation for both the section (for the link in the horizontal bar) as well as for the dashboard of the section (see screenshot below).

To add the translation, we need to add our own language file (works from Umbraco 7.3 and above). If we create a new XML file at ~/App_Plugins/SkriftDemo/Lang/en-US.xml, Umbraco will automatically pick it up. The contents of the file should then look like:

We just need to provide a translation for en-US, since Umbraco will use this file as fallback for other languages. Of course you could provide translations for another language if the title differs from en-US.

Adding your custom tree

In a similar way, trees can be added with code as well. Basically there are two ways to do this - using the first we simply add a new tree using the ApplicationTreeService (similar to how we added the section/app).

Another way is to create a new class that extends the TreeController class that comes with Umbraco. We still need to extend this class to feed our tree with tree items, so we might as well use this class for registering the tree.

For this article, let's assume that our tree will list some of the famous animals in the Umbraco universe - namely Umbraco Rabbit and Umbraco Giraffe (you probably know them if you have been to #cg15 or #cg16). For the tree to work, we can create a SkriftAnimalsTreeController class that extends TreeController. Initially, our class can look something like below:

To make the class register properly with Umbraco, we need to add a Tree attribute to the class. The first parameter is the alias for our section/app, which is skrift, the second is the alias of our tree - eg. animals. The third parameter is a more friendly name of the tree. The attribute also has a number of optional parameters for specifying some of the other options described earlier in this article - eg. a custom icon.

The PluginController attribute is to help with Umbraco's routing, and generally it is a good idea to name this the same as your package folder in ~/App_Plugins/ - eg. SkriftDemo in our case.

With the PluginController class above, the code won't compile since we haven't yet added the required methods. TheTreeController class requires us to implement two methods; GetTreeNodes and GetMenuForNode.

For our animals tree, we could update the class to look like (you also find the full SkriftAnimalsTreeController class in the GitHub repository):

To explain the methods a little further, GetTreeNodes (we'll get back to GetMenuForNode later) is responsible for serving the items of the tree. The id parameter represents the ID of the node we're requesting items/children for (-1 indicates the root of the tree). For a more advanced tree, we could also check for individual IDs, and return a list of child items based on particular item.

For this article, I have created a SkriftAnimalsRepository we can use to get the animals stored in our demo database. The implementation of this repository isn't really important, so let's just focus on the GetAll method, which we can use to iterate over the animals.

To add an animal to the tree, we can call the CreateTreeNode method. Here the first parameter is the ID of the tree item (as a string), the second parameter is the parent ID. The fourth and fifth parameters are the name and the icon of the item respectively.

Given the code above, we should have a section and a tree looking something like this:

When an editor clicks on one of the animals, the URL in the address bar will change to something like /umbraco/#/skrift/animals/edit/1, where skrift is the alias of our section/app, animals is the alias of our tree and 1 is the ID of the tree item that the editor clicked on.

The URL is generated by Umbraco based on the attributes we specified for our tree earlier. If we want another URL, it is possible to specify a custom URL, but that is out of the scope of this article since becomes slightly more advanced.

Also, when trying to access the above URL, Umbraco will automatically attempt to load the following view: /App_Plugins/SkriftDemo/backoffice/animals/edit.html. Since we haven't created this view yet, Umbraco will trigger an error message about the the view not being found.

If you have cloned the repository from GitHub and opened the Visual Studio solution, you can find this file (and related files) here. With the view implemented, it can look something like this:

Gotta catch 'em all

The animals tree we have created so far is a bit simple with just a single level of items and the right click menu is empty.

Since Pokémon GO is a big trend right now, we can create a Pokémon based tree that is a bit more advanced. A SkriftPokemonTreeController could look like this (with the methods omitted):

using System.Globalization;
using System.Linq;
using System.Net.Http.Formatting;
using umbraco.BusinessLogic.Actions;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Mvc;
using Umbraco.Web.Trees;
using SkriftDemo.Models.Pokemon;
using SkriftDemo.Trees.Actions;
namespace SkriftDemo.Controllers.Api {
[Tree(SkriftConstants.SkriftSectionAlias, "pokemon", "Pokémon")]
[PluginController("SkriftDemo")]
public class SkriftPokemonTreeController : TreeController {
}
}

Multiple levels of tree items

Compared to SkriftAnimalsTreeController, this class adds a couple of new things - eg. multiple levels of tree items.

In our database, we have a small list of Pokémon, which we want to group by their type. So when Umbraco requests the root items of the tree, the id parameter for GetTreeNodes is -1, we can add each type to the collection of tree items.

// Get all types of pokemon in our repository so we can group them
foreach (string type in SkriftPokemonRepository.Current.GetAll().SelectMany(x => x.Type).Distinct()) {
// Add a node/item for the group (the last parameter indicates that this node has children)
nodes.Add(CreateTreeNode(type, id, queryStrings, type, "icon-playing-cards", true));
}

Notice that the last parameter specified for CreateTreeNode is set to true, which indicates that the tree item has children - in this case the individual Pokémon. If a item has any children, you will see the small expand icon in the backoffice.

The ID of a group/type can have a value like Normal or Water. With the groups/types added to the tree, our section will now look like on the screenshot below:

Notice that the tree area looks a little different than before since we now have two trees instead of one.

When the user expands one of the tree items, GetTreeNodes will be called again, but with the ID of the group/type. We can then add the Pokémon of the requested type:

With both trees expanded and a view implemented for viewing/editing a Pokémon, the section will now look like shown in the screenshot below:

Since we obviously want to evolveour Pokémon, we can add a right click menu item for evolving an individual Pokémon. Also, for the root tree item and the groups, we want to add the Reload nodesmenu item known from other Umbraco trees.

For this we can use the GetMenuForNode method. When Umbraco requests the right click menu for a Pokémon, we can run the following code:

We only add the Evolve menu item if the Pokémon can be evolved. Also note that SkriftPokemonEvolveAction is a custom class (shown below with unimportant properties omitted - the full class can be found in the GitHub repository):

Clicking the menu item will open a new dialog with the /App_Plugins/SkriftDemo/BackOffice/Pokemon/Evolve.html view, where Evolve in the URL comes from the Alias property in the SkriftPokemonEvolveAction class.

That's it!

That's really it for now. I hope this article helps you understand the basics of working with sections and trees in Umbraco 7. There is more to sections and trees which I haven't covered in this article, but it also starts to get more complex. But feel free to ask if your have any questions, and I'll try my best to help ;)

About the Author

Anders Bjerner is a System Developer at Skybrud.dk (an Umbraco Gold Partner located in Vejle, Denmark) with a background in Computer Science, and has been working with Umbraco since 2011. His work at Skybrud.dk typically consists of implementing Umbraco solutions for various government and private clients as well as developing custom packages (where a number of them can be found on Our Umbraco and NuGet). When not working and playing around with Umbraco, he can be found on his bike exploring the hills around his hometown (some of the steepest/highest in an otherwise flat Denmark).