Плагины
Материал из ISPWiki
Содержание |
Введение
Механизм расширений (plugins) предназначен для добавления собственных модулей в панель управления. В данном документе приводится пример создания модуля, который будет работать со списком произвольных строк в файле /etc/myconfig. По аналогии с этим примером вы сможете создавать собственные расширения панели управления.
Процесс создания собственного расширения можно разделить на два этапа. Первый — это создание XML-документа, в котором будут описаны все функции, обрабатываемые данным расширением, а также все элементы интерфейса, необходимые для того, чтобы вы увидели ваш модуль в панели управления. Второй этап - это разработка программы или скрипта, который будет реализовывать обработку данных в процессе работы расширения, то есть реализовывать функции.
Создание XML-документа
XML-документ, в котором описывается интерфейс вашего расширения панели управления, должен располагаться в папке /usr/local/ispmgr/etc и называться BINNAME_mod_xxx.xml, где BINNAME — имя запускаемого файла панели управления (ispmgr, billmgr, vdsmgr и т.д.), а вместо xxx вы можете указать любое название. Например, myconfig (vdsmgr_mod_myconfig.xml). Документ обязательно должен быть в кодировке UTF-8 и иметь следующий вид:
<?xml version="1.0" encoding="UTF-8"?> <mgrdata> описание функций расширения описание элементов меню описание таблиц и форм описание сообщений </mgrdata>
Полная документация по каждой части XML-документа вы можете найти в статье XML.
Описание функций расширения
Данная часть XML-документа определяет имя программы или скрипта, который будет реализовывать функции, тип взаимодействия программы и панели управления, а также набор функций, предоставляемые данным расширением. Она должна иметь следующий вид:
<handler name="программа" type="тип"> <func>функция1</func> <func>функция2</func> ... </handler>
В качестве атрибута name укажите имя программы, которая реализует функции, например, myconfig.pl. В качества атрибута type укажите тип взаимодействия панели управления и вашей программы. На текущий момент возможны два типа:
- cgi
- Программа имеет интерфейс CGI, то есть получает данные из переменных окружения и/или стандартного ввода (POST).
- xml
- Программа получает из стандартного ввода XML-документ, описывающий данные, которые передала панель управления.
После этого нужно определить, какие функции будет предоставлять ваше расширение. Все функции можно поделить на три типа:
- Список элементов
- Таблица с панелью инструментов.
- Свойства элемента
- Форма, которая применяется для создания нового либо изменения существующего элемента. Также форма может применяться, если ваш модуль не рассчитан на работу с множеством элементов, а служит для настройки набора каких-либо параметров.
Действие - данный тип функций не предусматривает наличия собственных элементов интерфейса, а является операцией над одним или несколькими элементами из списка.
Имена функций могут содержать буквы латинского алфавита, цифры и точки и должны быть уникальными. Список уже имеющихся функций можно получить с помощью команды:
/usr/local/ispmgr/sbin/mgrctl -m BINNAME eventlist
Где BINNAME — имя запускаемого файла панели управления (ispmgr, billmgr, vdsmgr и т.д.).
Описание элементов меню
Для того, чтобы вы могли осуществить доступ к вашему расширению, необходимо создать соответствующие элементы меню навигации панели управления. Для этого вы должны добавить в ваш XML-документ следующий блок:
<mainmenu level="уровень доступа">
<node name="название категории">
<node name="функция1"/>
<node name="функция2"/>
</node>
</mainmenu>
В атрибуте level укажите цифру, соответствующую уровню доступа. Если вы хотите, чтобы модуль был доступен на нескольких уровнях, добавьте отдельный блок mainmenu для каждого из них.
В качестве названия категории укажите имя, указанное в атрибуте name узла node для соответствующей категории меню навигации, описанному в файле /usr/local/ispmgr/etc/BINNAME_menu.xml. Предположим, вы хотите дать доступ к вашему расширению всем владельцам виртуальных выделенных серверов и разместить соответствующий пункт меню в раздел "Инструменты" меню навигации. Для этого необходимо найти раздел mainmenu level="5" и соответствующий раздел node name="tool". То есть в качестве названия категории мы должны указать значение tool.
После этого необходимо добавить один или несколько элементов меню в соответствующий раздел меню навигации. Для этого добавляем узлы node и в качестве атрибута name указываем имя функции, к которой необходимо обратиться при нажатии соответствующего пункта меню.
Описание таблиц и форм
Далее нужно описать интерфейс для функций, предоставляемых вашим расширением. Наш тестовый модуль предполагает наличие возможности просмотра существующих записей в файле /etc/myconfig, а также добавления новых и изменения существующих записей. Также нужна возможность удаления, но эта функция относится к разряду действие и наличия интерфейса не предполагает.
Итак, для того, чтобы реализовать список записей, нужно добавить следующий блок в XML-документ:
<metadata name="функция1" type="list" key="имя_поля">
<toolbar>
<toolbtn func="функция2" type="new" img="t-new.gif" name="new"/>
<toolbtn func="функция2" type="edit" img="t-edit.gif" name="edit" default="yes"/>
<toolbtn func="функция3" type="group" img="t-delete.gif" name="delete"/>
</toolbar>
<coldata>
<col sort="alpha" sorted="yes" name="имя_поля" type="data"/>
</coldata>
</metadata>
Здесь написано, что мы хотим добавить интерфейс к нашей функции "функция1" типа список (type="list"), в котором ключевым элементом будет "имя_поля". Уникальный ключевой элемент необходим для того, чтобы однозначно идентифицировать запись в списке. Именно это значение будет передано функциям, работающим с элементами списка, таким как просмотр параметров элемента, удаление и прочим.
После этого описывается панель инструментов, в котором присутствуют три кнопки: создать (name="new"), редактировать (name="edit") и удалить (name="delete") элемент. Вы можете указать действие, которое будет осуществляться при двойном щелчке по записи путём добавления атрибута default="yes" в нужный узел, описывающий элемент панели инструментов. В нашем случае при двойном щелчке по записи будет вызвана функция редактирования параметров записи. Как вы могли заметить, в качестве атрибута func в первых двух кнопках панели инструментов указано одно и то же имя функции. Это получается вследствие того, что в нашем случае одна функция будет реализовывать создание, а также просмотр и редактирование параметров записей. Тип функции определяется атрибутом type:
- new
- Создание нового элемента.
- edit
- Редактирование существующего элемента.
- group
- Операция над группой элементов.
- editlist
- Дочерний список, связанный с одним из элементов текущего списка.
- list
- Дочерний список, не привязанный ни к одному из элементов текущего списка.
Далее указывается блок coldata, в который нужно добавить необходимое количество узлов col, по одному на каждую колонку в таблице. В нашем случае таблица состоит из единственной колонки, в которой будет отображаться поле "имя_поля", которое будет возвращать функция создания списка для каждого элемента. Вы должны указать тип записей в колонке. На текущий момент существует три типа:
- data
- В данной колонке будет отображаться информация по каждому элементу, переданная в поле с именем, указанным в атрибуте name (в нашем случае это "имя_поля").
- msg
- Этот тип может быть полезен, если вы имеете конечный набор значений, возвращаемых в поле с именем, указанным в атрибуте name, например, статус записи (включено,отключено и т.п.), и хотите, чтобы эти значения отображались по разному, в зависимости от языка панели управления. В этом случае значения, возвращаемые в этом поле, должны состоять из букв латинского алфавита, цифр, точек и знаков "_".
- indicator
- В данной колонке будeт отображаться индикаторы использования того или иного ресурса, например, дискового пространства или трафика. В этом случае для каждого элемента, для которого необходимо вывести индикатор, следует добавить XML-узел с именем данной колонки списка и добавить в этот узел два атрибута: used="число" и limit="число".
Если вы хотите, чтобы данные в столбце можно было сортировать, добавьте атрибут sort, в котором укажите одно из следующих значений, зависящее от типа данных в данном столбце:
- alpha
- Строки из букв латинского алфавита и цифр.
- digit
- Числа.
- indicator
- Индикаторы (например, дисковое пространство, трафик и прочее).
- ip
- IP-адреса.
- prop
- набор параметров (например, включен/отключен).
Если вы хотите, чтобы записи в таблице автоматически сортировались по одному из столбцов при входе в модуль, добавьте атрибут sorted="yes"
Если же вы хотите, чтобы на основе данных, содержащихся в одном или нескольких столбцах, отображалась статистическая информация, добавьте атрибут stat="yes".
После этого нужно добавить описание интерфейса для формы создания и редактирования свойств записи. Для этого добавьте следующий блок в XML-документ:
<metadata name="функция2" type="form">
<form>
<field name="сообщение">
<input type="text" name="имя_поля"/>
</field>
</form>
</metadata>
Здесь указывается, что мы хотим добавить форму (type="form") для работы с функцией "функция2". Для каждого элемента управления необходимо добавить отдельный блок field, состоящий из заголовка поля и, собственно, самого элемента управления. Заголовок для данного поля указывается с помощью атрибута name="сообщение". При этом необходимо добавить соответствующий узел в блок сообщений для данной функции. О правилах составления блоков сообщений будет сказано ниже. После этого нужно добавить элемент управления. Встречаются три типа элементов управления:
- input
- Поле ввода. Существует несколько типов полей ввода, которые указываются в атрибуте type:
- text
- Поле для ввода строки.
- password
- Поле для ввода пароля, при вводе символы заменяются звёздочками.
- checkbox
- Элемент управления типа флажок.
- textarea
- Многострочное поле ввода.
- select
- Выпадающий список.
С помощью атрибута name необходимо указать имя параметра функции, который связан с данным элементом управления. Если вы хотите задать некоторое значение по умолчанию для элемента управления, вы можете использовать атрибут value="значение_по_умолчанию".
Также существуют дополнительные атрибуты для полей field. Если вы хотите, чтобы поле было скрыто по умолчанию, и управлять его видимостью с помощью javascript, добавьте атрибут hidden="yes". Если же вы используете в качестве элемента управления <input type="text"/>, и это текстовое поле подразумевает ввод нескольких значений, вы можете добавить атрибут zoom="5". При этом рядом с текстовым полем появится дополнительная кнопка, при нажатии на которую текстовое поле будет расширяться на количество строк, указанных в значении данного атрибута, что позволит вводить данные построчно.
Описание сообщений
Сообщения панели являются по сути всем тем текстом, который вы видите в панели управления, и который не привязан непосредственно к данным, полученным в результате выполнения функций. Сообщения делятся по языкам интерфейса. Для каждого языка необходимо добавить блок:
<lang name="язык"> сообщения меню навигации сообщения функций </lang>
В качестве языка необходимо указать одно из следующих значений:
- de
- немецкий
- en
- английский
- es
- испанский
- fr
- французский
- ru
- русский
Основным языком интерфейса является английский. Если в качестве языка панели управления выбран другой язык, и необходимое сообщение не найдено, будет использовано аналогичное сообщение из английского языка.
Далее необходимо добавить в этот блок сообщения для всех пунктов меню, которые вы создали в описании элементов меню. Для этого добавим блок следующего вида:
<messages name="desktop"> <msg name="menu_функция1">сообщение1</msg> <msg name="menu_функция2">сообщение2</msg> </messages>
То есть для каждого пункта меню вида <node name="функция1"/> необходимо добавить сообщение вида <msg name="menu_функция1">сообщение1</msg>. В качестве сообщение1 укажите текст пункта меню в кодировке UTF-8.
Все остальные сообщения делятся по функциям, поэтому для каждой функции, предоставляемой вашим расширением панели управления, необходимо добавить в XML-документ блок следующего вида:
<messages name="функция1"> <msg name="имя_сообщения">сообщение</msg> ... </messages>
Существуют следующие стандартные типы сообщений для модулей (данные названия необходимо указать вместо имя_сообщения):
- title
- Заголовок модуля (списка или формы).
- title_new
- Заголовок формы создания нового элемента.
- hint_default
- Подсказка о назначении данного модуля
Все остальные сообщения делятся на следующие типы:
- названия столбцов списка
- В качестве имени сообщения необходимо указать значения, указанные в атрибуте name узла col.
- значения для столбцов типа msg
- В качестве имени сообщения необходимо указать значение, которое может быть передано в соответствующем параметре элемента, возвращённого функцией. Если значений несколько, необходимо создать отдельное сообщение для каждого из них.
- названия полей формы
- В качестве имени сообщения необходимо указать значения, указанные в атрибуте name узла field.
- значения для элементов управления типа select
- В качестве имени сообщения необходимо указать то значение, которое может быть передано в этом списке. Если соответствующее сообщение не будет найдено, будет отображено само значение, возвращённое функцией. Если значений может быть несколько, необходимо создать отдельное сообщение для каждого из них.
- подсказки к полям формы
- В качестве имени сообщения необходимо указать значения, указанные в атрибуте name узла field с префиксом hint_, например, hint_username.
- подсказки к кнопкам в панели инструментов
- В качестве имени сообщения необходимо указать значения, указанные в атрибуте name узла toolbtn с префиксом hint_, например, hint_new.
- сообщения javascript
- К ним относятся все сообщения, используемые в javascript. К стандартным сообщениям такого рода можно отнести сообщение подтверждения удаления элемента. В этом случае в качестве имени сообщение необходимо указать msg_функция1_delete, где в качестве значения "функция1" необходимо указать имя функции из расширения.
Ещё раз следует обратить ваше внимание на тот факт, что все сообщения должны быть представлены в кодировке UTF-8. В противном случае XML-документ не сможет быть загружен и панель управления не запустится.
Ниже приведён пример XML-документа ispmgr_mod_myconf.xml
<?xml version="1.0" encoding="UTF-8"?>
<mgrdata>
<handler name="myconf.pl" type="cgi">
<func>myconf</func>
<func>myconf.edit</func>
<func>myconf.delete</func>
</handler>
<metadata name="myconf" type="list" key="item">
<toolbar>
<toolbtn func="myconf.edit" type="new" img="t-new.gif" name="new"/>
<toolbtn func="myconf.edit" type="edit" img="t-edit.gif" name="edit" default="yes"/>
<toolbtn func="myconf.delete" type="group" img="t-delete.gif" name="delete"/>
</toolbar>
<coldata>
<col sort="alpha" sorted="yes" name="item" type="data"/>
</coldata>
</metadata>
<metadata name="myconf.edit" type="form">
<form>
<field name="item">
<input type="text" name="item"/>
</field>
</form>
</metadata>
<mainmenu level="7">
<node name="tool">
<node name="myconf"/>
</node>
</mainmenu>
<lang name="en">
<messages name="desktop">
<msg name="menu_myconf">Test module</msg>
</messages>
<messages name="myconf">
<msg name="title">Test module</msg>
<msg name="item">Item from myconf</msg>
<msg name="msg_myconf_delete">Delete item </msg>
<msg name="hint_new">New item</msg>
<msg name="hint_edit">Edit item</msg>
<msg name="hint_delete">Delete item</msg>
</messages>
<messages name="myconf.edit">
<msg name="title">Edit item</msg>
<msg name="title_new">New item</msg>
<msg name="item">Item value</msg>
<msg name="hint_item">The value of the item from myconf</msg>
</messages>
</lang>
<lang name="ru">
<messages name="desktop">
<msg name="menu_myconf">Тестовый модуль</msg>
</messages>
<messages name="myconf">
<msg name="title">Тестовый модуль</msg>
<msg name="item">Элемент из myconf</msg>
<msg name="msg_myconf_delete">Удалить элемент </msg>
<msg name="hint_new">Новый элемент</msg>
<msg name="hint_edit">Редактировать элемент</msg>
<msg name="hint_delete">Удалить элемент</msg>
</messages>
<messages name="myconf.edit">
<msg name="title">Редактировать элемент</msg>
<msg name="title_new">Новый элемент</msg>
<msg name="item">Значение</msg>
<msg name="hint_item">Значение элемента из myconf</msg>
</messages>
</lang>
</mgrdata>
Разработка программы
После того, как мы подготовили XML-документ, описывающий интерфейс расширения, необходимо написать программу или скрипт, который будет обрабатывать данные для расширения. Данная программа должна располагаться в директории /usr/local/ispmgr/addon, принадлежать пользователю root и иметь права доступа 700. Имя программы должно совпадать со значением, указанным в атрибуте name узла handler XML-документа.
В качестве результата работы программы должен быть XML-документ в формате вывода результатов выполнения функций.
Для того, чтобы выяснить от имени какого пользователя была вызвана функция панели управления, которая привела к выполнению вашей программы, можно использовать переменную окружения REMOTE_USER. При этом вы можете быть уверены, что данный пользователь был авторизован.
Ниже приведён пример CGI-программы myconf.pl, написанной на языке perl.
#!/usr/bin/perl
use CGI qw/:standard/;
$Q = new CGI;
$func = $Q->param(func);
print "<doc>";
if( $func eq "myconf" ){
&List;
} elsif( $func eq "myconf.delete" ){
&Delete;
} elsif( $func eq "myconf.edit" ){
if( $Q->param( "sok" ) ){
if( $Q->param( "elid" ) ){
&Set;
} else{
&New;
}
print "<ok/>";
} else{
&Get;
}
}
print "</doc>";
exit 0;
sub List {
if( open( IN, "/etc/myconf" ) ){
while( <IN> ){
chomp;
print "<elem><item>$_</item></elem>";
}
close( IN );
}
}
sub Get {
$elid = $Q->param( "elid" );
print "<elid>$elid</elid><item>$elid</item>" if( $elid );
}
sub Set {
$elid = $Q->param( "elid" );
$item = $Q->param( "item" );
if( open( IN, "/etc/myconf" ) ){
if( open( OUT, ">/etc/myconf.new" ) ){
for( <IN> ){
chomp;
if( $_ eq $elid ){
print OUT "$item\n";
$ok = 1;
} else {
print OUT "$_\n";
}
}
close( OUT );
}
close( IN );
}
if( $ok ){
rename( "/etc/myconf.new", "/etc/myconf" );
print "<ok/>";
} else {
print "<error>Item hasn`t been updated</error>";
}
}
sub New {
$elid = $Q->param( "elid" );
$item = $Q->param( "item" );
if( open( ADD, ">>/etc/myconf" ) ){
print ADD "$item\n";
close( ADD );
print "<ok/>";
} else {
print "<error>Item hasn`t been added</error>";
}
}
sub Delete {
$elid = $Q->param( "elid" );
if( open( IN, "/etc/myconf" ) ){
if( open( OUT, ">/etc/myconf.new" ) ){
for( <IN> ){
chomp;
print OUT "$_\n" if( $_ ne $elid );
}
close( OUT );
}
close( IN );
}
rename( "/etc/myconf.new", "/etc/myconf" );
print "<ok/>";
}
Если же вы используете программу в режиме xml, то, как было сказано выше, она получает из стандартного ввода XML, в котором также могут присутствовать блоки описания интерфейса для построения списков или форм, который вы описали в вашем XML-документе. У вас есть возможность корректировать с помощью вашей программы эти данные с целью изменения набора столбцов списка, полей форм, либо кнопок в панели инструментов. Также ваша программа должна добавить в этот документ необходимые XML-узлы с данными в формате вывода результатов выполнения функций и возвратить его на стандартный вывод.
