Creating a custom control panel interfaces
From ISPWiki
Following is an example on how to create an interface to edit a list of abstract elements.
Task
- We have the ISPmanager control panel
- The /etc/myconf file contains a list of elements, for which we need to create an interface
Solution
1) Create a file /usr/local/ispmgr/etc/ispmgr_mod_myconf.xml with the following contents
<?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> <mainmenu level="7"> <node name="tool"> <node name="myconf"/> </node> </mainmenu> <metadata name="myconf" type="list" key="item"> <toolbar> <toolbtn func="myconf.edit" type="new" img="t-new" name="new"/> <toolbtn func="myconf.edit" type="edit" img="t-edit" name="edit" default="yes"/> <toolbtn func="myconf.delete" type="group" img="t-delete" 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> <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">Test module</msg> </messages> <messages name="myconf"> <msg name="title">Test module</msg> <msg name="item">element from myconf</msg> <msg name="msg_myconf_delete">Delete element </msg> <msg name="hint_new">New element</msg> <msg name="hint_edit">Edit element</msg> <msg name="hint_delete">Delete element</msg> </messages> <messages name="myconf.edit"> <msg name="title">Edit element</msg> <msg name="title_new">New element</msg> <msg name="item">Value</msg> <msg name="hint_item">Element value from myconf</msg> </messages> </lang> </mgrdata>
2) Create a file handler /usr/local/ispmgr/addon/myconf.pl with the following contents
#!/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 { $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 { @all_elem = split(", ", $Q->param( "elid" )); if( open( IN, "/etc/myconf" ) ){ if( open( OUT, ">/etc/myconf.new" ) ){ for( <IN> ){ chomp; $found = "0"; for my $elid (@all_elem) { if ($elem eq $_) { $found = "1"; last; } } print OUT "$_\n" if( $found ne "1" ); } close( OUT ); } close( IN ); } rename( "/etc/myconf.new", "/etc/myconf" ); print "<ok/>"; }
3) Set privileges on the file handler
chmod 750 /usr/local/ispmgr/addon/myconf.pl chown 0:0 /usr/local/ispmgr/addon/myconf.pl
Comments
Describe a script handler specifying that it will be executed while calling your functions. You can describe it in the same manner as events, but change the event tag into the func element.
<handler name="myconf.pl" type="cgi">
<func>myconf</func>
<func>myconf.edit</func>
<func>myconf.delete</func>
</handler>
The information about how to describe a link can be found in the article Adding external links to the menu
<mainmenu level="7">
<node name="tool">
<node name="myconf"/>
</node>
</mainmenu>
Describe a data table:
<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>
We have a table with one column. The control panel has three buttons that can be used to create, edit and delete. More information about tags and attributes can be found in the article Form descriptions
Describe an edit form:
<metadata name="myconf.edit" type="form">
<form>
<field name="item">
<input type="text" name="item"/>
</field>
</form>
</metadata>
The form has one input field. More information about tags and attributes can be found in the article Form descriptions
Describe text messages for your new interfaces. Information on how to form a message name can be found in the article Message names
Script handler
Get parameters and a name of the function to be called
use CGI qw/:standard/; $Q = new CGI; $func = $Q->param(func);
Depending on the function, call a corresponding procedure
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;
}
}
Data list output
sub List {
if( open( IN, "/etc/myconf" ) ){
while( <IN> ){
chomp;
print "<elem><item>$_</item></elem>";
}
close( IN );
}
}
Read the data file "/etc/myconf" and form the XML document. The elem tag defines a data line and contains child elements that describe data by columns, you can use a random name for the item tag, but it should correspond to the name attribute in the table column description (the col tag).
Reading data while opening an element edit form
sub Get {
$elid = $Q->param( "elid" );
print "<elid>$elid</elid><item>$elid</item>" if( $elid );
}
This function will be called when opening an edit form to create or edit an element. If the elid parameter is not empty, we will edit the element. This parameter contains a value from the list column and its name is specified in the key attribute of the metadata tag when describing a data table interface.
As we have a very simple interface and the form only contains an input field, we do not read anything from the files, but return a parameter from the requested element. It would be a good thing to check that this element exists in the data file., if not, return an error. The elid tag defines a key value for the element, it will be displayed on the form heading. The item tag in our case contains the value that will be displayed in the item input form.
Editing a line
sub Set {
$elid = $Q->param( "elid" );
$item = $Q->param( "item" );
.......
}
Pay attention to the two parameters when editing a line. elid contains a name (key field) of the element to be edited. The item parameter contains a value that a user provided in the item field. If a required parameter is not found, an error message should be returned.
Creating a new element
sub New {
$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>";
}
}
Take a value from the item parameter and add it into the end of our data file. If you failed to open a file, the error message should be returned.
Deleting an element
sub Delete {
@all_elem = split(", ", $Q->param( "elid" ));
......
}
Since we can delete several elements at one call, the elid parameter may contain several names separated by the ", " symbols (a comma and space). Form an array of elements to be deleted, read our data file and delete the lines that are included into the element array.
