Sitecore Item Editor Tricks and ASP.NET MVC
In a recent Sitecore project we did at Hanson Dodge Creative, we had a need for custom Item Editors to allow users to search and select items from a legacy CMS database. The basic idea was to select content from the legacy CMS, store content IDs in Sitecore fields, and render the content within Sitecore sublayouts. The approach worked great, but we ran into some challenges (and their solutions) along the way with our Item Editors, which I wanted to share with the Sitecore community. We also made some good use of ASP.NET MVC to build the Item Editors. I’m a big fan of using MVC with Sitecore, so I thought I’d also take an opportunity here to show one great use of it.
Lots of good info here on creating Item Editors in Sitecore, so read on.
Saving Data from a Custom Item Editor
There is information out there already on the basics of setting up an item editor in the core database, and adding it to the Standard Values of your data template. This is nothing new. Some guides to the basics of Item Editors:
- The Item Editors section of the Client Configuration Cookbook
- This post from Paul Caponetti
- Or another post from Mark van Aalst
- Our save was not working consistently. We confirmed via Firebug that the values from the Content tab were also being saved, overriding the values from the Item Editor.
- It is confusing to the end user where they should edit the value -- in the Content tab, or in the Item Editor.
iframeField1.html and iframeField2.html -- Simulates the IFrame fields, which "delegate" to the item editor to get their field values.
ASP.NET MVC for your Item Editor
/// <summary>
/// Aborts the Sitecore pipeline if there is a registered System.Web.Routing route.
/// If placed after sitecore resolvers,
/// allows us to use valuable Sitecore context items (e.g. Sitecore.Context.Database).
/// </summary>
public class SystemWebRoutingResolver : Sitecore.Pipelines.HttpRequest. HttpRequestProcessor
{
public override void Process(Sitecore.Pipelines.HttpRequest. HttpRequestArgs args)
{
RouteData routeData = RouteTable .Routes.GetRouteData( new HttpContextWrapper (args.Context));
if (routeData != null )
{
args.AbortPipeline();
}
}
}
With MVC working side by side with Sitecore, we created Actions and Views for our Item Editors and IFrame fields. With MVC it's easy enough to read the "id" and "db" parameters passed into the Item Editor URL. You can then construct your View Model and return the appropriate view for your Item Editor. In our case, we needed to retrieve values from the legacy CMS, based on the Sitecore item, and place those in the View Model.
public ActionResult Index( string id, string db)
{
Sitecore.Data. Database database = Sitecore.Data. Database .GetDatabase(db);
Item item = database.GetItem(id);
//do some stuff with Item
//construct viewModel
return View(viewModel);
}
We also created a reusable shared view for our IFrame fields, so that we could easily add new fields and delegate them to the item editor. The view is essentially the markup from the test harness, with some of the values coming from a view model instead.
public ActionResult Field( string name)
{
return View( new IFrameField () { FieldName = Server.UrlDecode(name), Editor = "Choose Articles" });
}
After setting up routes in our Global.asax, we configured these MVC actions as the URLs for our Item Editors and IFrame fields. Note that we configured the routes under the /sitecore/shell URL path. More on why this is important later.
routes.MapRoute(
"SitecoreShellDefault" ,
"sitecore/shell/mvc/{controller}/{action}" ,
new { controller = "ItemEditor" , action = "Index" }
);
Improving the Solution: Item Locking
We implemented 3 custom Item Editors with this approach, with good results. However when we started testing with non-Administrative users, we found we had forgotten one important aspect of the Content Editor interface.... item locking! Even if the Content tab indicated that an item was locked, or needed to be locked, our custom tab still acted as if everything was groovy. However with a few lines of code in our Controllers, a couple more shared Views, and a bit of styling, we were able to replicate the behavior of the Content tab.
if (!Sitecore. Context .User.IsAdministrator && item.Locking.IsLocked() && item.Locking.GetOwner() != Sitecore. Context .User.Name)
{
ViewData[ "lockUser" ] = item.Locking.GetOwner();
ViewData[ "currentUser" ] = Sitecore. Context .User.Name;
return View( "Locked" );
}
if (!Sitecore. Context .User.IsAdministrator && !item.Locking.IsLocked())
{
return View( "GetLock" );
}
When we first tested this solution, we noticed something strange... after Previewing a page, users would get a message that they cannot edit the item, because their own user has it locked. Debugging revealed that the Context user was coming back as extranet\Anonymous. Why? Because our routes were not yet mapped under /sitecore/shell -- so they were not running in the context of the shell site, or the shell user. That's why above our routes are mapped to that path. Makes sense though -- your Item Editor should run under the shell context.
Other Advice
We picked some other helpful notes on Item Editors and MVC along the way:
- If you're getting erroneous "save changes" warnings from the content editor, check for Javascript errors in your Item Editor.
- With the IFrame approach outlined here for saving Item Editor values, keep in mind that turning on Raw Values will disable the IFrame field, and your values from the Item Editor will not be saved.
- The Velir Custom Item Generator is a great complement to MVC-based Item Editors -- the generated Custom Items make great View Models.
That's it! Thanks to my dev team who helped prove out this architecture, including @ALittleTooQuiet, @ddysart, and @veryfancy. Hopefully there are some bits in our experience to help you with your Item Editor implementation, or you have a better understanding of how ASP.NET MVC can be used with Sitecore. Please leave any feedback below, or hit me up on Twitter if you have questions.
- Nick Wesselman / techphoria414