Wednesday, 21 December 2011

Umbraco 5 Surface Controller

In a recent post I blogged my first steps in creating plugins for Umbraco 5 - creating a tree and editor using a custom hive provider for a third party database, in order to present and edit information within the Umbraco back office.

I noted how the information could also be made available on the front-end of the site by writing code within the razor view template. Whilst that worked, it wasn't very satisfactory and certainly wasn't the MVC way with too much code and logic in the view.

Since then the project has moved on and details have been made available for working with surface controllers. They can be created both directly within the Umbraco project in VS.Net or as plugins.

Creating a Surface Controller

To create a surface controller for use in a plug-in you create a class within a project with a reference to System.Web.Mvc and Umbraco.CMS.Web, with the following setup:
  • Inherits from SurfaceController
  • Named with a suffix of "SurfaceController"
  • Has a class attribute of Surface with a unique GUID
  • Contains one or more action methods that return either PartialViewResult or ContentResult and are marked with the [ChildActionOnly] attribute
  • The project AssemblyInfo.cs file must contain [assembly: AssemblyContainsPlugins]
Otherwise you use standard MVC concepts such as view models and tightly or loosely bound views, either standalone or embedded. You deploy to your package folder as detailed in the previous post.

Using the Surface Controller

There are a couple of ways to make use of the surface controller once it has been built and deployed to the Umbraco application.

One is the standard MVC way - after all this is just a controller action returning a partial view or content result. So you can use @Html.Action. As it's a plug-in, which is created as it's own area, you need to specify the area the controller and action is found within, as follows:

@Html.Action("ActionName","ControllerName", new {area = "PackageName"})

Unfortunately as of writing, this doesn't seem to work. An error of "No route in the route table matches the supplied values." results. Hopefully will sort this out or determine if it's a bug to be fixed shortly.

In any case the better way is really to create a Child Action Macro. This is done via the Developer section and once created the deployed package, controller and action should be selectable from the list. You can then render the macro, e.g. in a template with:

@Umbraco.RenderMacro("artistList")

Further reading

In addition to Shannon Deminick's introductory post linked to earlier, Sebastiaan Janssen also covers surface controllers in his post on creating an Umbraco 5 package.


Get the code

If you’d like to look further you can download the code here - this has been updated from the previous post with amends to the hive, tree and editor plug-ins to adhere to changes in the Umbraco code base; with an additional project added for the Surface Controller.

Monday, 21 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>
<ul>
@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) {
<li>@artist.Attributes["Name"].DynamicValue</li>
}
}
}
</ul>
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.

Tuesday, 21 June 2011

Upgrade ASP.Net MVC2 to MVC3

Having recently upgraded a couple of ASP.Net MVC 2 projects to MVC 3, I came across a couple of tools and techniques to simplify the process.

ASP.Net MVC3 requires .Net 4 and hence use of VS.Net 2010 (or express editions) are required. There's also an additional download of the MVC3 framework required too.

One of the simplifying changes of the upgrade is a slimmed down web.config file - but I found this meant an in-place upgrade of this and all the necessary references rather complicated. So instead I created a new, empty MVC3 project and copied over all the controllers, models, views and necessary web.config settings into the new project.

An important security update is to HTML encode all output by default, using the new <:= > syntax (as opposed to <%= %>). If you've been encoding output properly in previous versions any potentially insecure content would have been wrapped in Html.Encode methods. But updating all of these to the new syntax looks to be a pretty time consuming and tedious task.

So was very pleased to discover this tip, which details how VS.Net's find/replace syntax can be used to make the update across the project.

Having done that though, what I really wanted to do was make use of the new Razor view engine. But again, updating all the views to use this syntax would take quite some time working manually.

This time a tool produced by Telerik proved invaluable in carrying out this task. You can run it on a folder of view files that use the web forms view engine, and it will convert them into ones using Razor. It's not 100% perfect - I found in particular a few brackets need manually adding here and there to ensure valid syntax - but does most of the job and saves an awful lot of time.