﻿<?php
/**
*
* Database Management plugin: manages tables and column data
*
* The function action_db is called when the user has navigated to the database namespace
*
* @license  GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author   Langham Associates <enquiries@langhamAssociates.com>
* v1.0.4
*/

/*
Description
-----------
A simple database management system which stores data in files in a
namespace in the dokuwiki folder
There is a new folder under dokuwiki named database (held as $dbName)
This folder contains
- a new "system" folder called "Create Table"
- one folder per user table , e.g. "01-Countries" and "02-people"
For any session, the record is assigned an id, which is the row number in the file

User data is held in the folders with names of the form nn-Name , e.g.
01-Countries

When a user creates a table, a new folder is created, with the next sequence number assigned, e.g. 02-People

Each folder holds two files and data within the files is pipe(|)-separated:
  definition.txt holds the column definitions (see below)
  data.txt holds the values in the columns

There are two types of reference fields, where the field holds a pointer rather than a value.
To implement this, there are two special types of column, over and above string , number etc.
The concept of LOOKUP is that within one set of data (held in the language file) are held such things as gender
If the column has type 101, this relates to the first row in the set, i.e. gender.
The field then holds the number 1 or 2 to distinguish the value.
The concept of REFERENCE is that a user table is defined with an ID as the first field.
This will dynamically add it to the list of reference tables when a column is defined.
If, for example, this was a table of countries, and a row with id = 1 and name = France existed,
whenever a column was defined as referring to that table, if 1 was in the field, France would be displayed.
The above is hidden from the user, since they only ever see the drop-downs showing France etc.


The main objects are:
Environment
Session
Table
Column
File
Row
Record


Table consists of Columns
File consists of Rows

The data for a Table is held in a File

When a row is read in from the file it is read into a Record

Admin
-----
this involves treating definition.txt as a table with one record per column
The elements in the definition of each column are
name - a name for the column (not used)
label - displayed on the screen
mandatory - yes/no
unique - yes/no - particularly need for reference tables for both the id
and the value e.g. countries
type - 1    String
       2    Number
       3    Date
       101  Lookup Gender   )
       102  Yes and No      ) more can be added in lang.php
       103  Lookup Title    )
       1001 Reference Countries (for example) - added when table created
with unique numeric id as first column
visible - yes/no

N.B The following are not permitted in v1.0
Removing tables
Removing columns

The following are not recommended :
Deleting or amending records in reference tables

V1.0.1
------
wild-cards in search
Remove any HTML errors
Correct sorting by reference column

V1.0.2
------
language-enable the following
- data types
- Referring to other tables
- lookups
ensure compatibility with IE

V1.0.3
-------
1. allow fields to be hidden , additional column attribute called visible defaults to Yes

2. New link fields
new field type INTERNAL_LINK
   - format [[wiki:aa:bb|Displayed Name]]
   - display_for_change displays above
   - display function displays "Displayed Name" as href to link with id=wiki:aa:bb
   - the string is stored with : instead of |
new field type EXTERNAL_LINK
   - format [[http://....... |Displayed Name]]
   - display_for_change displays above
   - display function displays "Displayed Name" as href to link
   - the string is stored with : instead of |
Must sort correctly on links last element

3. optimistic locking - lost updates
   - check each original field value against current contents
   - if any not the same there must have been an intermediate update
N.B To implement Lost updates it is necessary to store the original valueson
the update form and the delete form, these are then passed to the update function

New lang entries needed for
lost_update
external links
hidden

v1.0.4
------
Create the "Create table" folder if it is not there already 
This allows zip to hold only the code for database folder and to use Dokuwiki plugin manager
*/

/**
* Environment Class
*
* Establishes the location of the files
*
*/
class Environment {
    var $URL;
    var $TYPES=array();                 // the names of the types read from the lang file
    var $NUMBER_OF_BASIC_TYPES = 5;     // the number of basic types string, number etc
    var $TYPE_STRING = 1;
    var $TYPE_NUMBER =2;
    var $TYPE_DATE = 3;
    var $TYPE_INTERNAL_LINK = 4;
    var $TYPE_EXTERNAL_LINK = 5;
    var $COLUMNS=array();             // holds the definition of the structure, e.g. column attributes
    var $YES = 1;
    var $LOOKUP_OFFSET = 100;         // the first set of lookups is 101
    var $REFERENCE_OFFSET = 1000;     // the first reference table is 1001
    var $TYPE_LOOKUP = 999;           // the column is of type lookup
    var $DICTIONARY_FOLDER = "Create table";
    var $DEBUG_FILE = "debug.txt";
    var $WILD_CARD = '%';             // can be used at the start or end of a filter
    var $COLUMN_SEPARATOR='|';        // used to separate fields when the data is written to the file
    var $COLON = ':';                 // used to separate the elements in the link field
    var $baseFolder ;
    var $imagesFolder;
    var $tableNames= array();         // the names of the tables
/**
*
* Constructor
*
* @param    none
* @returns  object
*
*/
    function Environment () {
        global $dbName;
        global $myLang;
// Set up the main data types
        $this->TYPES[1]=getMyLang('type_string');
        $this->TYPES[2]=getMyLang('type_number');
        $this->TYPES[3]=getMyLang('type_date');
        $this->TYPES[4]=getMyLang('type_internal_link');
        $this->TYPES[5]=getMyLang('type_external_link');
// The column parameters are set up here - the label comes from lang file
        $this->COLUMNS['name'][1] ='name';
        $this->COLUMNS['name'][2] ='label';
        $this->COLUMNS['name'][3] ='mandatory';
        $this->COLUMNS['name'][4] ='unique';
        $this->COLUMNS['name'][5] ='type';
        $this->COLUMNS['name'][6] ='visible';
        $this->COLUMNS['mandatory'][1] =1;
        $this->COLUMNS['mandatory'][2] =1;
        $this->COLUMNS['mandatory'][3] =1;
        $this->COLUMNS['mandatory'][4] =1;
        $this->COLUMNS['mandatory'][5] =1;
        $this->COLUMNS['mandatory'][6] =1;
        $this->COLUMNS['unique'][1] =1;
        $this->COLUMNS['unique'][2] =1;
        $this->COLUMNS['unique'][3] =2;
        $this->COLUMNS['unique'][4] =2;
        $this->COLUMNS['unique'][5] =2;
        $this->COLUMNS['unique'][6] =2;
        $this->COLUMNS['type'][1] =1;
        $this->COLUMNS['type'][2] =1;
        $this->COLUMNS['type'][3] =102;
        $this->COLUMNS['type'][4] =102;
        $this->COLUMNS['type'][5] =999;
        $this->COLUMNS['type'][6] =102;
        $this->COLUMNS['visible'][1] =1;
        $this->COLUMNS['visible'][2] =1;
        $this->COLUMNS['visible'][3] =1;
        $this->COLUMNS['visible'][4] =1;
        $this->COLUMNS['visible'][5] =1;
        $this->COLUMNS['visible'][6] =1;
// Pick up the column label
        for ($i=1;$i<= count($myLang['column_label']);$i++)
            $this->COLUMNS['label'][$i] = getMyLang('column_label',$i);
// The images oflder
        $this->imagesFolder=DOKU_BASE.'lib/plugins/'.$dbName.'/images/';
// The base folder
        $this->baseFolder = DOKU_INC.'data/pages/'.$dbName.'/';
        $this->URL = DOKU_URL.'doku.php?idx='.$dbName;
// Begin add v1.0.4
// Check if dictionary folder exists
        $directoryName = $this->baseFolder.$this->DICTIONARY_FOLDER;
        if (!file_exists($directoryName)) {
	//			echo "creating $this->baseFolder";
          mkdir("$this->baseFolder",0777);
//          echo ("creating $this->baseFolder$this->DICTIONARY_FOLDER");
          mkdir("$this->baseFolder$this->DICTIONARY_FOLDER",0777);
			}
// End add v1.0.4

// Get list of table names excluding Dictionary
        if ($handle = opendir($this->baseFolder)) {
            while (false !== ($file = readdir($handle))) {
                if ($file != "." && $file != ".." && $file != $this->DICTIONARY_FOLDER) {
                    $counter = abs(substr($file,0,strpos($file,'-')));
                    $this->tableNames[$counter] = substr($file,3);
                }
            }
            closedir($handle);
        }
    }
/**
* gets the folder name for a table
*
* @param    number     $p_id the id of the table
* @returns  string     the folder name
*
*/
    function get_folder($p_id) {
        $ret = $this->baseFolder;
        if ($p_id<10 ) $id='0'.$p_id;
        else $id = $p_id;
// form the name of the folder based on <nn>-<name>
        $ret .= $id."-".$this->tableNames[$p_id]."/";
        return $ret;
    }
}
/**
* Session Class
*
* Holds session-specific information
*
*/
class Session {
    var $columnTypeList = array();
    var $lookupTitles = array();
    var $lookupOptions = array();
/**
* Constructor
*
* Establishes the location of the files and any lookups
*
*/
    function Session() {
        global $display;
        global $environment;
        global $auditTrail;
        global $session;
        global $myLang;
// Get list of column types
        $auditTrail->add_header("Session");
        for ($i=1;$i<=$environment->NUMBER_OF_BASIC_TYPES;$i++) {
            $this->columnTypeList[$i] = $environment->TYPES[$i];
        }
// Get the list of lookups
        $auditTrail->add("Lookups");
// the lookups are in myLang$['lookup']
// Set up lookupTitles, lookupOptions and columnTypeList
        for ($lookupCount=1; $lookupCount<=count($myLang['lookup']);$lookupCount++) {
            $this->lookupTitles[$lookupCount] = getMyLang('lookup',$lookupCount,1);
            $this->columnTypeList[$environment->LOOKUP_OFFSET+$lookupCount]
                  = getMyLang('prompt_looking_up').' '.getMyLang('lookup',$lookupCount,1);
            for ($optionCount=2;$optionCount<=count($myLang['lookup'][$lookupCount]);$optionCount++) {
                $this->lookupOptions[$lookupCount][$optionCount-1] = getMyLang('lookup',$lookupCount,$optionCount);
            }
        }
        $counter=0;
// Get the list of reference tables
        $auditTrail->add("reference Tables");
        foreach ($environment->tableNames as $tableName) {
            $counter++;
            $tab = new Table($counter);
            if ($tab->possibleReference) {
               $this->columnTypeList[$environment->REFERENCE_OFFSET+$counter]
                       =getMyLang('prompt_referencing').' '.$tableName;
                 $auditTrail->add("reference Table ".$tableName);
            }
        }
        $auditTrail->add_footer("Session");
    }
/**
* Determines whether or not the user has the necessary permissions to perform the action
*
* @param    string     $action - the action being taken
* @returns  boolean    true if the user has the necessary permissions
*                      (not convinced that the perms are being used correctly (should be binary mask)
*
*/
    function allowed ($action) {
        global $auditTrail;
        $INFO = pageinfo();            // picked up from the dokuwiki environment
        $perm = $INFO['perm'];         // the permissions of the current user
        $auditTrail->add_header('Session->allowed');
// Decide on the level of permission required
        if ($action== 'insertForm')   $requiredLevel = AUTH_CREATE;
        if ($action== 'insertAction') $requiredLevel = AUTH_CREATE;
        if ($action== 'updateForm')   $requiredLevel = AUTH_EDIT;
        if ($action== 'updateaction') $requiredLevel = AUTH_EDIT;
        if ($action== 'createTable')  $requiredLevel = AUTH_ADMIN;
        if ($action== 'createTableForm')  $requiredLevel = AUTH_ADMIN;
        if ($action== 'admin')        $requiredLevel = AUTH_ADMIN;
        if ($action== 'deleteForm')   $requiredLevel = AUTH_DELETE;
        if ($action== 'deleteAction') $requiredLevel = AUTH_DELETE;
        $auditTrail->add('requiredLevel = '.$requiredLevel);
        $auditTrail->add('perm = '.$perm);
// Decide whether or not to procede
        $auditTrail->add_footer('Session->allowed');
        if ($perm >= $requiredLevel)
           return true;
        else
           return false;
    }
/**
* Get the list of data type including lookups and references
*
* @param    string     $value     the selected value which will be marked as selected
* @param    string     $name      e.g. "values" so that the names of the variables are values0 values1 etc
* @param    string     $type      if "search" the No Selection is added
* @returns  array      list of options for data type
*
*/
    function list_of_types($value, $name, $type) {
        global $environment;
        global $myLang;
        $list = "<select name='".$name."'>";
// For a search we need the No Selection option first
        if ($type=="search") {
            $list .= "<option value='' >";
            $list .= getMyLang('prompt_no_selection') ;
            $list .= "</option>";
        }
// The Basic types
        for ($i=1;$i<=$environment->NUMBER_OF_BASIC_TYPES;$i++) {
            $list .= "<option value = '".$i."'";
            if ($value == $i) $list .= " selected = 'selected' ";
            $list .=">".$environment->TYPES[$i]."</option>";
        }
        $lookups = $this->lookupTitles;;
        $counter = $environment->LOOKUP_OFFSET;
// The lookups
        foreach ($lookups as $lookup) {
            $counter +=1;
            $list .= "<option value = '".$counter."'";
            if ($value == $counter) $list .= " selected ='selected' ";
            $list .= '>'.getMyLang('prompt_looking_up').' '.$lookup.'</option>';
        }
// The reference tables
        for ($ptr =1;$ptr <=count($environment->tableNames);$ptr++) {
            $tab = new Table($ptr);
            if ($tab->possibleReference) {
                $counter=$ptr+$environment->REFERENCE_OFFSET;
                $list .= "<option value = '".$counter."'";
                if ($value == $counter) $list .= " selected ='selected' ";

                $list .= '>'.getMyLang('prompt_referencing').' '.$tab->name.'</option>';
            }
        }
        $list .= "</select>";
        return $list;
    }
}
/**
* Messages Class
*
* Record and display the messages
*
*/
class Messages {
    var $messageList = array();
    var $exist = false;
    function Messages () {
        $this->exist = false;
    }
/**
* add the message to the list
*
* @param    string     $p_message    the error message
* @returns  None
*
*/
    function add($p_message) {
        $this->messageList[] = $p_message;
        $this->exist = true;
    }
/**
* display the messages
*
* @param    none
* @returns  none
*
*/
    function fail_soft () {
        global $auditTrail;
/* Uncomment if debugging
        $auditTrail->display();
*/
   }
/**
* display all the messages
*
* @param    None
* @returns  None
*
*/
    function displayAll() {
        global $display;
        $display->output("<table border=1>");
        foreach($this->messageList as $message) {
            $display->output("<tr><td align=center>".$message."</td></tr>");
        }
        $display->output("</table>");
    }
}
/**
* AuditTrail Class
*
* accumulates and formats audit trail info
*
*/
class AuditTrail {
    var $messageList = array();
    var $indent;
/**
* Constructor
*
* @param    none
* @returns  object
*
*/
    function AuditTrail () {
        $this->indent=0;
    }
/**
* add a message to the audit trail
*
* @param    string     $p_message   the message
* @returns  none
*
*/
    function add($p_message) {
        global $display;
        $this->messageList[] = $this->tab().$p_message;
        $display->debug_output($this->tab().$p_message);
    }
/**
* add a header to the audit trail
*
* @param    string     $p_message   the message
* @returns  none
*
*/
    function add_header($p_message) {
        $this->indent++;
        $this->add("<--- Start of ". $p_message."-->");
    }
/**
* add a footer to the audit trail
*
* @param    string     $p_message   the message
* @returns  none
*
*/
    function add_footer($p_message) {
        $this->add("<--- End of ". $p_message."-->");
        $this->indent--;
    }
/**
* calculate the indentation for the audit trail
*
* @param    string     $p_message   the message
* @returns  none
*
*/
    function tab() {
        $t='';
        for ($i=0;$i<$this->indent;$i++)
            $t .= '>>';
        return $t;
    }
/**
* display the messages in the audit trail
*
* @param    string     $p_message   the message
* @returns  none
*
*/
    function display() {
        echo "<BR>Audit Trail";
        foreach($this->messageList as $message)
            echo "<BR>".$message;
        echo "<BR>";
    }
}
/**
* Column Class
*
* holds info on a column in a table
*
*/
class Column {
    var $id;
    var $name;
    var $label;
    var $mandatory;
    var $unique;
    var $type;
    var $visible;
    var $isVisible;
    var $allowNoSelection;
/**
*
* Constructor
*
* @param    number $p_id         id of the column
* @param    string $p_name       name of the column
* @param    string $p_label      label for the column
* @param    number $p_mandatory  lookup , 1=yes, 2= no
* @param    string $p_type       type of the column, string, number, date, lookup or reference
* @param    string $p_visible    visible , 1=yes, 2= no
* @returns  object
*
*/
    function Column ($p_id, $p_name, $p_label, $p_mandatory, $p_unique, $p_type, $p_visible) {
        global $display;
        global $environment;
        global $auditTrail;
        $auditTrail->add_header('Creating column '.$p_name);

        $this->id        = $p_id;
        $this->name      = $p_name;
        $this->label     = $p_label;
        $this->mandatory = $p_mandatory;
        $this->unique    = $p_unique;
        $this->type      = $p_type;
// allowNoSelection is the equivalent of optional
        if ($this->mandatory == $environment->YES) $this->allowNoSelection=false;
        else $this->allowNoSelection=true;
// set up isVisible
        $this->visible   = $p_visible;
        if ($this->visible == $environment->YES) $this->isVisible = true;
        else $this->isVisible = false;

        $auditTrail->add_footer('Creating column '.$p_name);
    }
/**
* return the string to display the column for edit
*
* @param    string     $value   the value to be displayed for edit
* @returns  string     the HTML to be displayed
*
*/
    function display_for_change($value='',$type='search') {
        global $environment;
        global $session;
        global $auditTrail;
//The output used for the search in the list and for update and create
        $auditTrail->add_header('Column - display_for_change column = '.$this->name);
        $name = "values".$this->id;
        $ret='';
//Convert the form held on the database to the form for display and change
        $value = $this->fromDatabase($value);
// When searching, you can have "No selection"
        if ($type=='search') $noSelectionAllowed = true;
        else $noSelectionAllowed = $this->allowNoSelection; // true if not mandatory

        if ($this->type==$environment->TYPE_LOOKUP) {
            $auditTrail->add('returning list_of_types');
            $ret=$session->list_of_types($value, $name,$type);
        }
        elseif ($this->type==$environment->TYPE_STRING
               || $this->type==$environment->TYPE_NUMBER
               || $this->type==$environment->TYPE_DATE
               || $this->type==$environment->TYPE_INTERNAL_LINK
               || $this->type==$environment->TYPE_EXTERNAL_LINK) {
            $ret .= '<input type="text" class="edit" name="'.$name.'"';
            if ($value > ' ')
                $ret .= ' value="'.$value.'"';
            $ret.=' />';
        }
        elseif ($this->type>$environment->LOOKUP_OFFSET && $this->type<$environment->REFERENCE_OFFSET) {
// It is a lookup
            $list=$session->lookupOptions[$this->type - $environment->LOOKUP_OFFSET];
            $ret .= $this->drop_down($name,$list,$noSelectionAllowed,$value);
        }
        elseif ($this->type>$environment->REFERENCE_OFFSET) {
//It is a reference table
            $referenceTable = new Table($this->type - $environment->REFERENCE_OFFSET,"data");
//get all the records
            $referenceTable->get_all();
// put items in list
            $recCounter=0;
            $listOfValues=array();
            foreach($referenceTable->recordObjs as $record) {
                $recCounter++;
                $auditTrail->add('Reference table = '.$record->values[1]);
                $listOfPointers[$recCounter] = $record->values[1];
                $listOfValues[$recCounter]=$record->values[2];
            }
            $sortedListObj = new SortedArray($listOfValues);
            $sortedList = $sortedListObj->sortedList;
            $counter = 0;
            foreach($sortedList as $ptr) {
                $counter++;
                $sortedListOfPointers[$counter] = $listOfPointers[$ptr];
                $sortedListOfValues[$counter] = $listOfValues[$ptr];
            }
            $ret .= $this->drop_down($name,$sortedListOfValues,$noSelectionAllowed,$value,$sortedListOfPointers);
        }
        $auditTrail->add_footer('column - display_for_change');
        return $ret;
    }
/**
* return the string to display the original value on an edit form as a hidden field
*
* @param    string     $value   the value of the field
* @returns  string     the HTML to display
*
*/
    function display_original($p_counter, $p_value) {
        $fieldName = "originals".$p_counter;
        $ret = '<input type="hidden" name="'.$fieldName.'" value="'.$p_value.'">';
        return $ret;
    }

/**
* prepare the field for inserting into/updating the database
*
* @param    string     $value   the value of the field
* @returns  string
*
*/
    function toDatabase($value) {
        global $environment;
        global $messages;
        $ret = $value; // default
// No | within field unless link
        $posOfPipe = strpos($value, $environment->COLUMN_SEPARATOR);
// Now test if there is a pipe character
        if ($posOfPipe === false) {
// If there is no pipe and this is a link then there is a problem
            if ($this->type == $environment->TYPE_INTERNAL_LINK
               || $this->type == $environment->TYPE_EXTERNAL_LINK) {
                       $messages->add($this->label.getMyLang('err_dat_missing_pipe').$value);
            }
        } else {
// But if there is a pipe and this is not a link then there is a problem
            if ($this->type != $environment->TYPE_INTERNAL_LINK
               && $this->type != $environment->TYPE_EXTERNAL_LINK) {
                $messages->add($this->label.getMyLang('err_dat_extra_pipe').$value);
            }
        }
// The display format of a link is either aaa:bbb:ccc|linkName or http://www...|linkName        }
// but the db version replaces the | with :
        if ($this->type == $environment->TYPE_INTERNAL_LINK) {
// convert the | into :
            $ret = str_replace( $environment->COLUMN_SEPARATOR, $environment->COLON, $value);
        }
        if ($this->type == $environment->TYPE_EXTERNAL_LINK) {
// convert the | into :
           $ret = str_replace( $environment->COLUMN_SEPARATOR, $environment->COLON, $value);
        }
        return $ret;
    }
/**
* convert the field from its form in the database
*
* @param    string     $value   the value of the field
* @returns  string
*
*/
    function fromDatabase($value) {
        global $environment;
        $ret = $value;
        if ($this->type == $environment->TYPE_INTERNAL_LINK
           || $this->type == $environment->TYPE_EXTERNAL_LINK) {
// convert the last : into |
            if ($value>'') {
                $ptr=strrpos ($value,$environment->COLON); // the last colon
                $ret = substr($value,0,$ptr).$environment->COLUMN_SEPARATOR.substr($value,$ptr+1);
            }
        }
        return $ret;
    }
/**
* validate the value being entered for a field
*
* @param    string     $value     the value of the field
* @param    object     $messages  the messages object
* @returns  none       but message added if not valid
*
*/
    function validate ($value, $messages) {
        global $environment;
        global $myLang;
        global $conf;
// Number
        if ($this->type == $environment->TYPE_NUMBER
              || $this->type > $environment->NUMBER_OF_BASIC_TYPES) {
            if (!is_numeric($value)) {
                $messages->add($this->label.getMyLang('err_dat_typ_num').$value);
            }
        }
// Date
        if ($this->type == $environment->TYPE_DATE) {
            if ($value>'' ) {
                $day=substr($value,8,2);
                $month = substr($value,5,2);
                $year = substr($value,0,4);
                if (substr($value,4,1) !='/'
                 ||substr($value,7,1) !='/'
                 ||!checkdate($month,$day,$year))
                    $messages->add($this->label.getMyLang('err_dat_typ_dat').$value);
            }
        }
// Internal Link
        if ($this->type == $environment->TYPE_INTERNAL_LINK) {
            if (substr($value,0,2) != '[['
               || substr($value,strlen($value)-2) != ']]'
               || strpos($value, $environment->COLUMN_SEPARATOR) === false
               ) {
                $messages->add($this->label.getMyLang('err_dat_typ_int').$value);
            }
        }
// External Link
        if ($this->type == $environment->TYPE_EXTERNAL_LINK) {
            if (substr($value,0,2) != '[['
               || substr($value,strlen($value)-2) != ']]'
               || strpos($value, $environment->COLUMN_SEPARATOR) === false
               ) {
                 $messages->add($this->label.getMyLang('err_dat_typ_ext').$value);
            }
        }
// Mandatory
        if ($this->mandatory==$environment->YES) {
            if ($value=='') {
                $messages->add($this->label.getMyLang('err_dat_typ_man'));
            }
        }
    }
/**
* display the value of a field
*
* @param    string     $value   the value of the field
* @returns  string
*
*/
    function display($value) {
//The output used for the display only of values
        global $environment;
        global $session;
        global $conf;
// String or number
        if ($this->type==$environment->TYPE_STRING
          ||$this->type==$environment->TYPE_NUMBER)
            $ret = $value;
// Date
        elseif ($this->type==$environment->TYPE_DATE) {
/*
            $dateFormat = $conf['dformat'];
            $year = substr($value,0,4);
            $month = substr($value,5,2);
            $day  = substr($value,8,2);
            $dateAndTime = mktime(0,0,0,$month,$day,$year);
            $ret = date($dateFormat, $dateAndTime);
*/
            $ret = $value;
        }
// Internal Link
        elseif ($this->type==$environment->TYPE_INTERNAL_LINK) {
            $elements=explode($environment->COLON, $value);
            if ($value>'') {
                $last=count($elements)-1;
                $name=$elements[$last];

                $ptr=strrpos ($value,$environment->COLON); // the last colon
                $ret ='<a href="'.DOKU_URL.'doku.php?id='.substr($value,2,$ptr-2).'">'.substr($name,0,strlen($name)-2).'</a>';
            }
        }
// External Link
        elseif ($this->type==$environment->TYPE_EXTERNAL_LINK) {
            $elements=explode($environment->COLON, $value);
            if ($value>'') {
                $last=count($elements)-1;
                $name=$elements[$last];
                $ptr=strrpos ($value,$environment->COLON); // the last colon
                $ret ='<a href="'.substr($value,2,$ptr-2).'">'.substr($name,0,strlen($name)-2).'</a>';
            }
        }
// Lookup of column types (admin)
        elseif ($this->type == $environment->TYPE_LOOKUP) {
            $ret = $session->columnTypeList[$value];
        }
// Lookup
        elseif ($this->type>$environment->LOOKUP_OFFSET && $this->type<$environment->REFERENCE_OFFSET) {
            $list=$session->lookupOptions[$this->type - $environment->LOOKUP_OFFSET];
            if ($value=='') $ret='';
            else $ret = $list[$value];
        }
// Reference
        elseif ($this->type>$environment->REFERENCE_OFFSET) {
//Open the reference table
            $referenceTable = new Table($this->type - $environment->REFERENCE_OFFSET,"data");
//get all the records
            $referenceTable->get_all();
// put items in list
            $recCounter=0;
            foreach($referenceTable->recordObjs as $record) {
                $recCounter++;
                if ($value == $record->values[1])
                    $ret = $record->values[2];
            }
        }
        return $ret;
    }
/**
* construct the drop-down list for a field
*
* @param    string     $name            the name of the column
* @param    array      $values          the list of values of the field
* @param    boolean    $plusZero        whether a zero value (No Selection) should be added
* @param    object     $p_value         the selected value
* @param    array      $listOfPointers  reference tables must have id as first field - this is the list of ids
* @returns  string     the HTML for the list
*
*/
    function drop_down($name,$values,$plusZero=false,$p_value='',$listOfPointers='') {
        global $myLang;
        $ret = '';
        $ret .= "<select name='".$name."'>";
        $counter=0;
// Is No Selection to be displayed at the top of the list
        if ($plusZero) {
            $ret .= "<option value='' >";
            $ret .= getMyLang('prompt_no_selection') ;
            $ret .= "</option>";
        }
        if (is_array($values)) {
// The order of the values is determined by $listOfPointers if it exists
            foreach($values as $value) {
                $counter++;
                $ret .= "<option value='";
                if (is_array($listOfPointers))
                    $itemPointer = $listOfPointers[$counter];
                else
                    $itemPointer =$counter;
                $ret .=$itemPointer. "'";
                if ($p_value!='' && $p_value==$itemPointer)
                    $ret .= " selected ";
                $ret .= ">";
                $ret .= $value;
                $ret .= "</option>";
             }
        }
        $ret .= "</select>";
        return $ret;
    }
}
/**
* Table Class
*
* holds the information on a table
*
*/
class Table {
    var $id;
    var $displayId;
    var $isAdmin;
    var $name;
    var $columnObjs = array();
    var $recordObjs = array();
    var $populated = false;
    var $possibleReference = false;
/**
* Constructor
*
* @param    number/string $p_idOrName   id of the table if it already
exists or name if it is to be created
* @returns  object
*
*/
    function Table ($p_idOrName) {
        global $display;
        global $environment;
        global $messages;
        global $auditTrail;
        global $session;
        global $myLang;
        $auditTrail->add_header("Table");
        $auditTrail->add("param = ".$p_idOrName);
// The id is passed so this is an existing table
        if (is_numeric($p_idOrName)) {
            $p_id = $p_idOrName;
            $this->displayId = $p_id;
            $this->id= abs($p_id);
// Get the folder name with this id
            if (abs($this->id)>count($environment->tableNames)) {
                $messages->add('TableDefinition : id too large');
                $messages->fail_soft();
            }
            $display->debug_output ("creating table with id=".$this->id);
            $this->name     = $environment->tableNames[abs($p_id)];
            if ($p_id<0) {
                $this->isAdmin=true;
                $auditTrail->add('administering the table');
            } else {
                $this->isAdmin=false;
                $auditTrail->add('standard table');
            }
// If this is admin definition then get from lang and hard-code
            if ($this->isAdmin) {
                for ($i=1;$i<=count($environment->COLUMNS['label']);$i++) {
                    $this->columnObjs[$i] = new Column($i
                                                        , $environment->COLUMNS['name'][$i]
                                                        , $environment->COLUMNS['label'][$i]
                                                        , $environment->COLUMNS['mandatory'][$i]
                                                        , $environment->COLUMNS['unique'][$i]
                                                        , $environment->COLUMNS['type'][$i]
                                                        , $environment->COLUMNS['visible'][$i]
                                                        );
                }

            } else {
                $this->columnObjs = $this->get_columns();
            }
// Could this be a reference table
            if ($this->columnObjs[1]->type==$environment->TYPE_NUMBER
               && $this->columnObjs[1]->unique==$environment->YES) {
                $auditTrail->add($this->name." is a reference table ");
                $this->possibleReference = true;
            } else {
                $auditTrail->add($this->name." is not a reference table ");
                $this->possibleReference = false;
            }
// Create Table option taken
       }elseif ($p_idOrName == $environment->DICTIONARY_FOLDER ) {
           $this->name = $environment->DICTIONARY_FOLDER;
           $this->displayId = 0;
           $this->id=0;
// No parameter passed
       } elseif ($p_idOrName == '') {
           $messages->add("Invalid null table");
           $messages->fail_soft();
       } else {
// This is a new table so create the folder and then the two files
            $tableName = $p_idOrName;
// Check that the name is different
            $alreadyExists = false;
            if (in_array($tableName,$environment->tableNames ,true))
                $alreadyExists = true;
            if (!$alreadyExists) {
                $tableId = count($environment->tableNames)+1;
                $this->id=$tableId;
                $this->isAdmin=false;
                if ($tableId<10) $id='0'.$tableId;
                else $id = $tableId;
                $directoryName = $environment->baseFolder.$id.'-'.$tableName;
                mkdir($directoryName,0777);
                $environment = new Environment();
                $file = new File($this,"data");
                $file->create_file();
                $file = new File($this,"definition");
                $file->create_file();
            } else
                $messages->add($tableName.getMyLang('err_tab_nam'));
        }
        $auditTrail->add_footer("Table");
    }
/**
* check if the row being inserted has been changed by another user
*
* @param    number  $recordPointer    the record in question
* @returns  boolean true if changed
*
*/
    function hasChanged($p_id, $p_originals) {
        global $environment;
        global $messages;
        global $myLang;
// Get the current database values
        $this->get_all();
// check all the original fields in this record
        $currentRecord =$this->get($p_id);
        $match=true;
// Compare $currentRecord->values with $p_originals
        $counter=0;
        foreach ($currentRecord->values as $currentValue) {
// check each field
            $counter++;
            if ($currentValue != $p_originals[$counter]) {
                $match=false;
            }
        }
// if the record does not match then return true - it has changed
        if (!$match) {
             $messages->add("****** ".getMyLang('err_lost_update') ." ******");
             return true;
        } else {
// no problem so return false i.e. nothing has changed
            return false;
        }
    }
/**
* check if the row being inserted is unique or breaks the uniqueness of columns shown as unique
*
* @param    array   $values         the values in the record
* @param    number  $recordPointer  the record in question
* @returns  boolean true if unique
*
*/
    function isUnique($values,$recordPointer=0) {
        global $environment;
        global $messages;
        global $myLang;
        if ($recordPointer == 0)
            $thisMode="create";
        else
            $thisMode="update";
// populate if not already
        if (!$this->populated) $this->get_all();
        $recordCounter = 0;
// check all the fields in each record
        foreach ($this->recordObjs as $record) {
            $recordCounter++;
            $match=true;
            $counter=0;
            foreach ($record->values as $value) {
// when updating - do not check the record itself
                if ($recordPointer!=$recordCounter) {
                    $counter++;
// check each field
                    if ($value != $values[$counter]) {
                        $match=false;
                    }
                } else
                    $match=false;
// now check the unique columns
                if ($this->columnObjs[$counter]->unique == $environment->YES) {
                   if ($values[$counter] == $value && $recordPointer != $recordCounter) {
                       $messages->add('****** '.$this->columnObjs[$counter]->label." : ".getMyLang('err_dup_val') .' ******');
                       return false;
                   }
                }
            }
// as soon as a record matches then return false
            if ($match) {
                $messages->add("****** ".getMyLang('err_dup_rec') ." ******");
                return false;
            }
        }
// no match so return true
        return true;
    }
/**
* get the columns for this table
*
* @param    none
* @returns  array  the columns
*
*/
    function get_columns () {
        global $auditTrail;
        global $environment;
        $auditTrail->add_header('get_columns');
        $definitionFile = new File($this, "definition");
        $definitionFile->read_buffer();
        $rowObjs=$definitionFile->rowObjs;
        $counter = 0;
        $columns = array();
        foreach ($rowObjs as $rowObj) {
            $counter = $counter + 1;
            $fields = $rowObj->fields;
// Check on the visible, which may not be populated
            if ($fields[6] == '') $fields[6] = $environment->YES;
            if ($rowObj->raw>'')
                $columns[$counter] =
                  new Column($counter, $fields[1],$fields[2],$fields[3],$fields[4], $fields[5], $fields[6]);
        }
        $auditTrail->add_footer('get_columns');
        return $columns;
    }
/**
* filter the values in the table according to the values entered on the form
*
* @param    array     $filters      array of filters to be applied
* @param    array     $sortedList   the sorted list
* @returns  array of strings
*
*/
    function filter($filters,$sortedList) {
        $recCounter=0;
        $listCounter=0;
        global $display;
        global $environment;
        $filterExists    = array();
        $wildCardExists  = array();
        $wildCardAtStart = array();
        $wildCardAtEnd   = array();

        $display->debug_output("In filter recCounter= ".count($this->recordObjs));
        $list=array();
// Which fields should we look at?
        for ($colCounter=1; $colCounter<=count($this->columnObjs);$colCounter++) {
// Look at each field
            $type = $this->columnObjs[$colCounter]->type;
            $filterValue = $filters[$colCounter];
            $filterExists [$colCounter] = false;
// check if there is a filter value for it
            if ($filterValue > ' ' ) {
                $filterExists [$colCounter] = true;
                if ($type= $environment->TYPE_STRING) {
                    $posOfWildCard = strpos($filterValue, $environment->WILD_CARD);
                    if ($posOfWildCard === false) $wildCardsExist [$colCounter] = false;
                    else {
                        $wildCardsExist [$colCounter]   = true;
                        $wildCardAtStart [$colCounter]  = false;
                        $wildCardAtEnd  [$colCounter]   = false;
// wildcard either at start or end or both
                        if ($posOfWildCard == 0) {
                            $wildCardAtStart [$colCounter] = true;
                        }
                        $posOfWildCard = strpos($filterValue, $environment->WILD_CARD,1);
                        if ($posOfWildCard === false) $wildCardAtEnd [$colCounter] = false;
                        else $wildCardAtEnd [$colCounter] = true;
                    }
                }
            }
        }
// The list has already been sorted so we must filter the sorted list
        for( $recCounter=1; $recCounter<=count($this->recordObjs); $recCounter++) {
// Pick the item from the sorted list
            $recordObj = $this->recordObjs[$sortedList[$recCounter]];
            $display->debug_output($recordObj->values[1]);
// Assume the row will be included unless it fails any of the filters
            $include=true;
// Test each value
            for ($colCounter=1;$colCounter<=count($this->columnObjs);$colCounter++) {
                $type = $this->columnObjs[$colCounter]->type;
                $ignore = false;
                if ($filterExists [$colCounter] ) {
                    $value = $recordObj->values[$colCounter];
// Offset fields with a value of zero are ignored since no selection has been made
                    if ($value==0 && $type>$environment->LOOKUP_OFFSET) {
                        $ignore = true;
                    }
// As soon as any field fails the test we break out of the loop round the fields
                    if (!$ignore) {
                        if ($wildCardsExist[$colCounter]) {
                            $filterValue = str_replace($environment->WILD_CARD,'',$filters[$colCounter]);
                            $pos = strpos($value, $filterValue);
                            if ($pos===false) {
                                $include = false;
                                break;
                            } else {
//There is a wildcard and the string has been found but is it in the right place
                                if ($wildCardAtEnd[$colCounter]) {
// If the wildcard is at the end then the find must be zero
                                    if ($pos!=0) {
                                        $include = false;
                                        break;
                                    }
                                }
// if the wildcard is at the start then the last find must be at the end
// the search is in restToCheck then the last find is in restOfString
                                if ($wildCardAtStart[$colCounter]) {
                                    $restToCheck = $value;
                                    $nextPos = $pos;
                                    while ($nextPos!=false) {
                                        $pos = $nextPos;
                                        $restOfString = substr($restToCheck, $nextPos);
                                        $restToCheck = substr($restToCheck, $nextPos+1);
                                        $nextPos= strpos($restToCheck, $filterValue);
                                    }
                                    if ($restOfString != $filterValue) {
                                        $include = false;
                                        break;
                                    }
                                }
                            }

                        } else {
// No wildcards so the test is simple - and if one fails we break out of the loop
                            if ($filters[$colCounter] != $value) {
                                $include=false;
                                break;
                            }
                        }
                    }
                }
            }
            if ($include) {
                $listCounter++;
                $list[$listCounter] = $recCounter;
                $display->debug_output("Adding record ".$recCounter);
            }
        }
        return $list;
    }
/**
* get all the records in this table
*
* @param    none
* @returns  row objects
*
*/
    function get_all () {
        global $display;
        global $auditTrail;
        $auditTrail->add_header('get_all for table '.$this->name);
        $this->populated = true;
        $dataFile = new File($this,"data");
        $dataFile->read_buffer();
        $recCounter=0;
        $display->debug_output( "The contents of the table ".$this->name."=". $dataFile->buf);
        foreach($dataFile->rowObjs as $rowObj) {
            $recCounter=$recCounter + 1;
            $this->recordObjs[$recCounter] = new Record($this,$recCounter,$rowObj->fields);
        }
        $auditTrail->add_footer('get_all for table '.$this->name);
        return $dataFile->rowObjs;
    }
/**
* get a particular record
*
* @param    number          $p_id      the record id
* @returns  array of record objects
*
*/
    function get($p_id) {
        global $display;
        $display->debug_output("Getting record with id=".$p_id." from table ".$this->name);
        return $this->recordObjs[$p_id];
    }
/**
* create a new record
*
* @param    array     $p_values       the values to be inserted into each column
* @returns  none
*
*/
    function create($p_values) {
        global $display;
        global $messages;
        if ($this->isUnique($p_values)) {
            $dataFile = new File($this,"data");
            $dataFile->write_record($p_values);
        }
    }
/**
* update a record in this table
*
* @param    number    $p_id       the id of the record to be updated
* @param    array     $p_values   the values for each column
* @returns  none
*
*/
    function update($p_id,$p_values) {
        global $display;
        $display->debug_output("table:update : record ".$p_id." in table ".$this->name);
        $dataFile = new File($this,"data");
        $dataFile->update($p_id, $p_values);
    }
/**
* delete a record in this table
*
* @param    number    $p_id       the id of the record to be deleted
* @returns  none
*
*/
    function delete ($p_id) {
        $dataFile = new File($this,"data");
        $dataFile->delete($p_id);
    }
}
/**
* ReferenceTable Class
*
* A sub-class of table specifically for the reference table
*
*/
class ReferenceTable extends Table {
    var $list;
/**
*
* Constructor
*
* @param    none
* @returns  object  the reference table contains the list
*
*/
    function ReferenceTable ($id) {
       global $auditTrail;
       $auditTrail->add_header("ReferenceTable");
        $this->Table($id);
        $this->get_all();
        $counter=0;
        foreach ($this->recordObjs as $rec) {
            $this->list[$rec->values[1]] = $rec->values[2];
        }
       $auditTrail->add_footer("ReferenceTable");
    }
/**
* Get the list of lookups e.g. Gender, Title
*
* @param    none
* @returns  the list of lookups e.g. Gender
*
*/
    function get_list () {
        return $this->list;
    }
}
/**
* Record Class
*
* Holds information on each record in each table
*
*/
class Record {
    var $tableObj;
    var $id;
    var $values;
/**
*
* Constructor
*
* @param    table object $p_tableObj   the table to which this record belongs
* @param    number       $p_recordId   the ID of the record
* @param    array        $p_values     an array containing the fields
* @returns  object
*
*/
    function Record ($p_tableObj,$p_recordId,$p_values='') {
        global $auditTrail;
        $auditTrail->add_header('Record creation '.$p_tableObj->name);

        $this->tableObj = $p_tableObj;
        $this->values = $p_values;
        if (count($p_values) < count($this->tableObj->columnObjs))
            for ($counter=count($p_values)+1; $counter<=count($this->tableObj->columnObjs);$counter++)
                $this->values[$counter]='';
        $this->id=$p_recordId;
        $auditTrail->add_footer('Record creation '.$p_tableObj->name);
    }
}
/**
* File Class
*
* Holds the information on a physical file
*
*/
class File {
    var $name;
    var $buf;
    var $rowObjs = array();
    var $CR ;
    var $ASCILF=10;
/**
*
* Constructor
*
* @param    table object or string $p_tableObjOrName   the table object to which this file belongs or the name of a non-database file
* @param    string                 $type               'data' for a data file, 'definition' for a definition file and 'debug' for a log file
* @returns  object
*
*/
    function File ($p_tableObjOrName, $type) {
        global $environment;
        $this->CR= chr(13);
        if ($type == "data" || $type == "definition")
            $this->file_maker($p_tableObjOrName, $type);
// This is a general file create and the name has been passed as the first param
        else {
            $this->name = $environment->baseFolder.$environment->DICTIONARY_FOLDER.'/'.$p_tableObjOrName;
        }
    }
/**
* Extends the constructor for files associated with user tables as opposed
to debug files etc
*
* @param    table object    $p_tableObj
* @param    string          $type           definition or data
* @returns  object
*
*/
    function file_maker($p_tableObj, $type) {
        global $environment;
        global $display;
        global $session;
        $dbFolder = $environment->get_folder($p_tableObj->id);
        if ($p_tableObj->isAdmin) {
            if ($type=="definition")
// v1.0.2 this was originally taken from a file - it is now a mix of hard-coding and $lang
                $this->name = 'definition.txt';
            else
                $this->name = $dbFolder."definition.txt";
        }
        else  {
            if ($type=="definition")
                $this->name = $dbFolder."definition.txt";
            else
                $this->name = $dbFolder."data.txt";
        }
        $display->debug_output( "The file name = ".$this->name);
    }
/**
* Creates this file
*
* @param    none
* @returns  none
*
*/
    function create_file() {
        global $messages;
        $handle = fopen($this->name,"w");
        if (!$handle)
            $messages->add("Unable to open the file ".$this->name." when attempting to create it");
        else
            fclose($handle);
    }
/**
* Deletes this file
*
* @param    none
* @returns  none
*
*/
    function delete_file() {
        if ($this->exists())
            unlink($this->name);
    }
/**
* Does this file exist
*
* @param    none
* @returns  boolean
*
*/
    function exists () {
       if (file_exists($this->name))
           return true;
       else
           return false;
    }
/**
* Reads in the contents of this file into rowObjs
*
* @param    none
* @returns  none
*
*/
    function read_buffer () {
        global $display;
        global $auditTrail;
        global $messages;
        $auditTrail->add_header('read_buffer');
// Intercept the admin definition
        if (!$this->exists()) {
            $messages->add('Unable to open the file '.$this->name.' when attempting to read');
        } else {
            $handle = fopen($this->name,"r");
            $display->debug_output( "<br>read_buffer opening ".$this->name);
            $buf = '';
// The filesize is cached and should be cleared
            clearstatcache();
            if (filesize($this->name) > 0 )
                $buf = fread($handle,filesize($this->name));
            fclose($handle);
            $auditTrail->add( "read in ".$buf);
            $this->buf = $buf;
// get the rows
            $rows = explode($this->CR,$buf);
// get the columns
            $counter = 0;
            foreach ($rows as $row) {
                if (ord(substr($row,0,1)) == $this->ASCILF)
                    $row = substr($row,1);
                if (strlen($row)>0) {
                    $counter = $counter + 1;
                    $this->rowObjs[$counter] = new Row($row);
                }
            }
            $auditTrail->add_footer('read_buffer');
        }
    }
/**
* writes record to this file
*
* @param    array    $p_values    the values to be written into the columns of this new record
* @returns  none
*
*/
    function write_record ($p_values) {
        global $messages;
        global $environment;
        if ($this->exists()) {
            $handle = fopen($this->name,"a");
        } else {
            $handle = fopen($this->name,"w");
        }
        if (!$handle) {
            $messages->add('Unable to open the file '.$this->name.' when attempting to write');
        } else {
            $sep='';
            $buf='';
            if (is_array($p_values)) {
                foreach($p_values as $value) {
                    $buf.= $sep.$value;
                    $sep=$environment->COLUMN_SEPARATOR;
                }
            } else {
                $buf.= $p_values;
            }
            if (fwrite ($handle,$buf.$this->CR) === FALSE)
               $messages->add('Error writing to the file '.$this->name);
            fclose($handle);
        }
    }
/**
* Deletes a record
*
* @param    number   $p_id  ID of the record to be deleted
* @returns  none
*
*/
    function delete ($p_id) {
        global $messages;
        $this->read_buffer();
        $handle = fopen($this->name,"w");
        if (!$handle) {
            $messages->add("Unable to open the file ".$this->name." when attempting to delete");
        } else {
            for ($counter=1;$counter<$p_id;$counter++)
                fwrite($handle,$this->rowObjs[$counter]->raw.$this->CR);

// Skip the record
// Write the rest
            for ($counter=$p_id+1;$counter<=count($this->rowObjs);$counter++)
                fwrite($handle,$this->rowObjs[$counter]->raw.$this->CR);
            fclose($handle);
        }
    }
/**
* Update a record in this file
*
* @param    number   $p_id       ID of the record to be deleted from the file
* @param    array    $p_values   the values to be inserted into each column
* @returns  none
*
*/
    function update ($p_id, $p_values) {
        global $display;
        global $messages;
        global $environment;
        $display->debug_output( "file:update updating rec ".$p_id."in ".$this->name);
        $this->read_buffer();
        $display->debug_output( "first line = ".$this->rowObjs[1]->raw);
        $handle = fopen($this->name,"w");
        if (!$handle)
            $messages->add("Unable to open the file ".$this->name." when attempting to update");
        else {
            for ($counter=1;$counter<$p_id;$counter++)
                fwrite($handle,$this->rowObjs[$counter]->raw.$this->CR);
// Create the record
            $sep='';
            $buf='';
            foreach($p_values as $value) {
                $buf.= $sep.$value;
                $sep=$environment->COLUMN_SEPARATOR;
            }
            fwrite ($handle,$buf);
            fwrite($handle,$this->CR);
//Write the rest
            for ($counter=$p_id+1;$counter<=count($this->rowObjs);$counter++)
                fwrite($handle,$this->rowObjs[$counter]->raw.$this->CR);
            fclose($handle);
        }
    }
}
/**
* Row Class
*
* Holds the information on a row in a file
*
*/
class Row {
    var $fields = array();
    var $raw;
/**
*
* Constructor
*
* @param    srtring $row    the raw data including the pip separator
* @returns  object
*
*/
    function Row($row) {
        global $auditTrail;
        global $environment;
        $auditTrail->add_header('Creating Row using '.$row);
        $this->raw = $row;
        $elements=explode($environment->COLUMN_SEPARATOR, $row);
        $counter = 0;
        foreach($elements as $element) {
            $counter = $counter + 1;
            $this->fields[$counter] = $element;
            $auditTrail->add('Element Number '.$counter.' = '.$element);
        }
        $auditTrail->add_footer('Creating Row ');
    }
}
/**
* Display Class
*
* Manages the display to the screen
*
*/
class Display {
    var $debug=false ;
    var $IDX;
    var $currentSteps =0;
/**
*
* Constructor
*
* @param     none
* @returns  object
*
*/
    function Display() {
        global $dbName;
        $this->IDX ='<input type="hidden" name="id" value="start" />';
        $this->IDX .='<input type="hidden" name="idx" value="'.$dbName.'"
/>';
    }
/**
* Displays the form for creating a record
*
* @param    table object     $tab         the table in which the record will be created
* @param    messages object  $messages    the messages object so that any errors are trapped and displayed
* @param    string array     $values      the values to be placed in the fields in case there have been errors
* @returns  none
*
*/
    function create_form ($tab, $messages, $values='') {
        global $environment;
        global $myLang;
// Indent
        $this->output ('<div class="level2">');
// The table name
        $this->output ('<h1>'.$tab->name.'</h1>');
        if ($tab->isAdmin) $title = getMyLang('prompt_col');
        else $title = getMyLang('prompt_rec');
// The title ie. Creating Records
        $this->output ('<h2>'.getMyLang('prompt_cre').' '.$title.'</h2>');
        if ($messages->exist)
            $messages->displayAll();
// Indent
        $this->output ('<div class="level3">');

        $this->output ($this->form_start());
        $this->output ('<table border=1>');
        $counter=0;
        foreach($tab->columnObjs as $column) {
            $counter++;
            $this->output ('<tr>');
            $this->output ('<th>');
            $this->output ($column->label);
            $this->output ('</th>');
            $this->output ('<td>');
// If there were validation messages, redisplay values
            $name = "values".$counter;
            if (is_array($values)) $this->output($column->display_for_change($values[$counter],'create'));
            else
               $this->output($column->display_for_change('','create'));
            $this->output ('</td>');
            $this->output ('</tr>');
        }
        $this->output ('</table>');
        $this->output ('<span style="white-space: nowrap;">'); // This form seems to behave differently from the others
        $this->output($this->button('insertAction',getMyLang('btn_cre_cre') ,false, $tab->displayId));
        $this->output ($this->form_end());
// The Home button
        $this->output($this->button('home',getMyLang('btn_home') ,true));
// The Back button
        $this->output($this->button('listall',getMyLang('btn_back') ,true, $tab->displayId));
        $this->output ('</span>');
        $this->output('</div>');
        $this->output ('</div>');
    }
/**
* Displays the form for editing a record
*
* @param    record object    $p_recordObj the record to be edited
* @param    messages object  $messages    the messages object so that any errors are trapped and displayed
* @param    string array     $values      the values to be placed in the fields
* @returns  none
*
*/
    function edit_form($p_recordObj,$messages,$values='') {
// $p_recordObj has the current db values, $values has the values from the last editing (if they exist)
        global $environment;
        global $myLang;
        $tableObj=$p_recordObj->tableObj;
// Indent
        $this->output ('<div class="level2">');
// The table name
        $this->output ('<h1>'.$tableObj->name.'</h1>');
        if ($tab->isAdmin) $title = getMyLang('prompt_col');
        else $title = getMyLang('prompt_rec');
// The title ie. Creating Records
        $this->output ('<h2>'.getMyLang('prompt_edt').' '.$title.'</h2>');
        if ($messages->exist)
            $messages->displayAll();
        $columnObjs = $tableObj->columnObjs;
// Indent
        $this->output ('<div class="level3">');
// Start the form
        $this->output ($this->form_start());
// Start of table
        $this->output( "<table border=1>");
        for($counter=1;$counter<count($columnObjs)+1;$counter++) {
            $column = $columnObjs[$counter];
            $this->output('<tr>');
            $this->output('<th>');
            $this->output($columnObjs[$counter]->label);
            $this->output ('</th>');
            $this->output ('<td>');
            if (is_array($values))
                $this->output($column->display_for_change($values[$counter],'update'));
            else
                $this->output($column->display_for_change($p_recordObj->values[$counter],'update'));
            $this->output('</td>');
            $this->output('</tr>');
        }
        $this->output('</table>');
// Now for the original db values as hidden fields
        for($counter=1;$counter<count($columnObjs)+1;$counter++) {
            $column = $columnObjs[$counter];
            $fieldName = "originals".$counter;
            $this->output($column->display_original($counter, $p_recordObj->values[$counter]));
        }
// Update button
        $this->output($this->button('updateAction',getMyLang('btn_edt_upd'),false, $tableObj->displayId,$p_recordObj->id));
        $this->output($this->form_end());
// The Home button
        $this->output($this->button('home',getMyLang('btn_home') ,true));
// The Back button
        $this->output($this->button('listall',getMyLang('btn_back') ,true, $tableObj->displayId));

        $this->output('</div>');
        $this->output('</div>');
    }
/**
* Displays the form for deleting a record
*
* @param    record object  $p_recordObj the record to be deleted
* @param    messages object  $messages  the messages object so that any errors are trapped and displayed
* @returns  none
*
*/
    function delete_form($p_recordObj,$messages) {
        global $environment;
        global $myLang;
        $tableObj=$p_recordObj->tableObj;
// Indent
        $this->output ('<div class="level2">');
// The table name
        $this->output ('<h1>'.$tableObj->name.'</h1>');
        if ($tableObj->isAdmin) $title = getMyLang('prompt_col');
        else $title = getMyLang('prompt_rec');
// The title ie. Deleting .....
        $this->output ('<h2>'.getMyLang('prompt_del_rec').'</h2>');
        if ($messages->exist)
            $messages->displayAll();
        $columnObjs = $tableObj->columnObjs;
// Indent
        $this->output ('<div class="level3">');
        $this->output ($this->form_start());
        $this->output( "<table border=1>");
// Display the contents
        for($counter=1;$counter<count($columnObjs)+1;$counter++) {
            $this->output('<tr>');
            $this->output('<th>');
            $this->output($columnObjs[$counter]->label);
            $this->output('</th>');
            $this->output('<td>');

            $this->output($columnObjs[$counter]->display($p_recordObj->values[$counter]));
            $this->output('</td>');
            $this->output('</tr>');
        }
// Now for the original db values as hidden fields
        for($counter=1;$counter<count($columnObjs)+1;$counter++) {
            $column = $columnObjs[$counter];
            $fieldName = "originals".$counter;
            $this->output($column->display_original($counter, $p_recordObj->values[$counter]));
        }
// Display the buttons
        $this->output('</table>');
        $this->output($this->button('deleteAction',getMyLang('btn_del_cnf'),false,
        $tableObj->displayId,$p_recordObj->id));
        $this->output ($this->form_end());
// The Home button
        $this->output($this->button('home',getMyLang('btn_home') ,true));
// The Back button
        $this->output($this->button('listall',getMyLang('btn_back') ,true, $tableObj->displayId));
        $this->output('</div>');
        $this->output ('</div>');
    }
/**
* Displays all the records in a table for sorting, searching etc
*
* @param    table object  $tab            the table whose contents are to be displayed
* @param    array         $list           the contents to be displayed
* @param    array         $sortedList     the sorted list of record IDs
* @param    number        $sortedColumnId the column on which it has been sorted
* @returns  none
*
*/
    function list_all($tab, $list,$sortedList='',$sortedColumnId=0) {
        global $environment;
        global $myLang;
        $numberOfCells = count($tab->columnObjs)+3;
        $this->output ('<div class="level2">');
// The title
        $this->output ('<h1>'.$tab->name.'</h1>');
// The Home button
        $this->output($this->button('home',getMyLang('btn_home') ,true));
//The create button
        $prompt = getMyLang('btn_lst_cre_new_rec');
        if ($tab->isAdmin) $prompt=getMyLang('btn_lst_cre_new_col');
        $this->output($this->button('insertForm',$prompt,true, $tab->displayId));
//The Admin button
        if (!$tab->isAdmin) {
             $this->output($this->button('admin',getMyLang('btn_adm_tab_str'),true, $tab->displayId));
         } else {
             $this->output($this->button('listall',getMyLang('btn_lst_tab_cnt') ,true, $tab->id));
         }
//************************************************************************************

        $this->output ($this->form_start());
        $this->output ('<table class="inline">');
        $colCounter=0;
        $this->output ('<thead>');
        $this->output ('<tr>');
// The No sort radio button
        $this->output ('<th align=center>');
        $this->output (getMyLang('rad_lst_no_srt'));
        $toGo = '<input type="radio" name="sort" value="0"';
        if ($sortedColumnId==0)
            $toGo .=' checked ';
        $toGo .= '/>';
        $this->output ($toGo);
        $this->output ('</th>');
        foreach($tab->columnObjs as $column) {
            $colCounter++;
            if ($column->isVisible) {
                $this->output ("<th>");
// The Sort radio button
                $this->output ($column->label);
                $toGo = '<input type="radio" name="sort" value="'.$colCounter.'"';
                if ($colCounter==$sortedColumnId)
                    $toGo .=' checked ';
                $toGo .= '/>';
                $this->output ($toGo);
                $this->output ('</th>');
            }
        }
        $this->output ('</tr>');
//The filter cells
        $this->output ('<tr>');
// The Search row of filters and the Search button
        $colCounter=0;
        $this->output ('<td class="rightalign">');
        $this->output($this->iconButton('list','search.png', getMyLang('btn_lst_srch_srt'), $tab->displayId));
        $this->output ('</td>');
        foreach($tab->columnObjs as $column) {
            $colCounter++;
            if ($column->isVisible) {
                $this->output ('<td>');
                $this->output ($column->display_for_change('','search'));
                $this->output ('</td>');
            }
        }
        $this->output ('</tr>');
        $this->output ('</thead>');
//**************************************************************************************
        $this->output ('<tbody>');
// the rest of the main table
        $recCounter = 0;
        $columnCount=count($tab->columnObjs);
//The row begins
        for ($recCounter=1;$recCounter<=count($tab->recordObjs);$recCounter++) {
            $recPointer= $sortedList[$recCounter];
            $record=$tab->recordObjs[$recPointer];
//Check against the filter list
            $okToDisplay = false;
            if (in_array($recCounter,$list,true))
                $okToDisplay = true;
            if ($okToDisplay) {
                $this->output ("<tr>");
// Each column in the row
                $this->output ('<td>');
// The Edit button
                $this->output($this->iconButton('updateForm','edit.png',getMyLang('btn_lst_edt'), $tab->displayId,$recPointer));
                if (!$tab->isAdmin) {
// The Delete button
                    $this->output($this->iconButton('deleteForm','delete.png',getMyLang('btn_lst_del'), $tab->displayId,$recPointer));
                }
                $this->output ('</td>');
// The columns displayed
                for ($fieldCounter=1;$fieldCounter<=$columnCount;$fieldCounter++) {
                    $column = $tab->columnObjs[$fieldCounter];
                    if ($column->isVisible) {
                        $value = $record->values[$fieldCounter];
                        $this->output ('<td>');
                        $this->output ($tab->columnObjs[$fieldCounter]->display($value));
                        $this->output ('</td>');
                    }
                }
                $this->output ("</tr>");
            }
        }
        $this->output ('</tbody>');
        $this->output ('</table>');
        $this->output ($this->form_end());
//************************************************************************************
        $this->output ('</div>');
    }
/**
* Displays the form for creating a new table
*
* @param    none
* @returns  none
*
*/
    function createTableForm() {
        global $environment;
        global $messages;
        global $myLang;
        $this->output ('<h1>'.getMyLang('ttl_cre_tab').'</h1>');
        if ($messages->exist) $messages->displayAll();
        $this->output ('<table>');
        $this->output ($this->form_start());
        $this->output('<tr>');
        $this->output('<td>');
        $this->output(getMyLang('prompt_ent_tab_name'));
        $this->output('</td>');
        $this->output('<td>');
        $this->output('<input type="text" name="tableName" size=30 />');
        $this->output('</td>');
        $this->output('</tr>');
        $this->output('</table>');
        $this->output($this->button('createTable',getMyLang('btn_cre_new_tab'),false));
        $this->output($this->form_end());
// The Home button
        $this->output($this->button('home',getMyLang('btn_home') ,true));
    }
/**
*  Constructs the HTML form start tag
*
* @param    none
* @returns  string    the HTML form end tag
*
*/
    function form_start() {
        global $environment;
        $ret = '<form method="post" action="'.$environment->URL.'">';
        return $ret;
    }
/**
* Displays a button if permitted
*
* @param    string  $action         the action taken
* @param    string  $name           the name of the button
* @param    boolean $withTopAndTail true if the <form> and </form> are to be included
* @param    number  $table          the ID of the table
* @param    number  $record         the ID of the record
* @returns  string  The HTML required
*
*/
    function button ($action, $name, $withTopAndTail=false, $table='', $record='') {
        $ret='';
        global $session;
        global $auditTrail;
        $auditTrail->add_header('button action = '.$action.' name = '.$name);
        if ($session->allowed($action)) {
            if ($withTopAndTail) $ret .= $this->form_start();
            $ret .= $this->IDX;
            $ret .='<input type="hidden" name="action" value="'.$action.'" />';
            if ($table!='') $ret .= '<input type="hidden" name="table" value="'.$table.'" />';
            if ($record!='') $ret .= '<input type="hidden" name="record" value="'.$record.'" />';
            $ret .='<input type="submit" value="'.$name.'" ';
            $ret .= 'class="button" />';
            if ($withTopAndTail) $ret .= $this->form_end();
        }
        $auditTrail->add_footer('button action = '.$action.' name = '.$name);
        return $ret;
    }
/**
* Displays a button icon if permitted
*
* @param    string  $action         the action taken
* @param    string  $image          the image to be displayed
* @param    string  $title          the title for the hint
* @param    number  $table          the ID of the table
* @param    number  $record         the ID of the record
* @returns  string  The HTML required
*
*/
    function iconButton ($action, $image, $title, $table='', $record='') {
        $ret='';
        global $session;
        global $auditTrail;
        global $environment;
        $auditTrail->add_header('button action = '.$action.' name = '.$name);
        if ($session->allowed($action)) {
            $ret .= $this->IDX;
            $ret .= '<input type="image" src="'.$environment->imagesFolder.$image.'" name="fn['.$action.']['.$table.$environment->COLUMN_SEPARATOR.$record.']" title="'.$title.'" alt="'.$title.'" />';
        }
        $auditTrail->add_footer('iconButton action = '.$action.' name = '.$name);
        return $ret;
    }
/**
* Constructs the HTML form end tag
*
* @param    none
* @returns  string  the HTML form end tag
*
*/
    function form_end() {
        $ret ='</form>';
        return $ret;
    }
/**
* Outputs/renders to the screen
*
* @param    string  $p_string    the string to be output
* @param    number  $p_indent = -1 : reduce indent
*                             = 0  ; leave indent
*                             = +1 : increase indent
* @returns  none
*
*/
    function output ($p_string, $p_indent=0) {
        if ($p_indent==0) {
            if (substr($p_string,0,3)=='</t') $this->currentSteps--;
        }
        $INCREMENT = 2; // for the indent of the HTML

        ptln( $p_string, $this->currentSteps*$INCREMENT);
        if ($p_indent==0) {
            if (substr($p_string,0,2)=='<t') $this->currentSteps++;
        }
    }
/**
* Outputs to the debug file if the debug flag is set to true
*
* @param    string  $p_string    the string to be output
* @returns  none
*
*/
    function debug_output ($p_string) {
        global $environment;
        if ($this->debug) {
            $file = new File($environment->DEBUG_FILE,"debug");
            $file->write_record($p_string);
        }
    }
/**
* the default screen
*
* @param    none
* @returns  none
*
*/
    function wiki () {
    }
}
/**
* TestObj Class
*
* Used specifically for the sort
*
*/
class TestObj {
    var $id;
    var $name;
/**
*
* Constructor
*
* @param    number $id     the ID of the element
* @param    string $name   the name of the element
* @returns  object
*
*/
    function TestObj($id,$name)
    {
        $this->id = $id;
        $this->name = $name;
    }
/**
* This is the static comparing function:
*
* @param    string  $a    the string to be compared
* @param    string  $b    the string to be compared
* @returns  boolean       0 if =, 1 if a>b, else -1
*
*/
    function cmp_obj($a, $b)
    {
        $al = strtolower($a->name);
        $bl = strtolower($b->name);
        if ($al == $bl) {
            return 0;
        }
        return ($al > $bl) ? +1 : -1;
    }
}
/**
* SortedArray Class
*
* Holds the sorted array specifically for sorting
*
*/
class SortedArray {
    var $sortedList = array();
/**
*
* Constructor
*
* @param    array $list        the array to be sorted
* @returns  object
*
*/
    function SortedArray($list) {
        $recCounter=0;
        foreach($list as $entry) {
            $recCounter++;
            $toBeSorted[]=new TestObj($recCounter,$entry);
        }
        usort ($toBeSorted,array("TestObj","cmp_obj"));
        $counter=0;
        foreach($toBeSorted as $testObj) {
            $counter++;
            $this->sortedList[$counter] = $testObj->id;
        }
    }
}
/**
* Main program which takes the appropriate action
*
* @param    none
* @returns  none
*
*/
function action_db() {
    global $environment;
    global $display;
    global $session;
    global $IDX;
    global $dbName;
    global $messages;
    global $auditTrail;
    global $myLang;
// Set up by action.php
    global $action;
    global $tableId;
    global $recordId;

    $display = new Display();
    $auditTrail = new AuditTrail();
    $INFO  = pageinfo();
    $auditTrail->add_header("action_db");
    $auditTrail->add('action = '.$action.' table = '.$tableId.' record = '.$recordId);
    $messages = new Messages();
// Check whether the debug flag is set
    if (isset($_REQUEST['debug'])) {
        if ($_REQUEST['debug']=='yes') {
           $file=new File($environment->DEBUG_FILE,"debug");
           $file->create_file();
           $display->debug=true;
        } elseif ($_REQUEST['debug']=='no') {
           $file=new File($environment->DEBUG_FILE,"debug");
           $file->delete_file();
           $display->debug=false;
        }
    }
    $session = new Session();
// Check if it has come from the wiki directly
    if ($action =='') {
// It has come directly from the wiki
        $action = "listall";
        $command = substr($IDX,strlen($dbName)+1);
        $tableId = substr($command,0,2);
// Pick up the Create Table option
        if ($IDX==$dbName.':'.$environment->DICTIONARY_FOLDER)
           $action='createTableForm';
        else {
// make sure all is OK
            $tab=new Table($tableId);
// if there was a problem then report it
            if ($messages->exist)
               $action="messages";
        }
    }
    $auditTrail->add("action=".$action);
// Check whether the user can carry out this action
    if (!$session->allowed($action)) {
        $action= "message";
        $message=getMyLang('msg_wrn_priv');
        $messages->add($message);
        $action = "messages";
    }
    $auditTrail->add("action=".$action);
// Now handle the various actions
// action = messages
    if ($action=="messages")
       $messages->displayAll();
// action = home
    if ($action == "home") {
        $display->wiki();
    }
// action = createTable
    if ($action == "createTable") {
        $tableName = '';
        if (isset($_REQUEST["tableName"]))
            $tableName  = $_REQUEST["tableName"];
        if ($tableName > '') {
           $tab = new Table($tableName);
        } else {
            $messages->add(getMyLang('msg_wrn_tab_nam') );
        }
        if ($messages->exist) {
            $action = "createTableForm";
        } else {
            $tableId = - $tab->id;
            $messages->add(getMyLang('msg_tab_cre'));
            $action = "insertForm";
        }
    }
// action = admin
    if ($action=="admin") {
        $auditTrail->add("action=".$action." tableId is ".$tableId."will be negative");
        $tableId = - $tableId;
        $action="listall";
    }
// action =  insertAction
    if ($action == "insertAction") {
        global $display;
        $tab=new Table ($tableId);
        for ($counter=1;$counter<=count($tab->columnObjs);$counter++) {
            $test="values".$counter;
            if (isset($_REQUEST[$test])) {
                $values[$counter]= $_REQUEST[$test];
                $column = $tab->columnObjs[$counter];
                $column->validate($values[$counter], $messages);
                $values[$counter] = $column->toDatabase($values[$counter]);
            }
        }
        $display->debug_output ( "val=".$values[1]." and ".$values[2]);
        if ($messages->exist) {
            $action = "insertForm";
        } else {
            $tab->create($values);
            if ($messages->exist)
                $action="insertForm";
            else
                $action="listall";
        }
    }
// action = updateAction
    if ($action == "updateAction") {
        global $display;
        $tab=new Table ($tableId);
        $done = false;
        $display->debug_output ("main:update action: table= ".$tab->name);
        for ($counter=1;$counter<=count($tab->columnObjs);$counter++) {
            $column = $tab->columnObjs[$counter];
// Get the changed values from the form
            $test="values".$counter;
            if (isset($_REQUEST[$test])) {
                $values[$counter]= $_REQUEST[$test];
                $column->validate($values[$counter], $messages);
                $values[$counter] = $column->toDatabase($values[$counter]);
            }
// Now to get the originals
            $test="originals".$counter;
            if (isset($_REQUEST[$test])) {
                $originals[$counter]= $_REQUEST[$test];
            }
        }
// The array values now has the values after being changed
// $originals has the original values stored as hidden fields on the form
        if ($messages->exist) {
// Problems with the validation so try again
            $action = "updateForm";
        } else {
            if (!$tab->isUnique($values,$recordId)) {
                $action = "updateForm";
                } else {
                    if ($tab->hasChanged($recordId, $originals)) {
                        $values = ''; // to force the refresh of the values in the screen
                    $action = "updateForm";
                    } else {
                        $tab->update($recordId, $values);
                    if ($messages->exist) {
// Problems with the update so try again
                        $action = "updateForm";
                    } else {
                        $action="listall";
                    }
                }
            }
        }
    }
// action = deleteAction
    if ($action == "deleteAction") {
        global $display;
        $tab=new Table ($tableId);
        $display->debug_output ("main:delete action: table= ".$tab->name);
        for ($counter=1;$counter<=count($tab->columnObjs);$counter++) {
            $column = $tab->columnObjs[$counter];
// Now to get the originals
            $test="originals".$counter;
            if (isset($_REQUEST[$test])) {
                $originals[$counter] = $_REQUEST[$test];
            }
        }
        if ($tab->hasChanged($recordId, $originals)) {
            $action = "deleteForm";
           } else {
            $tab->delete($recordId);
            if ($messages->exist)
// Problems with the delete so try again
                $action = "deleteForm";
            else
                $action = "listall";
            }
    }
// action = list or listall
    if ($action == "list" || $action == "listall") {
        $auditTrail->add("action=".$action." tableId is ".$tableId);
        $tab=new Table ($tableId);
        $tab->get_all();
        $sortedColumnId=0;
        $filters=array();
        $sortedList = array();
// **************** Any Sorting ? ************************************************
        if (isset($_REQUEST["sort"]) && $_REQUEST["sort"]>'0') {
            $sortedColumnId  = $_REQUEST["sort"];
// Prepare the sort array in $list
// If this is a link column then we should sort on the name of the link, i.e. the last element
            if ($tab->columnObjs[$sortedColumnId]->type == $environment->TYPE_EXTERNAL_LINK
               ||$tab->columnObjs[$sortedColumnId]->type == $environment->TYPE_INTERNAL_LINK) {
                $counter=0;
                foreach($tab->recordObjs as $recordObj) {
                    $counter++;
                    $value = $recordObj->values[$sortedColumnId];
                    $elements=explode($environment->COLON, $value);
                    $name=$elements[count($elements)-1];
                    $list[$counter] = $name;
                }
            }
// If this is a reference column then sort on the second column in the reference table
            elseif ($tab->columnObjs[$sortedColumnId]->type >= $environment->REFERENCE_OFFSET) {
                $refTableID = $tab->columnObjs[$sortedColumnId]->type - $environment->REFERENCE_OFFSET;
                $refTable = new ReferenceTable($refTableID);
                $baseList = $refTable->get_list();
                $counter = 0;
                foreach($tab->recordObjs as $recordObj) {
                    $counter++;
                    $list[$counter] = $baseList[$recordObj->values[$sortedColumnId]];
                }
// Any other type just sort on that column
            } else {
                $counter=0;
                foreach($tab->recordObjs as $recordObj) {
                    $counter++;
                    $list[$counter] = $recordObj->values[$sortedColumnId];
                }
            }
// now do the sorting  - the results are in sortedList as pointers
            $sortedListObj = new SortedArray($list);
            $sortedList = $sortedListObj->sortedList;
        } else {
// No Sorting required just put consecutive integers in $sortedList
            $counter=0;
            foreach ($tab->recordObjs as $recordObj) {
                $counter++;
                $sortedList[$counter] = $counter;
            }
        }
//check if any filters have been applied but not if it's listall
        if ($action != "listall") {
             for ($counter=1;$counter<=count($tab->columnObjs);$counter++) {
                 $test="values".$counter;
                 if (isset($_REQUEST[$test]))
                     $filters[$counter]= $_REQUEST[$test];
                 else $filters[$counter]='';
             }
        }
        $list=$tab->filter($filters,$sortedList);
        $display->list_all($tab, $list, $sortedList, $sortedColumnId);
    }
// action = createTableForm
    if ($action == 'createTableForm' ) {
            $display->createTableForm();
    }
// action = insertForm
    if ($action == "insertForm") {
        $auditTrail->add("Calling new Table with id = ".$tableId);
        $tab=new Table ($tableId);
        $display->create_form($tab,$messages,$values);
    }
// action = updateForm
    if ($action == "updateForm") {
        $tab=new Table ($tableId);
// If messages are empty then this must be the first time so store the originals
        if ($tab->possibleReference) {
            $messages->add(getMyLang('msg_ref_tab_wng_1'));
            $messages->add(getMyLang('msg_ref_tab_wng_2'));
            $messages->add(getMyLang('msg_ref_tab_wng_3'));
        }
        $tab->get_all();
        $rec=$tab->get($recordId);
        $display->edit_form($rec,$messages,$values);
    }
// action = deleteForm
    if ($action == "deleteForm") {
        $tab=new Table ($tableId);
        if ($tab->possibleReference) {
            $messages->add(getMyLang('msg_ref_tab_wng_1'));
            $messages->add(getMyLang('msg_ref_tab_wng_2'));
            $messages->add(getMyLang('msg_ref_tab_wng_3'));
        }
        $tab->get_all();
        $rec=$tab->get($recordId);
        $display->delete_form($rec, $messages);
    }
    $auditTrail->add_footer("action_db");
}
/**
* Get the translated element in $lang
*
* @param    $id1   first  id of the lang element
* @param    $id2   second  id of the lang element
* @param    $id3   third  id of the lang element
* @returns  string translation
*
*/
function getMyLang($id1,$id2='',$id3='') {
    global $myLang;
    if ($id3>'') $value = $myLang[$id1][$id2][$id3];
    elseif ($id2>'') $value = $myLang[$id1][$id2];
    else   $value = $myLang[$id1];

    return utf8_encode($value);
}
?>
