<?php
/**
* XML Document class for backup / import / export CB-data
*
*
*
*/
class xmldocument
{
/* pre-settings */
/* var $xmlNodeNames=array("root"=>"document",
"metainfo"=>"meta",
"articledata"=>"article",
"startlines" => array ("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ? >",
"<!DOCTYPE mitarbeiter SYSTEM 'mitarbeiter'>"));
*/
var $xmlHeader="<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ? >\n<!DOCTYPE CB_article SYSTEM 'CB_article'>";
// list of predefined XML-Node-Names in document; constructor will enhance this list
// with data delivered from clients, building a 3-level complete DOM-Tree
// Level deepness is represented by 1st level array position ("root"-element is level 0)
// this array structure defines CB XML Data Model in 1st and 2nd level ..
var $xmlNodes=array(
array("root"=>array("name"=>"document",
"parent"=>"root",
"value"=>""
)
),
array("metainfo"=>array("name"=>"meta",
"parent"=>"root",
"value"=>""
),
"articledata"=>array("name"=>"article",
"parent"=>"root",
"value"=>""
)
)
);
var $xmlFileDir="xmldir"; /* maybe overwritten by function setXmlFileDir($dir) */
var $xmlFileTime; /* will be set in constructor, maybe overriden by function setXmlFileTime()*/
var $safe_mode=FALSE; /* if set to TRUE, working with id-hashed subdirs will be avoided
set to TRUE happens by default in constructor if safe_mode is switched On */
var $xmlCDATANodeNames=array("title","intro","content","x_description","x_keywords"); // which xmlNodes contains CDATA content?
var $xmlFileList=array(); // after calling member function listXmlFiles() this list contains xmlFileNames
var $xmlDeepness=0; // expat parser var: actual node deepnees
var $xmlActiveNode=""; // expat parser var: actual node ...
var $parser; // Class-var for expat xml-parser object
var $xmldata=array();
var $xmlentity;
//var $metainfo=array();
//var $articledata=array();
var $articleID="";
var $cmsUser="";
var $xmlFileName;
/* Constructor of this class
* Function loads article-data into class-var arrays (no XML at this point ..) */
function xmldocument($articleID, $cmsUser, $metainfo, $articledata){
// set XML-file-dir, take care of safe_mode users ...
if ((ini_get("safe_mode"))||($this->safe_mode===TRUE)){
$this->safe_mode=TRUE;
}else{
$this->xmlFileDir.="/".$articleID;
}
// set $this->xmlFileTime
$this->setXmlFileTimeFromTime(time());
$this->articleID=$articleID;
$this->cmsUser=$cmsUser;
//$this->metainfo=$metainfo;
//$this->articledata=$articledata;
if (is_array($metainfo)){
foreach ($metainfo as $key => $value){
$this->xmlNodes[2][$key]["name"]=$key;
$this->xmlNodes[2][$key]["parent"]=$this->xmlNodes[1]["metainfo"]["name"];
$this->xmlNodes[2][$key]["value"]=$value;
}
}
if (is_array($articledata)){
foreach ($articledata as $key => $value){
$this->xmlNodes[2][$key]["name"]=$key;
$this->xmlNodes[2][$key]["parent"]=$this->xmlNodes[1]["articledata"]["name"];
$this->xmlNodes[2][$key]["value"]=$value;
}
}
// Initialisierung eines expat Parser-Objektes fuer das Einlesen von XML-Dateien
$this->parser=xml_parser_create();
xml_set_object($this->parser, $this);
xml_parser_set_option($this->parser,XML_OPTION_CASE_FOLDING,0);
xml_set_default_handler($this->parser, array(&$this, "xml_default_handler"));
xml_set_element_handler($this->parser, array(&$this, "xml_start_element_handler"), array(&$this, "xml_stop_element_handler"));
xml_set_character_data_handler($this->parser,array(&$this, "xml_value_handler"));
register_shutdown_function(array(&$this, "xml_parser_free"));
// only Maguma editor color display helper ...
$this->xmlHeader=str_replace("? >","?".">",$this->xmlHeader);
}
/* function listXmlFiles
* fuellt die array-Variable $xmlFileList mit den Dateinamen der gepeicherten XML-Dateien,
* die zum aktuellen Artikel gehoeren
* Gibt den Dateinamen des zuletzt gespeicherten XML zurueck (mit Pfadangabe) anhand
* des Zeitschluessels im Dateinamen
*/
function listXmlFiles(){
$regs=array();
$newestFile="";
if ((is_dir($this->xmlFileDir))&&($handle = opendir($this->xmlFileDir))) {
while (false !== ($file = readdir($handle))) {
$tmpID=$this->articleID;
if (is_file($this->xmlFileDir."/".$file)){
if (ereg("^".$tmpID."_(.+)[.]{1}xml$",$file,$regs)) {
$this->xmlFileList[]=$this->xmlFileDir."/".$file;
//trigger_error("\$file=".$file." \$regs[1]=".$regs[1]);
if ((! $newestFile)||(strcmp($regs[1],$newestFile)>0)) {
//trigger_error("\$newestFile='".$newestFile."' \$regs[1]='".$regs[1]."'");
$newestFile=$file;
}
} else {
//trigger_error("Fehler bei \$file ".$file);
}
}
}
closedir($handle);
}
return $this->xmlFileDir."/".$newestFile;
}
/*
* function loadXMLFile($fileName)
* Loads XML from $fileName to data structure $xmlNodes using expat parser
* returns TRUE if XML-File was written, FALSE otherwise
* @return boolean TRUE|FALSE
*/
function loadXmlFile($fileName){
// erzeuge Output-Dateiname, wenn durch das Objekt noch keiner erzeugt wurde ..
if ((! $fileName) && (! $this->xmlFileName)) {
$fileName=$this->listXmlFiles();
}
// fall out, if xml-file is not readable ...
if (! is_file($fileName)) return FALSE;
//trigger_error("\$fileName=".$fileName);
// get out all whitespaces, not neccessary to parse ...
$data=ereg_replace(">"."[[:space:]]+"."<","><",implode('', file($fileName)));
//trigger_error("$fileName='".$data."'");
if ($data){
$this->xmlNodes=array(); // empty existing nodes-object
$this->xmlDeepness=0;
$this->xmlActiveNode=array();
// let's parse via expat!!! actions are performed by handler functions defined in class constructor
$ret=xml_parse($this->parser,$data,TRUE);
if (! $ret){
trigger_error(sprintf("XML Error: %s at line %d",
xml_error_string(xml_get_error_code($this->parser)),
xml_get_current_line_number($this->parser)));
return FALSE; // we stranded in hell ...
}
return TRUE; // we are in heaven ...
}
//xml_parser_free($this->parser);
//print_r($this->xmlNodes);
//print_r($this->xmlActiveNode);
//trigger_error("\$this->xmlHeader='".$this->xmlHeader."'");
//trigger_error("\$this->xmlentity='".$this->xmlentity."'");
return FALSE;
}
/* Handler functions for expat parser */
function xml_default_handler($p,$data){
$this->xmlHeader .= $data;
}
function xml_start_element_handler($p,$name,$attrs){
//trigger_error("aktueller nodename='".$name."'");
$this->xmlDeepness++;
$this->xmlActiveNode[$this->xmlDeepness] = $name;
$this->xmlentity.="<".$name.">";
}
function xml_stop_element_handler($p,$name){
$this->xmlDeepness--;
//print_r($this->xmlNodes); echo "<hr>";
$this->xmldata=$this->xmlNodes;
}
function xml_value_handler($p,$data){
$tmpdata=$data;
$activeNode=$this->xmlActiveNode[$this->xmlDeepness];
$this->xmlNodes[$this->xmlDeepness][$activeNode]["name"]=$activeNode;
$this->xmlNodes[$this->xmlDeepness][$activeNode]["value"].=$tmpdata;
if ($this->xmlDeepness > 0){
$deepness=$this->xmlDeepness - 1;
$this->xmlNodes[$this->xmlDeepness][$activeNode]["parent"]=$this->xmlActiveNode[$deepness];
}
//print_r($this->xmlNodes); echo "<hr>";
}
function xml_parser_free() {
xml_parser_free($this->parser);
//trigger_error("shutdown...");
}
/**
* function saveXmlFile()
* Saves XML-File to <xml-dir>/<file-id>_yyyy-mm-dd_HH-MM-SS.xml if safe_mode is enabled,
* otherwise it saves something more nice to <xml-dir>/<file-id>/<file-id>_yyyy-mm-dd_HH-MM-SS.xml
* this function needs class data objects to be set before (via Constructor or function set ...)
* if last saved article-xml is aequivalent, no saving takes place ...
* returns TRUE if XML-File was written, FALSE otherwise
* @return boolean TRUE|FALSE
*/
function saveXmlFile() {
$oldContent="";
$newContent="";
$newestFile="";
// erzeuge Output-Dateiname, wenn durch das Objekt noch keiner erzeugt wurde ..
if (! $this->xmlFileName) {
$this->generateFilename($this->articleID);
}
// suche die letzte geschriebene XML-Version des Artikels und lade diese ggf.
if (($newestFile=$this->listXmlFiles())&&(is_file($newestFile))){
$oldContent=ereg_replace("\\\r","",implode('', file($newestFile)));
}
// schreibe den neuen Content in eine String-Variable ...
$newContent.=$this->xmlHeader."\n";
$newContent.="<".$this->xmlNodes[0]["root"]["name"].">\n";
foreach ($this->xmlNodes[1] as $akey => $avalues){
$newContent.="<".$avalues["name"].">\n";
foreach($this->xmlNodes[2] as $key => $values){
//trigger_error(implode(":",$values));
if ($values["parent"]==$avalues["name"]){
$newContent.="<".$values["name"].">";
// strings possibly containing html-markup needs special handling ...
if (in_array($values["name"],$this->xmlCDATANodeNames)){
$newContent.="<![CDATA[".$values["value"]."]]>";
}else{
$newContent.=htmlentities($values["value"]);
}
$newContent.="</".$values["name"].">\n";
}
}
$newContent.="</".$avalues["name"].">\n";
}
$newContent.="</".$this->xmlNodes[0]["root"]["name"].">\n";
// fertig: der gesamte XML_String ist zusammengebastelt ...
// jetzt noch die Windows-Spaesse rausparsen ...
$newContent=ereg_replace("\r","",$newContent);
// Vergleich: wurden Daten geaendert? Nur in diesem Fall eine neue Datei schreiben..
if (strcmp($oldContent,$newContent)!=0){
/* trigger_error("oldContent='".$oldContent."' Laenge=".strlen($oldContent));
trigger_error("Laenge=".strlen($oldContent));
trigger_error("newContent='".$newContent."' Laenge=".strlen($newContent));
for ($i=0;$i<strlen($newContent);$i++){
if (substr($oldContent,$i,1) != substr($newContent,$i,1)){
trigger_error("Fehlerposition ".$i."startend bei '".substr($oldContent,$i,25)."' mit oldContent-Zeichen '".ord( substr($oldContent,$i,1) )."' und newContent-Zeichen '".ord( substr($newContent,$i,1) )."'");
break;
}
}
*/ if ($this->xmlFileName) {
if ($fp=fopen($this->xmlFileName,"w")){
fputs($fp,$newContent,strlen($newContent));
fclose($fp);
return TRUE;
}
/* fputs($fp,"<".$this->xmlNodes[0]["root"]["name"].">\n");
foreach ($this->xmlNodes[1] as $akey => $avalues){
fputs($fp,"<".$avalues["name"].">\n");
foreach($this->xmlNodes[2] as $key => $values){
//trigger_error(implode(":",$values));
if ($values["parent"]==$avalues["name"]){
fputs($fp,"<".$values["name"].">");
$tmp=htmlspecialchars($values["value"]);
fputs($fp,$tmp,strlen($tmp));
fputs($fp,"</".$values["name"].">\n");
}
}
fputs($fp,"</".$avalues["name"].">\n");
}
*//* fputs($fp,"\t<".$this->xmlNodes[1]["articledata"]["name"].">\n");
foreach($this->articledata as $key => $values){
fputs($fp,"\t\t<".$key.">");
$tmp=htmlspecialchars($values);
fputs($fp,$tmp,strlen($tmp));
fputs($fp,"</".$key.">\n");
}
fputs($fp,"\t</".$this->xmlNodes[1]["articledata"]["name"].">\n");
*/
/* fputs($fp,"</".$this->xmlNodes[0]["root"]["name"].">\n");
fclose($fp);
return TRUE;
*/
}
}
return FALSE;
}
/* Function takes unix time in seconds and sets $this->xmlFileTime to formatted string.
* If you already have an appropriate prepared string, just use $this->xmlFileTime
* function setXmlFileTimeFromString($time)
* directly from your external code */
function setXmlFileTimeFromTime($time){
if ($tmp=date("Y-m-d_H-i-s",$time)){
$this->xmlFileTime=$tmp;
}
}
function setXmlFileTimeFromString($time){
if ($time) $this->xmlFileTime=$time;
}
/* Funktion fuer die Generierung eines XML-Dateinamens ...
* maybe more for class-internal usage ... */
function generateFilename($articleID) {
if (! is_dir($this->xmlFileDir)){
umask(0000);
mkdir($this->xmlFileDir,0777);
}
$this->xmlFileName=$this->xmlFileDir."/".$articleID."_".$this->xmlFileTime.".xml";
}
/* setup xmlFileDir overrides default class-var $xmlFileDir ... */
function setXmlFileDir($dir){
if (is_dir($dir)) $this->xmlFileDir=$dir;
}
/* returns member-var $this->xmldata ... */
function get_xmldata(){
return $this->xmldata;
}
/* returns member-var $this->xmlFileList ... */
function get_xmlFileList(){
return $this->xmlFileList;
}
}
?>