debug = 0;
$this->reset();
return;
}
public function __destruct()
{
return;
}
public function reset()
{
if ( $this->parser )
{
xml_parser_free($this->parser);
}
// XML file pointer
$this->fp = 0;
// which data has been read?
$this->metaDataRead = 0;
$this->allRead = 0;
// to maintain track of where we are inside the XML file
$this->inXml = 0;
$this->inData = 0;
$this->inMeta = 0;
$this->inSkin = 0;
$this->inTemplate = 0;
$this->currentName = '';
$this->currentPartName = '';
// character data pile
$this->cdata = '';
// list of skinnames and templatenames (will be array of array)
$this->skins = array();
$this->templates = array();
// extra info included in the XML files (e.g. installation notes)
$this->info = '';
// init XML parser, this parser deal with characters as encoded by UTF-8
$this->parser = xml_parser_create($this->parse_charset);
xml_set_object($this->parser, $this);
xml_set_element_handler($this->parser, 'start_element', 'end_element');
xml_set_character_data_handler($this->parser, 'character_data');
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
return;
}
/**
* Reads an XML file into memory
*
* @param $filename
* Which file to read
* @param $metaOnly
* Set to 1 when only the metadata needs to be read (optional, default 0)
*/
public function readFile($filename, $metaOnly = 0)
{
// open file
$this->fp = @fopen($filename, 'r');
if ( !$this->fp )
{
return _SKINIE_ERROR_FAILEDOPEN_FILEURL;
}
// here we go!
$this->inXml = 1;
$tempbuffer = null;
while ( !feof($this->fp) )
{
$tempbuffer .= fread($this->fp, 4096);
}
fclose($this->fp);
/*
* NOTE: conver character set.
* We hope all characters in the file also includes UTF-8 coded character set,
* because this PHP extension implements support for James Clark's expat in PHP
* and it supports juust US-ASCII, ISO-8859-1, UTF-8 character coding scheme.
*/
if ( i18n::get_current_charset() != $this->parse_charset )
{
$tempbuffer = i18n::convert($tempbuffer, i18n::get_current_charset(), $this->parse_charset);
}
$temp = tmpfile();
fwrite($temp, $tempbuffer);
rewind($temp);
while ( ($buffer = fread($temp, 4096) )
&& (!$metaOnly || ($metaOnly && !$this->metaDataRead)) )
{
$err = xml_parse( $this->parser, $buffer, feof($temp) );
if ( !$err && $this->debug )
{
echo 'ERROR: ', xml_error_string(xml_get_error_code($this->parser)), '
';
}
}
// all done
$this->inXml = 0;
fclose($temp);
return;
}
/**
* Returns the list of skin names
*/
public function getSkinNames()
{
return array_keys($this->skins);
}
/**
* Returns the list of template names
*/
public function getTemplateNames()
{
return array_keys($this->templates);
}
/**
* Returns the extra information included in the XML file
*/
public function getInfo()
{
return $this->info;
}
/**
* Writes the skins and templates to the database
*
* @param $allowOverwrite
* set to 1 when allowed to overwrite existing skins with the same name
* (default = 0)
*/
public function writeToDatabase($allowOverwrite = 0)
{
$existingSkins = $this->checkSkinNameClashes();
$existingTemplates = $this->checkTemplateNameClashes();
$invalidSkinNames = $this->checkSkinNamesValid();
$invalidTemplateNames = $this->checkTemplateNamesValid();
// if there are invalid skin or template names, stop executioin and return and error
if ( (sizeof($invalidSkinNames) > 0) || (sizeof($invalidTemplateNames) > 0) )
{
$inames_error = "
"._SKINIE_INVALID_NAMES_DETECTED."
\n";
$inames_error .= "";
foreach( $invalidSkinNames as $sName )
{
$inames_error .= "- ".i18n::hsc($sName)."
";
}
foreach( $invalidTemplateNames as $sName )
{
$inames_error .= "- ".i18n::hsc($sName)."
";
}
$inames_error .= "
";
return $inames_error;
}
// if not allowed to overwrite, check if any nameclashes exists
if ( !$allowOverwrite )
{
if ( (sizeof($existingSkins) > 0) || (sizeof($existingTemplates) > 0) )
{
return _SKINIE_NAME_CLASHES_DETECTED;
}
}
foreach ( $this->skins as $skinName => $data )
{
// 1. if exists: delete all part data, update desc data
// if not exists: create desc
if ( in_array($skinName, $existingSkins) )
{
$skinObj = SKIN::createFromName($skinName);
// delete all parts of the skin
$skinObj->deleteAllParts();
// update general info
$skinObj->updateGeneralInfo(
$skinName,
$data['description'],
$data['type'],
$data['includeMode'],
$data['includePrefix']
);
}
else
{
$skinid = SKIN::createNew(
$skinName,
$data['description'],
$data['type'],
$data['includeMode'],
$data['includePrefix']
);
$skinObj = new SKIN($skinid);
}
// 2. add parts
foreach ( $data['parts'] as $partName => $partContent )
{
$skinObj->update($partName, $partContent);
}
}
foreach ( $this->templates as $templateName => $data )
{
// 1. if exists: delete all part data, update desc data
// if not exists: create desc
if ( in_array($templateName, $existingTemplates) )
{
$templateObj = TEMPLATE::createFromName($templateName);
// delete all parts of the template
$templateObj->deleteAllParts();
// update general info
$templateObj->updateGeneralInfo($templateName, $data['description']);
}
else
{
$templateid = TEMPLATE::createNew($templateName, $data['description']);
$templateObj = new TEMPLATE($templateid);
}
// 2. add parts
foreach ( $data['parts'] as $partName => $partContent )
{
$templateObj->update($partName, $partContent);
}
}
return;
}
/**
* returns an array of all the skin nameclashes (empty array when no name clashes)
*/
public function checkSkinNameClashes()
{
$clashes = array();
foreach ( $this->skins as $skinName => $data )
{
if ( SKIN::exists($skinName) )
{
array_push($clashes, $skinName);
}
}
return $clashes;
}
/**
* returns an array of all the template nameclashes
* (empty array when no name clashes)
*/
public function checkTemplateNameClashes()
{
$clashes = array();
foreach ( $this->templates as $templateName => $data )
{
if ( TEMPLATE::exists($templateName) )
{
array_push($clashes, $templateName);
}
}
return $clashes;
}
/**
* returns an array of all the invalid skin names (empty array when no invalid names )
*/
private function checkSkinNamesValid()
{
$notValid = array();
foreach ( $this->skins as $skinName => $data )
{
if ( !isValidSkinName($skinName) )
{
array_push($notValid, $skinName);
}
}
return $notValid;
}
/**
* returns an array of all the invalid template names (empty array when no invalid names )
*/
private function checkTemplateNamesValid()
{
$notValid = array();
foreach ( $this->templates as $templateName => $data )
{
if ( !isValidTemplateName($templateName) )
{
array_push($notValid, $templateName);
}
}
return $notValid;
}
/**
* Called by XML parser for each new start element encountered
*/
private function start_element($parser, $name, $attrs)
{
foreach( $attrs as $key=>$value )
{
if ( $this->parse_charset != i18n::get_current_charset() )
{
$name = i18n::convert($name, $this->parse_charset, i18n::get_current_charset());
$value = i18n::convert($value, $this->parse_charset, i18n::get_current_charset());
}
$attrs[$key] = $value;
}
if ( $this->debug )
{
echo 'START: ', i18n::hsc($name), '
';
}
switch ( $name )
{
case 'nucleusskin':
$this->inData = 1;
break;
case 'meta':
$this->inMeta = 1;
break;
case 'info':
// no action needed
break;
case 'skin':
if ( !$this->inMeta )
{
$this->inSkin = 1;
$this->currentName = $attrs['name'];
$this->skins[$this->currentName]['type'] = $attrs['type'];
$this->skins[$this->currentName]['includeMode'] = $attrs['includeMode'];
$this->skins[$this->currentName]['includePrefix'] = $attrs['includePrefix'];
$this->skins[$this->currentName]['parts'] = array();
}
else
{
$this->skins[$attrs['name']] = array();
$this->skins[$attrs['name']]['parts'] = array();
}
break;
case 'template':
if ( !$this->inMeta )
{
$this->inTemplate = 1;
$this->currentName = $attrs['name'];
$this->templates[$this->currentName]['parts'] = array();
}
else
{
$this->templates[$attrs['name']] = array();
$this->templates[$attrs['name']]['parts'] = array();
}
break;
case 'description':
// no action needed
break;
case 'part':
$this->currentPartName = $attrs['name'];
break;
default:
echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . i18n::hsc($name) . '
';
break;
}
// character data never contains other tags
$this->clear_character_data();
return;
}
/**
* Called by the XML parser for each closing tag encountered
*/
private function end_elment($parser, $name)
{
if ( $this->debug )
{
echo 'END: ' . i18n::hsc($name) . '
';
}
if ( $this->parse_charset != i18n::get_current_charset() )
{
$name = i18n::convert($name, $this->parse_charset, i18n::get_current_charset());
$charset_data = i18n::convert($this->get_character_data(), $this->parse_charset, i18n::get_current_charset());
}
switch ( $name )
{
case 'nucleusskin':
$this->inData = 0;
$this->allRead = 1;
break;
case 'meta':
$this->inMeta = 0;
$this->metaDataRead = 1;
break;
case 'info':
$this->info = $charset_data;
case 'skin':
if ( !$this->inMeta )
{
$this->inSkin = 0;
}
break;
case 'template':
if ( !$this->inMeta )
{
$this->inTemplate = 0;
}
break;
case 'description':
if ( $this->inSkin )
{
$this->skins[$this->currentName]['description'] = $charset_data;
}
else
{
$this->templates[$this->currentName]['description'] = $charset_data;
}
break;
case 'part':
if ( $this->inSkin )
{
$this->skins[$this->currentName]['parts'][$this->currentPartName] = $charset_data;
}
else
{
$this->templates[$this->currentName]['parts'][$this->currentPartName] = $charset_data;
}
break;
default:
echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . i18n::hsc($name) . '
';
break;
}
$this->clear_character_data();
return;
}
/**
* Called by XML parser for data inside elements
*/
private function character_data ($parser, $data)
{
if ( $this->debug )
{
echo 'NEW DATA: ' . i18n::hsc($data) . '
';
}
$this->cdata .= $data;
return;
}
/**
* Returns the data collected so far
*/
private function get_character_data()
{
return $this->cdata;
}
/**
* Clears the data buffer
*/
private function clear_character_data()
{
$this->cdata = '';
return;
}
/**
* Static method that looks for importable XML files in subdirs of the given dir
*/
static public function searchForCandidates($dir)
{
$candidates = array();
$dirhandle = opendir($dir);
while ( $filename = readdir($dirhandle) )
{
if ( @is_dir($dir . $filename) && ($filename != '.') && ($filename != '..') )
{
$xml_file = $dir . $filename . '/skinbackup.xml';
if ( file_exists($xml_file) && is_readable($xml_file) )
{
//$xml_file;
$candidates[$filename] = $filename;
}
// backwards compatibility
$xml_file = $dir . $filename . '/skindata.xml';
if ( file_exists($xml_file) && is_readable($xml_file) )
{
//$xml_file;
$candidates[$filename] = $filename;
}
}
}
closedir($dirhandle);
return $candidates;
}
}
class SKINEXPORT {
private $templates;
private $skins;
private $info;
/**
* Constructor initializes data structures
*/
public function __construct()
{
// list of templateIDs to export
$this->templates = array();
// list of skinIDs to export
$this->skins = array();
// extra info to be in XML file
$this->info = '';
}
/**
* Adds a template to be exported
*
* @param id
* template ID
* @result false when no such ID exists
*/
public function addTemplate($id)
{
if ( !TEMPLATE::existsID($id) )
{
return 0;
}
$this->templates[$id] = TEMPLATE::getNameFromId($id);
return 1;
}
/**
* Adds a skin to be exported
*
* @param id
* skin ID
* @result false when no such ID exists
*/
public function addSkin($id)
{
if ( !SKIN::existsID($id) )
{
return 0;
}
$this->skins[$id] = SKIN::getNameFromId($id);
return 1;
}
/**
* Sets the extra info to be included in the exported file
*/
public function setInfo($info)
{
$this->info = $info;
}
/**
* Outputs the XML contents of the export file
*
* @param $setHeaders
* set to 0 if you don't want to send out headers
* (optional, default 1)
*/
public function export($setHeaders = 1)
{
if ( $setHeaders )
{
// make sure the mimetype is correct, and that the data does not show up
// in the browser, but gets saved into and XML file (popup download window)
header('Content-Type: text/xml; charset=' . i18n::get_current_charset());
header('Content-Disposition: attachment; filename="skinbackup.xml"');
header('Expires: 0');
header('Pragma: no-cache');
}
echo "\n";
// meta
echo "\t\n";
// skins
foreach ( $this->skins as $skinId => $skinName )
{
echo "\t\t" . '' . "\n";
}
// templates
foreach ( $this->templates as $templateId => $templateName )
{
echo "\t\t" . '' . "\n";
}
// extra info
if ( $this->info )
{
echo "\t\tinfo . "]]>\n";
}
echo "\t\n\n\n";
// contents skins
foreach ($this->skins as $skinId => $skinName)
{
$skinId = intval($skinId);
$skinObj = new SKIN($skinId);
echo "\t" . '' . "\n";
echo "\t\t" . i18n::hsc($skinObj->getDescription()) . "\n";
$res = sql_query('SELECT stype, scontent FROM '. sql_table('skin') .' WHERE sdesc=' . $skinId);
while ( $partObj = sql_fetch_object($res) )
{
echo "\t\t" . '';
echo 'escapeCDATA($partObj->scontent) . ']]>';
echo "\n\n";
}
echo "\t\n\n\n";
}
// contents templates
foreach ( $this->templates as $templateId => $templateName )
{
$templateId = intval($templateId);
echo "\t" . '' . "\n";
echo "\t\t" . i18n::hsc(TEMPLATE::getDesc($templateId)) . "\n";
$res = sql_query('SELECT tpartname, tcontent FROM '. sql_table('template') .' WHERE tdesc=' . $templateId);
while ( $partObj = sql_fetch_object($res) )
{
echo "\t\t" . '';
echo 'escapeCDATA($partObj->tcontent) . ']]>';
echo "\n\n";
}
echo "\t\n\n\n";
}
echo '';
}
/**
* Escapes CDATA content so it can be included in another CDATA section
*/
private function escapeCDATA($cdata)
{
return preg_replace('/]]>/', ']]]]>', $cdata);
}
}