Building Interface Modules, Forms and Lists in ISPConfig 3

Prerequisites

Before starting with the ISPConfig-3 interface development, the following two elements are required:

  1. A fully installed version of ISPConfig-3 from SVN svn://svn.ispconfig.org/ispconfig3/trunk/ where the directory /usr/local/ispconfig/interface/web/ and its subdirectories are writable by the Apache webserver process.
  2. A copy the web interface files svn://svn.ispconfig.org/ispconfig3/trunk/interface/to any webserver with PHP5 enabled. (This does not need to be the same machine as above and could even be a local apache webserver on Windows.)
    • Copy the file lib/db_local.php.skel to lib/db_local.php
    • Edit lib/db_local.php and change the the database connection parameters to match your enviroment.
    • Create a mysql database and populate with the SQL dump file that is in the svn://svn.ispconfig.org/ispconfig3/trunk/installer/sql/ directory.

Module Structure

An ISPConfig-3 interface module is basically a directory in interface/web/, some associated subdirectories and a module.conf.php file within the lib/ directory. A module has the following layout.

{modulename}/
Root directory of the module.
{modulename}/form/
The form/ directory contains the form definition files. The format of the form definition files will be explained later.
{modulename}/lib/
The lib/ directory may contain local libraries for the module. It must contain the files named module.conf.php and admin.conf.php which define the navigation and template settings for the module.
{modulename}/lib/lang/
The lib/lang directory contains the wordbook files with the language translation of the interface.
{modulename}/list/
This directory contains the list definitions for all list pages used in the module. The format of the list definition files will be explained later.
{modulename}/templates/
The templates directory contains all HTML templates of the module.

Creating a module

Creating the directories

For this example, create the following directories inside the interface/web/ directory. These directories MUST be writable by the Apache webserver process.

cd /usr/local/ispconfig/interface/web/
mkdir help
mkdir help/form
mkdir help/lib
mkdir help/lib/lang
mkdir help/list/
mkdir help/templates
The module.conf.php file

Create the file help/lib/module.conf.php with the following content:

<?php

//**** Module Definition ****

// Name of the module. The module name must match the name of the module directory.
// The module name may not contain spaces.
$module['name']      = 'help';

// Title of the module which is dispalayed in the top navigation.
$module['title']     = 'Help';

// The template file of the module. This is always 'module.tpl.htm' unless
// there are any special requirements such as a three column layout.
$module['template']  = 'module.tpl.htm';

// The page that is displayed when the module is loaded.
// The path must is relative to the web/ directory
$module['startpage'] = 'help/index.php';

// The width of the tab. Normally you should leave this empty and
// let the browser define the width automatically.
$module['tab_width'] = '';

//****  Menu Definition ****

// Make sure that the items array is empty
$items = array();

// Add a menu item with the label 'Send message'
$items[] = array( 'title'   => 'Send message',
                  'target'  => 'content',
                  'link'    => 'help/support_message_edit.php'
                );

// Add a menu item with the label 'View messages'
$items[] = array( 'title'   => 'View messages',
                  'target'  => 'content',
                  'link'    => 'help/support_message_list.php'
                );

// Append the menu $items defined above to a menu section labeled 'Support'
$module['nav'][] = array( 'title' => 'Support',
                          'open'  => 1,
                          'items'	=> $items
                        );

?>
The admin.conf.php file

Create the file help/lib/admin.conf.php with the following content:
We do not need a menu in the administration module, so we create just this empty php file.

<?php

/*
    The admin.conf.php file contains menu definitions to be displayed in the administration module.
    In this example there are none.
*/

?>
Activate the module

To activate the new ‘help’ module for the admin user, login to the ISPConfig-3 interface

[image later]

Adding Form and List pages

Next step is to create a form to send messages to the admin user. A form consists of four files.

  1. A Form definition file in the form/ directory of the module.
  2. A language file in the lib/lang/ directory of the module.
  3. A HTML template file in the templates/ directory of the module.
  4. A ‘starter’ or ‘action’ file in the root/top level directory of the module which is used to open the form in the browser.

Only the files in step 1. and 4. need to be created, the language file and HTML template are automatically created by the framework. They can however be edited after creation for fine tuning. Also the database table is created automatically.

The form to send messages contains the following fields:

recipient_id
sender_id
priority
subject
message

Form definition file

Create the file help/form/support_message.tform.php with the following code as the form definition.

<?php

// Title of the form.
$form['title'] 			= 'Support Message';

// Optional description of the form.
$form['description'] 	= '';

// Name of the form which cannot contain spaces or foreign characters.
$form['name'] 			= 'support_message';

// The file that is used to call the form in the browser.
$form['action']			= 'support_message_edit.php';

// The name of the database table used to store the data
$form['db_table']		= 'support_message';

// The name of the database table index field.
// This field must be a numeric auto increment column.
$form['db_table_idx']	= 'support_message_id';

// Should changes to this table be stored in the database history (sys_datalog) table.
// This should be set to 'yes' for all tables that store configuration information.
$form['db_history']		= 'no'; 

// The name of the tab that is shown when the form is opened
$form['tab_default']	= 'message';

// The name of the default list file of this form
$form['list_default']	= 'support_message_list.php';

// Use the internal authentication system for this table. This should
// be set to 'yes' in most cases, otherwise 'no'.
$form['auth']			= 'yes'; 

//** Authentication presets. The defaults below does not need to be changed in most cases.

// 0 = id of the user, > 0 id must match with id of current user
$form['auth_preset']['userid']  = 0;

 // 0 = default groupid of the user, > 0 id must match with groupid of current
$form['auth_preset']['groupid'] = 0;  user

// Permissions with the following codes: r = read, i = insert, u = update, d = delete
$form['auth_preset']['perm_user'] = 'riud';
$form['auth_preset']['perm_group'] = 'riud';
$form['auth_preset']['perm_other'] = ''; 

// The form definition of the first tab. The name of the tab is called 'message'. We refer
// to this name in the $form['tab_default'] setting above.
$form['tabs']['message'] = array(
    'title' 	=> 'Message', // Title of the Tab
    'width' 	=> 100,       // Tab width
    'template' 	=> 'templates/support_message_edit.htm', // Template file name
    'fields' 	=> array(

        //*** BEGIN Datatable columns **********************************

        'recipient_id' => array(
            'datatype'   => 'INTEGER',
            'formtype'   => 'SELECT',
            'default'    => '',
            'datasource' => array(
                        'type'         => 'SQL',
                        'querystring'  => 'SELECT userid,username FROM sys_user WHERE {AUTHSQL} ORDER BY username',
                        'keyfield'     => 'userid',
                        'valuefield'   => 'username'
                                 ),
            'validators' => array( 0 => array( 'type'	=> 'ISINT',
                                                'errmsg'=> 'recipient_id_is_not_integer'
                                                ),
                                 ),
            'value'      => ''
        ),

        'sender_id' => array(
            'datatype'   => 'INTEGER',
            'formtype'   => 'SELECT',
            'default'    => '',
            'datasource' => array(
                        'type'          => 'SQL',
                        'querystring'   => 'SELECT userid,username FROM sys_user WHERE {AUTHSQL} ORDER BY username',
                        'keyfield'      => 'userid',
                        'valuefield'    => 'username'
                                 ),
            'validators' => array( 0 => array( 'type'   => 'ISINT',
                                               'errmsg' => 'recipient_id_is_not_integer'
                                             ),
                                 ),
            'value'      => ''
        ),

        'subject' => array(
            'datatype'   => 'VARCHAR',
            'formtype'   => 'TEXT',
            'validators' => array( 0 => array( 'type'  => 'NOTEMPTY',
                                               'errmsg'=> 'subject_is_empty'
                                             ),
                                  ),
            'default'     => '',
            'value'      => '',
            'width'      => '30',
            'maxlength'  => '255'
        ),

        'message' => array(
            'datatype'	 => 'VARCHAR',
            'formtype'	 => 'TEXTAREA',
            'validators' => array( 0 => array( 'type'	=> 'NOTEMPTY',
                                                'errmsg'=> 'message_is_empty'
                                             ),
                                 ),
            'default'    => '',
            'value'      => '',
            'cols'       => '30',
            'rows'       => '10',
            'maxlength'  => '255'
        ),

        'tstamp' => array(
            'datatype'   => 'INTEGER',
            'formtype'   => 'TEXT',
            'default'    => time(),
            'value'      => '',
            'width'      => '30',
            'maxlength'  => '30'
        ),

        //*** END Datatable columns **********************************
	)
);
?>
Form action file

Next create the action file for the form. The file is named help/support_message_edit.php

<?php

// Set the path to the form definition file.
$tform_def_file = 'form/support_message.tform.php';

// include the core configuration and application classes
require_once('../../lib/config.inc.php');
require_once('../../lib/app.inc.php');

// Check the  module permissions and redirect if not allowed.
if(!stristr($_SESSION['s']['user']['modules'],'help')) {
    header('Location: ../index.php');
    die;
}

// Load the templating and form classes
$app->uses('tpl,tform,tform_actions');
$app->load('tform_actions');

// Create a class page_action that extends the tform_actions base class
class page_action extends tform_actions {

    //* Customisations for the page actions will be defined here

}

// Create the new page object
$page = new page_action();

// Start the page rendering and action handling
$page->onLoad();

?>

The content of the action file is identical for every simple form, just edit the line…

$tform_def_file = 'form/support_message.tform.php';

…to match the name of the form definition.

Initialise the form and database

Login to the ISPConfig interface, go to the Help module and click on the ‘Send message’ link in the left menu. You might see some warning messages, which is normal when the form is opened the first time as some of the files and the database are created on the fly. If you click again on the ‘Send message’ link, the form should appear without error messages.

We now have a fully functional web form that can be used to insert data to the database or edit exsiting database records. We’ll do some fine tuning in the next chapter, after we created the list file.

List definition file

Create the list definition file help/list/support_message.list.php

<?php

// Name of the list
$liste['name'] = 'support_message';

/* Database table
$liste['table'] = 'support_message';

// Index index field of the database table
$liste['table_idx'] = 'support_message_id';

// Search Field Prefix
$liste['search_prefix'] = 'search_';

// Records per page
$liste['records_per_page']= 15;

// Script File of the list
$liste['file'] = 'support_message_list.php';

// Script file of the edit form
$liste['edit_file'] = 'support_message_edit.php';

// Script File of the delete script
$liste['delete_file'] = 'support_message_del.php';

// Paging Template
$liste['paging_tpl'] = 'templates/paging.tpl.htm';

// Enable auth
$liste['auth'] = 'yes';

//****** Search fields

$liste['item'][] = array(
            'field'      => 'sender_id',
            'datatype'   => 'VARCHAR',
            'formtype'   => 'SELECT',
            'op'         => '=',
            'prefix'     => '',
            'suffix'     => '',
            'width'      => '',
            'datasource' => array(
                    'type' => 'SQL',
                    'querystring' => 'SELECT userid,username FROM sys_user WHERE {AUTHSQL} ORDER BY username',
                    'keyfield'    => 'userid',
                    'valuefield'  => 'username'
                                  ),
            'value'      => ''
        );

$liste['item'][] = array(
            'field'      => 'subject',
            'datatype'   => 'VARCHAR',
            'formtype'   => 'TEXT',
            'op'         => 'like',
            'prefix'     => '%',
            'suffix'     => '%',
            'width'      => '',
            'value'      => ''
        );

?>

Now create the action file for the list. The filename is help/support_message_list.php

<?php

require_once('../../lib/config.inc.php');
require_once('../../lib/app.inc.php');

// Path to the list definition file
$list_def_file = 'list/support_message.list.php';

// Check the module permissions
if(!stristr($_SESSION['s']['user']['modules'],'help')) {
    header('Location: ../index.php');
    die();
}

// Loading the class
$app->uses('listform_actions');

// Optional limit
// $app->listform_actions->SQLExtWhere = 'type = 'alias'';

// Start the form rendering and action ahndling
$app->listform_actions->onLoad();

?>

To create the HTML template and lanuage files for the list, login to ISPConfig, go to the Help module and click on the ‘View messages ‘ link in the left menu.

Delete record file

The last file that we needs to be created manually is the list action file help/support_message_del.php, which is called when a record is deleted.

<?php

// From and List definition files
$list_def_file = 'list/support_message.list.php';
$tform_def_file = 'form/support_message.tform.php';

// Include the base libraries
require_once('../../lib/config.inc.php');
require_once('../../lib/app.inc.php');

// Check module permissions
if(!stristr($_SESSION['s']['user']['modules'],'help')) {
    header('Location: ../index.php');
    die;
}

// Load the form
$app->uses('tform_actions');
$app->tform_actions->onDelete();

?>

Fine tuning the form page

In the last section, we created a working form to store, list and delete messages in the database. But for a real world support system, there is some functionality missing.

  1. Normal users are not be able to select the recipient.
  2. Only the admin shall be able to send messages to any user.
  3. The sender_id shall be set automatically.
  4. The timetsamp shall be set automatically.
The message edit html form

In a first step, we will edit the HTML template of the message form. The template is in the file help/templates/support_message_edit.htm

 
<!-- HTML markup may change in the near future with more .css style -->

<table width='500' border='0' cellspacing='0' cellpadding='2'>

    <!-- Recipient is for admin only -->
    <tmpl_if name='is_admin'>
    <tr>
      <td class='frmText11'>{tmpl_var name='recipient_id_txt'}:</td>
      <td class='frmText11'>
        <select name='recipient_id' class='text'>
            {tmpl_var name='recipient_id'}
        </select>
      </td>
    </tr>
    </tmpl_if>

   <!-- Subject -->
    <tr>
        <td class='frmText11'>{tmpl_var name='subject_txt'}:</td>
        <td class='frmText11'>
            <input name='subject' type='text' class='text' value='{tmpl_var name='subject'}' size='30' maxlength='255'>
        </td>
    </tr>

    <!-- Message -->
    <tr>
        <td class='frmText11'>
            {tmpl_var name='message_txt'}:
        </td>
        <td class='frmText11'>
            <textarea name='message' cols='30' rows='10'>{tmpl_var name='message'}</textarea>
        </td>
    </tr>

    <!-- Save and cancel buttons -->
    <tr>
        <td>&nbsp;</td>
        <td>
            <input name='btn_save' type='button' class='button' value='{tmpl_var name='btn_save_txt'}'
                onClick='submitForm('pageForm','help/support_message_edit.php');'>
            <div class='buttonEnding'></div>&nbsp;
            <input name='btn_cancel' type='button' class='button' value='{tmpl_var name='btn_cancel_txt'}'
                onClick='loadContent('help/support_message_list.php');'>
            <div class='buttonEnding'></div>
        </td>
    </tr>
</table>
<input type='hidden' name='tstamp' value='{tmpl_var name='tstamp'}'>
<input type='hidden' name='id' value='{tmpl_var name='id'}'>

In the next setp, we add some custom functionality to the page_action class in the file help/support_message_edit.php

// Create a class page_action that extends the tform_actions base class

class page_action extends tform_actions {

    //* Custom onSubmit Event handler
    function onSubmit()
    {
        global $app, $conf;

        // If the current user is not the admin user
        if($_SESSION['s']['user']['typ'] != 'admin') {
            //* Set the admin as recipient
            $this->dataRecord['recipient_id'] = 1;
        }

        // Set the sender_id field to the ID of the current user
        $this->dataRecord['sender_id'] = $_SESSION['s']['user']['userid'];

        // call the onSubmit function of the parent class
        parent::onSubmit();
    }

    //* Custom onShow Event handler
    function onShow()
    {
        global $app, $conf;

        // We do not want that messages get edited, so we switch to a
        // read only template  if a existing message is loaded
        if($this->id > 0) {
            $app->tform->formDef['tabs']['message']['template'] = 'templates/support_message_view.htm';
        }

        // call the onShow function of the parent class
        parent::onShow();
    }
}

Create the file templates/support_message_view.htm

<table width='500' border='0' cellspacing='0' cellpadding='2'>
  <tr>
    <td class='frmText11'>{tmpl_var name='subject_txt'}:</td>
    <td class='frmText11'>{tmpl_var name='subject'}</td>
  </tr>
  <tr>
    <td class='frmText11'>{tmpl_var name='message_txt'}:</td>
    <td class='frmText11'>{tmpl_var name='message'}</td>
  </tr>
  <tr>
    <td class='frmText11'> </td>
    <td class='frmText11'> </td>
  </tr>
</table>

More later…