OSDN Git Service

Now this trunk equal 3.62 without documentation. We need to test for install and...
[nucleus-jp/nucleus-jp-ancient.git] / utf8 / nucleus / libs / skinie.php
index 301d06a..99c15cb 100755 (executable)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
- * Copyright (C) 2002-2009 The Nucleus Group
+ * Copyright (C) 2002-2010 The Nucleus Group
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *     exporting Nucleus skins: SKINIMPORT and SKINEXPORT
  *
  * @license http://nucleuscms.org/license.txt GNU General Public License
- * @copyright Copyright (C) 2002-2009 The Nucleus Group
+ * @copyright Copyright (C) 2002-2010 The Nucleus Group
  * @version $Id$
  * @version $NucleusJP: skinie.php,v 1.9.2.1 2007/09/05 07:46:30 kimitake Exp $
  */
 
-class SKINIMPORT {\r
-\r
-       // hardcoded value (see constructor). When 1, interesting info about the\r
-       // parsing process is sent to the output\r
-       var $debug;\r
-\r
-       // parser/file pointer\r
-       var $parser;\r
-       var $fp;\r
-\r
-       // which data has been read?\r
-       var $metaDataRead;\r
-       var $allRead;\r
-\r
-       // extracted data\r
-       var $skins;\r
-       var $templates;\r
-       var $info;\r
-\r
-       // to maintain track of where we are inside the XML file\r
-       var $inXml;\r
-       var $inData;\r
-       var $inMeta;\r
-       var $inSkin;\r
-       var $inTemplate;\r
-       var $currentName;\r
-       var $currentPartName;\r
-       var $cdata;\r
-\r
-\r
-\r
-       /**\r
-        * constructor initializes data structures\r
-        */\r
-       function SKINIMPORT() {\r
-               // disable magic_quotes_runtime if it's turned on\r
-               set_magic_quotes_runtime(0);\r
-\r
-               // debugging mode?\r
-               $this->debug = 0;\r
-\r
-               $this->reset();\r
-\r
-       }\r
-\r
-       function reset() {\r
-               if ($this->parser)\r
-                       xml_parser_free($this->parser);\r
-\r
-               // XML file pointer\r
-               $this->fp = 0;\r
-\r
-               // which data has been read?\r
-               $this->metaDataRead = 0;\r
-               $this->allRead = 0;\r
-\r
-               // to maintain track of where we are inside the XML file\r
-               $this->inXml = 0;\r
-               $this->inData = 0;\r
-               $this->inMeta = 0;\r
-               $this->inSkin = 0;\r
-               $this->inTemplate = 0;\r
-               $this->currentName = '';\r
-               $this->currentPartName = '';\r
-\r
-               // character data pile\r
-               $this->cdata = '';\r
-\r
-               // list of skinnames and templatenames (will be array of array)\r
-               $this->skins = array();\r
-               $this->templates = array();\r
-\r
-               // extra info included in the XML files (e.g. installation notes)\r
-               $this->info = '';\r
-\r
-               // init XML parser\r
-               $this->parser = xml_parser_create();\r
-               xml_set_object($this->parser, $this);\r
-               xml_set_element_handler($this->parser, 'startElement', 'endElement');\r
-               xml_set_character_data_handler($this->parser, 'characterData');\r
-               xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);\r
-\r
-       }\r
-\r
-       /**\r
-        * Reads an XML file into memory\r
-        *\r
-        * @param $filename\r
-        *              Which file to read\r
-        * @param $metaOnly\r
-        *              Set to 1 when only the metadata needs to be read (optional, default 0)\r
-        */\r
-       function readFile($filename, $metaOnly = 0) {\r
-               // open file\r
-               $this->fp = @fopen($filename, 'r');\r
-               if (!$this->fp) {\r
-                       return _SKINIE_ERROR_FAILEDOPEN_FILEURL;\r
-               }\r
-\r
-               // here we go!\r
-               $this->inXml = 1;\r
-\r
-               $tempbuffer = null;\r
-\r
-               while (!feof($this->fp)) {\r
-                       $tempbuffer .= fread($this->fp, 4096);\r
-               }\r
-               fclose($this->fp);\r
-               $tempcharset = mb_detect_encoding($tempbuffer);\r
-               if ($tempcharset != 'UTF-8') {\r
-                       $tempbuffer = mb_convert_encoding($tempbuffer, 'UTF-8', $tempcharset);\r
-               }\r
-\r
-/*\r
-       [2004-08-04] dekarma - Took this out since it messes up good XML if it has skins/templates\r
-                                                  with CDATA sections. need to investigate consequences.\r
-                                                  see bug [ 999914 ] Import fails (multiple skins in XML/one of them with CDATA)\r
-\r
-               // backwards compatibility with the non-wellformed skinbackup.xml files\r
-               // generated by v2/v3 (when CDATA sections were present in skins)\r
-               // split up those CDATA sections into multiple ones\r
-               $tempbuffer = preg_replace_callback(\r
-                       "/(<!\[CDATA\[[^]]*?<!\[CDATA\[[^]]*)((?:\]\].*?<!\[CDATA.*?)*)(\]\])(.*\]\])/ms",\r
-                       create_function(\r
-                               '$matches',\r
-                               'return $matches[1] . preg_replace("/(\]\])(.*?<!\[CDATA)/ms","]]]]><![CDATA[$2",$matches[2])."]]]]><![CDATA[".$matches[4];'\r
-                       ),\r
-                       $tempbuffer\r
-               );\r
-*/\r
-               $temp = tmpfile();\r
-               fwrite($temp, $tempbuffer);\r
-               rewind($temp);\r
-\r
-               while ( ($buffer = fread($temp, 4096) ) && (!$metaOnly || ($metaOnly && !$this->metaDataRead))) {\r
-                       $err = xml_parse( $this->parser, $buffer, feof($temp) );\r
-                       if (!$err && $this->debug) {\r
-                               echo _ERROR . ': ' . xml_error_string(xml_get_error_code($this->parser)) . '<br />';\r
-                       }\r
-               }\r
-\r
-               // all done\r
-               $this->inXml = 0;\r
-               fclose($temp);\r
-       }\r
-\r
-       /**\r
-        * Returns the list of skin names\r
-        */\r
-       function getSkinNames() {\r
-               return array_keys($this->skins);\r
-       }\r
-\r
-       /**\r
-        * Returns the list of template names\r
-        */\r
-       function getTemplateNames() {\r
-               return array_keys($this->templates);\r
-       }\r
-\r
-       /**\r
-        * Returns the extra information included in the XML file\r
-        */\r
-       function getInfo() {\r
-               return $this->info;\r
-       }\r
-\r
-       /**\r
-        * Writes the skins and templates to the database\r
-        *\r
-        * @param $allowOverwrite\r
-        *              set to 1 when allowed to overwrite existing skins with the same name\r
-        *              (default = 0)\r
-        */\r
-       function writeToDatabase($allowOverwrite = 0) {\r
-               $existingSkins = $this->checkSkinNameClashes();\r
-               $existingTemplates = $this->checkTemplateNameClashes();\r
-\r
-               // if not allowed to overwrite, check if any nameclashes exists\r
-               if (!$allowOverwrite) {\r
-                       if ((sizeof($existingSkins) > 0) || (sizeof($existingTemplates) > 0)) {\r
-                               return _SKINIE_NAME_CLASHES_DETECTED;\r
-                       }\r
-               }\r
-\r
-               foreach ($this->skins as $skinName => $data) {\r
-                       // 1. if exists: delete all part data, update desc data\r
-                       //    if not exists: create desc\r
-                       if (in_array($skinName, $existingSkins)) {\r
-                               $skinObj = SKIN::createFromName($skinName);\r
-\r
-                               // delete all parts of the skin\r
-                               $skinObj->deleteAllParts();\r
-\r
-                               // update general info\r
-                               $skinObj->updateGeneralInfo(\r
-                                       $skinName,\r
-                                       $data['description'],\r
-                                       $data['type'],\r
-                                       $data['includeMode'],\r
-                                       $data['includePrefix']\r
-                               );\r
-                       } else {\r
-                               $skinid = SKIN::createNew(\r
-                                       $skinName,\r
-                                       $data['description'],\r
-                                       $data['type'],\r
-                                       $data['includeMode'],\r
-                                       $data['includePrefix']\r
-                               );\r
-                               $skinObj = new SKIN($skinid);\r
-                       }\r
-\r
-                       // 2. add parts\r
-                       foreach ($data['parts'] as $partName => $partContent) {\r
-                               $skinObj->update($partName, $partContent);\r
-                       }\r
-               }\r
-\r
-               foreach ($this->templates as $templateName => $data) {\r
-                       // 1. if exists: delete all part data, update desc data\r
-                       //    if not exists: create desc\r
-                       if (in_array($templateName, $existingTemplates)) {\r
-                               $templateObj = TEMPLATE::createFromName($templateName);\r
-\r
-                               // delete all parts of the template\r
-                               $templateObj->deleteAllParts();\r
-\r
-                               // update general info\r
-                               $templateObj->updateGeneralInfo($templateName, $data['description']);\r
-                       } else {\r
-                               $templateid = TEMPLATE::createNew($templateName, $data['description']);\r
-                               $templateObj = new TEMPLATE($templateid);\r
-                       }\r
-\r
-                       // 2. add parts\r
-                       foreach ($data['parts'] as $partName => $partContent) {\r
-                               $templateObj->update($partName, $partContent);\r
-                       }\r
-               }\r
-\r
-\r
-       }\r
-\r
-       /**\r
-         * returns an array of all the skin nameclashes (empty array when no name clashes)\r
-         */\r
-       function checkSkinNameClashes() {\r
-               $clashes = array();\r
-\r
-               foreach ($this->skins as $skinName => $data) {\r
-                       if (SKIN::exists($skinName)) {\r
-                               array_push($clashes, $skinName);\r
-                       }\r
-               }\r
-\r
-               return $clashes;\r
-       }\r
-\r
-       /**\r
-         * returns an array of all the template nameclashes\r
-         * (empty array when no name clashes)\r
-         */\r
-       function checkTemplateNameClashes() {\r
-               $clashes = array();\r
-\r
-               foreach ($this->templates as $templateName => $data) {\r
-                       if (TEMPLATE::exists($templateName)) {\r
-                               array_push($clashes, $templateName);\r
-                       }\r
-               }\r
-\r
-               return $clashes;\r
-       }\r
-\r
-       /**\r
-        * Called by XML parser for each new start element encountered\r
-        */\r
-       function startElement($parser, $name, $attrs) {\r
-               foreach($attrs as $key=>$value) {\r
-                       $attrs[$key] = htmlspecialchars($value, ENT_QUOTES);\r
-               }\r
-\r
-               if ($this->debug) {\r
-                       echo 'START: ' . htmlspecialchars($name, ENT_QUOTES) . '<br />';\r
-               }\r
-\r
-               switch ($name) {\r
-                       case 'nucleusskin':\r
-                               $this->inData = 1;\r
-                               break;\r
-                       case 'meta':\r
-                               $this->inMeta = 1;\r
-                               break;\r
-                       case 'info':\r
-                               // no action needed\r
-                               break;\r
-                       case 'skin':\r
-                               if (!$this->inMeta) {\r
-                                       $this->inSkin = 1;\r
-                                       $this->currentName = $attrs['name'];\r
-                                       $this->skins[$this->currentName]['type'] = $attrs['type'];\r
-                                       $this->skins[$this->currentName]['includeMode'] = $attrs['includeMode'];\r
-                                       $this->skins[$this->currentName]['includePrefix'] = $attrs['includePrefix'];\r
-                                       $this->skins[$this->currentName]['parts'] = array();\r
-                               } else {\r
-                                       $this->skins[$attrs['name']] = array();\r
-                                       $this->skins[$attrs['name']]['parts'] = array();\r
-                               }\r
-                               break;\r
-                       case 'template':\r
-                               if (!$this->inMeta) {\r
-                                       $this->inTemplate = 1;\r
-                                       $this->currentName = $attrs['name'];\r
-                                       $this->templates[$this->currentName]['parts'] = array();\r
-                               } else {\r
-                                       $this->templates[$attrs['name']] = array();\r
-                                       $this->templates[$attrs['name']]['parts'] = array();\r
-                               }\r
-                               break;\r
-                       case 'description':\r
-                               // no action needed\r
-                               break;\r
-                       case 'part':\r
-                               $this->currentPartName = $attrs['name'];\r
-                               break;\r
-                       default:\r
-                               echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . htmlspecialchars($name, ENT_QUOTES) . '<br />';\r
-                               break;\r
-               }\r
-\r
-               // character data never contains other tags\r
-               $this->clearCharacterData();\r
-\r
-       }\r
-\r
-       /**\r
-         * Called by the XML parser for each closing tag encountered\r
-         */\r
-       function endElement($parser, $name) {\r
-               if ($this->debug) {\r
-                       echo 'END: ' . htmlspecialchars($name, ENT_QUOTES) . '<br />';\r
-               }\r
-\r
-               switch ($name) {\r
-                       case 'nucleusskin':\r
-                               $this->inData = 0;\r
-                               $this->allRead = 1;\r
-                               break;\r
-                       case 'meta':\r
-                               $this->inMeta = 0;\r
-                               $this->metaDataRead = 1;\r
-                               break;\r
-                       case 'info':\r
-                               $this->info = $this->getCharacterData();\r
-                       case 'skin':\r
-                               if (!$this->inMeta) {\r
-                                       $this->inSkin = 0;\r
-                               }\r
-                               break;\r
-                       case 'template':\r
-                               if (!$this->inMeta) {\r
-                                       $this->inTemplate = 0;\r
-                               }\r
-                               break;\r
-                       case 'description':\r
-                               if ($this->inSkin) {\r
-                                       $this->skins[$this->currentName]['description'] = $this->getCharacterData();\r
-                               } else {\r
-                                       $this->templates[$this->currentName]['description'] = $this->getCharacterData();\r
-                               }\r
-                               break;\r
-                       case 'part':\r
-                               if ($this->inSkin) {\r
-                                       $this->skins[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();\r
-                               } else {\r
-                                       $this->templates[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();\r
-                               }\r
-                               break;\r
-                       default:\r
-                               echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . htmlspecialchars($name, ENT_QUOTES) . '<br />';\r
-                               break;\r
-               }\r
-               $this->clearCharacterData();\r
-\r
-       }\r
-\r
-       /**\r
-        * Called by XML parser for data inside elements\r
-        */\r
-       function characterData ($parser, $data) {\r
-               if ($this->debug) {\r
-                       echo 'NEW DATA: ' . htmlspecialchars($data, ENT_QUOTES) . '<br />';\r
-               }\r
-               $this->cdata .= $data;\r
-       }\r
-\r
-       /**\r
-        * Returns the data collected so far\r
-        */\r
-       function getCharacterData() {\r
-               if (_CHARSET != 'UTF-8') {\r
-                       return mb_convert_encoding($this->cdata, _CHARSET, 'UTF-8');\r
-               } else {\r
-                       return $this->cdata;\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Clears the data buffer\r
-        */\r
-       function clearCharacterData() {\r
-               $this->cdata = '';\r
-       }\r
-\r
-       /**\r
-        * Static method that looks for importable XML files in subdirs of the given dir\r
-        */\r
-       function searchForCandidates($dir) {\r
-               $candidates = array();\r
-\r
-               $dirhandle = opendir($dir);\r
-               while ($filename = readdir($dirhandle)) {\r
-                       if (@is_dir($dir . $filename) && ($filename != '.') && ($filename != '..')) {\r
-                               $xml_file = $dir . $filename . '/skinbackup.xml';\r
-                               if (file_exists($xml_file) && is_readable($xml_file)) {\r
-                                       $candidates[$filename] = $filename; //$xml_file;\r
-                               }\r
-\r
-                               // backwards compatibility\r
-                               $xml_file = $dir . $filename . '/skindata.xml';\r
-                               if (file_exists($xml_file) && is_readable($xml_file)) {\r
-                                       $candidates[$filename] = $filename; //$xml_file;\r
-                               }\r
-                       }\r
-               }\r
-               closedir($dirhandle);\r
-\r
-               return $candidates;\r
-\r
-       }\r
-\r
-\r
-}\r
-\r
-\r
-class SKINEXPORT {\r
-\r
-       var $templates;\r
-       var $skins;\r
-       var $info;\r
-\r
-       /**\r
-        * Constructor initializes data structures\r
-        */\r
-       function SKINEXPORT() {\r
-               // list of templateIDs to export\r
-               $this->templates = array();\r
-\r
-               // list of skinIDs to export\r
-               $this->skins = array();\r
-\r
-               // extra info to be in XML file\r
-               $this->info = '';\r
-       }\r
-\r
-       /**\r
-        * Adds a template to be exported\r
-        *\r
-        * @param id\r
-        *              template ID\r
-        * @result false when no such ID exists\r
-        */\r
-       function addTemplate($id) {\r
-               if (!TEMPLATE::existsID($id)) {\r
-                       return 0;\r
-               }\r
-\r
-\r
-               $this->templates[$id] = TEMPLATE::getNameFromId($id);\r
-\r
-               return 1;\r
-       }\r
-\r
-       /**\r
-        * Adds a skin to be exported\r
-        *\r
-        * @param id\r
-        *              skin ID\r
-        * @result false when no such ID exists\r
-        */\r
-       function addSkin($id) {\r
-               if (!SKIN::existsID($id)) {\r
-                       return 0;\r
-               }\r
-\r
-               $this->skins[$id] = SKIN::getNameFromId($id);\r
-\r
-               return 1;\r
-       }\r
-\r
-       /**\r
-        * Sets the extra info to be included in the exported file\r
-        */\r
-       function setInfo($info) {\r
-               $this->info = $info;\r
-       }\r
-\r
-\r
-       /**\r
-        * Outputs the XML contents of the export file\r
-        *\r
-        * @param $setHeaders\r
-        *              set to 0 if you don't want to send out headers\r
-        *              (optional, default 1)\r
-        */\r
-       function export($setHeaders = 1) {\r
-               if ($setHeaders) {\r
-                       // make sure the mimetype is correct, and that the data does not show up\r
-                       // in the browser, but gets saved into and XML file (popup download window)\r
-                       header('Content-Type: text/xml');\r
-                       header('Content-Disposition: attachment; filename="skinbackup.xml"');\r
-                       header('Expires: 0');\r
-                       header('Pragma: no-cache');\r
-               }\r
-\r
-               echo "<nucleusskin>\n";\r
-\r
-               // meta\r
-               echo "\t<meta>\n";\r
-                       // skins\r
-                       foreach ($this->skins as $skinId => $skinName) {\r
-                               $skinName = htmlspecialchars($skinName, ENT_QUOTES);\r
-                               echo "\t\t" . '<skin name="' . $skinName . '" />' . "\n";\r
-                       }\r
-                       // templates\r
-                       foreach ($this->templates as $templateId => $templateName) {\r
-                               $templateName = htmlspecialchars($templateName, ENT_QUOTES);\r
-                               echo "\t\t" . '<template name="' . $templateName . '" />' . "\n";\r
-                       }\r
-                       // extra info\r
-                       if ($this->info)\r
-                               echo "\t\t<info><![CDATA[" . $this->info . "]]></info>\n";\r
-               echo "\t</meta>\n\n\n";\r
-\r
-               // contents skins\r
-               foreach ($this->skins as $skinId => $skinName) {\r
-                       $skinId   = intval($skinId);\r
-                       $skinObj  = new SKIN($skinId);\r
-                       $skinName = htmlspecialchars($skinName, ENT_QUOTES);\r
-                       $contentT = htmlspecialchars($skinObj->getContentType(), ENT_QUOTES);\r
-                       $incMode  = htmlspecialchars($skinObj->getIncludeMode(), ENT_QUOTES);\r
-                       $incPrefx = htmlspecialchars($skinObj->getIncludePrefix(), ENT_QUOTES);\r
-                       $skinDesc = htmlspecialchars($skinObj->getDescription(), ENT_QUOTES);\r
-\r
-                       echo "\t" . '<skin name="' . $skinName . '" type="' . $contentT . '" includeMode="' . $incMode . '" includePrefix="' . $incPrefx . '">' . "\n";\r
-\r
-                       echo "\t\t" . '<description>' . $skinDesc . '</description>' . "\n";\r
-\r
-                       $que = 'SELECT'\r
-                                . '    stype,'\r
-                                . '    scontent '\r
-                                . 'FROM '\r
-                                .      sql_table('skin')\r
-                                . ' WHERE'\r
-                                . '    sdesc = ' . $skinId;\r
-                       $res = sql_query($que);\r
-                       while ($partObj = mysql_fetch_object($res)) {\r
-                               $type  = htmlspecialchars($partObj->stype, ENT_QUOTES);\r
-                               $cdata = $this->escapeCDATA($partObj->scontent);\r
-                               echo "\t\t" . '<part name="' . $type . '">';\r
-                               echo '<![CDATA[' . $cdata . ']]>';\r
-                               echo "</part>\n\n";\r
-                       }\r
-\r
-                       echo "\t</skin>\n\n\n";\r
-               }\r
-\r
-               // contents templates\r
-               foreach ($this->templates as $templateId => $templateName) {\r
-                       $templateId   = intval($templateId);\r
-                       $templateName = htmlspecialchars($templateName, ENT_QUOTES);\r
-                       $templateDesc = htmlspecialchars(TEMPLATE::getDesc($templateId), ENT_QUOTES);\r
-\r
-                       echo "\t" . '<template name="' . $templateName . '">' . "\n";\r
-\r
-                       echo "\t\t" . '<description>' . $templateDesc . "</description>\n";\r
-\r
-                       $que =  'SELECT'\r
-                                .     ' tpartname,'\r
-                                .     ' tcontent'\r
-                                . ' FROM '\r
-                                .     sql_table('template')\r
-                                . ' WHERE'\r
-                                .     ' tdesc = ' . $templateId;\r
-                       $res = sql_query($que);\r
-                       while ($partObj = mysql_fetch_object($res)) {\r
-                               $type  = htmlspecialchars($partObj->tpartname, ENT_QUOTES);\r
-                               $cdata = $this->escapeCDATA($partObj->tcontent);\r
-                               echo "\t\t" . '<part name="' . $type . '">';\r
-                               echo '<![CDATA[' .  $cdata . ']]>';\r
-                               echo '</part>' . "\n\n";\r
-                       }\r
-\r
-                       echo "\t</template>\n\n\n";\r
-               }\r
-\r
-               echo '</nucleusskin>';\r
-       }\r
-\r
-       /**\r
-        * Escapes CDATA content so it can be included in another CDATA section\r
-        */\r
-       function escapeCDATA($cdata)\r
-       {\r
-               return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata);\r
-\r
-       }\r
-}\r
-\r
+class SKINIMPORT {
+
+       // hardcoded value (see constructor). When 1, interesting info about the
+       // parsing process is sent to the output
+       var $debug;
+
+       // parser/file pointer
+       var $parser;
+       var $fp;
+
+       // which data has been read?
+       var $metaDataRead;
+       var $allRead;
+
+       // extracted data
+       var $skins;
+       var $templates;
+       var $info;
+
+       // to maintain track of where we are inside the XML file
+       var $inXml;
+       var $inData;
+       var $inMeta;
+       var $inSkin;
+       var $inTemplate;
+       var $currentName;
+       var $currentPartName;
+       var $cdata;
+
+
+
+       /**
+        * constructor initializes data structures
+        */
+       function SKINIMPORT() {
+               // disable magic_quotes_runtime if it's turned on
+               set_magic_quotes_runtime(0);
+
+               // debugging mode?
+               $this->debug = 0;
+
+               $this->reset();
+
+       }
+
+       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 = xml_parser_create();
+               xml_set_object($this->parser, $this);
+               xml_set_element_handler($this->parser, 'startElement', 'endElement');
+               xml_set_character_data_handler($this->parser, 'characterData');
+               xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
+
+       }
+
+       /**
+        * 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)
+        */
+       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);
+
+/*
+       [2004-08-04] dekarma - Took this out since it messes up good XML if it has skins/templates
+                                                  with CDATA sections. need to investigate consequences.
+                                                  see bug [ 999914 ] Import fails (multiple skins in XML/one of them with CDATA)
+
+               // backwards compatibility with the non-wellformed skinbackup.xml files
+               // generated by v2/v3 (when CDATA sections were present in skins)
+               // split up those CDATA sections into multiple ones
+               $tempbuffer = preg_replace_callback(
+                       "/(<!\[CDATA\[[^]]*?<!\[CDATA\[[^]]*)((?:\]\].*?<!\[CDATA.*?)*)(\]\])(.*\]\])/ms",
+                       create_function(
+                               '$matches',
+                               'return $matches[1] . preg_replace("/(\]\])(.*?<!\[CDATA)/ms","]]]]><![CDATA[$2",$matches[2])."]]]]><![CDATA[".$matches[4];'
+                       ),
+                       $tempbuffer
+               );
+*/
+               $temp = tmpfile();
+//             fwrite($temp, $tempbuffer);
+               if (!function_exists('mb_convert_encoding')) {
+                       fwrite($temp, $tempbuffer);
+               } else {
+                       if (strtoupper(_CHARSET) == 'ISO-8859-1') {
+                               fwrite($temp, $tempbuffer);
+                       } else {
+                               mb_detect_order("ASCII, JIS, SJIS, UTF-8, EUC-JP, ISO-8859-1");
+                               $temp_encode = mb_detect_encoding($tempbuffer);
+                               fwrite($temp, mb_convert_encoding($tempbuffer, 'UTF-8', $temp_encode));
+                       }
+               }
+               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)) . '<br />';
+                       }
+               }
+
+               // all done
+               $this->inXml = 0;
+               fclose($temp);
+       }
+
+       /**
+        * Returns the list of skin names
+        */
+       function getSkinNames() {
+               return array_keys($this->skins);
+       }
+
+       /**
+        * Returns the list of template names
+        */
+       function getTemplateNames() {
+               return array_keys($this->templates);
+       }
+
+       /**
+        * Returns the extra information included in the XML file
+        */
+       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)
+        */
+       function writeToDatabase($allowOverwrite = 0) {
+               $existingSkins = $this->checkSkinNameClashes();
+               $existingTemplates = $this->checkTemplateNameClashes();
+
+               // 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);
+                       }
+               }
+
+
+       }
+
+       /**
+         * returns an array of all the skin nameclashes (empty array when no name clashes)
+         */
+       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)
+         */
+       function checkTemplateNameClashes() {
+               $clashes = array();
+
+               foreach ($this->templates as $templateName => $data) {
+                       if (TEMPLATE::exists($templateName)) {
+                               array_push($clashes, $templateName);
+                       }
+               }
+
+               return $clashes;
+       }
+
+       /**
+        * Called by XML parser for each new start element encountered
+        */
+       function startElement($parser, $name, $attrs) {
+               foreach($attrs as $key=>$value) {
+                       $attrs[$key] = htmlspecialchars($value, ENT_QUOTES);
+               }
+
+               if ($this->debug) {
+                       echo 'START: ' . htmlspecialchars($name, ENT_QUOTES) . '<br />';
+               }
+
+               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 . htmlspecialchars($name, ENT_QUOTES) . '<br />';
+                               break;
+               }
+
+               // character data never contains other tags
+               $this->clearCharacterData();
+
+       }
+
+       /**
+         * Called by the XML parser for each closing tag encountered
+         */
+       function endElement($parser, $name) {
+               if ($this->debug) {
+                       echo 'END: ' . htmlspecialchars($name, ENT_QUOTES) . '<br />';
+               }
+
+               switch ($name) {
+                       case 'nucleusskin':
+                               $this->inData = 0;
+                               $this->allRead = 1;
+                               break;
+                       case 'meta':
+                               $this->inMeta = 0;
+                               $this->metaDataRead = 1;
+                               break;
+                       case 'info':
+                               $this->info = $this->getCharacterData();
+                       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'] = $this->getCharacterData();
+                               } else {
+                                       $this->templates[$this->currentName]['description'] = $this->getCharacterData();
+                               }
+                               break;
+                       case 'part':
+                               if ($this->inSkin) {
+                                       $this->skins[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
+                               } else {
+                                       $this->templates[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
+                               }
+                               break;
+                       default:
+                               echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . htmlspecialchars($name, ENT_QUOTES) . '<br />';
+                               break;
+               }
+               $this->clearCharacterData();
+
+       }
+
+       /**
+        * Called by XML parser for data inside elements
+        */
+       function characterData ($parser, $data) {
+               if ($this->debug) {
+                       echo 'NEW DATA: ' . htmlspecialchars($data, ENT_QUOTES) . '<br />';
+               }
+               $this->cdata .= $data;
+       }
+
+       /**
+        * Returns the data collected so far
+        */
+       function getCharacterData() {
+//             echo $this->cdata;
+               if ( (strtoupper(_CHARSET) == 'UTF-8')
+                       or (strtoupper(_CHARSET) == 'ISO-8859-1')
+                       or (!function_exists('mb_convert_encoding')) ) {
+                       return $this->cdata;
+               } else {
+                       return mb_convert_encoding($this->cdata, _CHARSET ,'UTF-8');
+               }
+       }
+
+       /**
+        * Clears the data buffer
+        */
+       function clearCharacterData() {
+               $this->cdata = '';
+       }
+
+       /**
+        * Static method that looks for importable XML files in subdirs of the given dir
+        */
+       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)) {
+                                       $candidates[$filename] = $filename; //$xml_file;
+                               }
+
+                               // backwards compatibility
+                               $xml_file = $dir . $filename . '/skindata.xml';
+                               if (file_exists($xml_file) && is_readable($xml_file)) {
+                                       $candidates[$filename] = $filename; //$xml_file;
+                               }
+                       }
+               }
+               closedir($dirhandle);
+
+               return $candidates;
+
+       }
+
+
+}
+
+
+class SKINEXPORT {
+
+       var $templates;
+       var $skins;
+       var $info;
+
+       /**
+        * Constructor initializes data structures
+        */
+       function SKINEXPORT() {
+               // 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
+        */
+       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
+        */
+       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
+        */
+       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)
+        */
+       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');
+                       header('Content-Disposition: attachment; filename="skinbackup.xml"');
+                       header('Expires: 0');
+                       header('Pragma: no-cache');
+               }
+
+               echo "<nucleusskin>\n";
+
+               // meta
+               echo "\t<meta>\n";
+                       // skins
+                       foreach ($this->skins as $skinId => $skinName) {
+                               $skinName = htmlspecialchars($skinName, ENT_QUOTES);
+                               if (strtoupper(_CHARSET) != 'UTF-8') {
+                                       $skinName = mb_convert_encoding($skinName, 'UTF-8', _CHARSET);
+                               }
+                               echo "\t\t" . '<skin name="' . htmlspecialchars($skinName, ENT_QUOTES) . '" />' . "\n";
+                       }
+                       // templates
+                       foreach ($this->templates as $templateId => $templateName) {
+                               $templateName = htmlspecialchars($templateName, ENT_QUOTES);
+                               if (strtoupper(_CHARSET) != 'UTF-8') {
+                                       $templateName = mb_convert_encoding($templateName, 'UTF-8', _CHARSET);
+                               }
+                               echo "\t\t" . '<template name="' . htmlspecialchars($templateName, ENT_QUOTES) . '" />' . "\n";
+                       }
+                       // extra info
+                       if ($this->info) {
+                               if (strtoupper(_CHARSET) != 'UTF-8') {
+                                       $skin_info = mb_convert_encoding($this->info, 'UTF-8', _CHARSET);
+                               } else {
+                                       $skin_info = $this->info;
+                               }
+                               echo "\t\t<info><![CDATA[" . $skin_info . "]]></info>\n";
+                       }
+               echo "\t</meta>\n\n\n";
+
+               // contents skins
+               foreach ($this->skins as $skinId => $skinName) {
+                       $skinId   = intval($skinId);
+                       $skinObj  = new SKIN($skinId);
+                       $skinName = htmlspecialchars($skinName, ENT_QUOTES);
+                       $contentT = htmlspecialchars($skinObj->getContentType(), ENT_QUOTES);
+                       $incMode  = htmlspecialchars($skinObj->getIncludeMode(), ENT_QUOTES);
+                       $incPrefx = htmlspecialchars($skinObj->getIncludePrefix(), ENT_QUOTES);
+                       $skinDesc = htmlspecialchars($skinObj->getDescription(), ENT_QUOTES);
+                       if (strtoupper(_CHARSET) != 'UTF-8') {
+                               $skinName = mb_convert_encoding($skinName, 'UTF-8', _CHARSET);
+                               $contentT = mb_convert_encoding($contentT, 'UTF-8', _CHARSET);
+                               $incMode  = mb_convert_encoding($incMode,  'UTF-8', _CHARSET);
+                               $incPrefx = mb_convert_encoding($incPrefx, 'UTF-8', _CHARSET);
+                               $skinDesc = mb_convert_encoding($skinDesc, 'UTF-8', _CHARSET);
+                       }
+
+                       echo "\t" . '<skin name="' . $skinName . '" type="' . $contentT . '" includeMode="' . $incMode . '" includePrefix="' . $incPrefx . '">' . "\n";
+
+                       echo "\t\t" . '<description>' . $skinDesc . '</description>' . "\n";
+
+                       $que = 'SELECT'
+                                . '    stype,'
+                                . '    scontent '
+                                . 'FROM '
+                                .      sql_table('skin')
+                                . ' WHERE'
+                                . '    sdesc = ' . $skinId;
+                       $res = sql_query($que);
+                       while ($partObj = sql_fetch_object($res)) {
+                               $type  = htmlspecialchars($partObj->stype, ENT_QUOTES);
+                               $cdata = $this->escapeCDATA($partObj->scontent);
+                               if (strtoupper(_CHARSET) != 'UTF-8') {
+                                       $type  = mb_convert_encoding($type,  'UTF-8', _CHARSET);
+                                       $cdata = mb_convert_encoding($cdata, 'UTF-8', _CHARSET);
+                               }
+                               echo "\t\t" . '<part name="' . $type . '">';
+                               echo '<![CDATA[' . $cdata . ']]>';
+                               echo "</part>\n\n";
+                       }
+
+                       echo "\t</skin>\n\n\n";
+               }
+
+               // contents templates
+               foreach ($this->templates as $templateId => $templateName) {
+                       $templateId   = intval($templateId);
+                       $templateName = htmlspecialchars($templateName, ENT_QUOTES);
+                       $templateDesc = htmlspecialchars(TEMPLATE::getDesc($templateId), ENT_QUOTES);
+                       if (strtoupper(_CHARSET) != 'UTF-8') {
+                               $templateName = mb_convert_encoding($templateName, 'UTF-8', _CHARSET);
+                               $templateDesc = mb_convert_encoding($templateDesc, 'UTF-8', _CHARSET);
+                       }
+
+                       echo "\t" . '<template name="' . $templateName . '">' . "\n";
+
+                       echo "\t\t" . '<description>' . $templateDesc . "</description>\n";
+
+                       $que =  'SELECT'
+                                .     ' tpartname,'
+                                .     ' tcontent'
+                                . ' FROM '
+                                .     sql_table('template')
+                                . ' WHERE'
+                                .     ' tdesc = ' . $templateId;
+                       $res = sql_query($que);
+                       while ($partObj = sql_fetch_object($res)) {
+                               $type  = htmlspecialchars($partObj->tpartname, ENT_QUOTES);
+                               $cdata = $this->escapeCDATA($partObj->tcontent);
+                               if (strtoupper(_CHARSET) != 'UTF-8') {
+                                       $type  = mb_convert_encoding($type,  'UTF-8', _CHARSET);
+                                       $cdata = mb_convert_encoding($cdata, 'UTF-8', _CHARSET);
+                               }
+                               echo "\t\t" . '<part name="' . $type . '">';
+                               echo '<![CDATA[' .  $cdata . ']]>';
+                               echo '</part>' . "\n\n";
+                       }
+
+                       echo "\t</template>\n\n\n";
+               }
+
+               echo '</nucleusskin>';
+       }
+
+       /**
+        * Escapes CDATA content so it can be included in another CDATA section
+        */
+       function escapeCDATA($cdata)
+       {
+               return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata);
+
+       }
+}
+
 ?>
\ No newline at end of file