<?php
/*
xmlForm Class: Generate HTML forms from XML config file. HTML is generated using XSL transformations.
Version: 1.0
Copyright (C) 2002 Herman Veluwenkamp
Version: 2.0
Copyright (C) 2005 Yo Keller
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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
Copy of GNU Lesser General Public License at: http://www.gnu.org/copyleft/lesser.txt
Contact author at: hide@address.com
*/
class xmlForm {
var $verb = 0; // verbose flag
/*
Constructor initialising config file location
*/
function xmlForm($conf) {
//$conf = str_replace('&','&',$conf);
//$conf = str_replace('&','&',$conf);
$this->conf = $conf;
$this->conf_validate($conf);
}
/*
Validate $conf using rules defined in $conf
*/
function conf_validate($conf='') {
if (empty($conf)) $conf = $this->conf;
$this->error = '';
$this->validationError = FALSE; // flag validation error
$this->currentNode = ''; // keep track of current node
$this->newConf = ''; // growing XML output string
$this->var = ''; // keeps track of VAR name we are looking at
$this->type = ''; // keeps track of VAR type we are looking at
$this->regexp = false; // regexp belonging to current VAR
$this->exp = false; // exp associated to current VAR
$this->inputData = ''; // input data to be validated against XML config
$this->confValidation = true; // validation of xml configuration file
$this->file = ''; // keeps track of an associated file (e.g. selection list)
$xmlParser = xml_parser_create();
xml_parser_set_option($xmlParser, XML_OPTION_CASE_FOLDING, false); // preserve case
xml_set_element_handler($xmlParser, array(&$this,"startElement"), array(&$this,"endElement"));
xml_set_character_data_handler($xmlParser, array(&$this,"characterData"));
if (xml_parse($xmlParser, $conf)) $this->conf = $this->newConf; // if ok replace with new
else $this->error = sprintf("(conf_validate)XML Error: %s at line %d",
xml_error_string(xml_get_error_code($xmlParser)),
xml_get_current_line_number($xmlParser));
xml_parser_free($xmlParser);
return !$this->validationError;
}
/*
Validate $data using rules defined in $conf
*/
function validate($data) {
$this->error = '';
$this->validationError = FALSE; // flag validation error
$this->currentNode = ''; // keep track of current node
$this->newConf = ''; // growing XML output string
$this->var = ''; // keeps track of VAR name we are looking at
$this->type = ''; // keeps track of VAR type we are looking at
$this->regexp = false; // regexp belonging to current VAR
$this->exp = false; // exp associated to current VAR
$this->inputData = $data; // input data to be validated against XML config
$this->confValidation = false; // no validation of xml configuration file
$this->file = ''; // keeps track of an associated file (e.g. selection list)
if (!empty($data['formAction'])){
switch($data['formAction']){
case 'cancel':
return !$this->validationError;
break;
case 'ok':
$this->newConf .= "<formAction><act>".$data['formAction']."</act>\n";
break;
case 'apply':
$this->newConf .= "<formAction><act>".$data['formAction']."</act>\n";
break;
default:
$this->newConf .= "<formAction><act>".$data['formAction']."</act>\n";
//break;
}
}
$xmlParser = xml_parser_create();
xml_parser_set_option($xmlParser, XML_OPTION_CASE_FOLDING, false); // preserve case
xml_set_element_handler($xmlParser, array(&$this,"startElement"), array(&$this,"endElement"));
xml_set_character_data_handler($xmlParser, array(&$this,"characterData"));
if (xml_parse($xmlParser, $this->conf)) $this->conf = $this->newConf; // if ok replace with new
else $this->error = sprintf("(validate)XML Error: %s at line %d",
xml_error_string(xml_get_error_code($xmlParser)),
xml_get_current_line_number($xmlParser));
/*
print "<h2>Input data</h2>\n";
print_r($data);
print "<hr />";
*/
if (!empty($data['formAction'])){
switch($data['formAction']){
case 'ok':
$this->conf .= "</formAction>\n";
$this->newConf .= "</formAction>\n";
break;
case 'apply':
$this->conf .= "</formAction>\n";
$this->newConf .= "</formAction>\n";
break;
default:
$this->conf .= "</formAction>\n";
$this->newConf .= "</formAction>\n";
}
}
xml_parser_free($xmlParser);
if (!empty($this->error)){
print "<h2>XML Parse error:</h2>\n".$this->error."<hr />\n";
//print "<h2>generated data:</h2>\n".$this->newConf."<hr />\n";
//$this->conf = $this->newConf;
}
return !$this->validationError;
}
/*
Handler when start of element is encountered
*/
function startElement($parser, $name, $attributes) {
$this->currentNode = $name; // keep track of current node
switch($name) {
case 'value':
if (!$this->confValidation) return; // ignore VALUE nodes
break;
case 'var': // save var 'NAME' and 'TYPE' attributes
$this->var = $attributes['name'];
$this->type = $attributes['type'];
break;
}
$attributeString = ''; // concatenate attributes;
foreach ($attributes as $key => $value) $attributeString .= $key."=\"".$value."\" ";
$this->newConf .= "<$name $attributeString>";
}
/*
Handler when end of element is encountered
*/
function endElement($parser, $name) {
$this->currentNode = ''; // reset current node
switch($name) {
case 'value':
if (!$this->confValidation) return; // ignore VALUE nodes
break;
case 'var': // end of VAR element
if ($this->confValidation){
if ($this->type == "select-file" || $this->type == "multiselect-file" ){
if (empty($this->file) || !file_exists($this->file)){
$this->newConf .= "<validation-error>cannot find selection file".$this->file." </validation-error/>\n";
$this->validationError = TRUE; // flag validation error
}
// read the file and generate the option list
$fseln = fopen($this->file,'r');
$contents = fread($fseln, filesize($this->file));
fclose($fseln);
// do some cleanup
$contents = str_replace("\r","",$contents);
$contents = htmlentities($contents,ENT_QUOTES);
$contents = str_replace('&','&',$contents);
// ... and more cleanup
$sels = array();
$sels = explode("\n", $contents);
for($j=0;$j<sizeof($sels);$j++)
$sels[$j] = trim($sels[$j]);
$sels = array_unique($sels);
// ... and generate the option list
//foreach ($sels as $seln)
for($j=0;$j<sizeof($sels);$j++)
{
$jp = $j+1; // use values > 0! otherwise the 0-valued items are ignored ...
$this->newConf .= "<option value=\"".$jp."\">".$sels[$j]."</option>\n";
}
} // end if ($this->type == "select-file" || $this->type == "multiselect-file" )
} // end if ($this->confValidation)
else {
if (!$this->isVarValid()) {
$this->newConf .= "<validation-error/>\n";
$this->validationError = TRUE; // flag validation error
}
$data = $this->inputData[$this->var];
if (is_array($data)){
foreach($data as $k => $elt){
$data[$k] = htmlentities($elt,ENT_QUOTES);// 01-03-2005-10:59
$data[$k] = str_replace('&','&',$data[$k]);// 01-03-2005-10:59
}
}
else {
$data = htmlentities($data,ENT_QUOTES);// 01-03-2005-10:59
$data = str_replace('&','&',$data);// 01-03-2005-10:59
}
if (is_array($data)) $data = '<value>'.implode("</value>/n<value>", $data).'</value>';
else $data = "<value>$data</value>";
$this->newConf .= $data."\n";
}
break;
}
$this->newConf .= "</$name>\n";
}
/*
Handler for when character data is encounered
*/
function characterData($parser, $data) {
if ($this->currentNode == '') return; // ignore whitespace
if (!$this->confValidation && $this->currentNode == 'value') return; // ignore VALUE nodes
if ($this->currentNode == 'validation-regexp') $this->regexp = $data; // regular expression for validation
if ($this->currentNode == 'validation-exp') $this->exp = $data; // expression for validation
if ($this->currentNode == 'file') $this->file = $data; // keep the associated param file
$this->newConf .= $data; //str_replace('&','&',$data); //$data; //
if ($this->verb && $this->currentNode == 'validation-exp' && !empty($data))
echo "<h3>validation-exp: ".$this->exp." data: ".$data."</h3>\n";
}
/*
Validate current $var
*/
function isVarValid() {
$type = $this->type;
$var = $this->var;
$regexp = $this->regexp;
$exp = $this->exp;
$data = $this->inputData[$var];
switch ($type) {
case 'text':
$test_value = $data;
break;
case 'textarea':
$test_value = $data;
break;
case 'select':
$test_value = sizeof($data);
break;
case 'multiselect':
case 'multiselect-file':
$test_value = sizeof($data);
$value = @implode(',', $data);
break;
case 'radio':
$test_value = sizeof($data);
break;
case 'checkbox':
$test_value = sizeof($data);
$value = @implode(',', $data);
break;
case 'select-file':
$test_value = sizeof($data);
break;
default:
$test_value = $data;
}
$matches = array();
if (!preg_match($regexp, $test_value,$matches))return FAlSE;
elseif (empty($exp))return TRUE;
else
{
if($this->verb)echo "<h2>isVarValid-0: exp: ".$exp." </h2>\n";
$srch = array('matches','.lt.','.gt.','.eq.','.diff.');
$repl = array('\$matches','<','>','==','!='); // recover lt and gt signs etc - insert '$' in front of 'matches[i]'
$exp = str_replace($srch,$repl,$exp); // insert '$' in front of 'matches[i]'
if($this->verb)echo "<h2>isVarValid-1: exp: ".$exp." </h2>\n";
$res = eval("return (".$exp.");");
if($this->verb)echo "<h2>isVarValid-2: resultat: $res</h2>\n";
if($this->verb)print_r($matches);
if($this->verb)echo "<br />\n";
return $res;
}
}
/*
Perform XSL transformation on $conf passing in $parameters
*/
function xslTransform($xsl, $parameters) {
$result = '';
$hasError = FALSE;
$this->error = '';
$this->output = '';
$xh = xslt_create();
$conf = str_replace('&','&',$this->conf); //01-03-05 11:37
$args = array('/_xml' => $conf, '/_xsl' => $xsl);
$result = xslt_process($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $args, $parameters);
if ($result) $this->output = str_replace('&','&',str_replace('&','&',$result)); //str_replace('&','&',$result); // //$result; //01-03-05 11:14
else {
$this->error = "(xslTransform)XML Error: ".xslt_error($xh).". Code is ".xslt_errno($xh);
$hasError = TRUE;
}
xslt_free($xh);
return !$hasError;
}
}
?>