XML-описание страниц
Как уже упоминалось ранее, обработчики плагинов возвращают XML трех видов: описание страниц, ошибки и результаты успешного выполнения действий. В свою очередь, страницы в ispmanager разделяются на следующие виды, для каждого из которых структура XML будет отличаться:
- Дашборд
- Форма
- Список (таблица)
- Отчет
- Страница помощи
Ниже будет более подробно описана структура XML для страниц форм и списков.
Для дашборда и отчетов есть соответствующие статьи в справочной документации: дашборд , отчеты.
На самом верхнем уровне XML-описания страниц всех видов похожи, обобщенно их структуру можно описать так:
<?xml version="1.0" encoding="UTF-8"?>
<doc lang="ru" func="[функция]">
<metadata type="тип_страницы">
<!-- ...метаданные, отличаются для разных типов страниц -->
</metadata>
<messages>
<msg name="имя_строки">Текст</msg>
<!-- ... строки <msg> ... -->
</messages>
</doc>
- В атрибуте func тэга doc нужно указывать функцию, соответствующую данной странице. В случае, если один обработчик обслуживает несколько разных функций, обрабатываемую функцию можно получить из переменной окружения PARAM_func.
- Атрибут type тэга <metadata> принимает одно из пяти значений, соответствующих типам страниц: dashboard, form, list, report, helpboard
- В отличие от XML-описания плагина, здесь не используется тэг <lang>, позволяющий указать язык для отображаемых строк. Если нужно отображать плагин на разных языках, это можно сделать через XML-описание плагина (см ниже соответствующий раздел) с использованием тэга <lang>. Сопоставление строки в <messages> с элементами UI происходит по атрибуту name.
Составление XML-описаний страниц по аналогии с существующими
При создании XML-описаний страниц, возвращаемых обработчиками плагинов, часто удобно взять XML-описание какой-то существующей страницы за основу или готовить свое XML-описание по аналогии с существующим. Для этого достаточно использовать утилиту mgrctl с опцией -o devel, например, вот так можно получить XML для формы создания сайта:
/usr/local/mgr5/sbin/mgrctl -m ispmgr webdomain.edit -o devel
Вывод этой команды может содержать какое-то количество элементов вне <metadata> и <messages>, часть которых не будет иметь эффекта при использовании в плагине, поэтому лучше брать за основу в первую очередь содержимое элементов <metadata> и <messages>.
XML-описание формы
Страница формы состоит из заголовка, под ним - полей в один столбец, которые могут быть сгруппированы в сворачиваемые разделы, и еще ниже - кнопки, отображаемые горизонтальным рядом. XML-описание формы можно обобщенно представить так:
<?xml version="1.0" encoding="UTF-8"?>
<doc lang="ru" func="[функция]">
<metadata type="form">
<form>
<field name="имя_поля">
<!-- элементы <input>,
<select>, <textdata> и т.п.
-->
</field>
<!-- ... элементы <field> ... -->
<buttons>
<button name="имя_кнопки" type="тип_кнопки">
</buttons>
</form>
</metadata>
<messages>
<msg name="имя_поля">Подпись поля</msg>
<msg name="имя_кнопки">Текст на кнопке</msg>
<msg name="title">Заголовок формы</msg>
<!-- ... строки <msg> ... -->
</messages>
<имя_поля>Значение поля</имя_поля>
</doc>
В случае, если поля формы сгруппированы в разделы, структура внутри элемента <form> будет выглядеть так:
<form>
<page name="имя_раздела">
<field name="имя_поля">
<!-- элементы <input>,
<select>, <textdata> и т.п.
-->
</field>
<!-- ... элементы <field> ... -->
</page>
<!-- ... элементы <page> ... -->
<buttons>
<button name="имя_кнопки" type="тип_кнопки">
</buttons>
</form>
Несколько пояснений:
- Значение атрибута name="title" на элементе <msg> зарезервировано для передачи заголовка страницы.
- Подпись к полю задается на уровне элемента <field>, за исключением групп чекбоксов, для каждого из которых задается подпись по его атрибуту name, помимо подписи ко всей группе.
- Внутри одного элемента <field> как правило бывает один элемент <input>, <select>, <textdata>, <textarea> и т.п., но бывают и исключения, одно из них - группа чекбоксов.
- Если нужно передать начальные значения для полей формы, это делается посредством XML-элементов вида <имя_поля>. Значение поля</имя_поля>, причем имя_поля здесь соответствует значению атрибута name на элементе внутри элемента <field>, то есть если нужно задать значение для поля ввода: <field name="fieldname"><input name="inputname" /></field>, то это делается так: <inputname>Значение поля</inputname>
Подробнее об XML-описании форм, типах полей ввода и кнопок и атрибутах элементов можно прочитать в справочной статье.
XML-описание списков (таблиц)
Страница списка состоит из заголовка, тулбара (строки кнопок) и таблицы. Для таблицы возможен поиск по строке, сортировка и фильтрация (за исключением столбцов, по которым фильтрация запрещена метаданными столбца). В случае, если не получено ни одной строки для отображения в таблице с учетом фильтров и поисковой строки, отображается сообщение “Список пуст”.
XML-описание списка можно обобщенно представить так:
<?xml version="1.0" encoding="UTF-8"?>
<doc lang="ru" func="[функция]">
<metadata type="form"
key="поле_id_строки"
keyname="поле_имени_строки">
<toolbar>
<toolgrp name="update">
<toolbtn name="имя_кнопки"
func="функция_вызываемая_по_кнопке"
type="тип_кнопки"
default="yes"/>
<!-- ... элементы <toolbtn> ... -->
</toolgrp>
<!-- ... элементы <toolgrp> ... -->
</toolbar>
<coldata>
<col name="имя_столбца"
type="тип_столбца"
sort="тип_сортировки" />
<!-- ... элементы <col> ... -->
</coldata>
</metadata>
<messages>
<msg name="имя_столбца">Заголовок столбца</msg>
<msg name="имя_кнопки">Текст на кнопке</msg>
<msg name="title">Заголовок списка</msg>
<!-- ... строки <msg> ... -->
</messages>
<elem>
<имя_столбца>Значение ячейки</имя_столбца>
<!-- ... элементы вида
<имя столбца1 />
<имя столбца2 /> и т.д... -->
</elem>
<!-- ... элементы <elem> ... -->
</doc>
Комментарии к коду выше:
- Поведение UI после нажатия на кнопку может сильно различаться в зависимости от типа кнопки, задаваемого атрибутом type элемента <toolbtn>
- Атрибут default="yes" элемента <toolbtn> позволяет указать кнопку, поведение которой будет срабатывать при двойном клике по строчке таблицы
- Как и для формы, элемент <msg name="title"> позволяет задать заголовок для всей страницы
- Атрибут sort элемента <col> указывает на тип сортировки столбца, которая будет применена по клику на его заголовке, примеры значений - "alpha" - сортировка по алфавиту, "digit" - сортировка чисел и т.п., все варианты описаны в справочной статье об XML списков по ссылке в последнем пункте
- Для простоты объяснения обобщенная структура кода не включает ряд элементов и атрибутов, позволяющих описать важный функционал UI списка (информация об этом приведена в справочной статье об XML-описании списков,
например:
- Пагинация
- Фильтрация
- Иконки кнопок
- Отображение кнопок по условию
XML-описание ошибок и сообщений об успехе действий
Кнопки и другие UI-элементы форм и списков приводят к выполнению обработчиков плагинов и/или запросам к стандартному API ispmanager. UI ispmanager получает обратную связь о результате таких действий посредством сообщения одного из трех типов:
- XML-описание страницы - в этом случае новая страница открывается вместо текущей или в новой вкладке, в зависимости от типа кнопки;
- XML-описание результата успешного выполнения действия - при получении такого сообщения текущая вкладка может быть закрыта или быть сохранена, или может произойти редирект на другую страницу, в зависимости от типа кнопки и содержания сообщения;
- XML-описание ошибки - в этом случае сообщение об ошибки отображается в виде уведомления (при действии по кнопке списка), в виде сообщения рядом с полем формы или баннера над кнопками формы.
Сообщение об успешном выполнении действия
Ниже представлена обобщенная структура сообщения об успехе действия:
<?xml version="1.0" encoding="UTF-8"?>
<doc lang="ru" func="[вызванная_функция]">
<ok>
<!-- элемент <ok> может быть пустым, либо включать
информацию о редиректе -->
</ok>
<!-- могут быть другие элементы, помимо <ok>,
но при наличии <ok> они в основном игнорируются -->
</doc>
В этой структуре имеет значение содержимое элемента <ok>, о возможных опциях можно прочесть в разделе справочной статьи о формах. В наиболее частом и простом случае, если от UI требуется остаться на том же списке или закрыть форму, <ok> будет пустым.
Сообщение об ошибке
Обобщенная структура сообщения об ошибке:
<?xml version="1.0" encoding="UTF-8"?>
<doc lang="ru" func="[вызванная_функция]">
<error type="тип_ошибки" object="имя_элемента_UI">
<msg>Текст сообщения об ошибке</msg>
<!-- другие элементы информации об ошибке -->
</error>
</doc>
Для возврата сообщений об ошибках в плагинах достаточно такой упрощенной структуры, однако у элемента <error> возможны и другие атрибуты и дочерние элементы, пример можно найти здесь.
Реакция UI ispmanager на полученное сообщение об ошибке будет зависеть от типа страницы, на которой выполнялось действие, а также от типа кнопки или другого UI-элемента, с помощью которого было инициировано действие. В случае формы в атрибуте object может быть указано имя поля, в которое введена невалидная информация, тогда ошибка будет отображена рядом с этим полем.
XML-описание плагинов
Обработчики плагинов возвращают XML-описания страниц, сообщения об успехе или ошибке, и это всегда происходит в процессе запроса к той или иной функции (func).
Привязка обработчика к функции происходит посредством XML-описания плагина, кроме того, в XML-описании плагина задаются строки локализации для разных языков и настраиваются некоторые важные аспекты вызова обработчика.
Файлы XML-описаний плагинов помещаются в папку /usr/local/mgr5/etc/xml и должны иметь имя вида ispmgr_mod_[имя_плагина].xml, например: ispmgr_mod_helloworld.xml.
Применение изменений в XML-описании плагинов
Чтобы начал работать новый плагин, для которого только что добавлено XML-описание, или чтобы применить изменения в XML-описании существующего плагина, нужно перезапустить панель следующей Shell-командой:
pkill core
Обратите внимание, что данная команда перезапускает только панель ispmanager, а не весь сервер.
Структура XML-описания плагина
Обобщенная структура XML-описания плагина:
<?xml version="1.0" encoding="UTF-8"?>
<!-- тэг mgrdata всегда должен быть на верхнем уровне любого плагина -->
<mgrdata>
<mainmenu>
<modernmenu>
<node name="имя_группы">
<node name="имя_пункта_меню"/>
</node>
</modernmenu>
</mainmenu>
<handler name="имя_файла_обработчика" type="xml">
<func name="имя_функции" />
<event name="имя_функции" after="yes"/>
<!-- ... элементы <func> и <event> -->
</handler>
<lang name="ru">
<messages name="desktop">
<msg name="modernmenu_имя_пункта_меню">
Текст пункта меню
</msg>
<!-- элементы <msg> -->
</messages>
<!-- элементы <messages> -->
</lang>
</mgrdata>
XML-описание плагина состоит из элемента <mgrdata>, внутри которого может быть любое количество элементов <mainmenu>, <handler> и <lang>. В XML-описании плагина может быть любое количество элементов <mainmenu>, <handler> и <lang> внутри элемента <mgrdata> в том числе любой из этих трех элементов может отсутствовать. Далее о каждом из них подробнее.
Элемент <handler>
<handler> связывает обработчик и обрабатываемую функцию. Путь к исполняемому файлу обработчика задается атрибутом name, это относительный путь от папки /usr/local/mgr5/addon.
Возможны два способа привязки обработчика к функции:
- Обработка функции с привязкой посредством элемента <func>: данный обработчик становится основным для данной функции. Имя функции задается в атрибуте name.
- Выполнение обработчика до или после основного обработчика, привязка посредством элемента <event>. Имя функции задается в атрибуте name, если требуется выполнить обработчик до основного обработчика, используется атрибут before="yes", если после - используется атрибут after="yes".
Внутри элемента <handler> может быть любое число элементов <func>и <event>, то есть один обработчик может обрабатывать любое число функций в каждой из этих двух ролей. Имя обрабатываемой функции в коде обработчика можно получить из переменной окружения PARAM_func.
Добавление дополнительных UI-элементов в стандартные страницы
Привязка обработчика к функции посредством элемента <event> дает возможность добавлять дополнительную функциональность в стандартные страницы ispmanager. При использовании атрибута after="yes" обработчик плагина выполняется после стандартного обработчика встроенной функции ispmanager и получает в STDIN XML-описание страницы, созданное стандартным обработчиком.
В это XML-описание обработчик плагина может добавлять свои UI-элементы, менять логику существующих или при необходимости создавать свое XML-описание, которое заменит полученное от стандартного обработчика. Итоговое XML-описание обработчик плагина, как обычно, должен передать в STDOUT и затем оно будет использовано ispmanager для создания страницы.
Пример использования этой возможности дан в статье [Добавление быстрого действия в стандартный дашборд (будущая страница: Добавление быстрого действия в стандартный дашборд)].
Элемент <mainmenu>
<mainmenu> позволяет добавлять пункты в навигационное меню ispmanager. Атрибут level этого элемента задает уровень доступа, для которого отображаются пункты, заданные внутри: 30 - root, 29 - администратор, 16 - обычный пользователь, 9 - пользователь почтового ящика.
Путь к любому пункту меню имеет два уровня, оба задаются элементами <node>: верхний уровень - группы меню, второй - сами пункты меню, которые являются ссылками на соответствующие страницы:
<mainmenu>
<node name="имя_группы">
<node name="имя_пункта_меню"/>
</node>
</mainmenu>
Код выше описывает структуру добавляемых пунктов меню для ispmanager Business, а в ispmanager Lite (Pro, Host) элементы <node> верхнего уровня помещаются в промежуточный элемент <modernmenu>:
<mainmenu>
<modernmenu>
<node name="имя_группы">
<node name="имя_пункта_меню"/>
</node>
</modernmenu>
</mainmenu>
Атрибут name элемента <node> второго уровня задает функцию (func), которая должна быть выполнена при клике по соответствующему пункту меню. К тому же атрибут name на обоих уровнях элемента <node> привязывает его к соответствующему элементу <msg> для задания отображаемого текста группы/пункта меню (с использованием соответствующего префикса - см. об этом в разделе об элементе <lang>).
Если нужно добавить пункт меню в имеющуюся группу, то для <node> верхнего уровня задается атрибут name, соответствующий имеющейся группе; например, код ниже показывает, как добавить пункт меню в группу "Настройки" (name="set") в ispmanager:
<mainmenu>
<modernmenu>
<node name="set">
<node name="myfunc"/>
</node>
</modernmenu>
</mainmenu>
Если нужно отобразить пункт меню без группы, элементу <node> верхнего уровня, то добавляется атрибут type="noname" (см. пример в статье.
В XML-описании плагина может быть только один элемент <mainmenu>, либо он может отсутствовать.
Элемент <lang>
Элемент <lang> задает группу строк локализации для одного языка (код языка задается атрибутом name, например <lang name="ru">).
Внутри элемента <lang> может быть любое число элементов <messages>, каждый из которых задает группу строк локализации для одной функции (имя функции задается атрибутом name). Каждая строка локализации внутри <messages> задается элементом <msg> c указанием имени соответствующего UI-элемента в атрибуте name, иногда с использованием префикса, например: <msg name="myfield"> для поля формы с name="myfield" или <msg name="modernmenu_myfunc" > для пункта меню в ispmanager Lite c name="myfunc".
Некоторые используемые префиксы:
- menu_ — для пунктов меню в ispmanager Business
- modernmenu_ — для пунктов меню в ispmanager Lite (Pro, Host)
- msg_— для кнопок
При задании строк локализации для навигационного меню нужно использовать <messages name="desktop">.
В XML-описании плагина может быть любое количество элементов <lang>, по одному для каждого языка, для которого задаются строки локализации. Пример одной такой группы:
<lang name="ru">
<messages name="desktop">
<msg name="modernmenu_writetosupport">Отправка сообщения в поддержку</msg>
</messages>
<messages name="writetosupport">
<msg name="messagetext">Введите текст:</msg>
<msg name="msg_send">Отправить</msg>
<msg name="title">Отправка сообщения в поддержку</msg>
</messages>
</lang>