Location: PHPKode > projects > FiForms Framework > FiForms/FiForms-includes/FiForms_FiForm.inc.php
<?php
/**
*******************************************************************************

    FiForms -- A collection of PHP classes designed 
    to facilitate rapid development of web-database software

    Copyright (C) 2003-2008  Daniel McFeeters

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


    The original author of this library can be contacted at the following 
    address:

    Daniel McFeeters
    182 Baker Rd.
    Faubush, KY 42544-6526
    email:databases [at] fiforms [dot] org
    http://www.fiforms.org/


Project Started May 4, 2003
*******************************************************************************
FiForms_FiForm.inc.php

Main FiForms include file

This file contains the definition for the main iForm class 
This single file should be included in any script that uses 
the functionality of the FiForms library

*******************************************************************************
**/

/* ?><code><?php */

/******************************************************************************
                                  INCLUDES
******************************************************************************/

/**
 * This does some automatic checking to attempt to load the local config file
 * and templates.
 */
/*
    if(!isset($FIFORMS_CONFIG))
    {
        if(!@include('localconfig.php'))
        {
            // can't include config from php path, so we'll try to get the config
            // from the same directory as the calling php (which is always
            // the first file in get_included_files()

            $includes = get_included_files();
            if(!@include(dirname($includes[0])."/localconfig.php"))
            {
                die('No Configuration found. Did you perhaps call an include file' .
                    ' directly instead of calling it as part of the FiForms' .
                    ' application?');
            }
        } // end if can't include config
    } // end if no config

*/


/* ?><ignore><?php */

    require_once("FiForms_iContainer.inc.php");
    require_once("FiForms_iFormControl.inc.php");
    require_once("FiForms_imports.inc.php");
    require_once("FiForms_QueryParser.inc.php");
    require_once("htmltemplate.php");

/* ?></ignore><?php */


/******************************************************************************
                            FIFORM CLASS DEFINITION 
******************************************************************************/
class FiForm extends iContainer
{
        
/******************************************************************************
                                  PROPERTIES 
******************************************************************************/

    public $dataServer; // the name or IP address of MySQL Server

    public $dataDB;     // the name of the database to connect to

    public $dataTable;  // name of table where data comes from, and also the 
                    // table to update. If $dataQuery not given, used to 
                    // construct $dataQuery

    public $dataQuery;  // Query used to retreive data from MySQL table. If 
                    // not given, set to "SELECT * FROM $dataTable;"
                    // NOTE: never include a WHERE, GROUP BY, or HAVING
                    // clause in dataQuery. Use the where property
                    // to set the WHERE clause

    public $formAction; // the page to load. If not given, use PHP_SELF

    public $currentRec; // Index of current record in MySQL dataset

    public $maxRec;     // Index of the last record in MySQL dataset

    private $keyArray;   // String of html containing primary keys in hidden 
                    // inputs. Used to identify the record when updated.

    public $readOnly;   // global readOnly; if TRUE, sets all 
                    // $this->inputs[]->readOnly=TRUE

    private $isProcessed;// tests whether the processData function has been 
                    // called in this instance

    private $formatStrSV;// format string when form in in sheetView mode

    public $sheetView;  // true if form is in sheetView mode

    public $recordLimit;
                    // Maximum number of records returned by dataQuery

    public $controlsOnTop;
                    // show the form control links on top and on bottom

    public $noNavigation;
                    // disables the navigation links on the form control

    public $showDelete; // show the delete function, even if navigation 
                    // is disabled

    public $having;    // HAVING clause (without the keyword)
    public $groupBy;    // GROUP BY clause (without the keyword)
    public $orderBy;    // field to order the query by

    private $orderDesc;  // if TRUE, sort in descending order by field orderBy

    public $wrapper;    // contains HTML code to "wrap" the form into a 
                    // complete HTML page

    public $noMoveOnInsert; // if true, return to inserted record after 
                        // insert, rather than moving to a new record.

    private $auth;       // authentication Module

    public $where;      // WHERE clause of query (without WHERE keyword)

    public $allowSheetView; // set to TRUE if sheet view is allowed at all

    public $allowFormView;  // set to TRUE if form (normal) view is allowed

    public $allowNewView;  // set to TRUE if form (normal) view is allowed 
                           // in insert mode

    public $debugMode;      // set to TRUE to show all debug info in HTML 

    private $merge_query;    // saved data query

    private $connectID;      // MySQL connection ID

    public $excludeLinkTerms; 
                        // Parameters to preserve in links passed in the URL
                        // source
    public $allowedParameters; 
                        // Parameters allowed to be passed to the form
                        // query WHERE clause directly through the URL.
    private $controlHeader;
    private $controlHeaderSpacer;
    private $controlFooter;
    public $timing; // array of timing information for performance tuning
    public $preQueries; // array of queries to execute before the main query

/******************************************************************************
                                CONSTRUCTOR 
******************************************************************************/
    public function FiForm()    
    // class constructor. Used to initialize variables
    {
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['CONSTRUCTOR'] = microtime(true);
        }
        $this->auth = new FiFormsAuth();

        $this->dataServer = $GLOBALS['FIFORMS_CONFIG']['DEFAULT_SERVER'];
        $this->dataDB = $GLOBALS['FIFORMS_CONFIG']['DEFAULT_DATABASE'];
        $this->name = "FiFormData";
        if(func_num_args() >= 3)
        {
            $formArgs = func_get_args();
            if($formArgs[0] != "")
            {
                $this->name = $formArgs[0];
            }
            $this->dataDB = $formArgs[1];
            $this->dataTable = $formArgs[2];
            if($GLOBALS['FIFORMS_CONFIG']['CONNECT_EARLY'])
            {
                $this->makeConnection();
            }
        }

        // Initialize $formatStr


        $this->formatStrSV->rowStartEven = "<tr class=\"even\">";
        $this->formatStrSV->headStart = "<thead><tr class=\"odd\">";
        $this->formatStrSV->headStop = "</tr></thead><tbody>";
        $this->formatStrSV->rowStartOdd = "<tr class=\"odd\">";
        $this->formatStrSV->rowStop = "</tr>";
    
        if(array_key_exists("deleteRec",$_GET) && $_GET["deleteRec"] == "confirm")
        {
            $this->readOnly = TRUE;
        }
        if(array_key_exists("readonly",$_GET) && $_GET["readonly"] == "TRUE")
        {
            $this->readOnly = TRUE;
        } 


        $this->formAction = htmlentities(stripslashes(
                        $GLOBALS['FIFORMS_CONFIG']['SERVER_ADDRESS'].
                        $_SERVER["REQUEST_URI"]));
        $this->currentRec = array_key_exists("currentRec",$_GET) ? 
                        $_GET["currentRec"] : "";
        if(array_key_exists("sheetView",$_GET) && $_GET["sheetView"] == "YES")
        {
            $this->sheetView = TRUE;
        }       

        $this->wrapper = new htmlTemplate();
        $this->wrapper->errorMsg = "";
        $this->errorMsg = &$this->wrapper->errorMsg;
        $this->wrapper->pageHeader = "<div class=\"FiFormPage\">";
        $this->allowSheetView = TRUE;
        $this->allowFormView = TRUE;
        $this->allowNewView = TRUE;
        $this->excludeLinkTerms = array("deleteRec","currentRec",
                                         "PRIMARYKEYS","PRIMARYKEY_");
        $this->moveOnInsert = TRUE;
        $this->allowUserSort = TRUE;
        $this->recNum = $this->currentRec;
        $this->inputs = array();
        $this->inputs["_CONTROLS"] = new iFormControl($this);
        $this->inputs["_CONTROLS"]->visible = FALSE;
        $this->allowedParameters = array();
        $this->isProcessed = FALSE;
        $this->controlHeader = "";
        $this->controlFooter = "";
        $this->having = "";
        $this->groupBy = "";
        $this->preQueries = array();
        $this->boundForm = &$this;
    } //function FiForm

    
    function getXMLContent($query,$fiform,$doc)
    {
        $content = "";
        $rslt = $fiform->query($query)->item(0);
        if(is_object($rslt) && property_exists($rslt,'firstChild'))
        {
            $element = $rslt->firstChild;
        }
        else
        {
            return false;
        }
        while($element) 
        {
            $content .= $doc->saveXML($element);
            $element = $element->nextSibling;
        }
        return str_replace(array("<html:","</html:","<default:","</default:"),
                           array("<",     "</",     "<",        "</",      ),$content);
    }


    function loadXMLDef($doc)
    {
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['LOADXML_START'] = microtime(true);
        }
        // Initialize Variables
        $doc->preserveWhiteSpace = false;
        
        // Create a DOMXPath object $fiform so we can access data in $doc
        $fiform = new DOMXPath($doc);
        foreach($GLOBALS['FIFORM_XML_INPUT_NAMESPACES'] as $prefix => $uri)
        {
            $fiform->registerNamespace($prefix,$uri);
        }
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['LOADXML_GOTDOC'] = microtime(true);
        }
        
        // Process Conditional Statements (<if> tags)
        $ifNode = $doc->getElementsByTagName('if')->item(0);
        while($ifNode)
        {
            $subElement = $ifNode->firstChild;
            while($subElement)
            {
                $nextElement = $subElement->nextSibling;
                $paramName = $ifNode->getAttribute('param');
                $paramTest = $ifNode->getAttribute('test');
                $paramCompare = $ifNode->getAttribute('value');
                $paramValue = array_key_exists($paramName,$_GET) ? 
                                $_GET[$paramName] : "";
                if(get_magic_quotes_gpc())
                {
                    $paramValue = stripslashes($paramValue);
                }
                switch($paramTest)
                {
                    case 'equals':
                        $switch = ($paramValue == $paramCompare);
                        break;
                    case 'does not equal':
                        $switch = ($paramValue != $paramCompare);
                        break;
                    case 'is greater than':
                        $switch = ($paramValue > $paramCompare);
                        break;
                    case 'is less than':
                        $switch = ($paramValue < $paramCompare);
                        break;
                    case 'is greater than or equal to':
                        $switch = ($paramValue >= $paramCompare);
                        break;
                    case 'is less than or equal to':
                        $switch = ($paramValue <= $paramCompare);
                        break;
                    case 'does not exist':
                        $switch = !array_key_exists($paramName,$_GET) ||
                            $paramValue === '';
                        break;
                    case 'exists':
                    default:
                        $switch = array_key_exists($paramName,$_GET) && 
                            $paramValue !== '';
                } // switch
                if($switch)
                {
                    $ifNode->parentNode->insertBefore($subElement,$ifNode);
                }
                $subElement = $nextElement;
            } // while subElement
            $ifNode->parentNode->removeChild($ifNode);
            $ifNode = $doc->getElementsByTagName('if')->item(0);
        } // while ifNode
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['LOADXML_STRIPPEDIFS'] = microtime(true);
        }

        $fiformatt = $fiform->query("/f:fiform")->item(0)->attributes;
        $noMoveOnInsert = FALSE;
        if($fiformatt->getNamedItem('insert'))
        {
            if($fiformatt->getNamedItem('insert')->nodeValue == 'none' && $_GET['currentRec'] == 'new')
            {
                    $this->allowNewView = FALSE;
                    $_GET['currentRec'] = '';
                    $_POST = array();
            }
            if($fiformatt->getNamedItem('insert')->nodeValue == 'sheet' && $_POST)
            {
                    $_GET['sheetView'] = 'YES';
            }
            if($fiformatt->getNamedItem('insert')->nodeValue == 'current')
            {
                    $noMoveOnInsert = TRUE;
            }
        }
        if($fiformatt->getNamedItem('allowInsert') and $fiformatt->getNamedItem('allowInsert')->nodeValue == 'no')
        {
            $this->allowNewView = FALSE;
            $_GET['currentRec'] = '';
            $_POST = array();
        }
        if($fiformatt->getNamedItem('allowView') and $fiformatt->getNamedItem('allowView')->nodeValue == 'no' && $_GET['currentRec'] != 'new')
        {
            $_GET['currentRec'] = 'new';
        }
        $viewsAllowed = $fiformatt->getNamedItem('viewsAllowed')->nodeValue;
        switch($viewsAllowed)
        {
            case "none":
                $this->allowSheetView = FALSE;
                $this->allowFormView = FALSE;
                break;
            case "sheet":
                $this->allowFormView = FALSE;
                $this->allowSheetView = TRUE;
                $_GET['sheetView'] = 'YES';
                break;
            case "form":
                $this->allowSheetView = FALSE;
                $this->allowFormView = TRUE;
                unset($_GET['sheetView']);
                break;
            default:
                $this->allowSheetView = TRUE;
                $this->allowFormView = TRUE;
        }

        // Extract form definition from XML and build the fiform

        // Create new FiForm object
        $this->noMoveOnInsert = $noMoveOnInsert;

        // Start processing the definition, generating FiForm objects and 
        // copying properties
        $db =
            trim($fiform->query("/f:fiform/f:connect/@db")->item(0)->nodeValue);
        if($db)
        {
            $this->dataDB = $db;
        }
        $this->dataTable =
            trim($fiform->query("/f:fiform/f:connect/@update")->item(0)->nodeValue);
        $connectID = $fiform->query("/f:fiform/f:connect/@id")->item(0)->nodeValue;
        foreach($fiform->query('/f:fiform/f:param') as $param)
        {
            $name = trim($param->attributes->getNamedItem('name')->nodeValue);
            $this->allowedParameters[] = $name;
        }

        // Load any "pre" queries
        $i = 1;
        $sqlNode = $fiform->query("/f:fiform/f:query[@mode='pre'][$i]/f:sql/text()");
        while(is_object($sqlNode->item(0)))
        {
            $j = 0;
            $sql = "";
            while($sqlNode->item($j))
            {
                $sql .= $sqlNode->item($j)->nodeValue." ";
                $j++;
            }
            $this->preQueries[] = $sql; 
            $i++;
            $sqlNode = $fiform->query("/f:fiform/f:query[@mode='pre'][$i]/f:sql/text()");
        }  // end while
        
        $sqlNode = $fiform->query("/f:fiform/f:query[not(@mode)]/f:sql/text()");
        if(is_object($sqlNode))
        {
            $i = 0;
            $sql = "";
            while($sqlNode->item($i))
            {
                $sql .= $sqlNode->item($i)->nodeValue." ";
                $i++;
            }
            $this->setQuery($sql);
        } // if is_object

        if($fiformatt->getNamedItem('allowUpdate') &&
           $fiformatt->getNamedItem('allowUpdate')->nodeValue == 'no' &&
           $_GET['currentRec'] != 'new')
        {
            $this->readOnly = TRUE;
            $_POST = array();
        }
        $this->caption = $fiform->query("/f:fiform/f:title")->item(0)->nodeValue;
        $ctrlPosition = $fiformatt->getNamedItem('ctlPosition')->nodeValue;
        if(($ctrlPosition == 'top' || $ctrlPosition == 'both') && 
            $_GET['sheetView'] != 'YES')
        {
            $this->controlsOnTop = TRUE;
        }
        $whichString = $fiformatt->getNamedItem('whichControls')->nodeValue;
        $which = explode(",",$whichString);

        if($ctrlPosition == 'none' || (!is_null($whichString) &&
           !in_array('all',$which) && !in_array('nav',$which)))
        {
            $this->noNavigation = TRUE;
        }
        // FIXME: showDelete should be false if not "all" or "delete" or nothing
        if(in_array('delete',$which))
        {
            $this->showDelete = TRUE;
        }
        
        $this->clearPrimaryKeys(); 
        foreach($fiform->query('/f:fiform/f:connect/f:primaryKey') as $pkey)
        {
            $this->setPrimaryKey(trim($pkey->attributes->getNamedItem('field')->nodeValue));
        }
        $svPosition = $fiformatt->getNamedItem('svctlPosition')->nodeValue;
        if($svPosition == 'left' || $svPosition == 'both')
        {
            $this->inputs[] = new iLinkBack();
        }
        $rLimitNode = $fiformatt->getNamedItem('recordLimit');
        if(is_object($rLimitNode))
        {
            $rLimit = $rLimitNode->nodeValue;
            if($rLimit > 1)
            {
                $this->recordLimit = $rLimit;
            }
        } // if is_object

        // Record timing mark before starting loop

        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['LOADXML_SETPROP'] = microtime(true);
        }

        // Loop over each element in the body of the form
        $startPath = '/f:fiform/';
        foreach($fiform->query(
                    $startPath.
                    implode(' | '.$startPath,
                    $GLOBALS['FIFORM_XML_INPUTS'])) as $input)
        {
            $this->loadInputFromXML($input);
        }

        if($svPosition == "right" || $svPosition == "both" || !$svPosition)
        {
            $this->inputs[] = new iLinkBack();
        }
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['LOADXML_BUILTINPUTS'] = microtime(true);
        }

        $this->wrapper->pageHeader .=
            $this->getXMLContent("/f:fiform/f:wrapper/f:pageHeader",$fiform,$doc);
        $this->wrapper->pageFooter .= 
            $this->getXMLContent("/f:fiform/f:wrapper/f:pageFooter",$fiform,$doc);
        $this->wrapper->headerTags .= 
            $this->getXMLContent("/f:fiform/f:wrapper/f:headerTags",$fiform,$doc);
        $this->wrapper->bodyProperties .=   
            $this->getXMLContent("/f:fiform/f:wrapper/f:bodyProperties",
                $fiform,$doc);
        $element = $fiform->query("/f:fiform/f:wrapper/f:bodyProperties")->item(0);
        if($element)
        {
            foreach($element->attributes as $attr)
            {
                $this->wrapper->bodyProperties .=
                    $attr->nodeName."=\"".$attr->nodeValue."\" ";
            }
        }

        unset($doc);
        // Record Timing Point
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['LOADXML_END'] = microtime(true);
        }
    }  // function loadXMLDef
    
/******************************************************************************
                                MAKE CONNECTION
******************************************************************************/

    public function makeConnection() 
    {
        // Connect to the MySQL database and select the database
        if(!$this->dataDB) 
        {
            return(false);
        }
        if($this->connectID) 
        {
            return($this->connectID);
        }
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['DBCONNECT_START'] = microtime(true);
        }
        $this->wrapper->debugQueries .= "Making Connection<br/><br />";
        $this->connectID = @mysql_connect($this->dataServer,
                            $this->auth->username,
                            $this->auth->passwd);
        if(!$this->connectID) 
        {
            $this->auth->connectFailure();
            $this->throwError("Unable to connect to server ".$this->dataServer);
            return(false);
        }
        elseif(!mysql_select_db($this->dataDB))
        {
            $suggestion = "";
            if($GLOBALS['FIFORMS_CONFIG']['ALLOW_DD_OPERATIONS'])
            {
                        $uri = $GLOBALS['FIFORMS_CONFIG']['URI']."createSchema.php";
                        $app = htmlentities($_GET['app']);
                        $formname = htmlentities($_GET['formname']);
                        $suggestion = <<<EOD
                        <br /><p>
                        If you want, and if your account has enough permissions,
                        FiForms can correct this problem for you by creating the 
                        database and the table that you need to use this form.
                        </p>
                        <p>
                        <a href="$uri?createDB=YES&amp;app=$app&amp;formname=$formname">Click here
                         to attempt to create the necessary items.</a>
                        </p>
EOD;
            }
            $this->throwError("Unable to select database ".$this->dataDB.$suggestion);
            return(false);
        }
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['DBCONNECT_END'] = microtime(true);
        }
        return($this->connectID);
    }  //makeConnection


/******************************************************************************
                                CLOSE CONNECTION
******************************************************************************/

    private function closeConnection ()
    {
        if($this->connectID)
        {
            $this->wrapper->debugQueries .= "Closing Connection<br/><br />";
            mysql_close($this->connectID);
            $this->connectID = 0;
        }
        else
        {
            return 0;
        }
    }
    
/******************************************************************************
                                  BUILD TABLE DESCRIPTION
******************************************************************************/

    private function runQuery($sql)
    {
    	if(trim($sql) == '')
	{
	    return false;
	}
        $lastTime = microtime(true);
        $this->wrapper->debugQueries .= 
                    "<p>".nl2br(htmlentities($sql)).";</p>";
        $rslt = @mysql_query($sql);
        $err = mysql_error();
        if($err)
        {
            $this->throwError( 
                "MySQL returned an error while running a query: ".
            $err);
            $this->wrapper->debugQueries .= "<p>ERROR: $err</p>";
        }

        // Record timing in debugQueries output
        $thisTime = microtime(true);
        $this->wrapper->debugQueries .= 
            "<p># Running Time:".($thisTime - $lastTime)."</p>";
        return $rslt;
    }
    
    private function buildTableDescription()
    {
        // Get a description of dataTable so we know how to build
        // an update / insert statement

        $describeQuery = "DESCRIBE ".$this->dataTable;
        $describeRslt = $this->runQuery($describeQuery);
        if(!$describeRslt)
        {
                $this->throwError(
            "<b>Error getting field information from table.
            Unable to save data.</b><br />");
            
                return(FALSE);
        }
        $buildKeys = (count($this->keyArray) == 0);
        while($describerow=mysql_fetch_array($describeRslt))
        {
            $describeArray[] = $describerow["Field"];
            if($describerow["Key"] == "PRI" && $buildKeys)
            {
                $this->keyArray[] = $describerow["Field"];
            }
        }
        return($describeArray);
    } // function buildTableDescription

/******************************************************************************
                                SAVE POST DATA 
******************************************************************************/

    private function savePostData()
    // Save the data submitted via _POST to MySQL dataTable
    {

        if($this->dataTable=="")
        {
          if(count($_POST) == 0)
          {
            return(TRUE);
          }    
          else
          {
            $this->throwError(
              "<b>No data table specified. 
              Unable to save data.</b><br />");
            
            return(FALSE);
          } // if POST    
        }  // if dataTable
        if(count($_POST) > 0 || count($this->keyArray) == 0)  
            $describeArray = $this->buildTableDescription();
        if(!$describeArray && count($_POST) > 0)
            return(FALSE);

        if(count($_POST) == 0)
        {
          return(TRUE);
        }

        if($this->error)
        {
            return(FALSE);
        }
        if(($GLOBALS['FIFORMS_CONFIG']['ENCRYPT_KEY']) && 
                isset($_POST['_CHECK_DATA']) 
                && strlen($_POST['_CHECK_DATA']) > 50 
                && function_exists('mcrypt_module_open'))
        {
          $hash = substr($_POST['_CHECK_DATA'],0,40);
          $data = substr($_POST['_CHECK_DATA'],40);
          if(sha1($data.$GLOBALS['FIFORMS_CONFIG']['ENCRYPT_KEY']) != $hash)
          {
              $this->throwError("Check data appears to be invalid. Unable to save data.");
          return FALSE;
          }
          $check_data = unserialize($this->decompress($this->crypt(pack("H*",$data),true)));
          //print_r($check_data);
          if(is_array($check_data['preQueries']))
          {
            foreach($check_data['preQueries'] as $preQuery)
            {
                $this->runQuery($preQuery);
            }
          }
          $result = $this->runQuery($check_data['dataQuery']);
          if(!$result)
          {
              $this->throwError("Error executing check data query. Unable to save data.");
          }
          $rslt_array = @mysql_fetch_array($result,MYSQL_ASSOC);
          if($rslt_array != $check_data['resultArray'])
          {
                $this->throwError( 
            "<b>This record has been modified in the database while you were editing it.
            Following is a proposed merge of the changes. Please verify that the changes
            are correct and click \"Update Record\" again to save your changes.</b><br />");
            $this->merge_record = $rslt_array;
            $this->merge_query = $check_data['dataQuery'];
            $this->original_record = $check_data['resultArray'];
          }
          if($this->error)
          {
                $this->closeConnection();
                $this->makeConnection();
                return false;
          }
        }
        else if(($GLOBALS['FIFORMS_CONFIG']['ENCRYPT_KEY']) && $this->currentRec != "new" && function_exists('mcrypt_module_open'))
        {
            $this->throwError("No check data available. Unable to save data.");
            return false;
        }
        
        if($this->currentRec === "new")
        {
            $tmp = "";
             $this->buildInsertQuery($describeArray,$updateQuery,$tmp);
        } // if currentRec = new
        
        elseif($_GET["deleteRec"] == "")
        {
             $this->buildUpdateQuery($describeArray,$updateQuery);
        } // else if deleteRec

        elseif($_GET["deleteRec"] == "confirm" && 
              $_POST["_DELETECONFIRM"]=="yes")
        {
            $updateQuery = $this->buildDeleteQuery($describeArray);
        $this->readOnly = FALSE;
        }// else if deleteRec 

        else
        {
            $updateQuery = TRUE;
        } // else (nothing)
        if(!$updateQuery)
        {
            // exit on error
            $this->throwError("<b>Error building the query statement.</b><br />");
            return(FALSE);
        }
        if($this->currentRec !== "new")
        {
            $criteria = $this->buildQueryCriteria();
                if(!$criteria)
                {
                    $this->throwError(
                        "<b>Unable to determine criteria for updating record. Update aborted.</b><br />");
                    return(FALSE);
                }
            $updateQuery .= $criteria;
        } // if currentRec=
        if($this->error)
        {
            return false;
        }

        $success = $this->runQuery($updateQuery);
	$this->insertID = selectValue('SELECT @last_fiforms_UUID');
        if(!$this->insertID)
        {
	    $this->insertID = mysql_insert_id();
        }
        $this->closeConnection();
        $this->makeConnection();

        if($success)
        {
            return(TRUE);
        }
        else
        {
            return(FALSE);
        }
    } // function savePostData
    
/******************************************************************************
                            BUILD INSERT QUERY 
******************************************************************************/


    function buildInsertQuery($describeArray,&$updateQuery,&$valuestring)
    // build an INSERT query to insert posted data into the database
    {
        $updateQuery = "INSERT INTO ".$this->dataTable." (";
        $valuestring = "";
        parent::buildInsertQuery($describeArray,$updateQuery,$valuestring);

        $updateQuery = substr($updateQuery,0,strlen($updateQuery)-2);
        $updateQuery .= ") VALUES (".$valuestring;
        $updateQuery = substr($updateQuery,0,strlen($updateQuery)-2);
        $updateQuery .= ")";
        return($updateQuery);
    }  // function buildInsertQuery

/******************************************************************************
                            BUILD UPDATE QUERY 
******************************************************************************/


    function buildUpdateQuery($describeArray,&$updateQuery)
    {
        $updateQuery = "UPDATE ".$this->dataTable." SET ";

        parent::buildUpdateQuery($describeArray,$updateQuery);

        $updateQuery = substr($updateQuery,0,strlen($updateQuery)-2);
    }
/******************************************************************************
                            BUILD DELETE QUERY 
******************************************************************************/

    private function buildDeleteQuery($describeArray)
    {
            $updateQuery = "DELETE FROM ".$this->dataTable; 
        return($updateQuery);
    }
/******************************************************************************
                            BUILD QUERY CRITERIA 
******************************************************************************/

    private function buildQueryCriteria()
    // build the WHERE clause for the select statement used in 
    // savePostData
    {
        $updateQuery = " WHERE ";
      
        foreach($this->keyArray as $thisKey)
        {
            if(!array_key_exists("PRIMARYKEY_".$thisKey,$_POST))
            // if value doesn't exist
            {
                return(FALSE);
            } // if
            $updateQuery .= "`".$thisKey."` = \"".
                      $_POST["PRIMARYKEY_".$thisKey]."\" AND ";
        } // foreach in keyArray
        
        $updateQuery = substr($updateQuery,0,strlen($updateQuery)-4);
        $updateQuery .= "LIMIT 1";
        return($updateQuery);
    }


/******************************************************************************
                                GET NEXT RECORD 
******************************************************************************/

    public function crypt($input,$decrypt = false)
    {
        $key = $GLOBALS['FIFORMS_CONFIG']['ENCRYPT_KEY'];
        $td = mcrypt_module_open('tripledes', '', 'ecb', '');
        $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        mcrypt_generic_init($td, $key, $iv);
        if($decrypt)
            $encrypted_data = mdecrypt_generic($td, $input);
        else
            $encrypted_data = mcrypt_generic($td, $input);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        return $encrypted_data;
    }


    private function getNextRecord()
    {
        $this->resultArray = 
              @mysql_fetch_array($this->resultSet,MYSQL_ASSOC);
        // get the next row from the resultSet. If there is no row, and 
        // hence mysql_fetch_array returns false, then getNextRecord also 
        // returns false without futher processing.
        if($this->resultArray == FALSE)
        {
            return(FALSE);
        }
        $this->buildKeyTags();

        // Check the verification data to see if this record has been modified.
        if(($GLOBALS['FIFORMS_CONFIG']['ENCRYPT_KEY']) &&
            isset($this->inputs['_CHECK_DATA']) &&
            function_exists('mcrypt_module_open'))
        {
            $check_data = array();
            $check_data['resultArray'] = $this->resultArray;
            $check_data['dataQuery'] = $this->dataQuery;
            $check_data['preQueries'] = $this->preQueries;
            $data_string = bin2hex($this->crypt($this->compress(serialize($check_data))));
            $this->inputs['_CHECK_DATA']->setValue(
                sha1($data_string.
                $GLOBALS['FIFORMS_CONFIG']['ENCRYPT_KEY'])
                .$data_string);
        }

        $this->fillInputs();
        return(TRUE);
    } // function getNextRecord

/******************************************************************************
                                RESTORE RECORD 
******************************************************************************/

    private function restoreRecord()
    // reinstate values submitted via _POST back into the form
    // (called when an error occurs in saving the data)
    {
        $merge_mode = ($GLOBALS['FIFORMS_CONFIG']['ENCRYPT_KEY']) &&
                  is_array($this->original_record) && 
                  is_array($this->merge_record);

        $this->restoreMerge($merge_mode);

        if($merge_mode)
        {
            $this->inputs['_CHECK_DATA'] = new iHidden('_CHECK_DATA');
            $check_data = array();
            $check_data['resultArray'] = $this->merge_record;
            $check_data['dataQuery'] = $this->merge_query;
            $preQueries = $this->preQueries;
            $params = $_GET;
            if(!get_magic_quotes_gpc())
            {
                foreach($params as $key => $value)
                {
                    $params[$key] = addslashes($value);
                }
            } // if not magic quotes
            foreach($preQueries as $k => $q)
            {
                $preQueries[$k] = fillVars($_GET,$q);
            }
            $check_data['preQueries'] = $preQueries;
            $data_string = bin2hex($this->crypt($this->compress(serialize($check_data))));
            $this->inputs['_CHECK_DATA']->setValue(
                sha1($data_string.$GLOBALS['FIFORMS_CONFIG']['ENCRYPT_KEY']).
                $data_string);
        } // if

        // output PRIMARYKEY inputs
        foreach($_POST as $fieldName => $fieldValue)
        {
            if(strpos($fieldName,"PRIMARYKEY") !== FALSE)
            {
                $this->inputs[$fieldName] = new iHidden($fieldName);
                $this->inputs[$fieldName]->setValue($fieldValue);
            } // if
        } // foreach
    } // function restoreRecord
/******************************************************************************
                                BUILD SELECT QUERY 
******************************************************************************/

    private function buildSelectQuery()
    {
        $dataQuery = &$this->dataQuery;

        // Build a complete SELECT query 
        if($dataQuery == "" && $this->dataTable != "") 
        {
            // generate a "generic" query if none exists.
            if($GLOBALS['FIFORMS_CONFIG']['MYSQL_VERSION'] < 4.0)
            {
                $dataQuery = "SELECT * FROM `".$this->dataTable."`";
            }
            else
            {
                $dataQuery = "SELECT SQL_CALC_FOUND_ROWS * FROM `".
                            $this->dataTable."`";
            }
          
        } // if no dataQuery
        if($dataQuery == "")
        {
            return;
        }
        // make sure the query isn't already terminate by a semicolon
        if(substr($dataQuery,strlen($dataQuery)-1) == ";")
        {
            $dataQuery = substr($dataQuery,0,-1);
        }

        if($_GET["PRIMARYKEYS"] == "" || $_POST["_DELETECONFIRM"] == "yes")
        {
            if(is_array($this->allowedParameters))
            {
                if($this->where != "")
                {
                    $this->where .= " AND ";
                }
                foreach($this->allowedParameters as $param)
                {
                    if(isset($_GET[$param]))
                    {                   
                        $this->where .= "$param = \"".$_GET[$param]."\" AND ";
                    }
                }
                $this->where .= "1";
            }
            if($this->where == "")
            {
                $this->where = "1";
            }
            $dataQuery .= " WHERE ".$this->where;
        }  // if no PRIMARYKEYS

        
        // Generate an ORDER BY clause to be added to the query
        // This fixes the bug that required an ORDER BY clause
        // to be declared on every form
        if($this->allowUserSort && $_GET['sort'])
        {
            $sort = $_GET['sort'];
            if(array_key_exists($sort,$this->inputs))
            {
                if($this->inputs[$sort]->allowUserSort)
                {
                    $this->orderBy = " ORDER BY ".$this->inputs[$sort]->dbField." ";
                    if($_GET['st'] == 'DESC')
                        $this->orderDesc = TRUE;
    
                }
            }
        }
        if(trim($this->orderBy) == "" && $this->keyArray[0])
        {
            $this->orderBy = " ORDER BY 1 ";
            foreach($this->keyArray as $key)
            {
                $this->orderBy .= ", $key ";
            }
        }
        else if(trim($this->orderBy) == "")
        {
            $this->orderBy = " ORDER BY 1 ";
        }
            // add a LIMIT to the query. (Note that this will generate an 
        // error if the query already has a LIMIT)
        //$this->buildTableDescription();
        if($_GET["PRIMARYKEYS"] != "" && 
          $_POST["_DELETECONFIRM"] != "yes" && $this->keyArray)
        {
            $dataQuery .= " WHERE 1 ";
            foreach($this->keyArray as $key)
            {
                $dataQuery .= " AND $key = \"".$_GET["PRIMARYKEY_$key"]."\"";
            }
        } // if single record query

        if($this->groupBy != "")
        {
                $dataQuery .= " GROUP BY ".$this->having;
        }
        if($this->having != "")
        {
                $dataQuery .= " HAVING ".$this->having;
        }

        if($_GET["PRIMARYKEYS"] != "" && 
          $_POST["_DELETECONFIRM"] != "yes" && $this->keyArray)
        {
            $dataQuery .= " LIMIT 0,1";
        }
        else
        {
            $dataQuery .= $this->orderBy.($this->orderDesc ? " DESC " : "").
                        " LIMIT 0".
            $this->currentRec.
            ",0".
            $this->recordLimit;
        }  // if 

    }  // function buildSelectQuery
    
/******************************************************************************
                                BUILD KEY TAGS 
******************************************************************************/

    private function buildKeyTags() 
    // Generates PRIMARYKEY_ iHidden inputs to identify the record for 
    // updating and $this->primaryKeyFilter to identify the record in
    // the delete link
    {
        if($this->keyArray != "")
        {
            $this->primaryKeyFilter = "YES&amp;";
            foreach($this->keyArray as $thisKeyField)
            {
                if(!$this->sheetView)
                {       
                    $this->inputs["PRIMARYKEY_".$thisKeyField] = 
                  new iHidden("PRIMARYKEY_".$thisKeyField);
                    $this->inputs["PRIMARYKEY_".$thisKeyField]->setValue( 
                  $this->resultArray[$thisKeyField]);
                    $this->inputs["PRIMARYKEY_".$thisKeyField]->dbField = 
                  "PRIMARYKEY_".$thisKeyField;
                }       
                $this->primaryKeyFilter .= htmlentities("PRIMARYKEY_".urlencode($thisKeyField)."=".urlencode($this->resultArray[$thisKeyField])."&");
            } // foreach keyArray
        } // if keyArray
        else
        {
            $this->primaryKeyFilter = "NO&amp;";
        }

    } // function buildKeyTags

/******************************************************************************
                                FIX CURRENT RECORD 
******************************************************************************/

    private function fixCurrentRec()
    {
        // This function is called to force the currentRec 
        // position counter to stay within bounds (not negative
        // and not greater than maxRec)
        if($this->maxRec > 1 && $this->currentRec >= $this->maxRec) 
        {
            $this->currentRec = "new";
        }
        if($this->currentRec < 0)
        {
            $this->currentRec = 0;
        }
    }  // fixCurrentRec
    
/******************************************************************************
                                PROCESS DATA 
******************************************************************************/

    public function processData()
    // Connect to and query database, and perform any operations directed 
    // in POST variables. This method should be called after the class has 
    // been constructed and variables set, but before output has been sent 
    // to client.
    {
        if($this->isProcessed)
        {
            return(0);
        }
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['PROCESSD_START'] = microtime(true);
        }

        if($this->makeConnection() === false) // connect to MySQL
        {
                return(false);
        }  
        $this->isProcessed = TRUE; // set flag to prevent processing twice.

        if(($GLOBALS['FIFORMS_CONFIG']['ENCRYPT_KEY']) && !$this->sheetView)
        {
            $this->inputs["_CHECK_DATA"] = new iHidden('_CHECK_DATA');
        }
        if(is_array($this->allowedParameters) && !$this->sheetView)
        {
            foreach($this->allowedParameters as $param)
            {
                if(!array_key_exists($param,$this->inputs))
                {
                  $this->addIn('iHidden',$param);
                }
                if(isset($_GET[$param]))
                {
                    $this->inputs[$param]->setValue($_GET[$param]);
                }
            }
        }
        // Insert or update any record in POST variables
        $saveSuccess = $this->savePostData();

        // set the maximum number of records to retreive
        if($this->sheetView && $this->recordLimit == "")
        {
            $this->recordLimit = $GLOBALS['FIFORMS_CONFIG']['DEFAULT_LIMIT'];
        }
        elseif($this->recordLimit == "" || !$this->sheetView)
        {
            $this->recordLimit = 1;
        }

        if($saveSuccess && $this->currentRec != "new") 
        {
            // The save was successful, and this isn't a new record, so
            // we need to query the database and display something      
        
            if($this->sheetView && $this->currentRec >= 1)
            {
                $this->currentRec -= $this->currentRec % $this->recordLimit;
            }

            // build query
            $this->buildSelectQuery();

            // record timing point
            if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
            {
                $this->timing['QUERYSTART'] = microtime(true);
            }
            
            // Execute any pre-queries
            foreach($this->preQueries as $pqKey => $preQuery)
            {
                // fill %% variables in $preQuery
                $sql = fillVars($_GET,$preQuery);
                $this->preQueries[$pqKey] = $sql;
                // execute the query
                $this->runQuery($sql);
            }
            $this->resultSet = $this->runQuery($this->dataQuery);

            // record timing point
            if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
            {
                $this->timing['QUERYEND'] = microtime(true);
            }
            if($this->resultSet == FALSE)
            {
                $err = mysql_error();
                if($GLOBALS['FIFORMS_CONFIG']['ALLOW_DD_OPERATIONS'] &&
                     strpos($err,'doesn\'t exist'))
                {
                    $uri = $GLOBALS['FIFORMS_CONFIG']['URI']."createSchema.php";

                    $suggestion = <<<EOD
                    <br /><p>
                    If you want, and if your account has enough permissions,
                    FiForms can correct this problem for you by creating the 
                    table that you need to use this form.
                    </p>
                    <p>
                    <a href="$uri?createDB=NO&amp;app=$_GET[app]&amp;formname=$_GET[formname]">Click here
                        to attempt to create the necessary table.</a>
                    </p>
EOD;
                    $this->throwError($suggestion);
                } // if doesn't exist error
            }  // if no result set
            elseif(mysql_num_rows($this->resultSet) < 1)
            {
                if($this->allowNewView && 
                    (!$this->sheetView || $this->allowFormView))
                {
                    // This is record edit mode, and there are no records, 
                    // so we need to go to a new record
                    $this->excludeLinkTerms[] = 'sheetView';
                    $newURL = html_entity_decode(iFormControl::buildMyURL(
                        $this->excludeLinkTerms));
		    if($GLOBALS['FIFORMS_CONFIG']['QUERY_DEBUGGING'])
		    {
		        echo($this->wrapper->debugQueries);
			echo('<a href="'.htmlentities($newURL).'&amp;currentRec=new">Click to Continue</a>');
			die();
		    }
		    else
		    {
                    header("Location: $newURL"."&currentRec=new");
                    die("Redirecting to $newURL"."&currentRec=new");
		    }
                }
                $err = mysql_error();
                if($err)
                {
                    $this->throwError("No rows returned from query:<br />".$err);
                }
            }
            $this->getNextRecord();
            if(strpos($this->dataQuery,"SQL_CALC_FOUND_ROWS"))
            {
              $explainQuery = ( "SELECT FOUND_ROWS() as rows;");
              $explainRslt = $this->runQuery($explainQuery);
              $explainArray = @mysql_fetch_array($explainRslt);
              $this->maxRec = $explainArray["rows"];
            }
            else if($this->where)
            {
                // get the count of rows by running count(*) 
                // NOTE: this is could be a source of performance issues 
                // and it is not always totally accurate. The preferred method is 
                // to always include SQL_CALC_FOUND_ROWS in the data query
                $explainQuery = (
                          "SELECT count( * )  as rows FROM ".
                  $this->dataTable.
                  " WHERE ".
                  $this->where
                );
                // run the query
                $explainRslt = $this->runQuery($explainQuery);
                $explainArray = @mysql_fetch_array($explainRslt);
                    
                $this->maxRec = $explainArray["rows"];
            }
            $this->fixCurrentRec();
    
            $this->buildKeyTags();

        } // if not currentRec=new
        elseif($saveSuccess)  
        // current record is new
        {
            if(count($_POST) > 0 && $this->noMoveOnInsert)
            {
                // Find the primary keys of the inserted record
                // and move back to that record, if noMoveOnInsert
                // is TRUE.
                foreach($this->keyArray as $keyName)
                {
                    $keyValue = $_POST[$keyName];
                if($keyValue == '')
                {
                        $keyValue = $this->insertID;
                }
                    $newKeyFilter .= "PRIMARYKEY_$keyName=$keyValue&";
                }
                $newKeyFilter .= "TRUE";
                        $newURL = html_entity_decode(iFormControl::buildMyURL($this->excludeLinkTerms));
		if($GLOBALS['FIFORMS_CONFIG']['QUERY_DEBUGGING'])
		{
		        echo($this->wrapper->debugQueries);
			echo('<a href="'.htmlentities($newURL)."PRIMARYKEYS=YES&amp;".htmlentities($newKeyFilter).'">Click to Continue</a>');
			die();
		}
		else
		{
                header("Location: ".$newURL."PRIMARYKEYS=YES&$newKeyFilter");
                die("Redirecting to ".$newURL."PRIMARYKEYS=YES&$newKeyFilter");
		}
            } 
        } // if new and save successful
        else  
        // save unsuccessful, restore data
        {
            $this->restoreRecord(); 
        }
        // Set up _CONTROLS page contorls
        if(isset($this->inputs["_CONTROLS"]))
        {
        $this->inputs["_CONTROLS"]->processControls();
        if($this->controlsOnTop)
        {
            $this->inputs['_CONTROLS']->htmlClass = "FiForm_Control_Top";
            $this->controlHeader = $this->inputs["_CONTROLS"]->drawInput();
            $this->controlHeaderSpacer = "<div class=\"FiFormControlSpacer\">&#160;</div>";
        }
        $this->inputs['_CONTROLS']->htmlClass = "FiForm_Control";
        $this->controlFooter = $this->inputs["_CONTROLS"]->drawInput();
        }


        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['PROCESSD_END'] = microtime(true);
        }
    } // function processData


/******************************************************************************
                                ERASE INPUT 
******************************************************************************/

    public function eraseIn($inputName)
    {
        unset($this->inputs[$inputName]);
    }

/******************************************************************************
                                RENAME INPUT 
******************************************************************************/

    public function renameIn($inputName,$newName)
    // doesn't really rename, just puts a different caption
    {
        $this->inputs[$inputName]->caption = $newName;
    }

/******************************************************************************
                                HIDE INPUT 
******************************************************************************/

    public function hideIn($inputName)
    {
        $this->inputs[$inputName]->otherTags .= " hidden=\"hidden\" ";
    }
/******************************************************************************
                                DISABLE INPUT 
******************************************************************************/

    public function disableIn($inputName = "_this")
    {
        $this->inputs[$inputName]->disableIn();
    }

/******************************************************************************
                                DRAW INPUT 
******************************************************************************/

    public function drawBody($type)
    {
        switch($type)
        {
            case 'fieldNames':
                return("<th class=\"FiFormSV\">$this->drawCaption</th>\n");
            case 'sv':
                return("<td class=\"FiFormSV\">$this->drawInput</td>\n");
            case 'body':
                return("\n<tr>\n<th class=\"FiForm\">$this->drawCaption</th>\n".
            "<td class=\"FiForm\">$this->drawInput</td>\n</tr>\n");

        }
    } // function drawBody

    function drawHead()
    {
        if($this->sheetView)
        {
            return($this->controlHeader.
            $this->controlHeaderSpacer.
            $this->wrapper->pageHeader.
            "<div id=\"calendardiv\" 
            style=\"position:absolute;visibility:hidden;\"></div><h1>".
            $this->wrapper->pageTitle.
            "</h1>".
            "<div class=\"ruler\"></div>".
            "<p class=\"error\">".
            $this->wrapper->errorMsg.
            "</p>"
            ."<table class=\"FiFormSV\">".$this->wrapper->formHeader);
        }
        else
        {
            return("<form ".($GLOBALS['FIFORMS_CONFIG']['USE_XHTML'] ? 
                    "id" : "name").
            "=\"$this->name\" ".
            "action=\"$this->formAction\" ".
            "method=\"post\" enctype=\"multipart/form-data\"
            onchange=\"markDirty();\"
            onsubmit=\"validateSubmit();\">".
            $this->controlHeaderSpacer.
            $this->wrapper->pageHeader.
            "<div id=\"calendardiv\" 
            style=\"position:absolute;visibility:hidden;\"></div><h1>".
            $this->wrapper->pageTitle.
            "</h1>".
            "<div class=\"ruler\"></div>".
            "<p class=\"error\">".
            $this->wrapper->errorMsg.
            "</p>".
            $this->wrapper->formHeader.
            $this->controlHeader
            ."<table class=\"FiForm\">");
        } // if-else
    }  // function drawHead

    function drawFoot()
    {
        if($this->sheetView)
        {
            return('</tbody></table>'.
                $this->wrapper->formFooter.$this->wrapper->pageFooter.
                '</div>'.$this->controlFooter);
        }
        else
        {
            return('</table>'.$this->wrapper->formFooter.
                                  $this->wrapper->pageFooter.'</div>'.
                                  $this->controlFooter.'</form>');
        }
    }  // function drawFoot

    public function drawInput($withFooter = TRUE)
    // return the output to draw the form (without the wrapper)
    // processData() should be called before this function is called.
    // Note that the actual process of calling the drawInput 
    // of each child iInput is done in outputRow() function, not here.
    {
        if(!$this->recordLimit)
        {
            $this->recordLimit = 1;
        }
        $this->recNum = $this->currentRec - ($this->currentRec % $this->recordLimit);
        if(!$this->allowSheetView && !$this->allowFormView)
        {
            return("This form has been disabled. No views are allowed.");
        }
        if(!$this->allowSheetView)
        {
            $this->sheetView = FALSE;
        }
        if(!$this->allowFormView)
        {
            $this->sheetView = TRUE;
        }
        if(count($this->keyArray) == 0)
        {
            $this->readOnly = TRUE;
        }
        
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['DRAWINGSTART'] = microtime(true);
        }
        $output = $this->drawHead();
        if($this->sheetView)
        {
            // Output a single header followed by multiple rows (records)
            // For now, sheet view is always read-only
            $this->readOnly = TRUE;
            $this->wrapper->headerTags .= <<<EOD
            <script type="text/javascript">
              sheetView = true;
            </script>
EOD;
            $output .= $this->formatStrSV->headStart;
            $output .= $this->outputRow($this,true,'fieldNames');
            $output .= $this->formatStrSV->headStop;
            $recordsRemain = true;
            $even = TRUE;
            // loop over each record
            while($this->isProcessed && $recordsRemain == true)
            {
                if($even)
                {
                    $output .= $this->formatStrSV->rowStartEven;
                }
                else
                {
                    $output .= $this->formatStrSV->rowStartOdd;
                }
                $even = !$even;  // alternate even and odd rows, for coloring
                $output .= $this->outputRow($this,false,'sv');
                $output .= $this->formatStrSV->rowStop;
                $recordsRemain = $this->getNextRecord();
                $this->recNum++;
            }  // while loop over all records
        } // if sheetView
        else
        {
            $this->wrapper->headerTags .= <<<EOD
            <script type="text/javascript">
              warnOnUnload = true;
            </script>
EOD;
            // output the normal FiForm view.
            $output .= $this->outputRow($this,true,'body');
        }
            // output the footer
        $output .= $this->drawFoot();
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['DRAWINGDONE'] = microtime(true);
        }
        //$this->closeConnection();
        return($output);
    
    } // function drawInput


/******************************************************************************
                                DRAW FORM 
******************************************************************************/

    public function drawForm()
    // Calls processData() to run all necessary queries of the database
    // server, then draws the form with all controls and returns
    // the result as a string, which can be placed in the body
    // of an HTML document.
    {
        $this->processData();
        return($this->drawInput());
    } //function drawForm
        

/******************************************************************************
                                DRAW FORM PAGE
******************************************************************************/

    public function drawFormPage()
    // Same as drawForm(), but wrapped in HTML to make a complete page
    {

        reset($this->inputs);
        list($ctrlName) = each($this->inputs);
        if($this->caption == "")
        {
            $this->wrapper->pageTitle = "Data Form: ".$this->name;
        }
        else
        {
            $this->wrapper->pageTitle = $this->caption;
        }
        if(!$this->readOnly && !$this->wrapper->bodyProperties && !$this->sheetView)
        {
            $this->wrapper->bodyProperties = 
              "  onload=\"FiForm_onLoad();\" ";
        }
        $form = $this->drawForm();
        $output = $this->wrapper->returnHeader(); 
        $output .= $form;
        $output .= $this->wrapper->returnFooter();
        
        return($output);
    }


/******************************************************************************
                                AUTO INPUTS 
******************************************************************************/

    public function autoInputs()
    {
        $this->makeConnection();
        // get a description of the table
        $describeQuery = "DESCRIBE ".$this->dataTable;
        $describeRslt = $this->runQuery($describeQuery);
        // loop through each field in the description
        while($describerow = mysql_fetch_array($describeRslt))
        {
            $type = $describerow["Type"];
        // determine the input type based on the field type
        if(strpos($type,'date') !== FALSE)
        {
                $this->inputs[$describerow["Field"]] = 
              new iDateText($describerow["Field"],$describerow["Field"]);
        }
        elseif(strpos($type,'enum') !== FALSE)
        {
            // convert the enum declaration into an array declaration
            // and evalutate it.
            $valueArray = array();
            eval("\$valueArray = array".substr($type,4).";");
                $this->inputs[$describerow["Field"]] = 
              new iDBSelect($describerow["Field"],
                    $describerow["Field"],
                $valueArray);
        }
        elseif(strpos($type,'text') !== FALSE)
        {
                $this->inputs[$describerow["Field"]] = 
              new iTextArea($describerow["Field"],$describerow["Field"],30,5);
        }
        else // default
        {
                $this->inputs[$describerow["Field"]] = 
              new iText($describerow["Field"],$describerow["Field"]);
        } // if       
        } // while
    }  // function autoInputs

    function throwError($errorMessage)
    {
        $this->wrapper->errorMsg .= $errorMessage;
        $this->error = true;
    }

    public function setQuery($sql)
    {
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['LOADXML_STARTSETQUERY'] = microtime(true);
        }
            $qparser = new QueryParser($sql);
            $query = trim(fillVars($_GET,$qparser->getClause('SELECT')." ".$qparser->getClause('FROM')));
            if($query)
            {
                $this->dataQuery = $query;
            }
            else
            {
                $this->dataQuery = ""; //"SELECT * FROM ".$this->dataTable;
            }
            $this->where = substr(trim(fillVars($_GET,$qparser->getClause('WHERE'))),5);
            $this->groupBy = substr(trim(fillVars($_GET,$qparser->getClause('GROUP BY'))),8);
            $this->having = substr(trim(fillVars($_GET,$qparser->getClause('HAVING'))),6);
            $this->orderBy = " ".trim($qparser->getClause('ORDER BY'));
        if($GLOBALS['FIFORMS_CONFIG']['PERF_STAT'])
        {
            $this->timing['LOADXML_ENDSETQUERY'] = microtime(true);
        }
    }

    public function clearPrimaryKeys()
    {
        $this->keyArray = array();
    }
    
    public function setPrimaryKey($keyName)
    {
        $this->keyArray = array_merge($this->keyArray,array($keyName));
    }
    
    public function compress($data)
    {
        if(function_exists('gzcompress'))
        {
            return gzcompress($data,9);
        }
        return $data;
    }

    public function decompress($data)
    {
        if(function_exists('gzuncompress'))
        {
            return gzuncompress($data);
        }
        return $data;
    }

} //class FiForm

/* ?></code><?php */

?>
Return current item: FiForms Framework