A guide to creating a custom tree in Umbraco
This section describes how to work with and create trees with Umbraco APIs.
To Create a Tree in a section of the Umbraco backoffice, you need to take several steps:
Create a TreeController
class in C#. A new controller which inherits from the abstract Umbraco.Cms.Web.BackOffice.Trees.TreeController
*` class and provides an implementation for two abstract methods:
GetTreeNodes (returns a TreeNodeCollection
) - Responsible for rendering the content of the tree structure;
GetMenuForNode (returns a MenuItemCollection
) - Responsible for returning the menu structure to use for a particular node within a tree.
You will need to add a constructor as TreeController
requires this. See full code snippet in the "Implementing the Tree" section below.
The Tree
attribute used to decorate the TreeController
has multiple properties.
SectionAlias
- Alias of the section in which the tree appears
TreeAlias
- Alias of the tree
TreeTitle
- The title of the tree
TreeGroup
- The tree group, the tree belongs to
SortOrder
- Sort order of the tree
For example:
The example above would register a custom tree with a title 'Favourite Things Name' in the Settings section of Umbraco, inside a custom group called 'Favourites'.
Tree Groups enable you to group trees in a section. You provide the alias of the Tree Group name, you wish to add your tree to - see Constants.Trees.Groups for a list of existing group alias. An example of tree groups in the backoffice would be the Settings tree group and the Templating tree group in the Settings section.
If you add your own alias, you'll need to add a translation key. This can be done by adding a language file to a lang
folder with your application folder in App_Plugins
: App_Plugins/favouriteThings/lang/en-us.xml
. This will avoid the alias appearing as the header in [square brackets].
The language file should contain the following XML:
The first node in the tree is referred to as the Root Node. You can customise the Root Node by overriding the abstract CreateRootNode
method. You can assign a custom icon to the Root Node. You can also specify a custom URL route path in the backoffice to use with your custom tree. The method can be useful if your section has a single node (single page app).
See Also: How to create your own custom section
The actions on items in an Umbraco Tree will trigger a request to load an AngularJS view, with a name corresponding to the name of the action, from a subfolder of the views folder matching the name of the 'customTreeAlias'.
Clicking on one of the 'Favourite Things' in the custom tree example will load an edit.html
view from the folder: /views/favouriteThingsAlias/edit.html
. The 'Delete' menu item would also load a view from: /views/favouriteThingsAlias/delete.html
When creating a custom tree as part of a Umbraco package, it is recommended to change the location of the default folder. It should be changed to the App_Plugins
folder. You achieve this by decorating your MVC TreeController
with the PluginController
attribute.
The edit view in the example would now be loaded from the location: /App_Plugins/favouriteThings/backoffice/favouriteThingsAlias/edit.html
You can instruct the Umbraco backoffice to load additional JavaScript resources (eg. angularJS controllers) to use in conjunction with your 'tree action views' by adding a package.manifest
file in the same folder location as your views.
For example...
...this manifest would load files for two controllers to work with the edit and delete views and a general resource file, perhaps containing code to retrieve, create, edit and delete 'favourite things' from some external non-Umbraco API.
Our Tree Action View would then be wired to the loaded controller using the ng-controller
attribute. The delete view would perhaps the delete view look a little bit like this:
see Tree Actions for a list of tree ActionMenuItems and IActions
It is possible to create 'trees' consisting of only a single node - perhaps to provide an area to control some settings or a placeholder for a single page backoffice app. See the LogViewer in the settings section for a good example. (or as in the case of the 'settings/content templates' tree, it's possible to have a custom view for the root node as an 'introduction' page to the tree).
In both scenarios you need to override the CreateRootNode
method for the custom tree.
You can override the CreateRootNode
method to set the 'RoutePath' to where the single page application will live (or introduction page). Setting HasChildren
to false
will result in a Single Node Tree.
The RoutePath should be in the format of: section/treeAlias/method. As our example controller uses the PluginController
attribute, clicking the root node would now request /App_Plugins/favouriteThing/backoffice/favouritistThingsAlias/overview.html
. If you are not using the PluginController
attribute, then the request would be to /umbraco/views/favouritistThingsAlias/overview.html
.
It's possible to make your single node tree app stretch across the full screen of the backoffice (no navigation tree) - see Packages section for an example. To achieve this add an additional attribute IsSingleNodeTree
, in the Tree attribute for the custom controller.
All tree notications are defined in the namespace Umbraco.Cms.Core.Notifications
.
For more information about registering and using notifications see Notifications
The RootNodeRenderingNotification
is published whenever a tree's root node is created.
Members:
TreeNode Node
FormCollection QueryString
string TreeAlias
Usage:
The TreeNodesRenderingNotification
is published whenever a list of child nodes are created.
Members:
TreeNodeCollection Nodes
FormCollection QueryString
string TreeAlias
Usage:
The MenuRenderingNotification
is raised whenever a menu is generated for a tree node.
Members:
MenuItemCollection Menu
string NodeId
FormCollection QueryString
string TreeAlias
Usage:
An explanation on sections and trees in Umbraco
The Umbraco backoffice consists of sections (sometimes referred to as applications) which contain Trees.
Each section is identified by its name in the top navigation ribbon of the Umbraco Backoffice.
For example, when you load the backoffice, you'll see that the 'Content' section contains one tree: the content tree. Meanwhile, the 'Settings' section contains a number of trees such as Stylesheets, Document Types, Media Types, etc...
You can create your own sections and trees to extend Umbraco.
Describes Umbraco Sections, configuration and APIs.
Describes Umbraco Trees, configuration, APIs and events.
Explains how to customise the backoffice search of a Section Tree.
An explanation on sections and trees in Umbraco
The Umbraco backoffice consists of Sections, also referred to as Applications, which contain Trees.
Each section is shown in the top navigation ribbon of the Umbraco Backoffice.
To create a new custom section in your Umbraco backoffice, the first thing you have to do is create a new folder inside /App_Plugins
. We will call it MyFavouriteThings
.
Next we need to create a manifest where we'll include some basic configuration for our new section.
There are two approaches to registering a custom section to appear in the Umbraco Backoffice:
Create a new file in the /App_Plugins/MyFavouriteThings/
folder and name it package.manifest
. In this new file, copy the code snippet below and save it.
This would create a new section in your Umbraco backoffice called 'My Favourite Things'.
When registering your section this way, it is added to the end of the collection of sections. But as more package.manifest
files may do the same, the order of the additional sections depends on the order of which the package.manifest
files are loaded. Registering your section this way doesn't allow further control of its order.
By creating a C# class that implements ISection
from Umbraco.Cms.Core.Sections
:
For your C# type to be discovered by Umbraco at application start up, it needs to be appended to the SectionCollectionBuilder
.
You can achieve this by creating a Composer
and call the Append
method:
This would also create a new section called 'My Favourite Things' in your Umbraco Backoffice.
Similar to registering the section via a package.manifest
file, the Append<>
method will add the section to the end of the list of sections.
If you wish to control the order of your section a bit more, you can use some of the other methods on SectionsCollectionBuilder
. For instance, you can add your section at a specific index:
Or before or after an existing section:
The final order of the sections is down to the order of which the composers are executed. For instance, two composers may add a new section before SettingsSection
, in which case the latter will add its section between the section of the first composer and SettingsSection
.
You will also need to allow your current Umbraco User group access to this new Custom Section via the backoffice! (you will need to logout and back in again to see the change)
When your new custom section appears, you'll notice only the section 'Alias' is displayed inside square brackets. This is because Umbraco caters for Multiple Languages in the backoffice, and is looking for a translation file for the current backoffice culture, containing a translation key for your custom section alias.
Create a /lang folder in the folder where you are creating the implementation for your custom section. If you do not have one already, create a /lang
folder within the folder where you are creating the implementation for your custom section, eg. /App_Plugins/MyFavouriteThings/lang/
.
It is worth knowing that the /lang
folder does not have to be directly in the MyFavouriteThings folder, it can be nested deeper if you need it to be. The only requirement is that the folder is called lang. E.g. ~/App_Plugins/MyFavouriteThings/Some/Another/Lang/.
Inside this folder create a file called en-us.xml. This is the 'default' fallback language translation file. Add the following definition:
Recycle the application pool, and the square brackets will be gone, and your section will have the title 'My Favourite Things'.
You can add custom language translation keys in this file for providing translated versions of text used throughout your custom section/tree implementation.
To provide translations in other languages, duplicate the en-us.xml file in the /lang folder and rename it to match the lang/culture combination of your newly supported language. Update the contents of the language element attributes, and provide a translation for each 'language translation key'.
You will need to recycle the application pool, to see changes to the language translation files reflected in the backoffice.
A guide to creating a custom tree action in Umbraco
Items in an Umbraco Tree can have associated Actions. The actions visible to the currently logged in user can be controlled via User Permissions.
You can set a User's permissions for each item in the Umbraco Content tree from the User Section of the Umbraco Backoffice.
If you are developing a custom section, or a custom Dashboard, you might want to display some different options based on a User's permission set on a particular item.
For example, on a custom dashboard you might add a quick 'Create a Blog Post' button for an editor, but only if that editor has permissions to create a blog post. You could create some sort of API endpoint, to call from your AngularJS controller, that in turn uses the UserService to return the current user's permissions. Then you can see whether they have the required permission to 'create' within the site's blog section.
Each tree action in Umbraco implements the IAction interface, and each Action has a corresponding 'Letter', and a boolean value describing whether permissions can be assigned for an action.
When you pull back the AssignedPermissions for a user on a particular item, it is these letters that indicate which actions the User is permitted to perform in the context of the tree item.
Here is a list of the tree actions and associated user permission codes shipped by Umbraco CMS and add-on projects (such as Umbraco Deploy), as well as those used by some community packages.
If building a package or adding custom tree actions to your solution, it's important to pick a permission letter that doesn't clash with one of these.
If you have created a package using a custom tree action, please consider providing an update to this documentation page via a PR to the documentation repository, such that other developers can discover and avoid using the same permission letter.
Note: up until Umbraco Deploy 9.2.0, the letter "N" was used for the "Queue For Transfer" action. In 9.2.1 it was changed to be "T", to avoid clashing with the letter selected for the Umbraco CMS "Notify" action, introduced in CMS version 8.18.
When you type a search term into the Umbraco backoffice search field, you'll see search results from all the Section Trees that your user account has permission to access:
The results are grouped by 'Section Tree' like Content, Media, Document Types. Each 'Tree' has its own associated search mechanism that receives the search term and looks for matches in the tree that is responsible for searching.
You can create your own search mechanisms for your own custom sections or replace the default search implementation for a particular section tree.
To create a search for your own custom tree you need to create a C# class that implements the interface Umbraco.Cms.Core.Trees.ISearchableTree
.
Your implementation needs to return an IEnumerable of SearchResultEntity
items:
A SearchResultEntity
consists of a Score (a Float value) identifying its relevance to the search term, and the set of EntityBasic
properties that all Umbraco objects share: eg Name, Id, Udi, Icon, Trashed, Key, ParentId, Path, Alias, AdditionalData.
If we have a custom section Tree with the alias 'favouriteThingsAlias' (see the custom tree example) then we could implement searchability by creating the following C# class in our site:
That's all we need, after an application pool recycle, if we now search in the backoffice we'll see matches from our custom 'Favourite Things' tree:
Umbraco automatically finds any implementation of ISearchableTree
in your site and automatically configures it to be used for the custom section mentioned in the TreeAlias property. Be careful not to accidentally have two ISearchableTree
implementations trying to search the 'same' TreeAlias, it's one ISearchableTree
per TreeAlias.
Perhaps you want to change the logic for searching an existing section of the site, (why? - well you might have a 'company name' property on a MemberType in the Member section, and you want searches for that company name to filter the members who work there, the default implementation will only search on Member Name).
Or perhaps you want to replace Examine search in the backoffice with an external Search Service, e.g. Azure Search. In a cloud-hosted implementation you don't need to build the Examine indexes on each new server as your cloud hosting scales out.
First create your replacement custom ISearchableTree
implementation, using the same approach as above, but specifying the TreeAlias of the Tree you aim to replace, e.g. 'Member'.
To avoid your custom implementation clashing with the default ISearchableTree
for a Tree, you need to remove its ISearchableTree
implementation from the collection of SearchableTrees using an IComposer
when Umbraco starts up:
This would then allow your custom implementation of ISearchableTree
with TreeAlias 'member' to be used when searching the Member Section Tree.
Type | Alias | Letter | Can Be Permission Assigned |
---|---|---|---|
Umbraco.Cms.Core.Actions.ActionAssignDomain
assignDomain
I
True
Umbraco.Cms.Core.Actions.ActionBrowse
browse
F
True
Umbraco.Cms.Core.Actions.ActionCopy
copy
O
True
Umbraco.Cms.Core.Actions.ActionCreateBlueprintFromContent
createblueprint
ï
True
Umbraco.Cms.Core.Actions.ActionDelete
delete
D
True
Umbraco.Cms.Core.Actions.ActionMove
move
M
True
Umbraco.Cms.Core.Actions.ActionNew
create
C
True
Umbraco.Cms.Core.Actions.ActionNotify
notify
N
True
Umbraco.Cms.Core.Actions.ActionProtect
protect
P
True
Umbraco.Cms.Core.Actions.ActionPublish
publish
U
True
Umbraco.Cms.Core.Actions.ActionRestore
restore
V
False
Umbraco.Cms.Core.Actions.ActionRights
rights
R
True
Umbraco.Cms.Core.Actions.ActionRollback
rollback
K
True
Umbraco.Cms.Core.Actions.ActionSort
sort
S
True
Umbraco.Cms.Core.Actions.ActionToPublish
sendtopublish
H
True
Umbraco.Cms.Core.Actions.ActionUnpublish
unpublish
Z
True
Umbraco.Cms.Core.Actions.ActionUpdate
update
A
True
Umbraco.Deploy.UI.Actions.ActionDeployRestore
deployRestore
Q
True
Umbraco.Deploy.UI.Actions.ActionDeployTreeRestore
deployTreeRestore
Ψ
True
Umbraco.Deploy.UI.Actions.ActionPartialRestore
deployPartialRestore
Ø
True
Umbraco.Deploy.UI.Actions.ActionQueueForTransfer
deployQueueForTransfer
T
True
Umbraco.Deploy.UI.Actions.Export
deployExport
П
True
Umbraco.Deploy.UI.Actions.Import
deployImport
Џ
True
Jumoo.TranslationManager.Core.Actions.ActionTranslate
translate
5
True
Jumoo.TranslationManager.Core.Actions.ActionManageTranslation
manageTranslations
Ť
True
uSync.Publisher.Actions.PushToServer
pushContent
>
True
uSync.Publisher.Actions.PullFromServer
pullContent
<
True
uSync.Publisher.Action.PushButton
pushContentButton
^
True
Our.Umbraco.LinkedPages.LinkedAction
linkPages
l
True