/
/
/
Examples of plugins in PHP, Python, Node.js

Examples of plugins in PHP, Python, Node.js

 

In any case, when developing a plugin, at some point you will have to consider whether you want to use the version of the interpreter installed by the system, and whether there is a chance that some actions in ispmanager will remove that interpreter. Therefore, even if an interpreter is found with the which command, it often makes sense to install a separate version from the OS repository or an external source, and to specify the full path to the interpreter executable in scripts.

The "Hello, world!" plugin article gave an example of a simple Bash script handler, and the Structure and functionality of plugins section detailed basic plugin writing techniques such as describing the UI structure via XML, reading form field values from environment variables, and interacting with the ispmanager API. Here is a more complex example than "Hello, world!" in three popular interpreted languages — Python, Node.js, and PHP — that implements the following steps:

  • the handler retrieves the name of the user who opened the plugin page from the AUTH_USER environment variable, and the value of the form field named db from the PARAM_db variable - there will be no such variable when the page is initially loaded;
  • using the user name, the handler makes a request to the ispmanager db API function using the mgrctl utility to retrieve a list of databases that this panel user has access to;
  • if nothing is retrieved from the PARAM_db environment variable, the handler displays a form consisting of a drop-down list of databases that have been retrieved in the past. The button sends data to the same dbselect function for which we are creating a handler;

If the PARAM_db environment variable is not empty, a text containing the name of the selected database (the value of the db field obtained from this variable) is displayed.

Connect a handler

To test any of the following handlers, you need to connect it in ispmanager as a handler and add the corresponding function to the menu. This can be done by creating a file /usr/local/mgr5/etc/xml/ispmgr_mod_dblist.xml with the following contents (see the comment in the code for the line that should be changed depending on the specific handler example):

<?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" or name="populateselect.js"
        for PHP and Node.js, accordingly -->
    <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>

This XML description of the plugin will add a handler - a file populateselect.py (populateselect.php, populateselect.js) for the dbselect function and a link ("Select DB Demo") to the page with this function in the left menu.

Now we need to create a handler file - create a file with this name in the folder /usr/local/mgr5/addon, give it permissions 750 (root user and group) and copy the code of the corresponding handler from the ones below.

To have ispmanager apply the XML description of the added plugin, run the command from the server command line:

pkill core

This command is only needed when making changes to the plugin's XML description, and changes to the handler code will be applied immediately the next time the corresponding function is requested.

Python handler

Note that the code given is in Python 2, minor code changes may be required for Python 3.

PHP handler

#!/usr/bin/php
<?php 
// environment variable — current user
$user = $_SERVER['AUTH_USER'];
// The values of the form fields are also available as environment variables.
$db = $_SERVER['PARAM_db'];
// generate metadata for the form
$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>';
// ask in the command line what databases we have
$db_output = `/usr/local/mgr5/sbin/mgrctl -m ispmgr db su=$user`;
// define database names
preg_match_all(
    "/name=([a-zA-Z0-9_]+)[^\n]*/",
    $db_output,
    $db_matches,
    PREG_PATTERN_ORDER
);
$db_names = $db_matches[1];
// form xml describing selector options
$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">Database selection</msg>
        <msg name="db">Select database</msg>
        <msg name="msg_ok">Send</msg>
        <msg name="selected_message">Database selected: <?php echo $db; ?></msg>
    </messages>
    <slist name="db">
        <?php echo $slist_xml; ?>
    </slist>
</doc>

Node.js handler

Note that the path to the Node.js interpreter in the example is the path to the node installed by ispmanager; make sure the first line has the correct path, especially if you installed Node.js from the OS repository or from an external source such as the Node.js website.

#!/usr/lib/ispnodejs/bin/node
const exec = require('child_process').exec;
// get the name of the authorized user
const user = process.env.AUTH_USER;
// get db field value from the form
const db = process.env.PARAM_db;
// if there is something in the db field, show the text,
// if it is empty, show select and button
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() {
    // get the list of databases available to this user
    const dbLines = await execute(
        `/usr/local/mgr5/sbin/mgrctl -m ispmgr db su=${user}`);
    // retrieve database names from the list
    const dbs = extractInfo(dbLines);
    // prepare options for database name selection
    const dbOptions = dbs.map(
        db => `<val msg="yes" key="${db}">${db}</val>`
    ).join('');
    // collect and output the final 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">Database selection</msg>
            <msg name="db">Select database</msg>
            <msg name="msg_ok">Send</msg>
            <msg name="selected_message">Database selected: ${db}</msg>
        </messages>
        <slist name="db">
        ${dbOptions}
        </slist>
    </doc>`)
}
returnXml();