TeamWox SDK: Настройка окружения пользовательских модулей - Часть 1

 

Введение

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

Из материалов предыдущих статей вы уже получили некоторый опыт в области разработки пользовательских модулей. В этой статье мы поговорим о том, как правильно настроить окружение своих модулей для их последующего распространения, в том числе на платной основе. В качестве примера будем использовать все тот же учебный модуль Hello World.

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

1. Настройка окружения
    1.1. Получение информации о модуле
    1.2. Получение интерфейсов модуля
2. Ресурсы и шаблоны
    2.1. Ресурсы
    2.2. Шаблоны
3. Файлы справки
    3.1. Структура папок и файлов
    3.2. Порядок разделов и страниц
4. Интеграция пользовательских команд в модули TeamWox
    4.1. Пользовательские команды в шапке главной страницы TeamWox
    4.2. Загрузка скриптов модуля в основном фрейме

 

1. Настройка окружения

DLL каждого модуля TeamWox обязательно должна экспортировать следующие методы:

  1. ModuleGetInfo(int index, ModuleInfo *info). Этот метод получает информацию о модуле путем заполнения структуры ModuleInfo. Эта информация будет использоваться при добавлении и регистрации нового модуля в системе TeamWox.

  2. ModuleGetInterface(int id, int server_api_version, IModule **module). Этот метод возвращает интерфейс IModule, чтобы сервер мог взаимодействовать с модулем.

 

1.1. Получение информации о модуле

Структура ModuleInfo

Рассмотрим, из чего состоит эта структура, содержащая всю необходимую информацию о модуле.

Поле Тип Описание
id int Уникальный идентификатор модуля. Первые 65535 цифр зарезервированы для модулей из стандартной поставки. Пользовательские модули должны иметь идентификаторы больше 65535.
version int Версия модуля. При отображении целочисленное значение будет представлено в виде числа с плавающей точкой с округлением до двух знаков после запятой. Например, число 100 будет отображаться как 1.00.
build int Номер сборки модуля. Важный параметр, по которому можно выполнять различные проверки (как, например, мы это делали в статье TeamWox SDK: Взаимодействие с СУБД). С каждой новой сборкой модуля разработчики должны инкрементировать этот параметр.
build_date wchar_t[16] Дата сборки модуля. Информационная строка.
version_api unsigned int Версия TeamWox API, с использованием которой был скомпилирован модуль. Проверяется в ModuleGetInterface относительно текущей версии API сервера. Если версия TeamWox API, с которой скомпилирован модуль, больше (новее) чем у сервера - модуль загружен не будет.
name wchar_t[64] Краткое название модуля, которое используется в HTTP запросах. Может содержать только символы латинского алфавита и символы подчеркивания.
dependences int[32] Список модулей, необходимых для работы данного модуля. DEPRECATED, функционал реализуется через IModule::PostInitialize.
color wchar_t[7] Цвет шапки с градиентной заливкой (6 символов и ноль) DEPRECATED, функционал реализуется через шаблоны.
flags int Флаги описания модуля. Берутся из перечисления EnModuleFlags (подробнее см. ниже).
icon_url wchar_t[260] Путь к иконке модуля, которая отображается на странице Управление -> Модули.
home_site wchar_t[260] Ссылка на страницу производителя модуля.
reserved char[256] Зарезервированное поле.

Поля id, name, icon_url, home_site и reserved как правило не изменяются в ходе разработки модуля и задаются один раз. При разработке модуля активнее всего изменяется поля build и build_date, а также периодически version_api при выходе новых версий сервера TeamWox (и соответственно TeamWox SDK, включающего TeamWox API).

Для модуля Hello World структура ModuleInfo заполняется в методе CHelloWorldModule::InfoGet(ModuleInfo *info).

ModuleInfo module_info=
{
HELLOWORLD_MODULE_ID,                                    // HELLOWORLD_MODULE_ID = 65536
ProgramVersion,                                          // #define ProgramVersion   100
ProgramBuild,                                            // #define ProgramBuild     100
ProgramBuildDate,                                        // #define ProgramBuildDate L"12 Oct 2008"
TEAMWOX_API_VERSION,                                     // #define TEAMWOX_API_VERSION 76
L"helloworld",                                           // 
{0},                                                   // 
L"",                                                     // 
TW_MODULE_FLAG_DIGITAL_SIGN | TW_MODULE_FLAG_MODULE_TAB, // Модуль сертифицирован разработчиками и имеет вкладку
L"/helloworld/res/i/logo.gif",                           // Логотип модуля
L"https://www.teamwox.com/"                               // 
};

Имя модуля (поле name) используется при формировании пути в строке HTTP запроса.

Краткое имя модуля

Логотип модуля отображается после его включения в интерфейсе модуля Управление на вкладке Модули,

Логотип модуля

а также в профиле пользователя в разделе Порядок вкладок.

Логотип модуля в профиле пользователя

 

Перечисление EnModuleFlags

Флаги из перечисления непосредственно влияют на отображение модуля в пользовательском интерфейсе.

Имя Значение Описание
TW_MODULE_FLAG_LEFTPAGE 0x01 Указывает на наличие левой панели. Если флаг выставлен, на странице модуля отображается левая панель, а веб-браузер посылает HTTP запрос вида /имя_модуля/left (подробнее см. далее).
TW_MODULE_FLAG_DIGITAL_SIGN 0x02 Защита модуля цифровой подписью. Выставленный флаг говорит о том, что модуль сертифицирован разработчиками TeamWox (так же, как модули из стандартной поставки).
TW_MODULE_FLAG_MODULE_TAB 0x04 Отображение вкладки модуля на главной странице TeamWox.
TW_MODULE_FLAG_HIDDEN 0x08 Модуль не имеет визуальной части, т.е. интегрируется с другими модулями (например, модуль Отчеты).
TW_MODULE_FLAG_MANUAL_CHECK 0x10 Отключение автоматической проверки прав на модуль. Если флаг не выставлен (по умолчанию) и модуль запрещен, то сервер не посылает запросы. Если же флаг выставлен, то запросы будут посылаться в любом случае (например, как в модуле Отчеты).

 

1.2. Получение интерфейсов модуля

Теперь, когда мы заполучили информацию о модуле, вторым этапом нам потребуется получить интерфейсы модуля. Класс любого модуля для работы в системе должен реализовывать интерфейс IModule. Это основной интерфейс, через который сервер взаимодействует с модулем.

Методы, унаследованные модулем от интерфейса IModule, вызываются сервером TeamWox в определенном порядке.

1. Инициализация - Initialize(class IServer *server,int prev_build). Можно запрашивать только интерфейсы сервера и серверных модулей. Для обеспечения корректности работы системы, на этом этапе нет никакого взаимодействия с другими модулями.

2. Пост-Инициализация - PostInitialize(). Все модули загружены, и теперь уже можно запрашивать интерфейсы других модулей.

3. Работа в системе. Это основной этап, на котором через пользовательский интерфейс страниц происходит взаимодействие с сервером.

  • Обработка запроса - Process(const Context *context). Для любого модуля в системе TeamWox зарезервированы 2 части URL, которые рекомендуется использовать в HTTP запросах:
    • /имя_модуля/index - Перенаправление на главную страницу модуля.
    • /имя_модуля/left - Отображение левой панели. При таком запросе в пользовательском интерфейсе модуля создается дополнительный фрейм (с разделителем), в котором отображается результат этого запроса. В пользовательском интерфейсе левой панели вы можете использовать любые элементы управления, однако рекомендуется применять элемент управления List - именно он используется в модулях из стандартной поставки TeamWox.

    Левая панель с разделителем

  • Перечисление необходимых интерфейсов - GetInterface(const wchar_t *name, void **iface). Данный метод должен возвращать интерфейсы, которые предоставляет и реализует модуль.

4. Деинициализация - PrepareDestroy(). Сервер прекращает все обработки, подготавливаясь к завершению работы.

5. Освобождение ресурсов - Release(). Завершение работы при останове или перезагрузке сервера.

Помимо этих основных методов, интерфейс IModule предоставляет несколько вспомогательных методов, с помощью которых можно получить дополнительную информацию о модуле,

Методы интерфейса IModule для отображения информации о модуле

а также управлять правами доступа к модулю.

Методы интерфейса IModule для управления правами доступа

 

2. Ресурсы и шаблоны

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

 

2.1. Ресурсы

Для каждого модуля необходимо настроить доступ к его ресурсам. Обработка HTTP запросов на статические файлы ресурсов реализована в самом сервере, и нет необходимости реализовывать ее для каждого модуля отдельно. Сервер обеспечивает эффективную обработку запросов на статические файлы - кэширует их в памяти для быстрой отдачи.

Чтобы сервер мог обрабатывать HTTP запросы на ресурсы, на первом этапе инициализации модуля (IModule::Initialize) необходимо зарегистрировать URL и путь до файлов с помощью метода IServer::VirtualPathRegister.

VirtualPathRegister(const wchar_t *path_virtual,const wchar_t *path_real,int flags)

Параметр Тип Описание
path_virtual wchar_t* Префикс URL для обработки ресурсных файлов. Этот путь будет обрабатывать сервер.
path_real wchar_t* Путь к ресурсному файлу или папке с ресурсными файлами относительно папки на сервере, в которой находится DLL модуля.
flags int Флаги из перечисления EnVirtualFoldersFlags.

Таким образом, мы связываем фактическое расположение ресурсов с текстом строки HTTP запроса. Для модуля Hello World регистрация виртуальных путей выглядит следующим образом.

//--- мапинг каталогов со статикой - рисунки, скрипты
m_server->VirtualPathRegister(L"/res",   L"res",    TW_VIRTUAL_FOLDER_FLAG_CACHE);
//---     JavaScript может содержать перевод - флаг TW_VIRTUAL_FOLDER_FLAG_LANGS
m_server->VirtualPathRegister(L"/res/js",L"res\\js",TW_VIRTUAL_FOLDER_FLAG_CACHE | TW_VIRTUAL_FOLDER_FLAG_LANGS);

Теперь рассмотрим флаги, которые можно выставить для ресурсов.

EnVirtualFoldersFlags

Имя Значение Описание
TW_VIRTUAL_FOLDER_FLAG_CACHE 0x001 В ответ сервера включается HTTP заголовок Expires, который указывают браузеру кэшировать файл до указанного срока, чтобы при повторной загрузке страницы не посылать запрос на сервер.
TW_VIRTUAL_FOLDER_FLAG_NOCACHE 0x002 В HTTP ответ добавляется заголовок Expires с заведомо прошедшей датой, так что файлы не кэшируются (выставляется несколько флагов, чтобы данные не кэшировались).
TW_VIRTUAL_FOLDER_FLAG_DYNAMIC 0x004 В HTTP ответ добавляется заголовок Last-Modified, а в HTTP запрос - заголовок If-Modified-Since. Выставление этого флага позволяет реализовать т.н. затратное кэширование.
TW_VIRTUAL_FOLDER_FLAG_LANGS 0x008 Файл может содержать токены <lngj:> для подстановки переводов. Такие файлы сервер "пропускает" через модуль переводов.
TW_VIRTUAL_FOLDER_NOT_SECURE 0x010

Разрешено отдавать файлы при запросе по незащищенному протоколу HTTP.

Примечание: Не стоит беспокоиться о потенциальных DDoS-атаках. При таких запросах публичная часть не будет отвечать, и это никак не скажется на производительности при работе с системой.

TW_VIRTUAL_FOLDER_NOT_AUTHORIZE 0x020 Могут обрабатываться HTTP запросы от неавторизованных пользователей.
TW_VIRTUAL_FOLDER_PUBLIC 0x040 Делает ресурс доступным для неавторизованных пользователей в публичных запросах.
TW_VIRTUAL_FOLDER_SEND_ATTACHMENT 0x080 Если флаг выставлен для отдельного файла или папки целиком, то в HTTP ответ включается заголовок Content-Disposition: attachment. Таким образом, вне зависимости от MIME-типа, для присланных файлов всегда будет открываться диалоговое окно загрузки.

Наиболее часто при разработке модулей используются флаги TW_VIRTUAL_FOLDER_FLAG_CACHE - для уменьшения нагрузки на сервер, и TW_VIRTUAL_FOLDER_FLAG_LANGS - для обеспечения мультиязычности.

 

2.2. Шаблоны

Теперь поговорим о шаблонах. Из предыдущих статей вы уже знаете, что в файлах шаблонов задается пользовательский интерфейс страниц. Файлы шаблонов следует помещать в папку относительно DLL модуля. Для удобства, файлы шаблонов группируются во вложенной папке \templates и имеют расширение *.tpl.

Так, например, при вызове метода обработки шаблона IServer::PageProcess для страницы PageIndex вторым параметром указывается следующий относительный путь:

return(server->PageProcess(context, L"templates\\index.tpl", this, TW_PAGEPROCESS_NOCACHE));

 

3. Файлы справки

Для качественного ПО наличие справочной документации является обязательным условием. Кроме того, это упрощает техническую поддержку пользователей. За отображение контекстной справки в TeamWox отвечает системный модуль Помощь (TWX_HELP).

В этот модуль изначально заложена возможность вывода электронной документации на всех поддерживаемых в системе языках (в зависимости от языковых настроек в профиле пользователя).

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

Модуль "Помощь" на английском языке Модуль "Помощь" на русском языке

Вызов справки для определенного модуля осуществляется соответствующей кнопкой в шапке страницы.

Кнопка вызова справки

Вы уже знаете, что шапка страницы создается с помощью элемента управления PageHeader, а кнопка справки - его методом Help. Параметр метода Help указывается в следующем формате:

имя_модуля/имя_html_файла

Для главной страницы модуля Hello World это выглядит следующим образом:

//+----------------------------------------------+
//| Шапка страницы с командами                   |
//+----------------------------------------------+
var header = TeamWox.Control("PageHeader","#41633C")
.Command("<lngj:MENU_HELLOWORLD_NUMBER1>","/helloworld/number_one","<lngj:MENU_HELLOWORLD_NUMBER1>")
.Command("<lngj:MENU_HELLOWORLD_NUMBER2>","/helloworld/number_two","<lngj:MENU_HELLOWORLD_NUMBER2>")
.Command("<lngj:MENU_HELLOWORLD_REPORTS>","/reports/helloworld/helloworld_report","<lngj:MENU_HELLOWORLD_REPORTS>")
.Help("helloworld/index")
.Search(65536);

 

3.1. Структура папок и файлов

Файлы справки - это обычные HTML документы с изображениями и стилями - т.е. относятся к ресурсам модуля. Для отображения файлов справки в модуле Помощь, разработчикам модуля следует создать структуру папок в следующем формате.

имя_модуля\папка_ресурсов\help\язык\
  • папка_ресурсов - Регистрируется методом IServer::VirtualPathRegister (см. выше).
  • help - Зарезервированное имя папки, которое обрабатывается модулем Помощь.
  • язык - Трехбуквенное имя папки, соответствующее языковому коду. Языковой код (например, eng, rus, fra, ger, spa и т.д.) также используется в файлах переводов (*.lng).

Так, для модуля Hello World на сервере эта структура выглядит следующим образом.

Структура папок справки для модуля Hello World

Соответственно, для каждой страницы модуля параметр элемента управления PageHeader будет выглядеть следующим образом.

helloworld\имя_html_файла

или

helloworld\имя_вложенной папки\имя_html_файла

Подробнее о создании подразделов см. ниже.

Разумеется, имена папок и файлов, а также их структура, внутри языковых папок должны быть полностью одинаковыми - различаться должно лишь содержимое. Для модуля Hello World нажатие кнопки Помощь в шапке главной страницы в окне модуля "Помощь" открывает файл index.html на русском или английском языках.

Справка главной страницы модуля Hello World на английском Справка главной страницы модуля Hello World на русском

 

3.2. Порядок разделов и страниц

Порядок корневых разделов в модуле Помощь устанавливается исходя из идентификаторов модулей - от меньшего к большему. Поскольку ID пользовательских модулей должны начинаться с 65536 (65535 - идентификатор модуля Управление), разделы справки для пользовательских модулей будут располагаться в конце списка в левой панели модуля Помощь.

Порядок корневых разделов справки

Внутренняя структура страниц в справке модуля определяется иначе. В исходном коде HTML страницы, в шапке (тэг <head></head>) добавляются мета-данные следующего вида:

<meta content="order:n" />

Значение n от меньшего к большему определяет порядок расположения страниц. Наименьшее n соответствует странице корневого раздела.

Порядок разделов справки

Для создания еще одного уровня вложенности достаточно создать папку следующего вида.

имя_модуля\папка_ресурсов\help\язык\папка

Затем в этой папке в коде HTML документов следует просто продолжить нумерацию n в тэге <meta content="order:n" />. При этом следует помнить, что страницы корневого раздела и всех подразделов должны именоваться index.html.

Новые разделы справки  Имена страниц для вложенных папок

Соответственно, обновленная структура папок и файлов будет выглядеть следующим образом.

Обновленная структура папок   Обновленная структура файлов   Обновленная стуктура файлов подраздела

Для отображения названия корневого раздела используется уже знакомый нам метод IModule::Title, а для отображения названий всех вложенных разделов - значение тэга <title></title> в HTML коде страниц.

Имена разделов справки

При редактировании названий разделов справки для вступления изменений в силу необходимо перезагрузить сервер.

 

4. Интеграция пользовательских команд в модули TeamWox

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

Решение этой задачи возможно двумя способами.

  1. Интерфейс IToolbar. С его помощью можно добавлять пользовательские команды в шапку главной страницы TeamWox.

  2. Интерфейс IModuleMainframeTopBar. С его помощью можно прогружать пользовательские скрипты в основном фрейме для последующего использования в других модулях.

Рассмотрим реализацию этого функционала на примере модуля Hello World (исходные коды со всеми изменениями см. в прикрепленном к статье файле).

 

4.1. Пользовательские команды в шапке главной страницы TeamWox

Интерфейс IToolbar предоставляет несколько методов, которые вызываются сервером при отрисовке шапки главной страницы TeamWox. Сервер также определяет порядок вызова этих методов.

  • Total(void) - Возвращает общее количество пользовательских команд модуля, отображаемых в шапке.
  • InfoGet(int toolbar_num,ToolbarInfo *info) - Получает информацию о командах.
  • IsAccessible(const Context *context,int toolbar_num) - В этом методе вы можете реализовать проверку прав доступа на определенные команды.
  • ProcessHeader(const Context *context) - Выводит дополнительный код, который загружает js-файл с пользовательской функцией.

Список команд пользовательского модуля, которые необходимо отобразить в шапке главной странице TeamWox, задается в структуре ToolbarInfo.

Поле Тип Описание
title wchar_t[64] Ключ перевода имени команды (текст ссылки).
description wchar_t[128] Ключ перевода описания команды (всплывающая подсказка).
url wchar_t[128] URL команды.

В качестве примера мы добавим простейшую JavaScript функцию, выводящую окно с сообщением. Файл с кодом функции должен находиться в ресурсах модуля Hello World (\modules\helloworld\res\js\helloworld.js).

function HelloWorld_Command()
{
alert("<lngj:HELLOWORLD_COMMAND />!!!");
}

4.1.1. Перечислим интерфейс IToolbar в CHelloWorldModule::GetInterface.

//---
if(StringCompareExactly(L"IToolbar",name))
{
*iface = &m_toolbar;
return(RES_S_OK);
}

4.1.2. Для удобства реализуем интерфейс в отдельном классе (менеджере).

#include "Managers\HelloWorldToolbar.h"
private:
IServer          *m_server;
CHelloWorldManager m_manager;
CHelloWorldToolbar m_toolbar;

4.1.3. Описание класса CHelloWorldToolbar.

//+------------------------------------------------------------------+
//| Команды главного меню                                            |
//+------------------------------------------------------------------+
class CHelloWorldToolbar : public IToolbar
{
private:
static ToolbarInfo m_toolbar[];
//---
IServer         *m_server;

public:
CHelloWorldToolbar();
~CHelloWorldToolbar();
//---
TWRESULT        Initialize(IServer *server);
//---
int             Total(void);
TWRESULT        InfoGet(int toolbar_num,ToolbarInfo *info);
TWRESULT        IsAccessible(const Context *context,int toolbar_num);
TWRESULT        ProcessHeader(const Context *context);
};

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

//---
if(RES_FAILED(res=m_toolbar.Initialize(m_server)))     ReturnErrorExt(res,NULL,"failed to initialize main page toolbar");

4.1.4. Реализация класса CHelloWorldToolbar.

  • Заполним структуру ToolbarInfo, добавив команду вызова пользовательской функции HelloWorld_Command() из ресурсов модуля. Не забудьте добавить переводы новых ключей в языковой файл helloworld.lng.
//+------------------------------------------------------------------+
//|                                                          TeamWox |
//|                   Copyright 2006-2011, MetaQuotes Software Corp. |
//|                                        https://www.metaquotes.net |
//+------------------------------------------------------------------+
#include "StdAfx.h"
#include "HelloWorldToolbar.h"
//--- Список команд
ToolbarInfo CHelloWorldToolbar::m_toolbar[] = {
{L"HELLOWORLD_COMMAND", L"HELLOWORLD_COMMAND_TITLE", L"javascript:HelloWorld_Command();"}
};
  • Реализация метода IToolbar::Total.
//+------------------------------------------------------------------+
//| Общее количество команд                                          |
//+------------------------------------------------------------------+
int CHelloWorldToolbar::Total(void)
{
//---
return(_countof(m_toolbar));
} 
  • Реализация метода IToolbar::Total.
//+------------------------------------------------------------------+
//| Получить информацию о командах                                   |
//+------------------------------------------------------------------+
TWRESULT CHelloWorldToolbar::InfoGet(int toolbar_num,ToolbarInfo *info)
{
//--- проверки
if(toolbar_num<0 || info==NULL) ReturnError(RES_E_INVALID_ARGS);
//---
if(toolbar_num>=_countof(m_toolbar)) return(RES_E_NOT_FOUND);
//---
memcpy(info,&m_toolbar[toolbar_num],sizeof(*info));
//---
return(RES_S_OK);
}
  • Реализация метода IToolbar::IsAccessible. Для простоты мы не будем реализовывать какие-либо проверки прав доступа, т.е. команды модуля Hello World будут доступны всем пользователям TeamWox.
//+------------------------------------------------------------------+
//| Доступность команды - проверка прав                              |
//+------------------------------------------------------------------+
TWRESULT CHelloWorldToolbar::IsAccessible(const Context *context,int toolbar_num)
{
//--- проверки
if(toolbar_num<0 || context==NULL) ReturnError(RES_E_INVALID_ARGS);
//---
if(toolbar_num>=_countof(m_toolbar)) return(RES_E_NOT_FOUND);
//---
return(RES_S_OK);
}  
  • Реализация метода IToolbar::ProcessHeader. Вывод дополнительного кода осуществляется также, как в IModuleMainframeTopBar::MainframeTopBar.
//+------------------------------------------------------------------+
//| Вывод дополнительного кода                                       |
//+------------------------------------------------------------------+
TWRESULT CHelloWorldToolbar::ProcessHeader(const Context *context)
{
//--- проверки
if(context==NULL || m_server==NULL) ReturnError(RES_E_INVALID_ARGS);
//---
context->response->Write(L"<script type='text/javascript' src='");
m_server->WriteStamp(context, L"/helloworld/res/js/helloworld.js");
context->response->Write(L"'></script>");
//---
return(RES_S_OK);
}  

Метод IServer::WriteStamp добавляет к имени ресурсного файла постфикс в виде хэша даты изменения. Таким образом, если при загрузке модуля ресурсный файл не изменился, то он не будет запрашиваться, т.к. браузер сможет получить его из кэша.

Если же в ресурсном файле произошли изменения, он гарантированно будет загружен заново, т.к. в конечном итоге за счет изменившегося постфикса ресурсный файл получит новое имя.

4.1.5. Итак, посмотрим, что же у нас получилось. Скомпилируйте модуль, запустите сервер и откройте главную страницу TeamWox.

Пользовательская команда в шапке главной страницы TeamWox    Вызов пользовательской команды из шапки главной страницы

 

4.2. Загрузка скриптов модуля в основном фрейме

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

Интерфейс IModuleMainframeTopBar предоставляет единственный метод MainframeTopBar, в котором вы можете добавлять свой JavaScript код, который будет однократно загружен вместе с кодом главной страницы. Затем загруженные функции можно вызывать из других модулей.

В качестве примера, иллюстрирующего эту концепцию, мы видоизменим пример из предыдущего раздела. Поместим JavaScript функцию в другой файл(\modules\helloworld\res\js\helloworld_topbar.js) и изменим текст выводимого сообщения.

function HelloWorld_TopCommand()
{
alert("Top!!! <lngj:HELLOWORLD_COMMAND />");
}

4.2.1. В методе CHelloWorldModule::GetInterface(const wchar_t *name, void **iface) перечислите и верните указатель на интерфейс IModuleMainframeTopBar, который будет реализован в модуле.

//---
if(StringCompareExactly(L"IModuleMainframeTopBar",name))
{
*iface = static_cast<IModuleMainframeTopBar*>(this);
return(RES_S_OK);
}

4.2.2. В классе модуля унаследуем метод интерфейса IModuleMainframeTopBar.

class CHelloWorldModule : public IModule, public IModuleMainframeTopBar

..........................................

public:
TWRESULT          MainframeTopBar(const Context *context);

4.2.3. В реализации метода IModuleMainframeTopBar::MainframeTopBar выведем кусок HTML кода, который загружает js-файл с пользовательской функцией.

//+------------------------------------------------------------------+
//| Получение интерфейсов модуля                                     |
//+------------------------------------------------------------------+
TWRESULT CHelloWorldModule::MainframeTopBar(const Context *context)
{
//--- проверки
if(context==NULL || m_server==NULL) ReturnError(RES_E_INVALID_ARGS);
//---
context->response->Write(L"<script type='text/javascript' src='");
m_server->WriteStamp(context, L"/helloworld/res/js/helloworld_topbar.js");
context->response->Write(L"'></script>");
//---
return(RES_S_OK);
}

4.2.4. Для вызова пользовательской функции необходимо использовать URL следующего вида. Префикс top означает, что функция доступна на странице, расположенной уровнем выше (согласно модели DOM), т.е. в основном фрейме страницы.

javascript:top.HelloWorld_TopCommand();

Вызов пользовательской команды, загруженной в основном фрейме

 

Заключение

Мы рассмотрели основные аспекты настройки окружения пользовательских модулей в системе TeamWox. Во второй части статьи мы поговорим о том, как дополнить модуль Совет дня своими текстами. Также вы узнаете о виджетах на главной странице и о распространении модулей в компактном виде.


helloworld-setupenvironment-part1-ru.zip (210.92 KB)

2011.03.22