Изучение C# и Orchard CMS
Что-то в этом году статей у меня совсем мало. Весной на Delphi ничего интересного не писал, лишь с десяток парсеров различных сайтов и взаимодействия с API некоторых ресурсов, а-ля SuperJob или HeadHunter в рамках рабочей деятельности. А начиная с этой осени, где то с сентбяря месяца вообще пришлось заняться вражьим языком - новый рабочий проект связан с разработкой сайта на платформе под .NET на C#. Тут вообще все увлекательно, потому что уже лет 7 не занимался разработкой толковых сайтов, а C# не был знаком вообще.
Выбор CMS
Первым делом необходимо было озадачиться выбором системы управления контентом. Из наших требований были работа на Windows платформе, использование MS SQL Server для хранения данных. Не многие PHP системы могут похвастать хорошей работой с СУБД от MS, а уж если заговорить про возможную интеграцию с MS Reporting Services, то тут вообще все плохо становится. Поэтому решили использовать C#. Есть, конечно, еще вариант разработки на Java, но в эту степь мне не хотелось лезть еще больше чем в C#. Собственно с точки зрения выбора CMS также были еще и такие требования как открытый исходный код и актуальность, то есть такая CMS, которая еще поддерживается и развивается в настоящее время.
В итоге поисков у нас на выбор было два варианта: Orchard CMS и Umbraco CMS. Чтобы хоть как то осознанно сделать выбор между двумя этими системами надо таки попробовать попользоваться и разобраться в обеих. Времени на это у нас, конечно, же не было. Обе они основаны на MVC, так что здесь паритет. В итоге выбор был сделан в пользу Orchard, отчасти тут повлиял вид админской части интерфейса. Спустя полтора-два месяца ковыряния с этой системой я уже не уверен, что надо было выбрать именно ее, но что есть то есть, и проект реализуется потихоньку.
Собственно, выбор бы сделан, и требовалось изучить мат. часть. Из всего того, что требовалось для разработки сайта знал я только MS SQL Server. Поэтому началось все с изучения книг по C#:
- Эндрю Троелсен - Язык программирования C# 2010 и платформа .Net 4
- Сандерсон С., Фримен А. - ASP .Net MVC3 Framework с примерами для профессионалов, 3-е издание
Про книги мне особо сказать нечего. Вообще, когда знаний в предметной области нет, то сложно дать какую то оценку книге. А вот когда ты уже эксперт в области, то читая книгу можно написать какую то рецензию. Какие то базовые знания мне эти книги дали. С учетом того что опыт вэб-программирования на PHP с MVC фреймворком был то я просто уже имеющиеся знания как то адаптировал к новым реалиям.
Про поддержку Orchard CMS
Далее получив некоторые базовые знания можно было переходить к чтению документации на саму Orchard CMS. В общем-то про документацию говорить особо тут нечего, то что есть на сайте сложно назвать документацией. Там имеется серия поверхностных материалов, которые больше тянут на статьи для блога. Так что тут данной системе похвастать абсолютно не чем. Как процитировано в одном из русскоязычных блогов, затрагивающих Orchard - "Документация на сайте покрывает только малую часть функционала, поэтому лучшим руководством является исходный код", это сказал/написал один из идеологов Orchard. Как по мне дак лучше промолчать, чем сказать такое. Имхо, невозможно создать хороший продукт, не имея документации и с такой позицией. Людям без опыта вообще сложно вникнуть в суть. Да и даже с опытом сложно представить всю архитектуру системы имея только исходники. Это слишком трудозатратно, проще выбрать другую систему, документированную.
Работа с системой началась с того что мы развернули дистрибутив, и попробовали прилинковать библиотеки в Visual Studio для разработки своих модулей. Ничего из этого не вышло, поэтому пришлось скачивать полный исходный код и работать с ним. Так что уже на самых ранних порах появлялись какие-то косяки.
Есть на сайте Orchard своя галерея модулей. Впечатление от нее, честно сказать, не самое приятное. Основная проблема в том, что обратной совместимости у версией Orchard во многом нет, а модули в галерее в за редким исключением не предназначены для последней версии 1.8. Пару модулей я в итоге скачал старых, и адаптировал для своих нужд и поддержки последней версии CMS.
Встречал мнение, что на вопросы про Orchard хорошо отвечают на StackOverflow и CodePlex. Но что то тут все мои вопрос либо заканчивались ничем, либо ответов вроде "Почему ты не используешь стандартный модуль _имя_модуля_". Очень любит так отвечать вышеупомянутый автор цитаты про исходный код и документацию.
Про контроль версий
Конечно, в работе планировалось использовать систему контроля версий, у себя мы используем SVN. Есть, конечно, множество других, в частности имеется экземпляр TFS, что для работы с VS было бы удобней, но и SVN нам достаточно. С хранением всего этого добра тоже возникли вопросы. Судя по моему опыту, берем ядро системы и базовые модули и выносим его в отдельный каталог. К каждому новому разворачиваемому проекту подключаем эти базовые модули которые хранятся где-то отдельно. Этим сразу решается задача обновления базовых модулей при выходе новой версии CMS. Обновил один каталог и все. Да и репозитории проектов остаются маленькими - только новые разработанные модули. Разделить группу проектов в VS (solution) отделив новые модули и базовые как то сходу не получилось. Поэтому под версирование попал весь исходный код, а это порядка 80 проектов и несколько тысяч файлов. Нагуглить по этому вопросу тоже ничего не удалось. В целом задача думаю решаемая вполне и думаю это не сложно, тут либо надо изменять sln-файл группы проектов, либо изменять пути к проектам, заменяя их на символические ссылки.
Про jQuery
Поскольку клиентская часть сайта тоже требовала программирования, то пришлось познакомится и с jQuery, прочитав книжку за авторством Б. Бибо и И. Кац "jQuery. Подробное руководство по продвинутом JavaScript". книга довольно легко читаемая, дала базовые навыки для написания не шибко сложных скриптов. На сайте наши основные потребности с jQuery были связаны с рисованием графиков и диаграмм и обработки различных выпадающих списков.
Для диаграмм использовалась библиотека Flot. Тоже, наверное, не самый удачный выбор инструмента. Сама библиотека рисует только базовые варианты графиков. Например, если вы хотите добавить несколько столбчатых серий на один график, так чтобы столбики рисовались рядом, то понадобится подключить плагин. Проблема в том, что когда плагинов в работе несколько, то они ничего не знают друг о друге и появляются сложности. Например, захотели над столбиками нарисовать числовые значения - нарисовали, а теперь захотели из диаграммы сделать горизонтальную - сделали. только вот на этом этапе значения начинают некорректно позиционироваться. В целом, конечно, работает, но грабли появляются, приходилось править исходный код плагинов.
Еще на сайте использовали карту в SVG формате. Есть тоже множество библиотек для работы с SVG-картами, но здесь мы решили обойтись своими силами, поскольку функционала много не требовалось, лишь показывать подсказку с наименованием района при наведении мыши, раскрашивать да обрабатывать клики на регионы.
Для реализации фотогалерей использовали Jssor Slider. Работает в целом хорошо. Не понравилось использование кастомных атрибутов в HTML-коде. Вопросов бы у меня не возникло если бы атрибуты как положено начинались бы с префикса "data-", но такого нет.
Ну и еще прикрутили YandexMaps для отображения имеющихся адресов на карте.
Случилось даже один раз для построения диаграмм написать ajax запрос для получения исходных данных графиков и при этом реализовать контроллер который вместо обычного ActionResult возвращает JsonResult.
Про архитектуру Orchard CMS
Документная модель построена таким образом, что мы имеем и можем создавать (с помощью интерфейса или в коде) типы документов - Content Type. Каждый тип документа состоит либо из контентных частей (Content part) либо из полей (Content Field). Главное отличие тут в том, что часть (одного типа) может включаться в документ только один раз, когда полей одного типа можно добавить несколько под разными именами. Мы также можем создавать и сами эти описания контентных частей и полей и в дальнейшем их использовать. При разработке проекта не всегда у нас удавалось грамотно создавать нужные части. Так, например, у нас в системе имелись сущности "Предприятие" и "Образовательное учреждение", в обоих случаях у них имелось описание каких то контактных данных и адресов. И в данном случае логично было создать контентную часть - Контакты. У нас же контактные данные внесены в основные контентные части для этих двух типов. На самом деле в полной мере объединить бы их не получилось, поскольку каждый из типов имеет какие то свои особенные поля, например, "Телефон приемной комиссии" в образовательных учреждениях. Такое поле в общую часть не вынесешь, а разносить контактную информацию в разные части (а следовательно хранить в разных таблицах БД) тоже не логично.
Система поддерживает версирование документов, при каждом редактировании создается новая копия. Вы можете вернуться к предыдущему варианту. Может быть иногда это и может быть полезно, но мы данный функционал ни коим образом не используем. Код наших модулей рассчитан на работу с только с одной версией документа. Второй нюанс по управлению документами - вы не можете физически удалить документ. При его удалении в CMS соответствующие строки помечаются в БД флагом и остаются на хранение. На мой взгляд это не совсем корректно, а в системах где часто может требоваться добавлять и удалять документы это может привести к ненужном разрастанию БД, особенно когда эти данные точно не нужны в будущем.
Каждый модуль системы представляется отдельным проектом (area в терминах ASP.net MVC). Все модули за исключением модулей ядра располагаются в отдельной папке Modules. Была пара историй, связанных с перемещением модулей из этой папки и с переименованием. Первый косяк был в том, что я создал библиотеку базовых классов по управлению перечнем документов и использую их во всех своих модулях. Эту библиотеку я вынес из папки Modules (поскольку модулем сама она не являлась), после чего вместо компиляции dll файла у меня происходило копирование и переименование csproj файла. То есть вместо исполняемого кода в dll у меня был текстовый контент csproj файла. Не знаю чья тут проблема VS или чего то еще, но неприятно. Второй казус был, когда я переименовал проект из faq в Faq. В конечном итоге пришлось чистить БД, пересоздавать и регистрировать модуль заново.
Обычно модуль реализует набор нескольких классов/интерфейсов. Во-первых для управления нашими контентными частями необходимо реализовать так называемые Обработчики (handlers) и драйверы. Обработчики отвечают за обработку некоторых событий, происходящих с контентной частью/полем. Вот если вспомнить убогую документацию по CMS, то там есть статья про поиск документов, в которой рассказывается, как его настроить. Все быстро и просто - Включил пару стандартных модулей, создал индекс, отметил контентные части которые необходимо индексировать и в общем то и все. Должно работать. С этой точки зрения эти статьи рассчитаны не на разработчика, а на простого пользователя, который код не пишет. И если вы проделаете тоже самое для своей самописной контентной части, то поиск по ней работать не будет. А что бы он работал необходимо написать обработчик события индексации OnIndexing в упомянутом хэндлере вашей контентной части.
Суть упомянутых драйверов состоит в следующем. Поскольку вы сами конфигурируете свой документ и можете добавлять туда разные поля и части, то выводом документа занимается система. Она ищет необходимый шаблон для каждый контентной части и компонует их в определенном порядке (который вы задаете в служебной файле placement.info). Дак вот драйвер определяет что и как выводить для разных режимов работы (просмотр, редактирование), а также занимается обработкой обновления модели при редактировании части.
Весь остальной код модуля обычно это контроллеры для внешней части или админки, и представления (Views). Мы, признаюсь, не очень подружились с использованием этой модели, когда системы сама строит представление документа, а вы разрабатываете шаблоны для частей/полей (такие перекрытия шаблонов называются alternates). То есть мы в полной мере используем это в админской части, но на внешней части (фронт-энд) мы использовали более низкоуровневый подход - обычные представления с использованием Razor, без всяких компоновок частей и т.п.
В целом где то система расширяемая, а где то наоборот заточена на использованием стандартных модулей. Вот, к примеру, написали вы свои контроллеры для вывода контента на страницах сайта. Теперь хотите чтобы на сайте отображался блок навигации, показывающий путь к текущему документу. Оказалось что для этого даже специальный термин есть - breadcrumbs (первый раз такое увидел). Данный элемент интерфейса строится с помощью виджета меню, а виджет выводит только те пути что есть в самом меню. А из вне перехватить это, чтобы добавить свои кастомные пути, не получается (хотя имхо потенциально возможно, но на Stackoverflow ответа не получил).
Про C#
Мне вообще раньше очень нравился С++ своим синтаксисом. Единственное что не нравилось в нем это были всякие указатели, звездочки и амперсанды. С точки зрения синтаксиса C#, надо признать, весьма лаконичный язык. Некоторые вещи не всегда удобны, но в целом интересен. При разработке с использованием Orchard очень часто используется неявная типизация переменных. Это очень удобно, когда используется локально в рамках одной процедуры или класса. Но когда это передается за тридевять земель, то разбираться в таком коде, конечно, весьма затруднительно. С этой точки зрения код, использующий неявные переменные, объявленные через var, или динамические типы (dynamic) весьма усложняют понимание. С точки зрения локального использования - здесь наоборот достигается краткость кода, т.к. часто используются достаточно длинные идентификаторы типов (имеется в виду использование generic-конструкций, причем вложенных). В этом язык несколько похож на PHP, который вообще не типизированный (по крайней мере был раньше), что позволяет легко создавать нужные структуры для представления данных. Очень удобны с этой точки зрения и анонимные типы. Хотя тут есть ограничения по их передаче, связанные в областью видимости, поэтому нельзя передать переменную анонимного типа в представление. Вернее, фактически передать вы ее сможете, а вот доступ к полям не получите.
Такие расширения как LINQ для типов данных и структур и лямбда-выражения также значительно повышают скорость написания кода, весьма удобно. Поскольку Orchard основан на использовании NHibernate для доступа к БД и Autofac для внедрения зависимостей, пришлось познакомится с ними. Один из минусов документации по Orchard, на мой взгляд состоит в том, что везде зависимости внедряются через параметр конструктора. Ни разу я не встретил там примера разрешения зависимости просто в коде. Хотя с другой стороны в документации и не должны обучать принципам использования тех или иных библиотек.
Итоги
В общем говоря, осень вышла весьма насыщенная на изучение нового материала. Начиналось все весьма туго, часто что то не получалось, приходилось подолгу разбираться, искать ответы, но чем дальше тем проще, на простых мелочах больше не задерживались. В целом нормальный процесс изучения нового. А вот книги по C# на будет перечитать. Теперь хоть будет понятно, о чем там написано :)
Несколько не хорошо, наверное, писать статью про вэб-ресурс и не давать на него ссылку, но он еще не доработан до конца и не опубликован. Потом отдельную заметку опубликую. Еще требуется разработать весьма увлекательный модуль тестирования (опросников) пользователей, реализующий несколько методик.
Что касается Orchard, то на хабре видел один из старых комментариев, который на мой взгляд неплохо описывает эту CMS:
Когда я последний раз имел дело с Orchard CMS(это был июнь 2012 года) у меня (с высоты нынешнего опыта работы с ASP.NET MVC) возникало и возникает непрекращающееся чувство того, что «просто сделать сайт с нуля» на MVC проще, чем допилить «это» до необходимого состояния.
Конечно, система не стоит на месте, но если вы разрабатываете свои модули управления контентом, то для полноценного управления вам придется написать кучу кода. Система тут не дает особых удобств. Взять банально построение шаблона редактирования документов: вот ваш контент тип состоит из множества контентных полей и контентных частей. Части в свою очередь содержат обычные поля данных. При редактировании документа система строит представление редакторов для всего этого дела. Вот теперь представьте, что у вас поле штук 40 в итоге. И часть из них это редакторы текста, вроде TinyMCE. Это все представляется в виде длинной-длинной портянки редакторов. Система не предоставляет никакого функционала группировки редакторов по вкладкам. Из положения, конечно, можно выйти несколькими способами. Поскольку такая ситуация не частая то в двух случаях мы с помощью jQuery перемещали блоки редакторов на вкладки. Более грамотный путь будет создание alternate-представления для редактирования контентного типа. Но даже это будет не полноценно, подобный функционал надо выносить на более высокий уровень, а не перекрывать где то на более низком. Так что, всегда придется что то допиливать.