PHP Classes

File: class.FormHandler.php

Recommend this page to a friend!
  Classes of T. H.  >  FormHandler  >  class.FormHandler.php  >  Download  
File: class.FormHandler.php
Role: Class source
Content type: text/plain
Description: Main class
Class: FormHandler
Generate and validate Web forms
Author: By
Last change:
Date: 17 years ago
Size: 84,996 bytes
 

Contents

Class file image Download
<?php

/*

FormHandler v3
 
Revision: $Date: 2005/05/04 13:08:55 $

Look for more info at http://www.formhandler.net

*/


// make sure this file is not accessed directly
if(strtolower(basename($_SERVER['PHP_SELF'])) == strtolower(basename(__FILE__))) {
    die("This file cannot be accessed directly! Include it in your script instead!");
}

/******* BUILD IN VALIDATOR FUNCTIONS ****/
define('FH_STRING',     'IsString',    true);	// any string that doesn't have control characters (ASCII 0 - 31) but spaces are allowed
define('FH_ALPHA',      'IsAlpha',     true);	// only letters a-z and A-Z
define('FH_DIGIT',      'IsDigit',     true);	// only numbers 0-9
define('FH_ALPHA_NUM',  'IsAlphaNum',  true);	// letters and numbers
define('FH_INTEGER',    'IsInteger',   true);	// only numbers 0-9 and an optional - (minus) sign (in the beginning only)
define('FH_FLOAT',      'IsFloat',     true);	// like FH_INTEGER, only with , (comma)
define('FH_FILENAME',   'IsFilename',  true);	// a valid file name (including dots but no slashes and other forbidden characters)
define('FH_BOOL',       'IsBool',      true);	// a boolean (TRUE is either a case-insensitive "true" or "1". Everything else is FALSE)
define('FH_VARIABLE',   'IsVariabele', true);	// a valid variable name (letters, digits, underscore)
define('FH_PASSWORD',   'IsPassword',  true);	// a valid password (alphanumberic + some other characters but no spaces: ASCII 33 - 126)
define('FH_URL',        'IsURL',       true);	// a valid URL (http connection is used to check if url exists!)
define('FH_EMAIL',      'IsEmail',     true);	// a valid email address (only checks for valid format: xxx@xxx.xxx)
define('FH_EMAIL_HOST', 'IsEmailHost', true);   // like FH_EMAIL only with host check
define('FH_TEXT',       'IsText',      true);	// like FH_STRING, but newline characters are allowed
define('FH_NOT_EMPTY',  'notEmpty',    true);   // check if the value is not empty

# for dutch people
define('FH_POSTCODE',   'IsPostcode',  true);   // valid dutch postcode (eg. 9999 AA)
define('FH_PHONE',      'IsPhone',     true);   // valid dutch phone-number(eg. 058-2134778)

# same as above, but with these the value is not required
define('_FH_STRING',     '_IsString',    true);
define('_FH_ALPHA',      '_IsAlpha',     true);
define('_FH_DIGIT',      '_IsDigit',     true);
define('_FH_ALPHA_NUM',  '_IsAlphaNum',  true);
define('_FH_INTEGER',    '_IsInteger',   true);
define('_FH_FLOAT',      '_IsFloat',     true);	
define('_FH_FILENAME',   '_IsFilename',  true);
define('_FH_BOOL',       '_IsBool',      true);
define('_FH_VARIABLE',   '_IsVariabele', true);
define('_FH_PASSWORD',   '_IsPassword',  true);
define('_FH_URL',        '_IsURL',       true);
define('_FH_EMAIL',      '_IsEmail',     true);
define('_FH_EMAIL_HOST', '_IsEmailHost', true);
define('_FH_TEXT',       '_IsText',      true);
define('_FH_POSTCODE',   '_IsPostcode',  true);
define('_FH_PHONE',      '_IsPhone',     true);


// make some variables global when the version < 4.1.0
if(IntVal( str_replace('.', '', phpversion()) ) < 410) {
    define('_global', false);
    $_GET =& $HTTP_GET_VARS;
    $_POST =& $HTTP_POST_VARS;
    $_FILES =& $HTTP_POST_FILES;
    $_SERVER =& $HTTP_SERVER_VARS;
} else {
    define('_global', true);
}

// Fields
define('FH_INCLUDE_DIR', dirname(__FILE__).'/');
require(FH_INCLUDE_DIR.'fields/class.Field.php');
require(FH_INCLUDE_DIR.'fields/class.TextField.php');
require(FH_INCLUDE_DIR.'fields/class.HiddenField.php');
require(FH_INCLUDE_DIR.'fields/class.TextArea.php');
require(FH_INCLUDE_DIR.'fields/class.PassField.php');
require(FH_INCLUDE_DIR.'fields/class.CheckBox.php');
require(FH_INCLUDE_DIR.'fields/class.RadioButton.php');
require(FH_INCLUDE_DIR.'fields/class.UploadField.php');
require(FH_INCLUDE_DIR.'fields/class.SelectField.php');
require(FH_INCLUDE_DIR.'fields/class.dbSelectField.php');
require(FH_INCLUDE_DIR.'fields/class.ListField.php');
require(FH_INCLUDE_DIR.'fields/class.Editor.php');
require(FH_INCLUDE_DIR.'fields/class.DateField.php');
require(FH_INCLUDE_DIR.'fields/class.TimeField.php');

// Buttons
require(FH_INCLUDE_DIR.'buttons/class.Button.php');
require(FH_INCLUDE_DIR.'buttons/class.SubmitButton.php');
require(FH_INCLUDE_DIR.'buttons/class.ImageButton.php');
require(FH_INCLUDE_DIR.'buttons/class.ResetButton.php');

// Includes
require(FH_INCLUDE_DIR.'includes/class.validator.php');
require(FH_INCLUDE_DIR.'includes/class.imageconverter.php');
require(FH_INCLUDE_DIR.'includes/class.fhMailer.php');
require(FH_INCLUDE_DIR.'includes/config.inc.php');
require(FH_INCLUDE_DIR.'includes/fckeditor.php');
require(FH_INCLUDE_DIR.'includes/error.inc.php');
require(FH_INCLUDE_DIR.'includes/mimeTypes.php');

// DB
require(FH_INCLUDE_DIR.'database/db.php');
require(FH_INCLUDE_DIR.'database/db.mysql.php');
require(FH_INCLUDE_DIR.'database/db.access.php');
require(FH_INCLUDE_DIR.'database/db.postgresql.php');


/**
* class FormHandler
*
* Create a new FormHandler object. 
*
*/
class FormHandler 
{
    // private
    var $_fields;           // array: contains all the fields    
    var $_posted;           // boolean: if the form is posted or not
    var $_name;             // string: the name of the form
    var $_action;           // string: the action of the form
    var $_displayErrors;    // boolean: if we have to display the errors in the form
    var $_mask;             // string: the mask which should be used
    var $_upload;           // array: contains the names of the uploadfields
    var $_date;             // array: contains the names of the datefields
    var $_onCorrect;        // string: the callback function when the form is correct
    var $_onSaved;          // string: the callback function when the form is saved
    var $_add;              // array: contains the data which was added by the user
    var $_sql;              // array: contains the names of the added values which are sql functions
    var $_dontSave;         // array: dont save these fields    
    var $_focus;            // string: the field which should get the focus
    var $_convert;          // array: fields which should be converted (eg. resizeimage or mergeimage)
    var $_gd;               // boolean: check if gd is enabled!
    var $_db;               // object: contains the database object if the option is used
    var $_id;               // array: the id(s) which we are editing
    var $_dbData;           // array: the database data 
    var $_buffer;           // array: buffer of set values (used when the field does not exists yet)
    var $_eh;               // string: the name of the original error handler 
    var $_text;             // array: the language array we are using to display the messages etc   
    var $_lang;				// string: the language used
    var $_table;			// boolean: set a html table arround the fields or has the user done that in the mask ?
    var $_extra;			// string: extra tag information for the <form> tag (like CSS or javascript)
    var $_pageCounter;      // int: how many pages has this form
    var $_curPage;          // int: current page 
    var $_mail;             // array: contains the mailing data
    var $_tabindexes;       // array: tab indexes of the fields...
    
    // public
    var $insert;            // boolean: if the form is an insert-form
    var $edit;              // boolean: if the form is an edit-form 
    
    /**
     * FormHandler::FormHandler()
     *
     * Public constructor: create a new instance of FormHandler
     *
     * @param string $name: the name of the form
     * @return FormHandler
     */
    function FormHandler($name = null, $action = null, $extra = null) { 
        // make vars global if needed
        if(!_global) global $_SERVER, $_POST, $_GET;
              
    	// try to disable caching from the browser if possible
    	if(!headers_sent()) {
    		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); 
			header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');		
			header('Cache-Control: no-store, no-cache, must-revalidate');  
			header('Cache-Control: post-check=0, pre-check=0', false);
			header('Pragma: no-cache');
    	}
 	
        // set all config values
        fh_conf();
        
        // set the name of the form
        if(!empty($name)) {
            $this->_name = $name;
        } else {            
        	// get a unique form name!
            $i = null;
            while(defined('FH_'.FH_DEFAULT_FORM_NAME.$i)) {
                $i = (is_null($i)) ? 1 : ($i+1);
            }            
            define('FH_'.FH_DEFAULT_FORM_NAME.$i, 1);
            $this->_name = FH_DEFAULT_FORM_NAME.$i;
        }
        
        // set the action of the form if none is given
        if( !empty($action) ) {
            $this->_action = $action;
        } else {
            $this->_action = $_SERVER['PHP_SELF'];
            if(!empty($_SERVER['QUERY_STRING'])) {
                $this->_action .= '?'.$_SERVER['QUERY_STRING'];
            }
        }      
        
        // get the JS to put into the <form> tag
        if( !empty( $extra ) ) {
        	$this->_extra = $extra;
        }      
        
        // initialisation
        $this->_fields        = array();
        $this->_date          = array();
        $this->_upload        = array();
        $this->_add           = array();
        $this->_sql           = array();
        $this->_dbData        = array();
        $this->_dontSave      = array();
        $this->_buffer        = array();
        $this->_convert       = array();
        $this->_mail          = array();
        $this->_tabindexes    = array();
        $this->_displayErrors = true;         
        $this->_table         = true;
        $this->_focus         = null;    
        $this->_pageCounter   = 1;  
        $this->insert         = !isset($_GET[FH_EDIT_NAME]);
        $this->edit           = !$this->insert;         
        
        // get the ID if it's an edit form           
        if($this->edit) {
            $this->_id = $_GET[FH_EDIT_NAME];
            if( !is_array($this->_id) ) {
            	$this->_id = array( $this->_id );
            }
        }
        
        // set our own error handler
        if(FH_DISPLAY_ERRORS) {
        	error_reporting( E_ALL );
            $this->_eh = set_error_handler('catchErrors');
        }
        
        // set the default mask
        $this->setMask( FH_DEFAULT_ROW_MASK );
        
        // check if the form is posted
        $this->_posted = ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST[$this->_name.'_submit']));
        
        // make a hidden field so we can identify the form
        $this->hiddenField($this->_name.'_submit', '1');
        
        // get the current page
        $this->_curPage = isset($_POST[$this->_name.'_page']) ? $_POST[$this->_name.'_page'] : 1;
        
        // check if gd is enabled
        ob_start();
        phpinfo( 8 );
        $module_info = ob_get_contents();
        ob_end_clean();
        $matches = array();
        if (preg_match("/\bgd\s+version\b[^\d\n\r]+?([\d\.]+)/i", $module_info,$matches)) {
        	$this->_gd = $matches[1];
        } else {
        	$this->_gd = 0;
        }
        
        // set the language...
        $this->setLanguage();        
    }  
    
    /******************/
    /***** FIELDS *****/  
    /******************/
    
    /**
     * FormHandler::textField()
     *
     * Public: creates a textfield on the form
     *
     * @param string $title: The title of the field
     * @param string $name: The name of the field
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param int $size: The size of the field
     * @param int $maxlength: The allowed max input of the field
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */
    function textField(
      $title, 
      $name, 
      $validator = null, 
      $size      = null, 
      $maxlength = null, 
      $extra     = null) 
    {
        // create the field
        $fld =& new TextField($this, $name);
        if(!is_null($validator)) $fld->setValidator($validator);
        if(!is_null($size))      $fld->setSize($size);
        if(!is_null($maxlength)) $fld->setMaxlength($maxlength);
        if(!is_null($extra))     $fld->setExtra($extra);
        
        // add the field to the fields array
        $this->_fields[$name] = array($title, &$fld);
    }   
    
    /**
     * FormHandler::passField()
     *
     * Public: Create a password field
     *
     * @param string $title: The title of the field
     * @param string $name: The name of the field
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param int $size: The size of the field
     * @param int $maxlength: The allowed max input of the field
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */
    function passField(
      $title, 
      $name, 
      $validator = null, 
      $size      = null, 
      $maxlength = null, 
      $extra     = null) 
    {
        // create the field
        $fld =& new PassField($this, $name);
        if(!is_null($validator)) $fld->setValidator($validator);
        if(!is_null($size))      $fld->setSize($size);
        if(!is_null($maxlength)) $fld->setMaxlength($maxlength);
        if(!is_null($extra))     $fld->setExtra($extra);
        
        // add the field to the fields array
        $this->_fields[$name] = array($title, &$fld);
    } 
    
    /**
     * FormHandler::hiddenField()
     *
     * Public: create a hidden field
     *
     * @param string $name: The name of the field
     * @param string $value: The value of the field     
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */
    function hiddenField(
      $name, 
      $value     = null, 
      $validator = null,
      $extra     = null)
    {
        // create new hidden field
        $fld =& new HiddenField($this, $name);
        if(!is_null($value) && !$this->isPosted() && !$this->edit)     
                                 $fld->setValue($value);
        if(!is_null($validator)) $fld->setValidator($validator);
        if(!is_null($extra))     $fld->setExtra($extra);
        
        // add the field to the fields array
        $this->_fields[$name] = array('__HIDDEN__',  &$fld);
    }
    
    /**
     * FormHandler::textArea()
     *
     * Public: Create a textarea on the form
     *
     * @param string $title: The title of the field
     * @param string $name: The name of the field
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param int $cols: How many cols (the width of the field)
     * @param int $rows: How many rows (the height of the field) 
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */ 
    function textArea(
      $title,
      $name,
      $validator = null,
      $cols      = null,
      $rows      = null, 
      $extra     = null)
    {
        // create new textarea
        $fld =& new TextArea($this, $name);
        if(!is_null($validator)) $fld->setValidator($validator);
        if(!is_null($cols))      $fld->setCols($cols);
        if(!is_null($rows))      $fld->setRows($rows);
        if(!is_null($extra))     $fld->setExtra($extra);
        
        // add the field to the fields array
        $this->_fields[$name] = array($title, &$fld);
    }     
    
    /**
     * FormHandler::selectField()
     *
     * Public: Create a selectField on the form
     *
     * @param string $title: The title of the field
     * @param string $name: The name of the field
     * @param array $options: The options used for the field
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param boolean $useArrayKeyAsValue: If the array key's are the values for the options in the field
     * @param int $size: The size of the field (how many options are displayed)
     * @param boolean $multiple: Should it be possible to select multiple options ? (Default: false)
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */ 
    function selectField(
      $title,
      $name,
      $options,
      $validator          = null,
      $useArrayKeyAsValue = null,
      $multiple           = null,
      $size               = null,      
      $extra              = null)
    {
        // options has to be an array
        if(!is_array($options)) {
            trigger_error(
              "You have to give an array as value with the selectfield '$name'",
              E_USER_WARNING
            );
            return;
        }
        
        // create new selectfield
        $fld =& new SelectField( $this, $name );
        $fld->setOptions($options);
        if(!is_null($validator))          $fld->setValidator( $validator );
        if(!is_null($useArrayKeyAsValue)) $fld->useArrayKeyAsValue( $useArrayKeyAsValue );
        if(!is_null($extra))              $fld->setExtra( $extra );
        if(!is_null($multiple))           $fld->setMultiple( $multiple );        
        if(!is_null($size)) {              
            $fld->setSize( $size );
        // if no size is set and multiple is enabled, set the size default to 4    
        } else if( !is_null($multiple) ) {
            $fld->setSize( 4 );
        }
        
        
        // add the field to the fields array
        $this->_fields[$name] = array( $title, &$fld );
    }
    
    
    /**
     * FormHandler::dbSelectField()
     *
     * Public: Create a selectField on the form with records loaded from a table
     *
     * @param string $title: The title of the field
     * @param string $name: The name of the field
     * @param string $table: The table where the records are retrieved from
     * @param mixed $fields: String or array with the field(s) which are retrieved from the table and put into the select field
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param int $size: The size of the field (how many options are displayed)
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */ 
    function dbSelectField( 
      $title, 
      $name, 
      $table, 
      $fields, 
      $validator          = null,
      $size               = null,
      $extra              = null) 
    {
        if( !is_object($this->_db) ) {
            trigger_error(
              'Error, you have to make use of the database option to make use of the field dbSelectField. '.
              'Use selectField() instead!', 
              E_USER_WARNING
            );
            return;
        } 
        
        // create new selectfield
        $fld =& new dbSelectField( $this, $name, $this->_db, $table, $fields );
        if(!is_null($validator))          $fld->setValidator($validator);
        if(!is_null($size))               $fld->setSize($size);
        if(!is_null($extra))              $fld->setExtra($extra);        
        
        // add the field to the fields array
        $this->_fields[$name] = array($title, &$fld);        
    }
    
    /**
     * FormHandler::checkBox()
     *
     * Public: Create a checkBox on the form
     *
     * @param string $title: The title of the field
     * @param string $name: The name of the field
     * @param array|string $value: The option(s) used for the field
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param boolean $useArrayKeyAsValue: If the array key's are the values for the options in the field
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @param string $glue: if more the 1 options are given, glue the fields together with this
     * @return void
     */ 
    function checkBox(
      $title,
      $name,
      $value              = 'on',
      $validator          = null,
      $useArrayKeyAsValue = null,
      $extra              = null,
      $glue               = null)
    {
        // create a new checkbox
        $fld =& new CheckBox($this, $name, $value);
        if(!is_null($validator))          $fld->setValidator($validator);
        if(!is_null($useArrayKeyAsValue)) $fld->useArrayKeyAsValue($useArrayKeyAsValue);
        if(!is_null($extra))              $fld->setExtra($extra);
        if(!is_null($glue))               $fld->setGlue($glue);
        
        // add the field to the fields array
        $this->_fields[$name] = array($title, &$fld);
    }
    
    /**
     * FormHandler::radioButton()
     *
     * Public: Create a radioButton on the form
     *
     * @param string $title: The title of the field
     * @param string $name: The name of the field
     * @param array $options: The options used for the field
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param boolean $useArrayKeyAsValue: If the array key's are the values for the options in the field
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @param string $glue: if more the 1 options are given, glue the fields together with this
     * @return void
     */
    function radioButton(
      $title,
      $name,
      $options,
      $validator          = null,
      $useArrayKeyAsValue = null,
      $extra              = null,
      $glue               = null)
    {
        // value has to be an array
        if(!is_array($options)) {
            trigger_error(
              "You have to give an array as value with the radiobutton '$name'",
              E_USER_WARNING
            );
            return;
        }
           
        // create a new checkbox
        $fld =& new RadioButton($this, $name, $options);
        if(!is_null($validator))          $fld->setValidator($validator);
        if(!is_null($useArrayKeyAsValue)) $fld->useArrayKeyAsValue($useArrayKeyAsValue);
        if(!is_null($extra))              $fld->setExtra($extra);
        if(!is_null($glue))               $fld->setGlue($glue);
        
        // add the field to the fields array
        $this->_fields[$name] = array($title, &$fld);
    }
    
    /**
     * FormHandler::uploadField()
     *
     * Public: Create a uploadField on the form
     *
     * @param string $title: The title of the field
     * @param string $name: The name of the field
     * @param array $config: The configuration used for the field
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @param string $alertOverwrite: Do we have to alert the user when he/she is going to overwrite a file?
     * @return void
     */
    function uploadField(
      $title,
      $name, 
      $config         = array(),
      $validator      = null,
      $extra          = null,
      $alertOverwrite = null) 
    {        
        $fld = new UploadField($this, $name, $config);
        if(!is_null($validator))      $fld->setValidator($validator);
        if(!is_null($extra))          $fld->setExtra($extra);
        if(!is_null($alertOverwrite)) $fld->setAlertOverwrite($alertOverwrite);
        
        // add the uploadfield to the fields array
        $this->_fields[$name] = array( $title, &$fld );
        
        // set that this form is using uploadfields
        $this->_upload[] = $name;
    }
    
    /**
     * FormHandler::listField()
     *
     * Public: Create a listField on the form
     *
     * @param string $title: The title of the field
     * @param string $name: The name of the field
     * @param array $options: The options used for the field
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param string $onTitle: The title used above the ON section of the field
     * @param string $offTitle: The title used above the OFF section of the field          
     * @param boolean $useArrayKeyAsValue: If the array key's are the values for the options in the field
     * @param int $size: The size of the field (how many options are displayed)
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */ 
    function listField(
      $title,
      $name,
      $options,
      $validator          = null,
      $useArrayKeyAsValue = null,
      $onTitle            = null,
      $offTitle           = null,
      $size               = null,
      $extra              = null)
    {
        // options has to be an array
        if(!is_array($options)) {
            trigger_error(
              "You have to give an array as value with the listfield '$name'",
              E_USER_WARNING
            );
            return;
        }
        
        // create a listfield
        $fld =& new ListField( $this, $name, $options );
        if(!is_null($validator))          $fld->setValidator( $validator );
        if(!is_null($useArrayKeyAsValue)) $fld->useArrayKeyAsValue( $useArrayKeyAsValue );
        if(!empty($size))                 $fld->setSize( $size );
        if(!is_null($extra))              $fld->setExtra( $extra ); 
        if(!empty($onTitle))			  $fld->setOnTitle( $onTitle );
        if(!empty($offTitle))             $fld->setOffTitle( $offTitle );	
        
        // add the listfield to the fields array
        $this->_fields[$name] = array($title, &$fld);
    } 
    
    /**
     * FormHandler::editor()
     *
     * Public: Create a editor on the form
     *
     * @param string $title: The title of the field
     * @param string $name: The name of the field
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param string $path: Path on the server where we have to upload the files     
     * @param string $toolbar: The toolbar we have to use
     * @param string $skin: The skin to use
     * @param int $width: The width of the field         
     * @param int $height: The height of the field         
     * @param boolean $useArrayKeyAsValue: If the array key's are the values for the options in the field
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */ 
    function editor(
      $title,
      $name, 
      $validator = null,
      $path      = null,
      $toolbar   = null,
      $skin      = null,
      $width     = null,
      $height    = null,
      $extra     = null)
    {
        // create a new editor
        $fld =& new Editor($this, $name);
        if(!is_null($validator)) $fld->setValidator($validator);
        if(!is_null($path))      $fld->setServerPath($path); 
        if(!empty($toolbar))     $fld->setToolbar($toolbar);
        if(!empty($skin))        $fld->setSkin($skin);
        if(!is_null($width))     $fld->setWidth($width);
        if(!is_null($height))    $fld->setHeight($height);
        if(!is_null($extra))     $fld->setExtra($extra); 
        
        // add the editor to the fields array
        $this->_fields[$name] = array($title, &$fld);
    } 
    
    /**
     * FormHandler::dateField()
     *
     * Public: Create a dateField on the form
     *
     * @param string $title: The title of the field
     * @param string $name: The name of the field
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param boolean $required: If the field is required to fill in or can the user leave it blank
     * @param string $display: How do we have to display the fields? These can be used: d, m and y.
     * @param string $interval: The interval between the current year and the years to start/stop.Default the years are beginning at 90 yeas from the current. It is also possible to have years in the future. This is done like this: "90:10" (10 years in the future).
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */
    function dateField(
      $title,
      $name,
      $validator = null,
      $required  = null,
      $display   = null,
      $interval  = null,      
      $extra     = null)
    {
        // create a new datefield
        $fld =& new DateField($this, $name);
        if(!is_null($validator)) $fld->setValidator($validator);
        if(!is_null($required))  $fld->setRequired($required);
        if(!empty($display))     $fld->setDisplay($display);
        if(!is_null($interval))  $fld->setInterval($interval);        
        if(!is_null($extra))     $fld->setExtra($extra);        
        
        // add the datefield to the fields array
        $this->_fields[$name] = array($title, &$fld);
        
        $this->_date[] = $name;
    }  
    
    /**
     * FormHandler::timeField()
     *
     * Public: Create a timeField on the form
     *
     * @param string $title: The title of the field
     * @param string $name: The name of the field
     * @param string $validator: The validator which should be used to validate the value of the field
     * @param int $format: 12 or 24. Which should we use?
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */
    function timeField(
      $title,
      $name,
      $validator = null,
      $required  = null,
      $format    = null,
      $extra     = null)
    {
        // create a new timefield
        $fld =& new TimeField($this, $name);
        if(!is_null($validator)) $fld->setValidator($validator);
        if(!is_null($required))  $fld->setRequired($required);
        if(!is_null($format))    $fld->setHourFormat($format); 
        if(!is_null($extra))     $fld->setExtra($extra);
        
        // add the timefield to the fields array
        $this->_fields[$name] = array($title, &$fld);
    }
      
    
    /*****************/
    /**** BUTTONS ****/
    /*****************/
    
    /**
     * FormHandler::button()
     *
     * Public: Create a button on the form
     *
     * @param string $caption: The caption of the button
     * @param string $name: The name of the button
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */
    function button( $caption, $name = null, $extra = null) {
        // get new button name if none is given
        if(is_null($name)) {
            $name = $this->_getNewButtonName();
        }
             
        // create new submitbutton
        $btn =& new Button( $this, $name );        
        $btn->setCaption( $caption );
        if(!is_null($extra)) $btn->setExtra($extra);
        
        // add the button to the fields array
        $this->_fields[$name] = array('__BUTTON__', &$btn);
    }
    
    /**
     * FormHandler::submitButton()
     *
     * Public: Create a submitButton on the form
     *
     * @param string $caption: The caption of the button
     * @param string $name: The name of the button
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */
    function submitButton( $caption = null, $name = null, $extra = null, $disableOnSubmit = null) {
        // get new button name if none is given
        if(is_null($name)) {
            $name = $this->_getNewButtonName();
        }
         
        // create new submitbutton
        $btn =& new SubmitButton($this, $name);
        if(!is_null($caption))         $btn->setCaption($caption);         
        if(!is_null($extra))           $btn->setExtra($extra);
        if(!is_null($disableOnSubmit)) $btn->disableOnSubmit( $disableOnSubmit );
        
        // add the button to the fields array
        $this->_fields[$name] = array('__BUTTON__', &$btn);
    }
    
    /**
     * FormHandler::imageButton()
     *
     * Public: Create a imageButton on the form
     *
     * @param string $image: The image URL which should be a button
     * @param string $name: The name of the button
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */
    function imageButton( $image, $name = null, $extra = null, $disableOnSubmit = null) {
        // get new button name if none is given
        if(is_null($name)) {
            $name = $this->_getNewButtonName();
        }
         
        // create the image button
        $btn =& new ImageButton( $this, $name, $image );
        if(!is_null($extra))           $btn->setExtra( $extra );
        if(!is_null($disableOnSubmit)) $btn->disableOnSubmit( $disableOnSubmit );
        
        // add the button on the form
        $this->_fields[$name] = array('__BUTTON__', &$btn);
    }  
   
    /**
     * FormHandler::resetButton()
     *
     * Public: Create a resetButton on the form
     *
     * @param string $caption: The caption of the button
     * @param string $name: The name of the button
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */
    function resetButton($caption = null, $name = null, $extra = null) {        
        // get new button name if none given
        if(is_null($name)) {
            $name = $this->_getNewButtonName();
        }
         
        // create new resetbutton
        $btn =& new ResetButton($this, $name);        
        if(!is_null($caption)) $btn->setCaption($caption);
        if(!is_null($extra))   $btn->setExtra($extra);
        
        // add the button to the fields array
        $this->_fields[$name] = array('__BUTTON__', &$btn);
    } 
    
    /**
     * FormHandler::cancelButton()
     *
     * Public: Create a cancelButton on the form
     *
     * @param string $caption: The caption of the button
     * @param string $url: The URL to go to when the button is clicked
     * @param string $name: The name of the button
     * @param string $extra: CSS, Javascript or other which are inserted into the HTML tag
     * @return void
     */
    function cancelButton($caption = null, $url = 'history.back(-1)', $name = null, $extra = null) {
        // get new button name if none given
        if(is_null($name)) {
            $name = $this->_getNewButtonName();
        }
        
        // where to go when the button is clicked...
        if(ereg('history', $url)) {
            $extra .= ' onclick="'.$url.'"';
        } else {
            $extra .= ' onclick="document.location.href=\''.$url.'\'"';
        }
        
        // if no caption is given, get our own caption
        if(is_null($caption)) {
            $caption = $this->_text( 28 );
        }
        
        // create new button
        $btn =& new Button($this, $name);        
        $btn->setCaption($caption);
        if(!is_null($extra))   $btn->setExtra($extra);
        
        // add the button to the fields array
        $this->_fields[$name] = array('__BUTTON__', &$btn);
    }
    
    /*******************/
    /****** STYLE ******/
    /*******************/
    
    /**
     * FormHandler::addHTML()
     *
     * Public: Add some HTML to the form
     *
     * @param string $html: The HTML we have to add to the form
     * @return void
     */
    function addHTML( $html ) {
    	static $iCounter = 1;
        $this->_fields['html'.$iCounter++] = array('__HTML__', $html);
    }
    
    /**
     * FormHandler::addLine()
     *
     * Public: Add a new row to the form. 
     *
     * @param string $data: Possible data to set into the row (line)
     * @return void
     */
    function addLine( $text = null ) {
    	static $iCounter = 1;
        $this->_fields['line'.$iCounter++] = array('__LINE__', 
          sprintf( FH_LINE_MASK, $text )
        );
    }  
    
    /**
     * FormHandler::borderStart()
     *
     * Public: Begin a new fieldset 
     *
     * @param string $caption: The caption of the fieldset
     * @param string $name: The name of the fieldset
     * @return void
     */
    function borderStart( $caption = null, $name = null ) {
        static $i = 1;
        
        if( is_null( $name ) ) {
		    $name = 'fieldset'.$i++;
        }
        
        $this->_fields[] = array('__FIELDSET__', array($name, $caption));
    }
    
	/**
     * FormHandler::borderStop()
     *
     * Public: Stops a fieldset 
     *
     * @return void
     */
    function borderStop() {
        $this->_fields[] = array('__FIELDSET-END__', true);
    }
    
    /**
     * FormHandler::setMask()
     *
     * Public: Stops a fieldset 
     *
     * @param string $mask: The mask we have to use
     * @param int|bool $repeat: If we have to repeat the mask. When a integer is given, it will be countdown
     * @return void
     */
    function setMask( $mask = null, $repeat = true, $setTable = null ) {
		if(is_null($mask)) {
			$mask = FH_DEFAULT_ROW_MASK;
		} else if(file_exists($mask)) {
			if(is_readable($mask)) {
				if( function_exists('file_get_contents')) {
            		$mask = file_get_contents( $mask );
				} else {
					$handle = fopen($mask, 'rb');
					$mask = '';
					while (!feof($handle)) {
					  $mask .= fread($handle, 4096);
					}
					fclose($handle);
				}
			} else {
				trigger_error('Could not read from template '.$mask, E_USER_WARNING );
			}			
        }
        
        // do we have to set a table arround the fields?
        if( !is_null( $setTable ) ) { 
        	$this->_table = $setTable;
        }
        
        $this->_fields[] = array( '__MASK__', array( $mask, $repeat ) );
    }
        
    /****************************/
    /**** DATA MANIPULATION *****/
    /****************************/
    
    
    /**
     * FormHandler::value()
     *
     * Public: get the value of the requested field
     *
     * @param string $field: The field which value we have to return
     * @return void
     */
    function value( $field ) {
        if(!_global) global $_POST;
        
        // is it a field?
        if( isset( $this->_fields[$field] ) ) {
            return $this->_fields[$field][1]->getValue();
        // is it a field of the database ?
        } else if( array_key_exists($field, $this->_dbData) ) {
            return $this->_dbData[$field];
        // is it a added value ?    
        } else if( isset($this->_add[$field]) ) {
            return $this->_add[$field];
            
        // BELOW is beta!!    
        //} else if( isset( $_POST[$field] ) ) {
        //    return $_POST[$field];
        } else {
            //trigger_error("Try to get the value of an unknown field '$field'!");  
            return null;
        }
    }
    
    /**
     * FormHandler::setValue()
     *
     * Public: set the value of the spicified field 
     *
     * @param string $field: The field which value we have to set
     * @param string $value: The value we have to set
     * @param boolean $overwriteCurrentValue: Do we have to overwrite the current value of the field (posted or db-loaded values)     
     * @return void
     */
    function setValue($sField, $sValue, $bOverwriteCurrentValue = false) {
        
    	// save the data (we realy set the value in the method flush() )
    	$this->_buffer[$sField] = array($bOverwriteCurrentValue, $sValue);	                    
    }
    
    /**
     * FormHandler::addValue()
     *
     * Public: add a value to the data array which is going 
     * to be saved/used in the oncorrect & onsaved functions 
     *
     * @param string $field: The field which value we have to set
     * @param string $value: The value we have to set
     * @param boolean $sqlFunction: Is the value an SQL function ?
     * @return void
     */ 
    function addValue($field, $value, $sqlFunction = false) {
        $this->_add[$field] = $value;                  
        
        // add to the sql list if the value is a sql function
        if($sqlFunction) {
            $this->_sql[] = $field;
        }
    }
    
    /***********************/
    /** DATABASE OPTIONS ***/
    /***********************/
    
    /**
     * FormHandler::dbInfo()
     *
     * Public: set the DBInfo. You can give the primary key's after the table name, like this:
     *
     * dbInfo( 'DB', table', 'mysql', 'prKey1');
     *
     * or
     *
     * dbInfo( 'DB', table', 'mysql', 'prKey1', 'prKey2', 'etc...');
     *
     * When you dont specify a primary key, FormHandler will try to 
     * retrieve it automaticly. When this failes, it will trigger an error.
     *
     * @param string $db: the db which we are using
     * @param string $table: the table where we have to save the data
     * @param string $type: the type of db which is used
     * @return
     */
    function dbInfo( $db, $table, $type = null ) {        
    	
    	// if no db-type is given, set the default
    	if(is_null($type)) {
    	   $type = FH_DEFAULT_DB_TYPE;
    	}
         
        // get prKey(s) if given
        $numArgs = func_num_args();
        if($numArgs > 3) {
            $keys = array();
            for($i = 3; $i < $numArgs; $i ++ ) {
                $keys[] = func_get_arg($i);
            }                
        } else {
            $keys = null;
        }
        
        $type = strtolower( $type );
        if(class_exists( 'fh_'.$type)) {
            // create a new database object
            eval('$this->_db = new fh_'.$type.'( $db, $table, $keys );');
        } else {
            trigger_error(
              "Error, could not find database handler for database type '".$type."'",
              E_USER_WARNING
            );
            return;
        }
        
        // try to fetch the database data allready
        if( $this->edit && $this->_db->isConnected() ) {
            $this->_loadDbData();
        }
    }
    
    /**
     * FormHandler::dbConnect()
     *
     * Public: Connect to the database (only needed when there is no connection yet!)
     *
     * @param string $username: the username used to login
     * @param string $password: the password used to login
     * @return void
     */
    function dbConnect($host = null, $username = '', $password = '') {
        if(is_object($this->_db)) {
            if(is_null($host)) {
                $host = FH_DEFAULT_DB_HOST;
            }
            
            if(!$this->_db->connect( $host, $username, $password )) {
            	trigger_error( 'Error, connecting failed: '.$this->_db->getError(), E_USER_WARNING );
            	return;
            }
        } else {
            trigger_error('No database object available! Make sure you call DBInfo() first!', E_USER_WARNING);
            $this->_db = null;
            return;            
        }
        
        // load the database values only on a edit form...
        if($this->edit && ( !is_array($this->_dbData) || sizeof($this->_dbData) == 0)) {
            $this->_loadDbData();
        }
    }   
    
    
    /*******************/
    /***** GENERAL *****/
    /*******************/
    
    /**
     * FormHandler::getTitle()
     *
     * Public: return the title of the given field name
     *
     * @param string $sField: The fieldname where to retrieve the title from
     * @return string
     */
    function getTitle( $sField ) {
    	static $aTitleFields = array(
		  'textfield', 'listfield', 'selectfield',
		  'radiobutton', 'checkbox', 'editor',
		  'passfield', 'textarea', 'timefield',
		  'datefield', 'uploadfield'
		);
		
    	if(isset($this->_fields[$sField]) && is_object( $this->_fields[$sField][1] )) 
    	{    		
    		$sClass = strtolower( get_class( $this->_fields[$sField][1] ) );
    		  
    		if( in_array($sClass, $aTitleFields) ) {
    			return $this->_fields[$sField][0];
    		} else {
    			trigger_error(
    			  'Error, cannot retrieve title of this kind of field: '.$sClass, 
    			  E_USER_WARNING 
    			);
    		}    			
    	} else {
    		trigger_error(
    		  'Could not find field "'.$sField.'"',
    		  E_USER_WARNING
    		);  
    	}
    	return null;
    }   
    
    /**
     * FormHandler::fieldExists()
     *
     * Public: check if the field exists in the form
     *
     * @param string $sField: The field to check if it exists in the form or not
     * @return boolean
     */
    function fieldExists( $sField ) {
    	return array_key_exists( $sField, $this->_fields);
    } 
    
    /**
     * FormHandler::getFormName()
     *
     * Public: return the name of the form
     *
     * @return string: the name of the form
     */
    function getFormName() {
    	return $this->_name;
    }
    
    /**
     * FormHandler::mailForm()
     *
     * Public: send the values of the form to the mail adress
     *
     * @param string $to: The e-mail adres to send the data to
     * @param string $subject: The subject of the mail 
     * @param string $headers: The headers which should be used by sending the mail
     * @return void
     */
    function mailForm( $to, $subject = null, $headers = null ) {
        if(is_null($subject)) {
            $subject = "New form mailed to you by FormHandler";
        }
        if(is_null($headers)) {
            $headers = "From: FormHandler <info@formhandler.nl>";
        }
        
        // add the mail items 
        $this->_mail[] = array( $to, $subject, $headers );
    }     
    
    /**
     * FormHandler::newPage()
     *
     * Public: Put the following fields on a seperate page
     *
     * @return void
     */
    function newPage() {
        $this->_fields[] = array( '__PAGE__', $this->_pageCounter++);
    }
    
    /**
     * FormHandler::setTabIndex()
     *
     * Public: set the tab index for the fields
     *
     * @param mixed $mTabs: array of comma seperated string with the field names.
     * When an array is given the array index will set as tabindex
     * @return void
     */
    function setTabIndex( $mTabs ) {
        if(is_string( $mTabs ) ) {
            $aTabs = explode(',', $mTabs);
            array_unshift($aTabs, '');
        } else if( is_array( $mTabs )) {
            // is set element 0, then move all elements 
            // (0 is not a valid tabindex, it starts with 1)
            if( isset( $mTabs[0])) {                
                ksort( $mTabs );
                $aTabs = array();
                
                foreach( $mTabs as $key => $value ) {
                    while( array_key_exists( $key, $aTabs) || $key <= 0) $key++;
                    $aTabs[$key] = $value;                    
                }
            } else {
                $aTabs = $mTabs;
            }
        }
        
        // array with tabs set ?
        if( isset( $aTabs ) ) { 
            foreach($aTabs as $key => $value ) {
                if(!empty($value)) {
                    $aTabs[$key] = trim($value);
                } else {
                    unset($aTabs);
                }
            }
            $this->_tabindexes = $this->_tabindexes + $aTabs ;
        }
    }

    /**
     * FormHandler::setLanguage()
     *
     * Public: Set the language we should use for error messages etc.
     * If no language is given, try to get the language defined by the visitors browser. 
     *
     * @param string $language: The language we should use
     * @return void
     */
    function setLanguage( $sLanguage = null ) {
        if(!_global) global $_SERVER;
        
    	if(is_null($sLanguage)) {
    		
    		// auto detect language ?
    		$bSet = false;
    		if( FH_AUTO_DETECT_LANGUAGE ) {
	    		// get all accepted languages by the browser
		        $aLang = array();
		        if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
		        	foreach(explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $sValue) {
		            	if(strpos($sValue, ';') !== false) {
		                	list($sValue, ) = split(';', $sValue);
		            	}
			            if(strpos($sValue, '-') !== false) {
			                list($sValue, ) = split('-', $sValue);
			            }
			            $aLang[] = $sValue;
			        }
		        }
		        
		        // set the language which we have too
		        while (list (, $l) = each ($aLang)) {
		        	if(file_exists(FH_INCLUDE_DIR.'language/'.strtolower($l).'.php')) {
		        		$this->setLanguage( $l );
		        		$bSet = true;
		        		break;
		        	}
		        }
    		}	
		       
    		// no language is set yet.. set the default language 
			if(!$bSet) {
				$this->setLanguage( FH_DEFAULT_LANGUAGE );
			}    		
    	} else {    	
	        $this->_lang = strtolower($sLanguage);	        
	        	        
	        if(file_exists(FH_INCLUDE_DIR.'language/'.$this->_lang.'.php')) {
	            include(FH_INCLUDE_DIR.'language/'.$this->_lang.'.php');
	            // load the array from the text file
	            $this->_text =& $fh_lang;
	        } else {
	            trigger_error('Unknown language: '.$this->_lang.'. Could not find file '.FH_INCLUDE_DIR.'language/'.$this->_lang.'.php!', E_USER_ERROR);
	        }
    	}
    }
    
    /**
     * FormHandler::catchErrors()
     *
     * Public: get the errors occoured in the form
     *
     * @param boolean $display: If we still have to display the errors in the form (default this is disabled)
     * @return array of errorsor an empty array if none occoured
     */
    function catchErrors( $display = false ) {
        
        // only return the errors when the form is posted!
        if($this->isPosted()) {
            $this->_displayErrors = $display;
            
            $errors = array();
            reset( $this->_fields );
            while(list($field, $obj) = each($this->_fields)) {
                if(method_exists($obj[1], 'getError') && method_exists($obj[1], 'isValid')) {
                    $obj[1]->isValid();
                    $err = strip_tags($obj[1]->getError());
                    if(strlen($err) > 0) {
                        $errors[$field] = $err;
                    }
                }
            }
            return $errors;
        } 
        return array();
    }
    
    /**
     * FormHandler::resizeImage()
     *
     * Public: resize the image uploaded in the given field
     *
     * @param string $field: The field where the image is uploaded
     * @param string $saveAs: How the image has to be saved (if not given, the original wil be overwritten)
     * @param int $maxWidth: The maximum width of the resized image
     * @param int $maxHeight: the maximum height of the resized image
     * @param int $quality: the quality of the resized image     
     * @return void
     */
    function resizeImage( $field, $saveAs = null, $maxWidth = null, $maxHeight = null, $quality = null )  {
        // is gd enabled ?
        if( ! $this->_gd ) {
            trigger_error( 'Error! To use the function resizeImage you have to enable GD Libary!', E_USER_WARNING);
            return;
        }
                
        if(is_null($maxWidth))  $maxWidth  = FH_DEFAULT_RESIZE_WIDTH;
        if(is_null($maxHeight)) $maxHeight = $maxWidth;

        $this->_convert[$field]['resize'][] = array( $saveAs, $maxWidth, $maxHeight, $quality );        
    }
     
    /**
     * FormHandler::mergeImage()
     *
     * Public: merge a image uploaded in the given field with another image
     *
     * @param string $field: The field where the image is uploaded
     * @param string $merge: The image which we should merge
     * @param int $align: The align of the merge image (eg: left, center, right)
     * @param int $valign: The vertical align of the merge image( eg: top, middle, bottom)   
     * @return void
     */
    function mergeImage( $field, $merge, $align = 'center', $valign = 'bottom', $transparantColor = null ) {
        // is gd enabled ?
        if( ! $this->_gd ) {
            trigger_error( 'Error! To use the function mergeImage you have to enable GD Libary!', E_USER_WARNING);
            return;
        }
        
        $this->_convert[$field]['merge'][] = array( $merge, $align, $valign, $transparantColor );
    }
    
    /**
     * FormHandler::setFocus()
     *
     * Public: set the focus to a sepcific field
     *
     * @param string $field: The field which should get the focus
     * @return boolean: true if the focus could be set, false if not
     */
    function setFocus($field) {
        if(!isset($this->_fields[$field])) {
            trigger_error('Could net set focus to unknown field '.$field);
            return;
        } 
        
        // some fields have gotten other names... change it.
        switch ( get_class($this->_fields[$field][1])) {
            case 'datefield':
              $field = $field.'_day';
              break;
            case 'listfield':
              $field = $field.'_ListOn';
              break;
            case 'timefield':
              $field = $field.'_hour';
              break;
            case 'editor':
            case 'radiobutton':
            case 'checkbox':
            case 'hiddenfield':
            // buttons cant have the focus
            case 'submitbutton':
            case 'resetbutton':
            case 'imagebutton':
            case 'button':
              $field = null;
              break;  
        }
        
        $this->_focus = $field;
        return !is_null($field);
    }
    
    /**
     * FormHandler::checkPassword()
     *
     * Public: Preform a password check on 2 password fields:
     * - both values are the same
     * - the values are longer then a minimum length (configured in the config file)
     * - on an add-form, the fields are required
     * - on an edit-form, the fields can be left empty, and the old password will stay (no changes will take place)
     *
     * @param string $field1: The first password field we should check
     * @param string $field2: The second password field we should check
     * @return void
     */
    function checkPassword($field1, $field2, $setEditMsg = true) {
        
        if(!isset($this->_fields[$field1]) || !isset($this->_fields[$field2]) || 
            strtolower( get_class( $this->_fields[$field1][1] ) ) != 'passfield' ||
            strtolower( get_class( $this->_fields[$field2][1] ) ) != 'passfield')
        {
            trigger_error('Error: unknown field used in checkPassword!');
            return;
        }
        
        // add some text to nitify the user that he only has to enter his 
        // password when he wants to change it
        if($this->edit && $setEditMsg) {
            $this->_fields[$field1][1]->setPre(
              $this->_text( 25 )
            );            
        } 
            
        // is the form posted
        if($this->isPosted()) {
            // let passfield 1 check if it matches passfield 2
            $this->_fields[$field1][1]->checkPassword( $this->_fields[$field2][1] );
        }
    }
    
    /**
     * FormHandler::onCorrect()
     *
     * Public: Set the function which has to be called when the form is correct
     *
     * @param string $callback: The name of the function
     * @return void
     */
    function onCorrect($callback) {
    	if(!is_array($callback)) {
    		if(function_exists($callback)) {
        	    $this->_onCorrect = $callback;
    		} else {
    			trigger_error("Error, the onCorrect function '".$callback."' does not exists!", E_USER_ERROR);
    		}
    	// check if the method exists		
    	} else {
    		if(method_exists($callback[0], $callback[1])) {
    			$this->_onCorrect =& $callback;
    		} else {
    			trigger_error(
    			  "Error, the onCorrect method '".$callback[1]."' does not exists ".
    			  "in object '".get_class($callback[0])."'!", 
    			  E_USER_ERROR);
    		}
    	}    			
    }
    
    /**
     * FormHandler::onSaved()
     *
     * Public: Set the function  which has to be called when the form data is saved in the database
     *
     * @param string $callback: The name of the function
     * @return void
     */
    function onSaved($callback) {
    	if(!is_array($callback)) {
    		if(function_exists($callback)) {
        	    $this->_onSaved = $callback;
    		} else {
    			trigger_error("Error, the onSaved function '".$callback."' does not exists!", E_USER_ERROR);
    		}
    	// check if the method exists		
    	} else {
    		if(method_exists($callback[0], $callback[1])) {
    			$this->_onSaved =& $callback;
    		} else {
    			trigger_error(
    			  "Error, the onSaved method '".$callback[1]."' does not exists ".
    			  "in object '".get_class($callback[0])."'!", 
    			  E_USER_ERROR);
    		}
    	}  
    }
    
    /**
     * FormHandler::isPosted()
     *
     * Public: If the form is posted
     *
     * @return boolean: if the form is posted or not
     */
    function isPosted() {
        return $this->_posted;
    }
    
    /**
     * FormHandler::isCorrect()
     *
     * Public: return if the form is filled correctly (for the fields which are set!)
     *
     * @return boolean: the form values valid or not
     */
    function isCorrect() {
    	// walk each field to check if they are valid
        $result = true;
        reset( $this->_fields );
        while( list( $id, $data ) = each($this->_fields) ) { 
            // check if the fields are valid
            if(method_exists($this->_fields[$id][1], 'isvalid') && !$this->_fields[$id][1]->isValid()) {
                if(is_null($this->_focus)) {
                    $this->setFocus($id);
                }
                $result = false;
            
            // if multiple pages are used, only make sure that 
            // all pages untill the current page are correct    
            } else if($data[0] == '__PAGE__') { 
                if( $this->_curPage == $data[1]) {
                    break;
                }
            } 
        }
        
        // only check for unique fields if the rest of the field is correct and 
        // the database option is used... 
        if( $result && isset($this->_db) && is_object($this->_db)) {
            $db =& $this->_db;              
            
            $unique = $db->fetchUniqueFields();
            
            // any unique fields found ?
            if(sizeof( $unique ) > 0) {
                
                // walk all unique fields
                foreach( $unique as $key => $field) {
                    // does the unique field exists
                    if( $this->fieldExists($field) ) {
                        // get the form-value of the field
                        $value = $db->escapeString( $this->_fields[$field][1]->getValue() );
                        
                        // quotes around the value ?
                        if(preg_match("/^-?([0-9]*\.?[0-9]+)$/", $value)) {
                            if( $db->_quoteNumbers ) {                         
                                $value = "'".$value."'";
                            }
                        } else {
                            $value = "'".$value."'";
                        }
                        
                        // check if the entry is double
                        $db->Query(
                          'SELECT COUNT('.$field.') AS num FROM '.$db->getTable().
                          ' WHERE '.$field." = ".$value 
                        );
                        
                        // if so, set the error message and return false;
                        $row = $db->getRecord();
                        if($row['num'] > 0) {
                            // set the error message
                            $this->_fields[$field][1]->_sError = 
                              sprintf( 
                                $this->_text(35), 
                                $this->_fields[$field][1]->getValue()
                              )
                            ;
                            
                            // return false (the form is not correct)
                            $result = false;
                        }                        
                    } 
                }                
            }   
        } 
        
        return $result;
    }
    
    /**
     * FormHandler::flush()
     *
     * Public: prints or returns the form
     *
     * @return string: the form or null when the form should be printed 
     */
    function flush( $return = false ) {    
        
        // when the form is not posted or the form is not valid
        if( !$this->_posted || !$this->isCorrect() ) {
            // set the values set by the user
        	foreach($this->_buffer as $sField => $aInf) {
        		if(! isset( $this->_fields[$sField] ) ) {
        			trigger_error('Value set of unknown field '.$sField, E_USER_WARNING );
        		} else {
        			list( $bOverwrite, $sValue ) = $aInf;
        			        			
        			// if the field does not exists in the database 
            	    if($bOverwrite || (!isset($this->_dbData[$sField]) && !$this->isPosted() )) {
            	    	$this->_fields[$sField][1]->setValue( $sValue );
            	    }
            	}         	
            } 
        
            // get the form
            $form = $this->_getForm();
            
        // when the form is not totaly completed yet (multiple pages)
        } else if( $this->_curPage < $this->_pageCounter ) {  
            // upload and convert uploads
            $this->_handleUploads();
                                  
            // get the next form
            $form = $this->_getForm( $this->_curPage + 1 );
            
        // when the form is valid
        } else {            
            // upload and convert uploads
            $this->_handleUploads();
            
            // generate the data array
            $data = array();
            foreach($this->_fields as $name => $fld) {
                if(method_exists($fld[1], 'getValue') && $name != $this->_name.'_submit') {
                    $data[$name] = $fld[1]->getValue();
                }
            }
            
            // add the user added data to the array
            $data = array_merge( $data, $this->_add );
            
            // do whe have to mail the form data?
            if( sizeof($this->_mail) ) {
                $mailer = new fhMailer( $this->_mail );
                
                // add the attachements
                foreach( $this->_upload as $field ) {
                    if( $this->_fields[$field][1]->isUploaded() ) {
                        $mailer->attachFile(
                          $this->_fields[$field][1]->getSavePath().
                          $this->_fields[$field][1]->getValue()
                        );
                    }
                }
                // mail 
                $mailer->sendMail( $data );                
            }
            
            // call the users oncorrect function
            if(!empty($this->_onCorrect)) {
                if(is_array($this->_onCorrect)) {
                    $hideForm = call_user_method($this->_onCorrect[1], $this->_onCorrect[0], $data);
                } else {
                    $hideForm = call_user_func($this->_onCorrect, $data);
                }
            }
            
            // add the user added data again to the array (could have been changed!)
            $data = array_merge( $data, $this->_add );
            
            // if the db option is used
            if( !is_null($this->_db)) {
                
                // get the data which should be saved
                foreach( $data as $field => $value ) {
                    // do we have to save the field ?
                    if(!in_array($field, $this->_dontSave)) {// || $value != '') {
                        
                        // Is the value ''  and is it a date or timefield? Then save NULL
                        // This is needed when the fields are NOT required
                        if($value == '' && isset($this->_fields[$field])) {
                            $class = strtolower( get_class( $this->_fields[$field][1] ) );
                            if($class == 'datefield' || $class == 'timefield') {
                                $value = 'NULL';
                                $this->_sql[] = $field;
                            } 
                        }
                        
                        $save[$field] = $value;
                    }
                } 
                
                // change the date's to the right format
                foreach( $this->_date as $field ) {
                    if($save[$field] != 'NULL' && preg_match('/[0-9]{1,2}-[0-9]{1,2}-[0-9]{4}/', $save[$field])) {
                        $save[$field] = $this->_db->getDbDate( $save[$field] );
                        $this->_sql[] = $field;
                    }
                }
                
                // save the data
                $id = $this->_db->saveData( $save, $this->_sql, $this->edit, $this->_id );
                
                // get the id
                if(is_array($id) && sizeof($id) == 1) {
                    $id = $id[0];
                }
                
                // call the onsaved function
                if(!is_null($this->_onSaved)) {
                    if(is_array($this->_onSaved)) {
                        $hideForm = call_user_method($this->_onSaved[1], $this->_onSaved[0], $data);
                    } else {
                        $hideForm = call_user_func($this->_onSaved, $id, $data);
                    }
                }
            }                
            
            // display the form again if wanted..
            if(isset($hideForm) && $hideForm === false) {
                $form = $this->_getForm();
            }  else if(is_string($hideForm)) {
                $form = $hideForm;
            } else {
                $form = '';
            }           
        }
        
        // return or print the form
        if($return) {
            return $form;
        } else {
            echo $form;
            return null;
        }
    }
    
    /*******************/
    /**** PRIVATE!! ****/
    /*******************/
    
    /**
     * FormHandler::_getNewButtonName()
     *
     * Private static: when no button name is given, get a unique button name
     *
     * @return string: the new button name
     */
    function _getNewButtonName() {
        static $counter = 1;
        
        return 'button'.$counter++;
    }
    
    /**
     * FormHandler::_loadMask()
     *
     * Private: load the fields into the mask
     *
     * @param array $data: contains the title and the field object
     * @param string $mask: the mask which should be used
     * @param string $name: the name of the field
     * @return string: the filled mask, or null when the mask isn't filled completly
     */
    function _loadMask( $data, $mask, $name ) {
		static $result = null;
		
		$title =& $data[0];
		$obj   =& $data[1];
		
		if($title == '__BUTTON__') {
		    $title = '';
		} 

		// search and replace values
		$search = array(
		  '/%field%/ie',
		  '/%error%/ie',
		  '/%title%/i', 	
		  '/%seperator%/ie'
		);

		$replace = array(
		  'str_replace("%", "____FH-percent____", '.
		  '(method_exists($obj, "getField")) ? $obj->getField() : '.
		  '((method_exists($obj, "getButton")) ? $obj->getButton() : ""))',
		  '($this->_displayErrors && method_exists($obj, "getError"))?$obj->getError():""',
		  $title,
		  '!strlen($title)?"":":"'
		);
		
		// for catching the errors
		if(!is_null($name) && method_exists($obj, 'getError')) {
		    $this->errors[$name] = $obj->getError();
		}

		// return a possible half filled mask if requested
		if( is_null($obj) && is_null($mask))
		    return preg_replace ( $search, array_fill(0, 4, ''), $result );

		// is the last mask finished? (if not, load it)
		if( !is_null($result) ) {
		    $mask = $result;
		}
		
		// replace the values
        $mask = preg_replace ( $search, $replace, $mask, 1 );

        // is the mask completly filled?
        $matches = array();
        preg_match_all('/(%field%|%error%|%title%|%seperator%)/i', $mask, $matches);
        if( isset($matches[1]) && count($matches[1]) > 0) {			
			$result = $mask;
			return null;
		} else {
			$result = null;
			return str_replace('____FH-percent____', '%', $mask);
		}
    }
    
    /**
     * FormHandler::_text()
     *
     * Private: return the given text in the correct language
     *
     * @param int $index: the index of the text in the textfile
     * @return string: the text in the correct language
     */
    function _text( $iIndex ) {
        
        // is a language set?
        if(!is_array($this->_text)) {
            trigger_error('No language file set!', E_USER_ERROR);
            return false;
        } else if(!array_key_exists($iIndex, $this->_text)) {
            trigger_error('Unknown index '.$iIndex.' to get language string!', E_USER_NOTICE);
            return '';
        }
        
        return $this->_text[$iIndex];        
    } 
    
    /**
     * FormHandler::_registerFileName()
     *
     * Private: Register the filenames which upload fields are using for there 
     * uploaded file so that other upload fields cannot use these.
     *
     * @param string $sFilename: the filename to register
     * @param string $sField: the field who is registering the file
     * @return bool: false if the filename is allready registered, true otherwise
     */
    function _registerFileName($sFilename, $sField) {
        static $aFilenames = array();
        
        if(isset($aFilenames[$sFilename]) && $aFilenames[$sFilename] != $sField) {
            return false;
        } 
        
        $aFilenames[$sFilename] = $sField;
        return true;
    }   
    
    /**
     * FormHandler::_loadDbData()
     *
     * Public: load the data when it's a edit form
     *
     * @return void
     */
    function _loadDbData() {
        $this->_dbData = $this->_db->loadData( $this->_id );
            
        // if data retrieving failed...
        if(!is_array($this->_dbData)) {
        	$this->_dbData = array();
        	
        	// error, data not found!
        	// insert the id if its a edit form and the record is not foud
	        if(FH_AUTO_INSERT) { 
	            $keys = $this->_db->getPrKey();               
	            $size1 = sizeof( $keys );
	            $size2 = sizeof($this->_id);
	            for($i = 0; $i < ( $size1 > $size2 ? $size2 : $size1 ) ; $i++ ) {
	                $this->addValue( $keys[$i], $this->_id[$i] );
	            }   
	            
	            $this->insert = true;
	            $this->edit   = false;
	        } else {
	            trigger_error('Try to edit a none existing record!', E_USER_ERROR);
	        }                
        } 
    }
    
    /**
     * FormHandler::_handleUploads()
     *
     * Private: method to handle the uploads and image convertions
     *
     * @return void
     */
    function _handleUploads() {        
        // upload the uploaded files
        foreach($this->_upload as $name) {
           $this->_fields[$name][1]->doUpload();
        }  
        
        // convert some images if needed
        reset( $this->_convert );
        while( list( $field, $convertions ) = each( $this->_convert ) ) {
            foreach( $convertions as $action => $data ) {
                            
                // is the field a uploadfield?
                if(in_array($field, $this->_upload)) {
                    // is there a file uploaded ?
                    if($this->_fields[$field][1]->isUploaded()) {
                        $image = 
                        $this->_fields[$field][1]->getSavePath().
                        $this->_fields[$field][1]->getValue();
                        
                        // does the file exists?     
                        if(!file_exists($image)) {
                            trigger_error("Error! Could not find uploaded file $image!", E_USER_WARNING);
                            unset($image);
                        }
                    } else {
                    	// image is not uploaded, continue with next field/image
                    	continue;
                    }            
                // it's not a uploadfield... is it an image ?
                } else if(file_exists($field)) {
                    $image = $field;
                // unknown field or file!    
                } else {
                    trigger_error('Could not find field or file to convert: '.$image , E_USER_WARNING);
                }
                
                // do the convert actions with the image (when the uploaded file is a jpg or png!)
                if( isset($image) && in_array( strtolower(substr( $image, -4) ), array('.jpg', '.png', 'jpeg')) ) {
                    $img =& new ImageConverter( $image );
                    
                    // stop when a error occoured
                    if( $img->getError() ) {
                        trigger_error( $img->getError(), E_USER_WARNING );
                        unset( $img );
                        continue;
                    }
                    
                    foreach($data as $info) {
                    	switch($action) {
                    	  case 'merge':
                    		list( $stamp, $align, $valign, $transparant ) = $info;                    
                    		 
                            $img->doMerge( $stamp, $align, $valign, $transparant );                		
                    		break;
                    	  case 'resize':  
                    		list( $destination, $maxX, $maxY, $quality ) = $info;
                    		if( empty( $destination ) ) {
                    			$destination = $image;
                    		}
                    		  
                            $img->setQuality( $quality );
                            $img->doResize( $destination, $maxX, $maxY );
                    		break;
                    	}
                    } 
                    
                    unset( $img );
                }   
            }
        }
    }
    
    /**
     * FormHandler::_getForm()
     *
     * Private: get the form
     *
     * @return string: the generated form
     */
    function _getForm( $iDisplayPage = null) {  
        if( is_null( $iDisplayPage ) ) {
            $iDisplayPage = $this->_curPage;
        }
        
        // set the tab indexes for the fields...
        ksort( $this->_tabindexes );
        reset( $this->_tabindexes );
        while( list( $index, $field ) = each( $this->_tabindexes )) {
            if( $this->fieldExists( $field ) ) {                
                $this->_fields[$field][1]->setTabIndex( $index );                
            } else {
                trigger_error(
                  'Error, try to set the tabindex of an unknown field "'.$field.'" !'
                );
            }
        }
        
        // set the focus to the first (tab index) field if no focus is set yet 
		if( is_null($this->_focus)) {
		    // are there tab indexes set ?
		    if( sizeof( $this->_tabindexes) > 0 ) {
		        // set the focus to the element with the lowest positive tab index
    		    reset( $this->_tabindexes );
    		    while( list( $key, $field ) = each( $this->_tabindexes )) {
    		        if( $key >= 0 && $this->setFocus( $field )) {
    		            break;
    		        }		            
    		    }    		
		    } 
		    
		    // no focus set yet. Set the focus to the first field    
		    if( is_null($this->_focus)) {
		        reset( $this->_fields );
		        while( list( $name, $data ) = each( $this->_fields ) ) {
		            // is it a object (only fields + buttons are objects)
		            if( is_object( $this->_fields[$name][1]) && $this->setFocus( $name )) {		
                        break;
		            }
		        }
		    }		        
		}
        
        // used vars
        $hidden = '';
        $form   = '';
        $buffer = array();
        $repeat = true;
        $page   = 1;
        
        // walk trought the fields array
        reset( $this->_fields );
        while( list($id, $field) = each($this->_fields) ) {    
                         
            switch( $field[0] ) {        	    
        	    // multiple pages in this form
        	    case '__PAGE__':
        	      if( $field[1] == $iDisplayPage) {
        	          break 2;
        	      } 
        	      $page++;
        	      break;
        	      
        		// hidden field
        		case '__HIDDEN__':
        		  $hidden .= $field[1]->getField()."\n";                
        		  break;
        		  
        		// new mask to set  
        		case '__MASK__':
                  list($this->_mask, $repeat) = $field[1];
                  break;
                  
                // insert html or a line
                case '__HTML__':
                case '__LINE__':
                  $form .= $field[1];
                  break;
                  
                // begin new fieldset  
                case '__FIELDSET__':
                  array_push( $buffer, array($form, $field[1][0], $field[1][1]) );
                  $form = '';
                  break;
                  
                // end new fieldset  
                case '__FIELDSET-END__':
                  if(sizeof($buffer) > 0) {
                      $d = array_pop($buffer);
                      $form = $d[0]. 
                      str_replace(
                        array('%name%', '%caption%', '%content%' ),
                        array($d[1], $d[2], $form ),
                        FH_FIELDSET_MASK
                      ); 
               	  } else {
                      trigger_error('Fieldset is closed whyle there is not an open fieldset!');
                  }
                  break;   
                
                // default action: field or button    
                default:
                  if($page != $iDisplayPage ) {
                      // put the data of the field in a hidden field
                      if($field[0] != '__BUTTON__') {
                          $h =& new HiddenField( $this, $id );                     
        	              $h->setValue( $field[1]->getValue() );                         
        	              $hidden .= $h->getField() ."\n";
        	              unset( $h );
        	          }      
                  } else {
                      // field is on the current page.. just display it
                      $html = $this->_loadMask( $field, $this->_mask, $id );
                      if($html) {
                          $form .= $html;
                          if(!$repeat) {
                          	  $this->_mask = FH_DEFAULT_ROW_MASK;
                          } else if(is_numeric($repeat)) {
                              $repeat--;
                          }
                      }
                  }
                  break;
            }
        }
        
        // give the form a page number...
        if($this->_pageCounter > 1) {
            $h = new HiddenField( $this, $this->_name .'_page' );
            $h->setValue( $iDisplayPage );
            $hidden .= $h->getField() ."\n";
            unset( $h );            
        }
        	          
        // get a possible half filled mask
        $form .= $this->_loadMask( null, null, null );
        
        // get errors
        $errors =& catchErrors();
        $errmsg = '';
        foreach($errors as $error) {
        	switch ($error['no']) {
        		case E_USER_WARNING: $type = 'Warning'; break;
        		case E_USER_NOTICE:  $type = 'Notice';  break;
        		case E_USER_ERROR:   $type = 'Error';   break;
        		default: 			 $type = 'Warning'; break;
        	}
            $errmsg .= "<b>".$type.":</b> ".basename($error['file'])." at ".$error['line']." ". $error['text'] ."<br />\n";
        }
        
        $result =
        $errmsg . 
        base64_decode(
          'PCEtLSANCiAgVGhpcyBmb3JtIGlzIGF1dG9tYXRpY2'.
          'x5IGJlaW5nIGdlbmVyYXRlZCBieSBGb3JtSGFuZGxl'.
          'ciB2My4NCiAgU2VlIGZvciBtb3JlIGluZm86IGh0dH'.
          'A6Ly93d3cuZm9ybWhhbmRsZXIubmV0DQogIFRoaXMg'.
          'Y3JlZGl0IE1VU1Qgc3RheSBpbnRhY3QgZm9yIHVzZQ'.
          '0KLS0+'
        ). 
        "\n<form name='".$this->_name."' method='post' action='".$this->_action."'".
        (sizeof($this->_upload) ? ' enctype="multipart/form-data"':'' ).
        ($this->_extra ? " ".$this->_extra : "" ).">\n".
        $hidden.
        ($this->_table ? "<table border='0' cellspacing='0' cellpadding='3'>\n" : '' ).
        $form.
        ($this->_table ? "\n</table>\n" : '').
        (FH_EXPOSE ? 
          base64_decode(
            'PHNtYWxsPjxmb250IGNvbG9yPSdCNUI1QjUnIGZh'.
            'Y2U9J3RhaG9tYScgc3R5bGU9J2ZvbnQtZmFtaWx5'.
            'OnRhaG9tYTtmb250LXNpemU6MTBweDtjb2xvcjpC'.
            'NUI1QjU7Zm9udC13ZWlnaHQ6bm9ybWFsOyc+VGhp'.
            'cyBmb3JtIGlzIGdlbmVyYXRlZCBieSA8L2ZvbnQ+'.
            'PC9zbWFsbD48YSBocmVmPSdodHRwOi8vd3d3LmZv'.
            'cm1oYW5kbGVyLm5ldCcgdGFyZ2V0PSdfYmxhbmsn'.
            'PjxzbWFsbD48Zm9udCBjb2xvcj0nQjVCNUI1JyBm'.
            'YWNlPSd0YWhvbWEnIHN0eWxlPSdmb250LWZhbWls'.
            'eTpUYWhvbWE7Zm9udC1zaXplOjEwcHg7Y29sb3I6'.
            'QjVCNUI1O2ZvbnQtd2VpZ2h0Om5vcm1hbDsnPjxi'.
            'PkZvcm1IYW5kbGVyPC9iPjwvZm9udD48L3NtYWxs'.
            'PjwvYT4='
          ):'').     
        "</form>\n".     
        base64_decode(
          'PCEtLSANCiAgVGhpcyBmb3JtIGlzIGF1dG9tYXRpY2'.
          'x5IGJlaW5nIGdlbmVyYXRlZCBieSBGb3JtSGFuZGxl'.
          'ciB2My4NCiAgU2VlIGZvciBtb3JlIGluZm86IGh0dH'.
          'A6Ly93d3cuRm9ybUhhbmRsZXIubmV0DQotLT4='
        ) .
        ($this->_focus != null ? 
          "\n<script language='javascript' type='text/javascript'>\n".
          "<!-- // set the focus on a specific field \n".
          "var sType = typeof( document.forms['".$this->_name."'].elements['".$this->_focus."'] );\n".
          "if( sType != 'undefined' && sType != null ) \n".
          "  document.forms['".$this->_name."'].elements['".$this->_focus."'].focus();\n".
          "//-->\n".
          "</script>\n":""
        );       
        
        return $result;
    }
}

?>
For more information send a message to info at phpclasses dot org.