Примеры плагинов на PHP, Python, Node.js
При использовании интерпретируемых языков в первую очередь понадобится убедиться, что в системе есть нужный интерпретатор. Зачастую выполнение команды типа which php или which python или which node вернет вам путь к существующему интерпретатору, в каких-то случаях являющемуся частью дистрибутива ОС, в каких-то - установленному из ispmanager (например, в стандартной установке ispmanager по умолчанию включается поддержка PHP).
В любом случае при разработке плагина вам на каком-то этапе придется задуматься, хотите ли вы использовать версию интерпретатора, установленную системой, и нет ли вероятности того, что при каких-то действиях в ispmanager этот интерпретатор будет удален. Поэтому даже в случае, если интерпретатор найден командой which, часто есть смысл установить отдельно версию из репозитория ОС или из внешнего источника и в скриптах указывать полный путь к исполняемому файлу интерпретатора.
В статье про плагин “Hello, world!” был приведен пример простейшего обработчика на Bash-скрипте, а в разделе Структура и возможности плагинов подробно разобраны базовые приемы написания плагинов, такие, как описание структуры UI посредством XML, чтение значений полей формы из переменных окружения и взаимодействие с API ispmanager. Здесь приводится более сложный пример, чем “Hello, world!”, на трех популярных интерпретируемых языках - Python, Node.js и PHP, - который реализует следующие шаги:
- Обработчик получает имя пользователя, который открыл страницу плагина, из переменной окружения AUTH_USER, а также значение поля формы с именем db из переменной PARAM_db - при начальной загрузке страницы такой переменной не будет;
- Используя имя пользователя, обработчик с помощью утилиты mgrctl делает запрос к функции db API ispmanager, чтобы получить список баз данных, к которым есть доступ у данного пользователя панели;
- В случае, если из переменной окружения PARAM_db ничего не получено, обработчик показывает форму, состоящую из выпадающего списка баз данных, полученных на прошлом шаге, и кнопки “Отправить”. Кнопка отправляет данные на ту же самую функцию dbselect, для которой мы создаем обработчик;
Если же переменная окружения PARAM_db не пустая, отображается текст, включающий имя выбранной БД (значение поля db, полученное из этой переменной).
Подключение обработчика
Чтобы протестировать любой из приведенных ниже обработчиков, нужно подключить его в ispmanager как обработчик и добавить соответствующую функцию в меню, это можно сделать, создав файл /usr/local/mgr5/etc/xml/ispmgr_mod_dblist.xml со следующим содержимым (см комментарий в коде к строке, которая должна быть изменена в зависимости от конкретного примера обработчика):
<?xml version="1.0" encoding="UTF-8"?>
<mgrdata>
<mainmenu level="30">
<modernmenu>
<node name="dbselect_group" type="noname">
<node name="dbselect" />
</node>
</modernmenu>
</mainmenu>
<!-- name="populateselect.php" или name="populateselect.js"
для PHP и Node.js, соответственно -->
<handler name="populateselect.py" type="xml">
<func name="dbselect"/>
</handler>
<lang name="ru">
<messages name="desktop">
<msg name="modernmenu_dbselect">Select DB Demo</msg>
</messages>
</lang>
</mgrdata>
Данное XML-описание плагина добавит обработчик - файл populateselect.py (populateselect.php, populateselect.js) для функции dbselect и ссылку (“Select DB Demo”) на страницу с этой функцией в левое меню.
Теперь осталось создать файл обработчика - создайте файл с этим названием в папке /usr/local/mgr5/addon, присвойте ему права 750 (пользователь и группа root) и скопируйте туда код соответствующего обработчика из приведенных ниже.
Чтобы ispmanager применил добавленное вами XML-описание плагина, нужно выполнить в командной строке сервера команду:
pkill core
Обработчик на Python
Обратите внимание, что приведен код на Python 2, для Python 3 могут потребоваться небольшие изменения кода.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import subprocess
import re
# получаем имя авторизованного пользователя
user = os.environ.get('AUTH_USER')
# получаем значение поля db из формы
db = os.environ.get('PARAM_db')
# если в поле db что-то есть, показываем текст,
# если в нем пусто, показываем селект и кнопку
if db is not None:
formElements = """
<field name="selected">
<textdata name="selected_message" type="msg" />
</field>
"""
else:
formElements = """
<field name="db">
<select name="db"/>
</field>
<buttons>
<button name="ok" type="ok" action="dbselect"/>
</buttons>
"""
# получаем список баз данных, доступных данному пользователю
try:
dbLines = subprocess.check_output(
["/usr/local/mgr5/sbin/mgrctl",
"-m",
"ispmgr",
"db",
"su={}".format(user)])
except subprocess.CalledProcessError as e:
dbLines = e.output
# извлекаем из списка имена БД
dbs = [m for m in re.findall(r'name=([a-zA-Z0-9_]+)[^\n]*', dbLines)]
# готовим опции для селекта с именами БД
dbOptions = "".join(map(
lambda d: '<val msg="yes" key="{}">{}</val>'.format(d, d), dbs))
xml = """<?xml version="1.0" encoding="UTF-8"?>
<doc lang="ru" func="dbselect">
<metadata type="form">
<form>
{}
</form>
</metadata>
<messages>
<msg name="title">Выбор базы данных</msg>
<msg name="db">Выберите базу данных</msg>
<msg name="msg_ok">Отправить</msg>
<msg name="selected_message">Выбрана БД: {}</msg>
</messages>
<slist name="db">
{}
</slist>
</doc>
"""
print(xml)
Обработчик на PHP
#!/usr/bin/php
<?php
// переменная окружения - текущий user
$user = $_SERVER['AUTH_USER'];
// значения полей формы тоже приходят как переменные окружения
$db = $_SERVER['PARAM_db'];
// формируем metadata для формы
$form_xml = $db ?
'<field name="selected">
<textdata name="selected_message" type="msg" />
</field>' :
'<field name="db">
<select name="db"/>
</field>
<buttons>
<button name="ok" type="ok" action="dbselect"/>
</buttons>';
// спрашиваем в коммандной строке какие у нас есть базы данных
$db_output = `/usr/local/mgr5/sbin/mgrctl -m ispmgr db su=$user`;
// определяем имена баз данных
preg_match_all(
"/name=([a-zA-Z0-9_]+)[^\n]*/",
$db_output,
$db_matches,
PREG_PATTERN_ORDER
);
$db_names = $db_matches[1];
// формируем xml, описывающий опции для селекта
$db_options = array_map(fn($name) =>
"<val msg=\"yes\" key=\"$name\">$name</val>",
$db_names);
$slist_xml = implode('', $db_options);
?>
<?php echo "<?" ?>xml version="1.0" encoding="UTF-8"?>
<doc lang="ru" func="dbselect">
<metadata type="form">
<form>
<?php echo $form_xml; ?>
</form>
</metadata>
<messages>
<msg name="title">Выбор базы данных</msg>
<msg name="db">Выберите базу данных</msg>
<msg name="msg_ok">Отправить</msg>
<msg name="selected_message">Выбрана БД: <?php echo $db; ?></msg>
</messages>
<slist name="db">
<?php echo $slist_xml; ?>
</slist>
</doc>
Обработчик на Node.js
Обратите внимание, что путь к интерпретатору Node.js в примере - это путь к node, который был установлен ispmanager, убедитесь, что в первой строке указан правильный путь, особенно если вы устанавливали Node.js из репозитория ОС или из внешнего источника, например, с сайта Node.js.
#!/usr/lib/ispnodejs/bin/node
const exec = require('child_process').exec;
// получаем имя авторизованного пользователя
const user = process.env.AUTH_USER;
// получаем значение поля db из формы
const db = process.env.PARAM_db;
// если в поле db что-то есть, показываем текст,
// если в нем пусто, показываем селект и кнопку
const formElements = db ? `
<field name="selected">
<textdata name="selected_message" type="msg" />
</field>` : `
<field name="db">
<select name="db"/>
</field>
<buttons>
<button name="ok" type="ok" action="dbselect"/>
</buttons>
`;
async function execute(command){
return new Promise(
res => exec(command, (error, stdout, stderr) => res(stdout) ));
}
function extractInfo(dblines) {
return [...dblines.matchAll(/name=([a-zA-Z0-9_]+)[^\n]*/g)]
.map(matches => matches[1]);
}
async function returnXml() {
// получаем список баз данных, доступных данному пользователю
const dbLines = await execute(
`/usr/local/mgr5/sbin/mgrctl -m ispmgr db su=${user}`);
// извлекаем из списка имена БД
const dbs = extractInfo(dbLines);
// готовим опции для селекта с именами БД
const dbOptions = dbs.map(
db => `<val msg="yes" key="${db}">${db}</val>`
).join('');
// собираем и выводим итоговый XML
console.log(`<?xml version="1.0" encoding="UTF-8"?>
<doc lang="ru" func="dbselect">
<metadata type="form">
<form>
${formElements}
</form>
</metadata>
<messages>
<msg name="title">Выбор базы данных</msg>
<msg name="db">Выберите базу данных</msg>
<msg name="msg_ok">Отправить</msg>
<msg name="selected_message">Выбрана БД: ${db}</msg>
</messages>
<slist name="db">
${dbOptions}
</slist>
</doc>`)
}
returnXml();