Tuesday, 22 November 2011

Creating an Umbraco 5 Hive Provider with Custom Tree and Editor Plugin

Having used Umbraco CMS on a couple of projects in the past and been very impressed with it, I’ve been keen to follow progress on version 5 (or “Jupiter”) – a rewrite of the code-base to use ASP.Net MVC. In particular I’ve been looking recently at some of the extension points of the platform.

One of these is building upon the data access abstraction layer that is being developed for Umbraco 5 – known as Hive. Umbraco itself will use NHibernate for data access, but via the Hive abstraction. The idea is that this default data access method could be swapped out – but perhaps more importantly by building a hive provider developers can expose other data sources to their Umbraco templates and back office.

The back-office itself is also very pluggable, with the various node trees and editor interfaces also being extendable with your own custom implementations. In light of this I’ve been looking at a small project to create a hive provider for a custom database, along with a custom tree and editor for managing the content from within the Umbraco interface. The use case here might be that we already have data within a legacy database. Rather than converting it to document types and importing into Umbraco itself, we might want to keep it in the existing system whilst also providing a familiar interface for both back office editors to manage the content and template developers to surface the content on our Umbraco based website.

Credits and Further Reading

Before going further first would like to note that others having been working on a similar project and the work they have shared has been invaluable in moving forward with this. Hopefully by similarly publishing the code I’ve adapted and extended will help others on the same path.
Building the Solution

In order to implement the hive provider and custom interfaces I’ve set up a separate VS.Net solution with 3 class library projects. I’ve also downloaded the full source of Umbraco and set this up on IIS and SQL server, so I have an installation of Umbraco running.

In order to deploy the functionality to the Umbraco installation the compiled assemblies need to be copied to /App_Plugins/Packages/(PackageName)/lib. To facilitate this I’ve used a post-build step that is set up with VS.Net to run an xcopy command on every successful build, e.g.:

xcopy "$(ProjectDir)bin\Debug\WebMatters.UmbracoAddOns.MusicCatalogHiveProvider.dll" "$(ProjectDir)..\..\UmbracoSource\Source\Web Apps\Umbraco.CMS.Web.UI\App_Plugins\Packages\WebMatters\lib" /i /f /y

It’s also necessary to restart the Umbraco web application when updates are deployed so that the updated are reloaded. To make this happen you can re-save web.config, or use another post-build script to copy an empty text file into the /bin folder.

With this setup deployment was fairly painless, apart from one issue with the embedded views that I used for the editor. I found at times I either needed to delete the temporary internet files or rename my view in order for changes to be picked up.

The Hive Provider

As mentioned I’m looking to create a hive provider to read and update data from a custom database, so the first thing I need is to set this up. To start things simply I’ve just got a single table for my “music catalog” database, a table of Artists:

Within the project’s Entity folder I’ve created a POCO class called Artist representing an artist and derived from the Hive class BaseEntity.

For data access to the custom database I’ve simply used ADO.Net with inline SQL, though of course this could be swapped with an ORM or stored procedures as preferred. The code for this is within SqlMusicCatalogRepository – providing basic CRUD methods.

The Hive Repository class inheriting from AbstractEntityRepository contains a reference to this custom data access class, and the appropriate methods to perform the reads and updates are passed along. Some helper methods have been created to convert between the POCO Artist and the base generic entity used within Hive called TypedEntity.

The TypedEntity contains some properties that will be common to all entities managed through Hive – things like an ID and created/last modified dates. It also contains reference to a collection of attributes for the custom fields – in my case the Name and Description of the Artist. These attributes and their definitions are defined within the various classes contained in the Schema folder.

Once the assembly has compiled and been deployed to the lib folder within the package, in order for Umbraco to use it it's necessary to make an amend to the file umbraco.hive.config found within /App_Data/Umbraco/Config. See the readme.txt file in the code download for details.

The Tree plugin

Tree plugins within Umbraco 5 are MVC controllers, inheriting from the Umbraco class SupportsEditorTreeController. You need to decorate the class it with a custom annotation called [Tree], providing a name and unique key. It’s also necessary to add [assembly: AssemblyContainsPlugins] to the project’s AssemblyInfo.cs file. This way when Umbraco loads it can pick up that the deployed dll contains a custom tree plugin.

Within the overridden GetTreeData method a reference to the hive provider is created and a method to get all entities is made. The resulting list is looped and a node created for each entity, setting properties like the name, the URL of the associated editor, the icon and the context menu items.

It's also necessary to modify a config file in order to tell Umbraco where the tree should live. The file is umbraco.cms.trees.config
found within /App_Data/Umbraco/Config. Again see the code download readme.txt file for details.

The Editor plugin

Editor plugins are also controllers, inheriting from StandardEditorController. As with the tree, steps are required to annotate the class and assembly info in order to notify Umbraco that the dll contains an editor plugin. The unique key provided here should match with the one created within the tree’s EditorControllerId property, such that the actions made on the tree nodes match up with the appropriate editor methods.

Taking just the Edit method as an example, as with the tree the first step is to get a reference to the hive provider and retrieve a single entity based on the passed Id. The resulting TypedEntity is passed to a strongly typed view for rendering the edit form.

I’m using embedded views to avoid having to deploy more than just a single assembly for each project into my Umbraco instance. This is easily done by creating a view as normal, and within its properties setting the Build Action to Embedded Resource. You then need to reference the path and namespace of the view when returning a ViewResult from the controller.

As with a standard MVC editing interface the form posts back to another method (also called Edit, but with a signature providing a reference to the posted form data). I’ve not attempted to use model binding here, rather am retrieving data direct from the Request.Form collection and using this to construct the TypedEntity. This is then passed to the Hive repository’s AddOrUpdate method in order to persist the change. Finally I’m returning a redirect to another view that simply displays a success message.

Accessing Hive data from templates

So thus far we have the list of artists displaying and being editable via the back-office, but one of the reasons for going via the Hive abstraction is that the content can also be rendered in the front-end templates using a consistent method. The following Razor code can be placed within a cshtml view and will display the list of artists from the database:

<h2>Artist list</h2>
@using Umbraco.Framework.Persistence.Model;
@using WebMatters.UmbracoAddOns.MusicCatalogHiveProvider.Entity;

var hive = RoutableRequestContext.Application.Hive.GetReader(new Uri("webmatters-music-catalog://"));
using (var uow = hive.CreateReadonly()) {
var artists = uow.Repositories.GetAll().ToArray();
foreach (var artist in artists) {
Update: see subsequent post for a better way to do this using surface controllers.

20/1/12: Further updated for RC 2 thanks to Ruben's comment below.

Known issues and further development

The current status of the hive provider tree and editor is to provide basic methods for reading, creating, updating and deleting the content through the Umbraco back office; and display in the view templates. Ideally I’d like to extend this to cover a wider range of use cases, in particular:
  • One-many relations: e.g. managing a list of albums for each artist
  • Many-many relations: e.g. managing a list of tags for each album
There are also a couple of key functional issues:
  • I haven’t yet managed to integrate Umbraco notifications on data updates. Currently I’m displaying a new view after updates with a message on it to manually reload the nodes. Obviously this isn’t ideal – would be better to have the usual notification dialog and re-synching of the tree automatically as needed.
  • There’s no validation set up yet.
Get the code

If you’d like to look further you can download the code here.

As I say there’s still a fair bit to I’d like to look at and improve and I’m sure there may be areas where I could have implemented things better – so if anyone would like to comment, or better yet go in and improve the code, please feel free to do so. Depending on feedback may look to add to the Umbraco 5 Contrib project too.