Umbraco CMS MVC - own controller and beautiful url

CMS Umbraco I have been studying for several months. It so happened that the main application of my research efforts is aimed at the latest version 7 of this system, and in the context of its work on the MVC engine, as an alternative to web forms of previous versions.

At some point, while developing the site of one company, I wanted to move the news repository of this company into a separate top-level element in the content tree. In the examples and video instructions posted on the Umbraco website, the simplest version of such an organization is proposed - the news is stored as children of a menu item (Fig. 1). As an example, in the section “Which way to approach Umbraco”, this approach is justified, but on a live site where there are dozens and hundreds of news, it looks rather awkward. To work with the news, the site editor will need to go deeper into the content tree - Home-News-Separate News . And the very concept of such an approach does not suit me very much - in the section of the multilevel menu items a list of news suddenly appears ...


image

Another option looks more preferable, as in Fig. 2 - the node containing the news is a separate element of the top level of the content tree.

image

Moreover, when organizing access to the news by reference, I wanted them to have a beautiful canonical appearance:

  • / News - a list of all news;
  • / News / 1234 - output of a separate news;
  • / News / Page12 - go to the news list page.

The MVC paradigm for solving this problem assumes the presence of a controller and a representation (we do not use the model in this case). Since everything is done “under the wing” of Umbraco, we cannot take and implement a news controller, inheriting it from the Controller class - nothing will happen. The Umbraco documentation suggests inheriting from the SurfaceController class that is specifically existing in the infrastructure of this CMS . Everything would be fine, but in this case, the links via which the controller actions are called take the form " / umbraco / surface / _controllername _ / _ actionname_". Such a url structure looks rather cumbersome, and from the point of view of search indexing, a page with a similar url is perceived as deeply hidden in the structure of the site, which probably lowers its search value. The

study of this issue led to the following solution - you need not inherit from the class SurfaceController , but from PluginController . In this case, the links are generated according to the canons of MVC, as required. However, there are some additional circumstances to consider, below is a complete solution to this problem. So:

Routes


Routes are not registered in the file App_Start / RouteConfig.cs , which is called as RouteConfig.RegisterRoutes (RouteTable.Routes) in Global.asax by default . In Umbraco MVC, for PluginController classes, routes are registered in the file App_Code / Startup.cs . In this file, we declare a class implementing the IApplicationEventHandler interface . It looks like this:

public class MyStartupHandler : IApplicationEventHandler
{
	public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
	{
		//Create a custom routes
		// News controller
		RouteTable.Routes.MapRoute(
			"",
			"News",
			new
			{
				controller = "News",
				action = "Index",
				page = 1
			});
		RouteTable.Routes.MapRoute(
			"",
			"News/Index",
			new
			{
				controller = "News",
				action = "Index",
				page = 1
			});
		RouteTable.Routes.MapRoute(
			"",
			"News/Page{page}",
			new
			{
				controller = "News",
				action = "Index",
				page = UrlParameter.Optional
			});
		RouteTable.Routes.MapRoute(
			"",
			"News/{id}",
			new
			{
				controller = "News",
				action = "News",
				id = UrlParameter.Optional
			});
	}
	public void OnApplicationInitialized(
		UmbracoApplicationBase umbracoApplication,
		ApplicationContext applicationContext)
	{
	}
	public void OnApplicationStarting(
		UmbracoApplicationBase umbracoApplication,
		ApplicationContext applicationContext)
	{
	}
}


Controller


The controller is created in the usual way in a file located in the Controllers folder . As stated, the controller class inherits from the PluginController class . It looks something like this:

public class NewsController : PluginController
{
	public NewsController() : this(UmbracoContext.Current) { }
	public NewsController(UmbracoContext umbracoContext) : base(umbracoContext) { }
	public ActionResult Index(string id)
	{
		/*
			Здесь находится код, осуществляющий поиск и получение узлов новостей 
			для той или иной цели - построение постраничного списка, 
			демонстрация отдельной новости и т.д.
		*/
		return View("News", CreateRenderModel(renderModel));
	}
	private RenderModel CreateRenderModel(IPublishedContent content)
	{
		var model = new RenderModel(content, CultureInfo.CurrentUICulture);
		//add an umbraco data token so the umbraco view engine executes
		RouteData.DataTokens["umbraco"] = model;
		return model;
	}
}

One clarification is required here. The news node search code returns an object of type IPublishedContent or IEnumerable. However, Umbraco requires that views called from PluginController 'a be strongly typed and accept a model of type RenderModel . For this, the private method CreateRenderModel is declared in the controller , which creates an object of the required type from IPublishedContent .

Representation


The presentation is created according to the standard scheme, there are no nuances in terms of solving this problem in it.

Side effect


Using this approach revealed one third-party effect, namely, the inability to use macros in the content on pages called in this way. An error occurs with the definition of “ PublishedContentRequest missing ”. As I understand it, it is connected with the fact that the document displayed through such a controller does not go through all the stages of Umbraco parsing, during which this same PublishedContentRequest is created , from which the macro generation code is repelled. It is comforting to think that working with macros when using MVC partial views is becoming less popular. In addition, the creators of Umbraco themselves say that the code that implements macro calls from Rich-text content is rather confusing and bears a serious imprint of a heavy pre-emotion past ...

References


Surface Controllers
Custom MVC routing in Umbraco

Also popular now: