Расширение Orchard CMS: создание виджетов
Введение
Это продолжение цикла статей на тему разработки собственных сайтов на базе системы управления контентом Orchard CMS. Первые статьи данного цикла вы можете найти по следующим ссылкам:
- Как создать свой сайт с нуля с помощью Orchard CMS. Часть 1. Введение в Orchard CMS
- Как создать свой сайт с нуля с помощью Orchard CMS. Часть 1. Введение в Orchard CMS (cont)
- Архитектура Orchard CMS. Основные понятия
- Архитектура Orchard CMS. Основные понятия. Концепции компоновки (cont)
- Архитектура Orchard CMS. Основные понятия. Концепции безопасности и разработки (cont 2)
- Расширение Orchard CMS: создание контентных типов
В Orchard виджет – это кусочек UI, который может быть повторно использован и может быть произвольно размещен на любой странице сайта. Примеры виджетов могут включать в себя: облако тегов, форму поиска, список твитов из Твиттера. Виджет является типом контента, который позволяет вам повторно использовать имеющийся UI и код.
В этой статье описывается порядок написание собственных виджетов через создание контентной части, которая затем трансформируется в виджет. Это статья основана на оригинальной статье Writing a content part.
Создание контентной части
Контентная часть – это небольшой кусок функциональности или пользовательского интерфейса, который может быть добавлен в любой тип контента в Orchard. Примеры таких частей: маршрут для возможности получить доступ к элементу контента, тэги для тегирования элементов контента, меню для возможности добавить в пользовательские меню элементы контента.
В этой части мы рассмотрим процесс создания новой контентной части с нуля, с помощью возможностей продуктивного инструмента скаффолдинга Orchard. Несмотря на то, что мы будем рассматривать разработку через Visual Studio, совершенно необязательно иметь Visual Studio для создания контентных частей, вы можете использовать любой редактор на свой вкус. В этой части руководства мы рассмотрим создание контентной части, которая будет отображать карту с возможностью настройки через параметры широты и долготы.
Важно. Перед тем, как вы начнете создавать файловую структуру для своего модуля, вам необходимо загрузить и установить функцию Code Generation (кодогенерация) в Orchard. Больше информации по этой функции вы можете найти по этой ссылке.
Для начала создания новой контентной части с картой, загрузите проект Orchard и откройте его в Visual Studio.
Наберите "codegen module Maps /IncludeInSolution:true" в командной строке Orchard. Параметр “IncludeInSolution” сообщает Orchard о необходимости добавить новый проект модуля в список проектов Orchard.
После выполнения этой команды Visual Studio попросит вас перезагрузить решение в среде разработки, согласитесь с этим. После этого в решении появится новый проект Maps, который будет содержать набор файлов добавленных по умолчанию для того, чтобы вы могли создать новую часть контента.
Откройте файл Module.txt в корне проекта, этот файл содержит некоторую информацию о вашем модуле, такую как имя, описание, версию, автора и набор функций предоставляемых данным модулем. Этот файл так же содержит дополнительную информацию, например, информацию о зависимостях. Созданный по умолчанию модуль очень простой и содержит только одну функцию, без дополнительных зависимостей. Отредактируйте Module.txt следующим образом:
Name: Maps AntiForgery: enabled Author: The Orchard Team Website: https://orchardproject.net Version: 1.0.0 OrchardVersion: 1.0.0 Description: Adds a map image to content items, based on longitude and latitude. Features: Maps: Description: Adds a map image to content items, based on longitude and latitude. Category: Geolocation
Теперь, давайте начнем создавать нашу контентную часть. Для того, чтобы начать нам необходимо создать файл, который будет содержать класс с описанием данных нашей контентной части. Обычно такие классы размещаются в папке “Models”. Нажмите правой кнопкой мыши на папке Models и выберите пункт Add –> Class…, в появившемся окне назовите новый файл Maps.cs.
В Orchard контентная часть представлена классом Record, который содержит поля данных сохраняемых в базу данных и класс ContentPart, который исопльзует Record для хранения. Добавьте классы MapRecord (ContentPartRecord) и MapPart (ContentPart) так, как показано ниже:
using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;
namespace Maps.Models {
public class MapRecord : ContentPartRecord {
public virtual double Latitude { get; set; }
public virtual double Longitude { get; set; }
}
public class MapPart : ContentPart<MapRecord> {
[Required]
public double Latitude
{
get { return Record.Latitude; }
set { Record.Latitude = value; }
}
[Required]
public double Longitude
{
get { return Record.Longitude; }
set { Record.Longitude = value; }
}
}
}
Теперь соберите проект с помощью команды Build, для того, чтобы убедиться в отсутствии ошибок.
Следующим шагом будет создание миграции данных для нашего модуля карт. Зачем нам нужен класс для миграции данных? Причина в том, что простое объявление классов Record и Part для хранения данных на самом деле никак не затрагивает базу данных. Миграция данных – это тот механизм, который говорит Orchard как необходимо обновить схему базы данных во время включения модуля карт на сайте. Миграция может так же обновить схему данных с предыдущей версии на новую, но этот вопрос лежит за рамками данной статьи и мы не будем его касаться.
Для того, чтобы создать класс миграции данных, вы можете использовать функцию кодогенерации Orchard. Запустить на выполнение в командной строке команду "codegen datamigration Maps".
Visual Studio опять попросит вас перезагрузить решение. После загрузки вы обнаружите в проекте новый класс для миграции данных.
Класс миграции данных добавленный командой содержит единственный метод Create(), который определяет структуру базы данных основываясь на классах Record в проекте. Из-за того, что у нас в проекте всего один файл MapRecord с двумя свойствами широты и долготы класс миграции данных очень прост. Обратите внимание, что метод Create вызывается в момент, когда функция модуля активируется, после чего база данных получает соответственное обновление.
using System;
using System.Collections.Generic;
using System.Data;
using Maps.Models;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
namespace Maps.DataMigrations {
public class Migrations : DataMigrationImpl {
public int Create() {
// Creating table MapRecord
SchemaBuilder.CreateTable("MapRecord", table => table
.ContentPartRecord()
.Column("Latitude", DbType.Double)
.Column("Longitude", DbType.Double)
);
ContentDefinitionManager.AlterPartDefinition(
typeof(MapPart).Name, cfg => cfg.Attachable());
return 1;
}
}
}
Строчка с вызовом AlterPartDefinition добавлена для того, чтобы созданная нами часть контента могла присоединяться к любому типу контента. Обратите внимание на необходимость добавить строку using Maps.Models;
для подключения сборки.
Теперь давайте добавим обработчик для нашей контентной части. Обработчик в Orchard – это класс, который определяет поведение контентной части, обрабатывает события или манипулирование моделью данных перед рендерингом части на веб-странице. Так как наша контентная часть карт очень простая, наш класс будет использовать только IRepository <MapRecord> как структуру для доступа к данным. Добавьте следующее содержимое в файл Handlers\MapHandler.cs:
using Maps.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
namespace Maps.Handlers {
public class MapHandler : ContentHandler {
public MapHandler(IRepository<MapRecord> repository) {
Filters.Add(StorageFilter.For(repository));
}
}
}
Кроме того, нам необходимо добавить драйвер для нашей контентной части. Драйвер в Orchard – это класс, который определяет связи фигур для отображения в каждом контексте, в котором часть может отображаться. Например, когда карта отображается на странице, метод Display определяет наименование шаблона, который будет использоваться для отображения в зависимости от разных значений displayTypes (например, “details” или “summary”).
Точно так же метод драйвера “Editor” определяет шаблон для отображения редактора контентной части (в нашем случае для карты это будет ввод широты и долготы). Наша часть будет простой, мы будем использовать фигуру с именем Map как для контекста Display так и для Editor. Добавьте класс MapDriver, так как написано ниже:
using Maps.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
namespace Maps.Drivers {
public class MapDriver : ContentPartDriver<MapPart> {
protected override DriverResult Display(
MapPart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_Map", () => shapeHelper.Parts_Map(
Longitude: part.Longitude,
Latitude: part.Latitude));
}
//GET
protected override DriverResult Editor(
MapPart part, dynamic shapeHelper) {
return ContentShape("Parts_Map_Edit",
() => shapeHelper.EditorTemplate(
TemplateName: "Parts/Map",
Model: part,
Prefix: Prefix));
}
//POST
protected override DriverResult Editor(
MapPart part, IUpdateModel updater, dynamic shapeHelper) {
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
}
Теперь мы можем с помощью Visual Studio создать представления для просмотра и редактирования контентной части. Сначала добавьте папки Parts и EditorTemplates/Parts в папку Views проекта Maps. Затем добавьте файлы под одним названием Maps.cshtml в каждую из папок Views/EditorTemplates/Parts:
@model Maps.Models.MapPart
<fieldset>
<legend>Map Fields</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Latitude)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => model.Latitude)
@Html.ValidationMessageFor(model => model.Latitude)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Longitude)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => model.Longitude)
@Html.ValidationMessageFor(model => model.Longitude)
</div>
</fieldset>
и Views/Parts со следующим содержимым:
<img alt="Location" border="1" src="https://maps.google.com/maps/api/staticmap?
&zoom=14
&size=256x256
&maptype=roadmap
&markers=color:blue|@Model.Latitude,@Model.Longitude
&sensor=false" />
Оба эти шаблона будут в дальнейшем отображаться как малые части большой страницы сайта. В связи с тем, что системе необходимо знать порядок и местоположение где части контента будут отображены, нам необходимо указать эти данные в специальном файле placement.info в корне папки модулей:
<Placement>
<Place Parts_Map="Content:10"/>
<Place Parts_Map_Edit="Content:7.5"/>
</Placement>
Это определение сообщает системе, что фигура Parts_Map (которая отображается с помощью Views/Parts/Maps.cshtml) должна отображаться в зоне Content, если такая существует, в 10 позиции. Кроме того, указывается, что редактор должен отображаться в зоне Primary во второй позиции.
Для активации нашей контентной части карты, перейдите в секцию Modules в панели администрирования Orchard и включите ее.
Вы можете протестировать контентную часть простым присоединением к любому типу контента в системе, с помощью раздела “Content Types” в панели администрирования Orchard. Давайте добавим нашу контентную часть к существующему контентному типу, например, типу Event, который мы создали в предыдущей статье Расширение Orchard CMS: создание контентных типов.
На странице Manage Content Types панели администрирования нажмите Edit дле редактирования определения контентного типа.
В списке частей нажмите Add для того, чтобы добавить созданную нами контентную часть для карты. Часть Map будет отображена в списке доступных контентных частей, выберите ее и нажмите Save.
Теперь, перейдите в раздел Manage Content и отредактируйте данные элемента контента. Обратите внимание, контентная часть Карты добавила в перечень параметров свои собственные: широту и долготу. Введите актуальные координаты местоположения и повторно опубликуйте элемент контента.
Теперь на странице вашего сайта вы сможете увидеть карту отображенную для конкретного мероприятия.
Вы можете загрузить готовый пакет с контентной частью Карты по этой ссылке: Orchard.Module.Maps.1.0.0.nupkg. Этот пакет готов к установке и использованию и содержит полные исходные коды.
Превращение контентной части в виджет
Для того, чтобы получить из контентной части готовый виджет вам необходимо обновить базу данных с описанием вашего виджета. Для этого необходимо добавить метод UpdateFrom<version#>
в специальный файл контентной части Migrations.cs.
Следующий код показывает такой файл для контентной части Map с добавленным методом UpdateFrom1:
using System.Data;
using Maps.Models;
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
namespace Maps
{
public class Migrations : DataMigrationImpl
{
public int Create()
{
// Creating table MapRecord
SchemaBuilder.CreateTable("MapRecord", table => table
.ContentPartRecord()
.Column("Latitude", DbType.Single)
.Column("Longitude", DbType.Single)
);
ContentDefinitionManager.AlterPartDefinition(typeof(MapPart).Name, cfg => cfg
.Attachable());
return 1;
}
public int UpdateFrom1()
{
// Create a new widget content type with our map
ContentDefinitionManager.AlterTypeDefinition("MapWidget", cfg => cfg
.WithPart("MapPart")
.WithPart("WidgetPart")
.WithPart("CommonPart")
.WithSetting("Stereotype", "Widget"));
return 2;
}
}
}
В этом примере метод UpdateFrom1 создает MapWidget путем комбинирования MapPart, WidgetPart и CommonPart, а затем задает специальный тип (стереотип) виджета. Объекты WidgetPart и CommonPart встроены в Orchard. Метод возвращает 2, что означает номер версии.
Теперь контентная часть трансформирована в виджет.
Отображение виджета
После того, как вы создали виджет, откройте панель администрирования и перейдите на вкладку Widgets. Здесь вы можете выбрать слой и зону для расположения своего виджета.
Заключение
В этой статье мы рассмотрели один из вариантов расширения функционала Orchard CMS с помощью создания контентной части и трансформирования ее в виджет, который в последствии может быть расположен на любой странице сайта, на любом слое, в любой зоне.
В следующих статьях мы рассмотрим вопросы управления виджетами и возможности упаковки и распространения виджета в качестве отдельного модуля.