Umbraco backend
In a previous post we saw the use of your own MVC controllers in the Umbraco front end. The Umbraco community has made effort to document these possibilities on our.umbraco.org. The use of MVC in the back end of the Umbraco interface is not very well documented and it took me some analysis and debugging of the source code to find out how this could be implemented.
Default you have 7 sections in the backend:
- Content
- Media
- Settings
- Developer
- Users
- Members
- Translation (hidden by default for the admin user)
These sections will do in a plain website where you don’t want to use custom code.
One of my personal projects is to create a new website for the sport club where I’m already 15 years affiliated with. The current website is already 7 or 8 years old and was written in PHP. Needless to say that the look and feel is outdated, the PHP code I’ve written 8 years ago wasn’t the most beautiful piece of code I’ve created.
I wanted to create a new website in ASP.NET MVC but I don’t want to put the effort in creating controllers and views for the administration part where a good CMS can fill up this gap.
Because I’m already familiar with Umbraco it looked a good choice to combine the strength of Umbraco with custom development. But therefor I had to get custom MVC controllers working in the backend.
New backend project
This blog post goes further on the project created in the previous post. You can download the source at Github.
In the existing solution we’ll add a new project by right clicking the solution and choose Add – New project.
Choose for a ASP.NET MVC 3 Web Application and give it a name (in this demo: UmbracoMVCDemo.BackEnd)
In the next window choose for Internet Application and the Razor view engine. We’ll need the core Umbraco libraries so open up the Package Manager Console and type ‘”Install-Package UmbracoCms.Core” and
hit enter. Wait for the “Successfully added ‘UmbracoCms.Core 4.11.1’ to UmbracoMVCDemo.BackEnd.” message and we’re ready to go.
Adding a new section
First we’ll have to create a new section in the Umbraco backend. Open op your windows explorer and go to the root of the Umbraco installation. Open up the config folder and open up the applications.config file.
<?xml version="1.0" encoding="utf-8"?>
<applications>
<add alias="content" name="Content" icon=".traycontent" sortOrder="0" />
<add alias="media" name="Media" icon=".traymedia" sortOrder="1" />
<add alias="settings" name="Settings" icon=".traysettings" sortOrder="2" />
<add alias="developer" name="Developer" icon=".traydeveloper" sortOrder="3" />
<add alias="users" name="Users" icon=".trayusers" sortOrder="4" />
<add alias="member" name="Members" icon=".traymember" sortOrder="5" />
<add alias="translation" name="Translation" icon=".traytranslation" sortOrder="6" />
</applications>
You’ll see the existing 7 sections configured in the config file. We’ll add a new one:
<add alias="demo" name="Demo" icon="demo.gif" sortOrder="7" />
We give the new application or section the alias ‘demo’ and the name ‘Demo’. For the icon we’ll add a new picture called ‘demo.gif‘ and set the sort order to 7. (The default images are showed using sprites where the icon name is actually the css class to define the background image. You can choose to alter the existing sprite and add a css class or select new picture). Create a new picture with your favorite image editor, name it ‘demo.gif’ and place it in the folder: [Umbraco root]/umbraco/images/tray/ folder.
We’ll have to force the application pool to restart before Umbraco loads the new section. Open up the web.config file in the umbraco installation root and add a line break, save and refresh the page.
Still the new section is not available in the backend because none of the users have access to this new section. Go to the users section, open up your user and add a check in the sections area next to the demo section. Refresh your page and you’ll see the new section appear.
When you click the Demo section at the bottom of the page we’ll see a new section opening up with an empty tree and the section name in square brackets.
Adding a tree
Next step will be to create the tree for our Demo section. This tree you can populate with data you choose. For this demo we’ll use some dummy data.
In our backed project add a folder ‘Trees’ and add a new class named DemoTree. This class will have to inhered from the umbraco.cms.presentation.Trees.BaseTree class. This class has 3 abstract methods that we’ll need to implement.
- RenderJS: the javascript that will run when a tree node is clicked
- Render: the creation of tree nodes
- CreateRootNode: to define the root node.
We’ll also have to create a constructor that will receive a string that will forward to the base class.
Last but not least we’ll have to decorate the class with the umbraco.businesslogic.TreeArrtibute where we define the application alias and title.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using umbraco.cms.presentation.Trees;
namespace UmbracoMVCDemo.BackEnd.Trees
{
[Tree("demo","demo","Demo")]
public class DemoTree : umbraco.cms.presentation.Trees.BaseTree
{
public DemoTree(string application) : base(application)
{
}
public override void RenderJS(ref StringBuilder Javascript)
{
throw new NotImplementedException();
}
public override void Render(ref XmlTree tree)
{
throw new NotImplementedException();
}
protected override void CreateRootNode(ref XmlTreeNode rootNode)
{
throw new NotImplementedException();
}
}
}
Let’s start with defining the root node what is as simple as setting the NodeType and NodeId parameter.
protected override void CreateRootNode(ref XmlTreeNode rootNode)
{
rootNode.NodeType = "init" + TreeAlias;
rootNode.NodeID = "init";
}
Next we’ll implement the Render method and attach 2 nodes to the tree.
public override void Render(ref XmlTree tree)
{
XmlTreeNode xNode = XmlTreeNode.Create(this);
xNode.NodeID = "1";
xNode.Text = "Demo Node 1";
xNode.Action = "";
xNode.Icon = "folder.gif";
xNode.OpenIcon = "folder_o.gif";
OnBeforeNodeRender(ref tree, ref xNode, EventArgs.Empty);
if (xNode != null)
{
tree.Add(xNode);
OnAfterNodeRender(ref tree, ref xNode, EventArgs.Empty);
}
XmlTreeNode xNode2 = XmlTreeNode.Create(this);
xNode2.NodeID = "2";
xNode2.Text = "Demo node 2";
xNode2.Action = "";
xNode2.Icon = "folder.gif";
xNode2.OpenIcon = "folder_o.gif";
OnBeforeNodeRender(ref tree, ref xNode2, EventArgs.Empty);
if (xNode2 != null)
{
tree.Add(xNode2);
OnAfterNodeRender(ref tree, ref xNode2, EventArgs.Empty);
}
}
In the RenderJS method remove the ‘throw new NotImplementedException” line and build the project. We’ll have to copy the UmbracoMVCDemo.BackEnd.dll from the bin folder to the Umbraco installation bin folder.
Refresh the page and you’ll see two new node appear. Notice that the Demo root node isn’t surrounded by square brackets any more.
If you open up the trees.config file in the umbraco root – config folder you’ll see Umbraco automatically added the tree.
For now the nodes don’t do much if you click on it because we left the Action attribute empty. Before we can add an action we need the editor for the right hand side.
Adding an editor – controller and views
Like I said before we want to create the editor in a MVC pattern instead of the default Webforms like was possible before. Therefor will create a new controller by right clicking the controllers folder in the backend project.
Name the controller DemoAdminController and choose for the empty controller template. Because we’ll already have a DemoController in the frontend application I named this one DemoAdmin to avoid confusion.
The Controller will have to inhered from the Umbraco.Web.Mvc.SurfaceController before we can use it in the backend.
IMPORTANT: Although the documentation of version 5 stated the controller didn’t need to be suffixed by ‘Surface’ I noticed there are constraints set on the PluginControllerResolver in the Umbraco source code. We can remove this constraint in the source code but the easiest way is to rename our controller to DemoAdminSurfaceController. This will change our URL’s but they won’t be visible in the Umbraco backend anyway.
We have to decorate the class with the Umbraco.Web.Mvc.PluginController attribute. (yes, the plugincontroller attribute although we’re inheriting a SurfaceController). In this attribute we’ll have to enter the area name. Every plugin will be placed in his own MVC area to avoid duplicate views and controllers. We’ll set the area name to ‘demo’.
Last but not least we’ll have to create two constructors. One without parameters, one that will take a Umbraco.Web.UmbracoContext instance and forward this to the base class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Umbraco.Web;
using Umbraco.Web.Mvc;
namespace UmbracoMVCDemo.BackEnd.Controllers
{
[PluginController("Demo")]
public class DemoAdminSurfaceController : SurfaceController
{
public DemoAdminSurfaceController()
{
}
public DemoAdminSurfaceController(UmbracoContext umbracoContext)
: base(umbracoContext)
{
}
public ActionResult Index()
{
return View();
}
}
}
Last but not least we’ll have to create a view to back up the Index action on our controller that will for now will be one line:
“<h1>Backend Demo Controller – view</h1>”
Build the backend project and copy the dll to the Umbraco installation bin folder. Default will the view be searched in the following folder:
[Umbraco ROOT]/App_Plugins/Demo/Views/DemoAdminSurface/. That is App_Plugins/[AreaName]/Views/[ControllerName]/. Create the folders and add the index view.
I we go to the Umbraco backend we”’ll notice the 2 demo nodes still don’t do anything if we click them. We’ll first have to assign an action to these nodes.
Assign an action to the nodes
To assign an action to the nodes we’ll have to do two things. We’ll have to implement the RenderJS method in out Tree class and fill out the Action parameter from our nodes in the Render method.
In the RenderJS method will render the necessary javascript that will open our editor in the right hand pane by using the ‘UmbClientMgr’ javascript object.
public override void RenderJS(ref StringBuilder Javascript)
{
Javascript.Append(
@"function openDemoController() {
UmbClientMgr.contentFrame('/Demo/DemoAdminSurface');
}");
}
In the Render method will add a call to the javascript action we just created (for now only on the first node).
public override void Render(ref XmlTree tree)
{
XmlTreeNode xNode = XmlTreeNode.Create(this);
xNode.NodeID = "1";
xNode.Text = "Demo Node 1";
xNode.Action = "javascript:openDemoController();";
xNode.Icon = "folder.gif";
xNode.OpenIcon = "folder_o.gif";
OnBeforeNodeRender(ref tree, ref xNode, EventArgs.Empty);
if (xNode != null)
{
tree.Add(xNode);
OnAfterNodeRender(ref tree, ref xNode, EventArgs.Empty);
}
XmlTreeNode xNode2 = XmlTreeNode.Create(this);
xNode2.NodeID = "2";
xNode2.Text = "Demo node 2";
xNode2.Action = "";
xNode2.Icon = "folder.gif";
xNode2.OpenIcon = "folder_o.gif";
OnBeforeNodeRender(ref tree, ref xNode2, EventArgs.Empty);
if (xNode2 != null)
{
tree.Add(xNode2);
OnAfterNodeRender(ref tree, ref xNode2, EventArgs.Empty);
}
}
Build the project and copy the dll to the Umbraco installation bin folder and refresh the backend in the browser. If we now click the first test node we’ll see the test view appear in the right hand side frame.
Automating the copy process
Just like in the previous blog post you can use the build events to automate the copy process of DLL’s and views. Place the 2 xcopy commands in the post build event.
xcopy $(TargetPath) $(SolutionDir)UmbracoMVCDemo\bin\ /C /Y
xcopy $(ProjectDir)Views $(SolutionDir)UmbracoMVCDemo\App_Plugins\Demo\Views\ /E /Y
Source code
You can find the solution above on Github.
To do’s
There are still some things I need to resolve:
- Block access to the backend controller for non authenticated users
- Adapt the actions that you get in the Umbraco back end by right clicking
- ….