I've been on a bit of a MonoRail kick lately and thought I might share some gems. In his recent post Hammett talks about, among other things, using a filter to change layouts at runtime. He didn't elaborate so I thought I would share the way I've implemented his idea. In our hosted environment we generally prefer each customer to have a separate host header. For various reasons this isn't possible for some customers. Instead, we identify them based on an internal id. We key everything about the application off of this id. We use this id to display a customized user interface and run custom logic on a per customer basis. The way I am currently implementing the customized user interface is, as Hammett stated, with Filter.
We grab the CustomerId from the query string and use that to determine if that particular customer has a custom view using a simple folder structure: Views -> CustomerId -> Controller -> View.brail. If no custom view is found, we instead use a 'base' view that is stored in an identical structure Views -> Base -> Controller -> View.brail. Pretty straight forward. We do the same thing for layouts. Decorate your base controller with an AfterAction attribute and you are all set.
namespace My.Product.Mvc
{
using System.IO;
using Castle.MonoRail.Framework;
public class CustomViewFilter : IFilter
{
public const string BASE = "Base";
public const string LAYOUTS = "Layouts";
#region IFilter Members
public bool Perform(ExecuteEnum exec, IRailsEngineContext context, Controller controller)
{
string customerId = context.Params["CustomerId"];
return Perform(exec, context, controller, customerId);
}
#endregion
public bool Perform(ExecuteEnum exec, IRailsEngineContext context, Controller controller, string customerId)
{
// Use the Base or Customer specific view
string viewName = null;
if( null != controller.SelectedViewName )
{
viewName = Path.Combine(BASE, controller.SelectedViewName);
if( null != customerId )
{
string customerViewName = Path.Combine(customerId, controller.SelectedViewName);
if( controller.HasTemplate(customerViewName) )
{
viewName = customerViewName;
}
}
}
controller.SelectedViewName = viewName;
// Use the Base or Customer specific layout
string layoutName = null;
if( null != controller.LayoutName )
{
layoutName = Path.Combine(BASE, controller.LayoutName);
if( null != customerId )
{
string customerLayoutName = Path.Combine(customerId, controller.LayoutName);
string customerLayoutPath = Path.Combine(LAYOUTS, customerLayoutName);
if (controller.HasTemplate(customerLayoutPath))
{
layoutName = customerLayoutName;
}
}
}
controller.LayoutName = layoutName;
return true;
}
}
}
posted @ Thursday, June 21, 2007 4:33 PM