...

четверг, 26 июня 2014 г.

[Из песочницы] 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<IPublishedContent>. Однако Umbraco требует, чтобы представления, вызываемые из PluginController’a, были строго типизированы и принимали модель типа RenderModel. Для этого в контроллере объявляется приватный метод CreateRenderModel, который из IPublishedContent создаёт объект требуемого типа.

Представление




Представление создаётся по стандартной схеме, никаких нюансов в плане решения данной задачи в нём нет.

Сторонний эффект




В использовании данного подхода обнаружился один сторонний эффект, а именно – невозможность использования макросов в контенте на страницах, вызываемых подобным образом. Возникает ошибка с определением “PublishedContentRequest missing”. Как я понял, связана она с тем, что отображаемый через такой контроллер документ не проходит все стадии разбора Umbraco, в процессе которого и создаётся этот самый PublishedContentRequest, от которого отталкивается код генерации макросов. Утешает мысль, что работа с макросами при использовании MVC partial views становится менее востребованной. К тому же сами создатели Umbraco говорят, что код, реализующий вызов макросов из Rich-text-контента, довольно запутан и несёт на себе серьёзный отпечаток тяжёлого доэмвсишного прошлого…

Ссылки




Surface Controllers

Custom MVC routing in Umbraco

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий