[Из песочницы] Umbraco CMS MVC — собственный контроллер и красивые url

CMS Umbraco я изучаю несколько месяцев. Так получилось, что основное приложение моих исследовательских усилий направлено на последнюю на сей день 7-ю версию этой системы, причём в контексте её работы на MVC-движке, как альтернативе веб-формам прежних версий.В какой-то момент, разрабатывая сайт одной компании, мне захотелось вынести репозиторий новостей этой компании в отдельный элемент верхнего уровня в content tree. В примерах и видеоинструкциях, размещённых на сайте Umbraco, предлагается самый простой вариант такой организации — новости хранятся как дочерние элементы какого-нибудь пункта меню (рис. 1). В качестве примера в разделе «С какой стороны подойти к Umbraco» такой подход оправдывается, но на живом сайте, где новостей будут десятки и сотни, это выглядит довольно неуклюже. Чтобы работать с новостями, редактору сайта нужно будет опускаться вглубь по дереву контента — Главная-Новости-ОтдельнаяНовость. Да и сама концепция такого подхода меня не очень устраивает — в разделе пунктов многоуровневого меню вдруг появляется список новостей…

image

Более предпочтительным выглядит другой вариант, как на рис. 2 — узел, содержащий новости, является отдельным элементом верхнего уровня дерева контента.

image

Более того — при организации доступа к новостям по ссылке, захотелось, чтобы они имели красивый канонический вид:

/News — список всех новостей; /News/1234 — вывод отдельной новости; /News/Page12 — переход на страницу списка новостей. Парадигма MVC для решения данной задачи предполагает наличие контроллера и представления (модель в данном случае не используем). Поскольку всё выполняется «под крылом» Umbraco, мы не можем взять и внедрить контроллер новостей, наследуя его от класса Controller — ничего не произойдёт. Документация по Umbraco предлагает наследоваться от специально существующего в инфраструктуре данной CMS класса SurfaceController. Всё бы хорошо, но в таком случае ссылки, по которым вызываются действия контроллера, приобретают вид »/umbraco/surface/_controllername_/_actionname_». Такая структура url выглядит довольно громоздкой, да и с точки зрения поисковой индексации страница с подобным url воспринимается как глубоко упрятанная в структуре сайта, что, вероятно, понижает её поисковую значимость.Исследование данного вопроса привело к следующему решению — наследоваться нужно не от класса SurfaceController, а от PluginController. В этом случае ссылки генерируются по канонам MVC, так, как и требуется. Однако при этом необходимо учитывать некоторые дополнительные обстоятельства. Ниже предоставлено полное решение данной задачи. Итак:

РоутыРоуты прописываются не в файле App_Start/RouteConfig.cs, который стандартно вызывается как RouteConfig.RegisterRoutes (RouteTable.Routes) в Global.asax. В Umbraco MVC для классов PluginController роуты прописываются в файле App_Code/Startup.cs. В этом файле объявляем класс, имплементирующий интерфейс IApplicationEventHandler. Выглядит это так: 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) { } } Контроллер Контроллер создаётся обычным образом в файле, расположенном в папке Controllers. Как было сказано, класс контроллера наследует класс PluginController. Выглядит это примерно так: 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; } } Здесь требуется одно уточнение. Код поиска узлов с новостями возвращает объект типа интерфейса IPublishedContent или IEnumerable. Однако Umbraco требует, чтобы представления, вызываемые из PluginController«a, были строго типизированы и принимали модель типа RenderModel. Для этого в контроллере объявляется приватный метод CreateRenderModel, который из IPublishedContent создаёт объект требуемого типа.Представление Представление создаётся по стандартной схеме, никаких нюансов в плане решения данной задачи в нём нет.Сторонний эффект В использовании данного подхода обнаружился один сторонний эффект, а именно — невозможность использования макросов в контенте на страницах, вызываемых подобным образом. Возникает ошибка с определением «PublishedContentRequest missing». Как я понял, связана она с тем, что отображаемый через такой контроллер документ не проходит все стадии разбора Umbraco, в процессе которого и создаётся этот самый PublishedContentRequest, от которого отталкивается код генерации макросов. Утешает мысль, что работа с макросами при использовании MVC partial views становится менее востребованной. К тому же сами создатели Umbraco говорят, что код, реализующий вызов макросов из Rich-text-контента, довольно запутан и несёт на себе серьёзный отпечаток тяжёлого доэмвсишного прошлого…Ссылки Surface ControllersCustom MVC routing in Umbraco

© Habrahabr.ru