Прикрепленные сайты. Часть 2 – Дополнение для BlogEngine.NET

После изучения возможностей прикрепленных сайтов, перейдем к реализации расширения для BlogEngine.NET. Результат его работы можно посмотреть на примере данного сайта.

Возможности разрабатываемого расширения

В начале перечислим какие возможности будет предоставлять создаваемое расширение:

  • Настройка отображения сайта с помощью элементов <meta>:
    • имя сайта;
    • описание;
    • ссылка для загрузки;
    • цвет кнопок;
    • размеры окна сайта при открытии.
  • Создание, настройка параметров и добавление произвольного списка Задач.
  • Автоматическая публикация в блоке Ссылок последних записей блога за заданное число дней.
  • Присвоение элементам блока Ссылок иконок на основании частей их URL.

Последнее означает, что можно будет задавать пары значений вида "строка-иконка". В случае, если в пути публикации, добавляемой в блок Ссылок, будет содержаться указанная строка, то она получит заданную для ней иконку.

Я не буду детально описывать весь код. Остановимся подробно только на принципах создания модулей расширений для BlogEngine.NET и ключевых элементах разработки. А в завершении будет приведен полный код C# под лицензией Microsoft Reciprocal License (Ms-RL) (под которой распространяется и код самого движка).

Принципы разработки расширений для движка BlogEngine.NET

Расширение для BlogEngine.NET это обычный класс, который может подписаться на глобальные события движка, список который можно найти в его документации.

Все создаваемые расширения должны быть расположены в папке App_Code\Extensions. Чтобы BlogEngine.NET подключил их, классам необходимо поставить следующий атрибут:

[Extension(description, version, author)]

Здесь все три параметра являются строками и задают следующие значения:

  • description – название расширения;
  • version – номер версии расширения;
  • author – автор (возможно использовать HTML-элементы).

Следующим требованием является наличие у создаваемого класса открытого конструктора без параметров. Почти все остальное ложится на разработчика. Все основные объекты, предназначенные для взаимодействия с движком, расположены в пространстве имен BlogEngine.Core.

Стоит отметить несколько ключевых и наиболее часто используемых событий, вызываемых:

  • Post.AddingComment – перед добавлением комментария;
  • Post.Serving – перед показом публикации (позволяет внести в её текст изменения);
  • Page.Serving – перед показом страницы.

Еще раз отмечу, что полный список доступен в документации.

Получение данные от движка

К сожалению BlogEngine.NET не содержит детальной документации разработчика. Поэтому при поиске нужных объектов приходится часто полагаться на подсказу IntelliSense в Visual Studio. С другой стороны, самих объектов не так много и имена их достаточно понятны для использования.

Наиболее часто используемыми для получения данных являются классы:

  • Post – позволяет работать с публикациями на сайте и содержит статические метод�� для получения их списка по различным параметрам:
public static Post GetPost(Guid id);
public static List<Post> GetPostsByAuthor(string author);
public static List<Post> GetPostsByCategory(Guid categoryId);
public static List<Post> GetPostsByDate(DateTime dateFrom, DateTime dateTo);
public static List<Post> GetPostsByTag(string tag);
  • Page – предназначен для работы со страницами сайта и также предоставляет доступ к их списку:
public static Page GetFrontPage();
public static Page GetPage(Guid id);

Вывод данных на страницу

Здесь возможны два варианта, в зависимости от решаемой задачи:

  1. Через аргументы событий, на которые подписано расширение, передаются данные текущей страницы или публикации. Таким образом можно изменить её содержимое.
  2. Можно использовать стандартные средства ASP.NET WebForms для добавления элементов на выводимую страницу.

Параметры расширения

BlogEngine.NET предоставляет удобный способ для хранения и редактирования настроек модулей расширения. Его суть заключается в следующем: класс ExtensionSettings используется как хранилище необходимых для конфигурации значений. При этом, по сохраненным с его помощью параметрам BlogEngine.NET самостоятельно создаст страницу для редактирования их значений.

Вот пример использования такого подхода:

private static void InitSettings()
{
    var siteSettings = new ExtensionSettings("Site properties");
    siteSettings.IsScalar = true;

    siteSettings.AddParameter("enabled", "Enable Meta tags", 10, true, false, ParameterType.Boolean);

    siteSettings.AddParameter(
        "application-name", "Application name", 128, true, false, ParameterType.String);
    siteSettings.AddValue("application-name", string.Empty);

    .........

    _siteSettings = ExtensionManager.InitSettings(ExtensionName, siteSettings);
}

В начале необходимо породить экземпляр класса ExtensionSettings с именем сохраняемых настроек. Оно же будет использоваться как заголовок на странице настройки. При этом расширение может создать несколько различных настроек.

Extension configuration exampleЗатем указывается тип настройки при помощи свойства IsScalar. Как можно догадаться по типу bool, есть два варианта. Если значение равно true, то все указанные далее параметры задаются только один раз. В противном случае будет создана таблица, каждая строчка которой будет включать их новую копию.

На копии экрана справа можно увидеть разницу между установкой IsScalar = true для JumpList Items и IsScalar = false для JumpList Icons.

Каждый параметр настройки описывается при помощи метода

public void AddParameter(
    string name,
    string label,
    int maxLength,
    bool required,
    bool keyfield,
    ParameterType parType);

Ему передаются следующие параметры:

  • name – имя параметра, уникальное для данного экземпляра ExtensionSettings.
  • label – название для отображения на странице настроек.
  • maxLength – максимальная длина строки со значением.
  • required – указывает является ли параметр обязательным (true).
  • keyfield – указывает является ли параметр ключевым полем в хранилище (true).
  • parType – определяет тип параметра и стиль его отображения в окне настроек модуля расширения. Данный параметр может принимать следующие значения:
    • ParameterType.String,
    • ParameterType.Boolean,
    • ParameterType.Integer,
    • ParameterType.Long,
    • ParameterType.Float,
    • ParameterType.Double,
    • ParameterType.Decimal,
    • ParameterType.DropDown,
    • ParameterType.ListBox,
    • ParameterType.RadioGroup.

Указать исходные значения можно при помощи вызова метода AddValue(). При этом для булевских, строковых, числовых и прочих простых параметров это будет значения по умолчанию. А для списков несколько вызовов AddValue() зададут его элементы.

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

    _siteSettings = ExtensionManager.InitSettings(ExtensionName, siteSettings);

В данном случае в переменную _siteSettings из хранилища движка будут загружены настройки для расширения под именем, указанным в константе ExtensionName. Формат, список и значения по умолчанию определены в объекте siteSettings.

Обратите внимание, что страница настроек расширения появится только после того, как в хранилище движка будет записаны конфигурационные значения. А это означает, что оно должно быть хотя бы раз использовано в работе сайта.

Ключевые моменты разработки расширения

Перейдем к разработке. В папке App_Code\Extensions создадим класс PinnedSiteSupport. Отметим его атрибутом [Extension] следующим образом:

[Extension(
    description: "Adds support for IE9 Pinned mode",
    version: "0.8",
    author: "<a target=\"_new\" href=\"http://andrey.moveax.ru/\">Andrey Veselov</a>")]
public class PinnedSiteSupport
{
    .........
}

Для того, чтобы добавлять необходимый код на страницы и в публикации сайта, подпишемся на соответствующие события в конструкторе класса:

static PinnedSiteSupport()
{
    BlogEngine.Core.Page.Serving += new EventHandler<ServingEventArgs>(BlogServingHandler);
    BlogEngine.Core.Post.Serving += new EventHandler<ServingEventArgs>(BlogServingHandler);
}

В методе BlogServingHandler() определим браузер пользователя. Выводить код поддержки прикрепленного режима стоит только для IE9 или старше. В остальных случаях расширение будет завершать свою работу.

Кроме того, при выводе списка публикаций событие Post.Serving будет вызвано для каждого из них. Поэтому установим флаг context.Items[ExtensionName], чтобы определять был ли код поддержки уже добавлен в HTML код страницу.

private static void BlogServingHandler(object sender, ServingEventArgs e)
{
    HttpContext context = HttpContext.Current;

    // Checking the browser version to see if it's IE9 or higher.
    var browser = context.Request.Browser;
    var isIE9orHigher = browser.Browser.Equals("IE") && (browser.MajorVersion >= 9);
    if (!isIE9orHigher) {
        return;
    }
    
    Page page = context.CurrentHandler as Page;
    if ((page == null) || (context.Items[ExtensionName] != null)) {
        return;
    }

    PinnedSitesSupport.InitSettings();

    PinnedSitesSupport.AddSiteProperties(page);
    PinnedSitesSupport.AddJumpListItems(page);
    PinnedSitesSupport.AddTasks(page);

    // Set the flag in order to add items once per page (not once per post).
    context.Items[ExtensionName] = new object();
}

Метод InitSettings определяет список переменных настройки расширения. В нем будут созданы экземпляры ExtensionSettings для нескольких групп настроек:

  • Site Properties (свойства сайта) – определяет параметры сайта в элементах <meta>.
  • JumpList Items (Ссылок) – задает общие параметры для блока Ссылок.
  • Tasks (Задачи) – включает список "Задачи".
  • Tasks List (элементы Задач) – список элементов в блоке "Задачи".

Большая часть остального кода сводится к чтению значений из конфигурации и присвоении их соответствующим элементам <meta>. Для упрощения этого процесса предназначен метод InsertMetaTag(), который создает и добавляет их в HTML код страницы.

Отдельно стоит только выделить создание JavaScript со списком последних публикаций. Он добавляется в тело страницы. Сам список имеет следующий формат:

getNewPostsForPinnedSite = function () {
    return [
        { name: "заголовок 1", postUrl: "ссылка 1", iconUrl: "ссылка на иконку" },
        { name: "заголовок 2", postUrl: "ссылка 2", iconUrl: "ссылка на иконку" }];
};

Для его формирования используется метод CreateJSPostsList(). А указанные в нем публикации будут пересылаться в список Ссылки с помощью следующего кода:

addIE9PinnedCategories = function (title) {
    var isPinnedSiteMode = window.external && "msIsSiteMode" in window.external && window.external.msIsSiteMode();
    if (isPinnedSiteMode) {
        window.external.msSiteModeCreateJumplist(title);
        window.external.msSiteModeClearJumplist();
        var posts = getNewPostsForPinnedSite();
        for (i = posts.length - 1; i >= 0; i--) {
            window.external.msSiteModeAddJumpListItem(
                posts[i].name, posts[i].postUrl, posts[i].iconUrl);
        }
        window.external.msSiteModeShowJumplist();
    }
};

Таким образом разработанные расширение будет состоять из двух файлов: PinnedSiteSupport.cs с его C# кодом и PinnedSiteSupport.js со вспомогательной функцией на JavaScript.

И в завершении – список методов класса PinnedSiteSupport и их назначение:

  • PinnedSiteSupport() – Статический конструктор который подписывает расширение на события, связанные с выводом содержимого блога посетителю.
  • BlogServingHandler() – Обработчик событий, вызываемый при выводе статей и страниц блога. Проверяет версию браузера посетителя. В случае если она равна IE9 или выше, то загружает настройки и вызывает методы, которые добавляют добавляют код поддержки прикреплённого режима на выводимую страницу.
  • SetSiteProperties() – добавляет описание сайта в виде элементов <meta> на выводимую страницу.
  • SetJumpListItems() – добавляет скрипты создания элементов JumpList на выводимую страницу.
  • SetTasks() – добавляет список задач на выводимую страницу.
  • InitSettings() – описывает параметры настройки и загружает их значения из хранилища.
  • CreateJSPostsList() – создает JavaScript массив со списком последних публикаций.
  • Вспомогательные методы для добавления элементов на выводимую страницу:
    • InsertMetaTag() – добавляет элемента <meta>.
    • InsertJavaScript() – внедряет JavaScript код.
    • InsertJavaScriptRef() – добавляет ссылку на указанный JavaScript файл.

В данный момент актуальна версия 0.8 данного расширения. Код является черновым, но при этом не сложным, даже не смотря на малое количество комментариев в нем.

Что можно еще сделать? Массив со списком публикацией создается при каждом запросе страницы. Как правило, новые публикации появляются раз в день или даже реже. Поэтому было бы логично кэшировать их перечень. При этом движок позволяет организовать его сброс по событиям, отражающим изменения содержимого блога, а также при смене даты. Но, к сожалению, у BlogEngine.NET нет возможности определять момент изменения самих настроек расширения. Поэтому при их изменении кэш не будет пересоздаваться. Вариантов обойти это пока не найдено.

Если у вас есть замечания или предложения – пишите через страницу "Контакты".

Остается только привести полный исходный код.

Комментарии (2) -

И в завершении – список методов класса PinnedSiteSupport и их назначение: =D no way

@ vladimiere: Не понял комментария. Smile

Добавить комментарий