Разработка веб-сайта на базе ASP.NET Core и Platformus CMS

  • Tutorial
В этой статье я хочу познакомить вас с Platformus CMS — молодой системой управления содержимым веб-сайтов (10-я альфа на момент написания статьи), построенной на базе не менее молодых ASP.NET Core и ExtCore framework. Подробно рассматривать архитектуру и другие технические детали мы сейчас не будем, а ограничимся упоминанием нескольких интересных фактов и затем сразу же попробуем CMS’ку в деле — создадим небольшой тестовый проект на ее основе.

Несколько интересных фактов


Платформус — бесплатная система управления содержимым с открытым исходным кодом. Она кроссплатформенная, модульная и расширяемая. Данные в ней представлены в виде объектов, которые описываются классами. Поддерживается мультиязычность.

Написана CMS на C#. Благодаря возможностям ASP.NET Core, она одинаково хорошо может работать на Windows, Linux и Mac. Сама исполняемая среда, необходимая для работы любого приложения на .NET Core, может быть как установлена отдельно, так и интегрирована непосредственно в само приложение. В качестве веб-сервера может использоваться IIS либо Kestrel (во втором случае приложение становится веб-сервером для самого себя и может работать самостоятельно или же быть встроено в контейнер вроде IIS или Nginx). В качестве хранилища данных на текущий момент поддерживаются Microsoft SQL Server, SQLite и PostgreSQL. С учетом всего перечисленного, можем получить конфигурацию, которой для работы не требуется вообще ничего стороннего — скопировал, запустил и пользуешься (может быть удобно для тестирования, например).

(В статье несколько десятков скриншотов.)

Приступаем


Если вы еще не работали с ASP.NET Core, то вот тут есть ссылки на все, что для этого понадобится.

Итак, скачиваем архив с 10-й альфой Платформуса по прямой ссылке (~ 9 МБ). Распаковываем его. (Как вариант, чтобы не выполнять самостоятельно все описанные в статье действия, можно скачать уже готовую демку, ссылка на нее есть в конце статьи.)

Чтобы запустить приложение, необходимо выполнить 2 команды в командной строке (предполагаем, что вы сейчас работаете в Windows-среде):

cd путь к распакованному архиву
dotnet webapplication.dll

Результат должен быть примерно следующим:


Теперь открываем браузер и переходим на http://localhost:5000/:



Платформус работает! Приступим к наполнению нашего веб-сайта, для чего перейдем в бекенд (админку), кликнув по соответствующей ссылке на странице-заглушке:



Описание структуры данных


Как я упоминал выше, данные в Платформусе представлены в виде объектов. Например, любая страница является объектом. Если на странице необходимо отобразить список новостей, то каждая новость также будет отдельным объектом, опционально, со своей отдельной страницей (объекты бывают автономные и встраиваемые).

Объекты описываются классами. Каждый класс, в свою очередь, описывается членами класса, представляющими свойства или связи объектов этого класса, и источниками данных. По умолчанию в базе данных уже присутствует два класса: Base Page и Page. Первый является абстрактным и содержит такие базовые свойства страниц, как заголовок, META-description и META-keywords, сгруппированные в таб SEO. Большинству автономных объектов необходимы эти свойства, поэтому, чтобы их не дублировать в каждом классе, достаточно унаследовать соответствующие классы от Base Page. Второй класс представляет обычную страницу с HTML-содержимым. Он унаследован от Base Page и имеет еще одно дополнительное свойство — Content. Вот как выглядит список классов по умолчанию:



Классы


В качестве примера, который может продемонстрировать многие возможности CMS’ки, давайте добавим раздел с новостями на наш веб-сайт. Каждая новость будет снабжаться тегами и иметь свою собственную страницу с детальной информацией. Для начала, создадим класс News Page:



Т. к. каждая страница новости должна иметь заголовок, META-description и META-keywords, унаследуем наш класс от Base Page, выбрав его в выпадающем списке Parent class. Далее, укажем название нашего класса в единственном и множественном числе. Добавлять слово Page не обязательно, но, пожалуй, так нагляднее. Устанавливаем флаг Is standalone, означающий, что объекты этого класса будут автономными (т. е. будут иметь собственные URL). И напоследок, указываем название представления по умолчанию для будущих объектов нашего класса. Нажимаем Save. Наш класс News Page создан и добавлен в список:



Теперь создадим еще один класс — Tag. На этот раз объекты будут встраиваемыми, поэтому родительский класс не выбираем и флаг Is standalone не устанавливаем. Также, нет нужды указывать название представления по умолчанию:



Члены классов


Теперь определим члены класса News Page. Кликаем по ссылке Members в соответствующей строке и переходим к пустому списку (унаследованные от Base Page члены класса тут не отображаются):



Члены класса бывают двух типов — свойства и связи.

Значением свойства является простой текст, редактируемый различными редакторами, в зависимости от типа данных свойства. Например, есть однострочный текст, многострочный текст, HTML или изображение (в последнем случае значением свойства является URL изображения). Свойство опционально может быть локализируемым (т. е. его значение отличается в зависимости от культуры (языка)). В таком случае при редактировании свойства для каждой культуры отображается отдельный редактор. Также можно выводить значения ключевых свойств в списке объектов соответствующего класса.

Значением связи является другой объект или другие объекты указанного класса. Также, если один объект одного класса может быть связан только с одним объектом другого класса (например, статья может входить лишь в одну категорию), можно сгруппировать эти «дочерние» объекты относительно своего «родителя». Это может показаться немного запутанным. В конце статьи есть ссылка на пример на GitHub, там этот прием используется.

Первым делом опишем свойство Name — название новости:



На вкладке General мы указываем код, название и позицию члена класса. По коду мы затем будем получать значение свойств (но не связей!) объекта, название отображается в редакторе свойства или связи, а позиция определяет порядок следования этих редакторов.



На вкладке Property мы указываем тип данных нашего свойства. Для названия выберем Single line plain text — однострочный простой текст. Установим флаг Is property localizable, т. к. название новости будет разным для разных культур. Также, поскольку название является ключевым свойством, установим и флаг Is property visible in list, чтобы значение этого свойства отображалось в списке объектов класса News Page.

Т. к. мы описываем свойство, а не связь, ко вкладке Relation не обращаемся. Нажимаем Save, наш новый член класса добавлен.

Теперь аналогично опишем свойство Preview — укороченный текст новости, который будет отображаться в списке новостей. Т. к. в превью будет больше текста, чем в названии, в качестве типа данных свойства выберем Multiline plain text и флаг Is property visible in list на этот раз устанавливать не будем. Далее, точно так же добавим и свойство с основным содержимым новости — Content. Выберем тип данных Html, чтобы иметь возможность удобно форматировать содержимое.

Последнее свойство, которое я бы хотел добавить, — иллюстрация к новости. Все точно так, как и с другими свойствами, но в качестве типа данных выбираем Image и не устанавливаем флаг Is property localizable, т. к. иллюстрация у нас не зависит от текущей культуры.

Теперь добавим нашему классу связь с классом Tag, чтобы для новостей можно было указывать теги:



На вкладке General все привычно. Переходим на этот раз на вкладку Relation:



В выпадающим списке Relation class выбираем класс, объекты которого будут привязываться к объектам класса News Page.

Все, теперь все готово. Должно получиться как-то вот так:



Аналогичным образом опишем единственное свойство класса Tag — Name. (Напомню, что флаги Is property localizable и Is property visible in list должны быть установлены.)

Источники данных


Несмотря на то, что мы описали связь между News Page и Tag, привязанные объекты класса Tag не будут загружены вместе с соответствующим объектом класса News Page. Чтобы это произошло, нам необходимо добавить еще кое-что в класс News Page — источник данных. Источники данных определяют, какие объекты загружаются вместе с текущим объектом и как именно. Например, если между объектом-новостью и несколькими объектами-тегами существуют связи, то с помощью источников данных, опираясь на связи, мы можем как загрузить все теги новости, так и все новости тега. Также, мы можем загружать объекты даже при отсутствии связи с текущим. Например, чтобы вывести новости на главной странице. Все источники данных описываются C#-классами, реализующими интерфейс IDataSource, и мы можем добавлять новые такие классы. Например, можно добавить источник данных, который будет выводит 100 последних новостей с разбивкой по 10 на страницу. Либо источник данных, отображающий самые популярные товары интернет-магазина. Удобно, что в итоге все данные группируются в единственном объекте, который является моделью для своего представления. Это вполне в духе MVC.

Итак, вернемся к списку наших классов и кликнем на ссылке Data sources в строке класса News Page. Добавляем новый источник данных:



Сохраняем и получаем такой список:



Наполнение содержимым


Ну что же, теперь можно приступить непосредственно к наполнению нашего веб-сайта содержимым. Переходим в раздел Objects:



Все объекты разделены на 2 группы — автономные (Standalone) и встраиваемые (Embedded). Внутри этих групп они отображаются по классам. Так как объекты класса News Page нуждаются в объектах класса Tag, начнем наполнение с последних:



Создадим наш первый объект:



Как видим, страница редактирования объекта очень простая и содержит всего 2 поля: View name (для встраиваемых объектов его можно не заполнять, а хардкодить позже в родительском представлении, если нет необходимости иметь возможность изменять представление в дальнейшем из бекенда) и Name. Свойство Name мы описали в нашем классе Tag. Давайте создадим несколько тегов, у меня получилось вот так:



Наконец-то перейдем к нашим новостям. Создадим объект класса News Page. Полей здесь уже больше:



Для свойств, имеющих тип данных Image, загрузка изображений производится с помощью вот такого загрузчика-обрезчика (на текущий момент манипуляции с изображениями в ASP.NET Core еще не поддерживаются, поэтому реальное кадрирование на сервере не происходит, изображение сохраняется как есть, целиком, вне зависимости от выбранной области):



А вот так выглядит редактор связи, выбор тегов в нашем случае:



Давайте создадим еще несколько новостей, чтобы было нагляднее, и попробуем протестировать, что у нас в итоге получилось. Перейдем по одному из URL наших новостей, предварительно добавив перед ним идентификатор культуры: http://localhost:5000/en/news/2016-olympic-games. И… получаем 500-ю ошибку. Если посмотреть в консоль, то становится очевидно, что мы не создали представление NewsPage для нашего объекта:



Представления и окончательная настройка


Попробуем создать представление для страницы новости. Перейдем в папку \Views\Default нашего приложения. Здесь мы увидим единственное представление Page.cshtml (наша главная страница). Скопируем его, назовем копию NewsPage.cshtml и откроем для редактирования в любом текстовом редакторе:



В качестве типа данных модели представления указан dynamic, но мы с вами знаем, что в действительности это будет объект C#-класса ObjectViewModel, содержащий данные нашего объекта класса News Page.

Доступ к значениям свойств объекта производится так:

Model.Properties["MemberCode"].Html

Доступ к объектам в источниках данных производится так:

Model.DataSources["DataSourceCode"].Objects

Модифицируем наше представление, например, таким образом (конечно, лучше было бы не описывать разметку, необходимую для отображения тега прямо тут, а сделать отдельное частичное представления _Tag для этого; мы так поступили для упрощения и наглядности):



Теперь обновляем страницу в браузере и видим такой результат:



Вот здорово, все работает! Осталось только вывести список новостей на главную. Для этого добавим в класс Page такой источник данных:



Как видим, C#-класс источника данных отличается от того, который мы использовали перед этим. Это более обобщенный источник данных, не учитывающий связей между объектами. Также, обратите внимание на поле Parameters. Здесь приведен идентификатор класса, объекты которого необходимо загрузить. В текущей версии Платформуса это делается не совсем изящно, без какого-либо визуального селектора.

Теперь перейдем к редактированию объекта главной страницы, уберем все лишнее содержимое. (Содержимое можно и не изменять, но открыть для редактирования и затем сохранить объект после добавления источника данных в его класс необходимо, иначе эта информация не подтянется в закешированный объект и не будет доступна представлению.)

Откроем теперь для редактирования представление Page.cshtml и изменим его примерно следующим образом:



Вот что мы получим теперь, если зайдем на главную страницу нашего веб-сайта в браузере:



Все готово! Конечно, было бы правильно добавить сюда еще, как минимум, меню или форму обратной связи, но сделать это очень просто (см. ссылку ниже), поэтому не будем на этом заострять внимание.

Заключение


Думаю, первый вопрос, на который необходимо ответить себе, прежде чем начинать разработку очередной CMS — «Зачем?». Если не вдаваться в подробности, то, как минимум, сейчас еще не так много CMS на ASP.NET Core, а мне эта платформа очень нравится и я верю в ее будущее.

Искренне надеюсь, что вам было интересно, а также, что получу критику, идеи, пищу для размышлений для дальнейшего совершенствования продукта — мы лишь в начале пути. Спасибо!

Я не выкладывал код именно этого проекта на GitHub, т. к. он очень похож на другой пример, который вы можете скачать и посмотреть, чтобы не проделывать все вручную.
Share post

Similar posts

Comments 14

    +3
    А в чем преимущество данной системы на другими решениями? есть ли какие-то сильные стороны или «технология ради технологии»?
      0
      Ожидаемый и логичный вопрос, спасибо. Я фанат C# и ASP.NET, не буду скрывать. На этой платформе не так много хороших CMS. Мне нравится, например, Umbraco, но пока-что насколько я знаю она не портирована на ASP.NET Core. Также мне не слишком по душе ее архитектура и сложность (в технологическом плане), вызванная, наверное, частично, большим сроком в open source. Хочется иметь простую, быструю и расширяемую систему на новой платформе, чтобы писать на C# и при этом не быть привязанным к Windows-среде, т. к. многим клиентам не нравится это ограничение (и их можно понять). Можно было бы посмотреть в сторону Java, там тоже есть и хороший язык (который я знаю благодаря Android-разработке) и кроссплатформенность, но, как я уже сказал, весь стек от MS мне как-то ближе что-ли. Так что, отвечая на ваш вопрос, я бы субъективно назвал сильными сторонами хорошую платформу, язык, легкость, расширяемость, простоту использования.
      0
      я правильно понял, что нужно будет пилить свою вьюху на каждую новость?
      WYSIWYG редакторы в классических цмс както сподручней для пользователя ЦРМ, который не является програмистом
        0
        Не совсем. Отдельные вьюхи создаются для отдельных классов объектов. Т. е. у всех новостей может быть одинаковая вьюха, но если необходимо, можно для какой-то особенной новости задать особенную вьюху.

        Если я правильно вас понял, то да, согласен, нужно сделать возможным редактировать представлений прямо из админки в будущем (как это сделано в Ubraco, например). Сейчас это приходится делать через внешний редактор + необходим доступ к файловой системе сайта.
          +1
          я не правильно понял, контент же в себе может содержать нужные картинки.
            0
            Верно, либо контент (например, Html), либо напрямую свойства с типом редактора Image.
        0
        Стоп, Razor views работает разве с дотнет кором? Мне казалось что там что-то с чем-то не дружило.
          +1
          Даже больше, это там стандартный движок представлений :)
          0
          Интересная cms. Единственное что резануло глаза мне, как seo-специалисту —
          Т. к. каждая страница новости должна иметь заголовок, META-description и META-keywords
          , META-keywords совершенно не обязательный, и вообще не нужный тэг. Вот пруф от гугла об этом ссылка
            +1
            Если считаете свойство лишним — просто удалите его из соответствующего класса и оно исчезнет. Контентом управляете вы. Что касается META-keywords, то как-то читал об эксперименте. Ребята придумали несуществующие слова и на полностью новом сайте добавили их только исключительно в META-keywords. После индексации гугл показывал сайт по соответствующему запросу. Это было не так давно, ссылку уже не найду, наверное. Но, повторюсь, в данном случае полная свобода в плане свойств, решать вам.
            +1
            Если еще не видели, то думаю было бы интересно посмотреть как референс на OrchardCMS https://github.com/OrchardCMS/Orchard2
            Вторая версия (версия по ссылке) будет на asp.net core
              0
              Спасибо! Конечно, сложно не знать об одной из самых популярных CMS на ASP.NET :) Интересно было посмотреть, как у них реализована модульность. В свое время я здорово помучался на этой теме.
            +1
            Как человек, работающий в команде, которая тоже занимается разработкой open-source системы на ASP.NET (nopCommerce), желаю вам удачи! Если не остановитесь в разработке и донесете продукт до ума и сердец пользователей, то будет здорово.

            Мы, кстати, тоже будет переходить на ASP.NET в следующем году

            Only users with full accounts can post comments. Log in, please.