Adding Umbraco to existing site

Heads Up!

This article is several years old now, and much has happened since then, so please keep that in mind while reading it.

Up first, a confession…

The majority of the projects that my company build do not use Umbraco

Now that's a bold opener for an article for 24 days in Umbraco! It's a true statement for 2016, who knows what 2017 holds. These projects are members-only data management, analysis and reporting applications for which pure MVC is absolutely the right approach to use.

At the beginning of each project it's obvious whether it's a pure MVC site, or an Umbraco one. If there are no CMS requirements then we crack on with our standard MVC framework enabling us to build and maintain complex applications quickly and reliably.

However client requirements change. Sometimes down the line they do actually want some public facing pages, and wouldn't it be nice if they could manage parts of the site themselves…

Keeping clients happy

That's our key objective. Working out how to meet their needs on time with a reasonable budget. To meet the above needs we have four options:

  1. 'Just' roll our own little CMS into the site… argh, not a thought I have entertained since I discovered Umbraco in 2012!
  2. Build them a separate Umbraco site… argh, then we have to work out how to share common layouts and user sessions for a seamless user experience
  3. Start a new Umbraco site and rebuild all the existing functionality… argh, it all already works, the client won't want to pay for that (and we don't really want to do it either)
  4. Add Umbraco into the existing site with as little disruption as possible, and then build the new features

Option 4 is definitely what we choose! Thanks to the sterling work done by the Umbraco Core team, this should be relatively straight-forward; assuming you are using Umbraco 7.3+, you have a good architecture, and you know where the important hoops are and how to jump through them.

Isn't this quite a niche requirement?

Well yes, I appreciate it may be. Although I imagine that there must be lots of experienced MVC developers only just discovering Umbraco. If they find their way to this article perhaps techniques here will enable them hit the ground running by using Umbraco for small parts of a site and familiar ways for the rest. It's to everyone's advantage to Keep It Simple (Stupid): weekends are not meant to be spent in front of a laptop!

The approach may also be useful for Umbraco developers who want to build front-end areas of a website that only handle data, not content.

Let's get started: a walkthrough

I built a new site specifically for this walkthrough on how to add Umbraco into an existing MVC site. It's called h5yabeer.com and, in short, it's a website that allows you record your promise of a thank you beer (or coffee, cake etc.) so that it doesn't get forgotten. But more on that later.

For now you just need to know that it is a fully working site using:

  • MVC (v5.2.3)
  • Entity Framework Code First (v6.1.3)
  • ASP.Net Identity (v2.2.1) and Owin
  • Dependency Injection using Castle Windsor (v3.3.0)

The Visual Studio solution has five projects, with the web UI project having just front-end assets, controllers, views and start-up code.

Our objective is for content editors to edit the home page using Umbraco without breaking our working application and whilst causing the least amount of 'disruption' to our code as possible.

I've created a PDF of a detailed walkthrough to avoid this article getting far too long. This is a brief overview of the steps involved:

  1. Update your application's nuget packages to match the version currently used by Umbraco
  2. Move your controllers and views to a new MVC area to keep your application 'safely' separate from Umbraco
  3. Change database connection string name to umbracoDbDSN
  4. Install Umbraco via nuget
  5. Create a master template and home page in Umbraco back office
  6. Use a _ViewStart.html in the MVC area to use the new master template
  7. Rewire the start-up code:
    1. Delete the MVC standard default route
    2. Move global.asax.cs code to a class that implements IApplicationEventHandler:
      public class ApplicationEventHandler : IApplicationEventHandler
      {
          public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
          {            
          }
      
          public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
          {
          }
      
          public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
          {
              AreaRegistration.RegisterAllAreas();
              BundleConfig.RegisterBundles(BundleTable.Bundles);
          }
      }
      

      Wiring up our start-up code using IApplicationEventHandler

Dependency injection?

If your site uses dependency injection, change your controller factory to implement the IFilteredControllerFactory interface (our site uses Castle Windsor):

public class WindsorControllerFactory : DefaultControllerFactory, IFilteredControllerFactory
{
	readonly IWindsorContainer _container;

	public bool CanHandle(RequestContext requestContext)
	{
		var controllerType = GetControllerType(requestContext, requestContext.RouteData.Values["controller"].ToString());
		return _container.Kernel.HasComponent(controllerType);
	}

	public WindsorControllerFactory()
	{
		this._container = IoC.Container;
	}

	public WindsorControllerFactory(IWindsorContainer container)
	{
		this._container = container;
	}

	public override void ReleaseController(IController controller)
	{
		_container.Release(controller);
	}

	protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
	{
		if (controllerType == null) return null;
		return (IController)_container.Resolve(controllerType);
	}
}

Implementing the IFilteredControllerFactory interface and CanHandle method

and insert your controller factory to the list:

public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
	IoC.Container.Install(FromAssembly.This());
	FilteredControllerFactoriesResolver.Current.InsertType<WindsorControllerFactory>(0);
}

Using FilteredControllerFactoriesResolver to add our controller factory to the site

ASP.Net Identity and Owin?

If your site uses Identity and Owin, change your start-up class to implement UmbracoDefaultOwinStartup. Your class must call the base configuration method first:

//[assembly: OwinStartup(typeof(Startup))]
namespace App.WebUmb
{
    public partial class Startup : Umbraco.Web.UmbracoDefaultOwinStartup
    {
        public override void Configuration(IAppBuilder app)
        {
            // Umbraco's Owin configuration must be done first
            base.Configuration(app);

            // Our Owin configuration
            ConfigureAuth(app);
        }
    }
}

Implementing UmbracoDefaultOwinStartup to add our start-up code

and do three web.config changes:

  • Change the owin:appStartup appSetting value to be your startup class
  • Remove the <authentication> node in <system.web>
  • In <system.webServer> remove the FormsAuthentication module

And that's it! Ok so the amount of work will vary depending on your specific application. But my site is now showing an Umbraco-rendered home page, and if I navigate to /Promises then my application is working exactly as it was before.

So objective achieved: we've got pure MVC and Umbraco playing nicely!

So What Next?

Now you can decide which features need to be moved into Umbraco and which ones can stay as they are.

Views used by both Umbraco and MVC

The easiest way I have found to have a .cshtml view that works as a master template for both Umbraco and pure MVC views is:

@inherits UmbracoViewPage<dynamic>
@{
    var homeNode = Umbraco.TypedContentAtRoot().FirstOrDefault();
}
...
<a href="/" class="navbar-brand">@homeNode.GetProperty("logoText").Value</a>

A template that can be a master for Umbraco templates and MVC views

The MVC area views are not rendering with models of type IPublishedContent, thus using dynamic.

Render MVC data in Umbraco templates

Let's say we want to display a list of the different gifts you can donate on the right hand side of a page rendered by Umbraco. To do this we create a child action in a controller in the MVC area:

[AllowAnonymous]
[ChildActionOnly]
public ActionResult List()
{
	var model = _service.GetGiftListItems();
	return View(model);
}

Child action in our MVC area

a view in the MVC area:

@model IEnumerable<App.ViewModels.Gifts.GiftListItem>
@{
    Layout = null;
}

@foreach (var item in Model)
{
    <ul>
        <li>
            @Html.DisplayFor(modelItem => item.Name)
        </li>
    </ul>
}

The view to render that child action

and then call that from our Umbraco template using Html.Action; remembering to set the name of the Area:

@Html.Action("List", "Gifts", new { area = "Promises" })

Calling the child action from an Umbraco template

Render Umbraco content in MVC views

Let's say that the content editors want to be able to add a welcome message for members when they've logged in. We can achieve this by creating a new property on the home page node (in a new tab called Members) called 'Members Welcome'. We have to "new up" the Umbraco helper in our MVC area in order to find the home page node and retrieve this property:

@using Umbraco.Web
@{
    ViewBag.Title = "Members Area";

    var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
    var homeNode = umbracoHelper.TypedContentAtRoot().FirstOrDefault();
}

<h1>Members Only</h1>

@homeNode.GetProperty("membersWelcome").Value

UmbracoHelper used in MVC views

Back-office members section

To be honest one thing I've not needed to try yet is getting the 'public access' feature of Umbraco working in this set-up. If it could then we should recreate the login page in Umbraco so that it can be selected as the Login Page when configuring the access. But I think I need to post a forum post on Our Umbraco about this as my knowledge of that bit of the back-office is not great!

I just hide the members section from our users so they don't get confused. And I've got all the member management functionality I need in the front-end of my website.

H5yabeer.com and Github

Hopefully by the time this article is published h5yabeer.com, the site I built to practise this walkthrough, will:

  1. Be online at http://h5yabeer.com.
  2. Have its source on github - https://github.com/LottePitcher/h5yabeer - the solution will contain both the original MVC website and the Umbraco-ified version for comparison.

If they aren't online then follow me on Twitter (@lottepitcher) and I'll tweet as soon as they are.

Disclaimer … the solution architecture is a pared down version of the MVC framework we usually use, but if you spot any mistakes or performance issues please still let me know!

Wrap Up

It's quite easy to add Umbraco to an existing site, depending on the specifics of your solution. Once you've got it all wired up you can crack on with standard MVC development in the MVC area, and good old Umbraco everywhere else.

At my company, years of Entity Framework Code First experience combined with custom scaffolding tools mean we can build the data areas quickly and reliably, without all the developers needing specialist Umbraco knowledge. So it may not be the 'Umbraco' way, but for us it is definitely the 'right' way, for now at least!

Fast Builds + Accurate Estimates = Happy Clients + Weekends Not Working!


Any thoughts, suggestions etc will be gratefully received. Merry Christmas one and all!

Lotte Pitcher

Lotte is on Twitter as