OSDN Git Service

Merge branch 'skinnable-master'
[nucleus-jp/nucleus-next.git] / nucleus / libs / skinie.php
index 3b06a30..03be6c1 100644 (file)
@@ -1,7 +1,8 @@
+<<<<<<< HEAD
 <?php\r
 /*\r
  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)\r
- * Copyright (C) 2002-2009 The Nucleus Group\r
+ * Copyright (C) 2002-2012 The Nucleus Group\r
  *\r
  * This program is free software; you can redistribute it and/or\r
  * modify it under the terms of the GNU General Public License\r
  *     exporting Nucleus skins: SKINIMPORT and SKINEXPORT\r
  *\r
  * @license http://nucleuscms.org/license.txt GNU General Public License\r
- * @copyright Copyright (C) 2002-2009 The Nucleus Group\r
+ * @copyright Copyright (C) 2002-2012 The Nucleus Group\r
  * @version $Id: skinie.php 1624 2012-01-09 11:36:20Z sakamocchi $\r
  */\r
 \r
-class SKINIMPORT\r
+class SkinImport\r
 {\r
        // hardcoded value (see constructor). When 1, interesting info about the\r
        // parsing process is sent to the output\r
@@ -222,11 +223,11 @@ class SKINIMPORT
                        $inames_error .= "<ul>";\r
                        foreach( $invalidSkinNames as $sName )\r
                        {\r
-                               $inames_error .= "<li>".ENTITY::hsc($sName)."</li>";\r
+                               $inames_error .= "<li>".Entity::hsc($sName)."</li>";\r
                        }\r
                        foreach( $invalidTemplateNames as $sName )\r
                        {\r
-                               $inames_error .= "<li>".ENTITY::hsc($sName)."</li>";\r
+                               $inames_error .= "<li>".Entity::hsc($sName)."</li>";\r
                        }\r
                        $inames_error .= "</ul>";\r
                        return $inames_error;\r
@@ -286,7 +287,7 @@ class SKINIMPORT
                        //    if not exists: create desc\r
                        if ( in_array($templateName, $existingTemplates) )\r
                        {\r
-                               $templateObj = TEMPLATE::createFromName($templateName);\r
+                               $templateObj = Template::createFromName($templateName);\r
                                \r
                                // delete all parts of the template\r
                                $templateObj->deleteAllParts();\r
@@ -296,8 +297,8 @@ class SKINIMPORT
                        }\r
                        else\r
                        {\r
-                               $templateid = TEMPLATE::createNew($templateName, $data['description']);\r
-                               $templateObj = new TEMPLATE($templateid);\r
+                               $templateid = Template::createNew($templateName, $data['description']);\r
+                               $templateObj = new Template($templateid);\r
                        }\r
                        \r
                        // 2. add parts\r
@@ -336,7 +337,7 @@ class SKINIMPORT
                \r
                foreach ( $this->templates as $templateName => $data )\r
                {\r
-                       if ( TEMPLATE::exists($templateName) )\r
+                       if ( Template::exists($templateName) )\r
                        {\r
                                array_push($clashes, $templateName);\r
                        }\r
@@ -396,7 +397,7 @@ class SKINIMPORT
                \r
                if ( $this->debug )\r
                {\r
-                       echo 'START: ', ENTITY::hsc($name), '<br />';\r
+                       echo 'START: ', Entity::hsc($name), '<br />';\r
                }\r
                \r
                switch ( $name )\r
@@ -446,7 +447,7 @@ class SKINIMPORT
                                $this->currentPartName = $attrs['name'];\r
                                break;\r
                        default:\r
-                               echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . ENTITY::hsc($name) . '<br />';\r
+                               echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . Entity::hsc($name) . '<br />';\r
                                break;\r
                }\r
                // character data never contains other tags\r
@@ -461,7 +462,7 @@ class SKINIMPORT
        {\r
                if ( $this->debug )\r
                {\r
-                       echo 'END: ' . ENTITY::hsc($name) . '<br />';\r
+                       echo 'END: ' . Entity::hsc($name) . '<br />';\r
                }\r
                \r
                if ( $this->parse_charset != i18n::get_current_charset() )\r
@@ -519,7 +520,7 @@ class SKINIMPORT
                                }\r
                                break;\r
                        default:\r
-                               echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . ENTITY::hsc($name) . '<br />';\r
+                               echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . Entity::hsc($name) . '<br />';\r
                                break;\r
                }\r
                $this->clear_character_data();\r
@@ -533,7 +534,7 @@ class SKINIMPORT
        {\r
                if ( $this->debug )\r
                {\r
-                       echo 'NEW DATA: ' . ENTITY::hsc($data) . '<br />';\r
+                       echo 'NEW DATA: ' . Entity::hsc($data) . '<br />';\r
                }\r
                $this->cdata .= $data;\r
                return;\r
@@ -589,7 +590,8 @@ class SKINIMPORT
        }\r
 }\r
 \r
-class SKINEXPORT {\r
+class SkinExport\r
+{\r
        private $templates;\r
        private $skins;\r
        private $info;\r
@@ -618,12 +620,12 @@ class SKINEXPORT {
         */\r
        public function addTemplate($id)\r
        {\r
-               if ( !TEMPLATE::existsID($id) )\r
+               if ( !Template::existsID($id) )\r
                {\r
                        return 0;\r
                }\r
                \r
-               $this->templates[$id] = TEMPLATE::getNameFromId($id);\r
+               $this->templates[$id] = Template::getNameFromId($id);\r
                return 1;\r
        }\r
        \r
@@ -679,12 +681,12 @@ class SKINEXPORT {
                // skins\r
                foreach ( $this->skins as $skinId => $skinName )\r
                {\r
-                       echo "\t\t" . '<skin name="' . ENTITY::hsc($skinName) . '" />' . "\n";\r
+                       echo "\t\t" . '<skin name="' . Entity::hsc($skinName) . '" />' . "\n";\r
                }\r
                // templates\r
                foreach ( $this->templates as $templateId => $templateName )\r
                {\r
-                       echo "\t\t" . '<template name="' . ENTITY::hsc($templateName) . '" />' . "\n";\r
+                       echo "\t\t" . '<template name="' . Entity::hsc($templateName) . '" />' . "\n";\r
                }\r
                // extra info\r
                if ( $this->info )\r
@@ -699,14 +701,14 @@ class SKINEXPORT {
                        $skinId = intval($skinId);\r
                        $skinObj = new SKIN($skinId);\r
                        \r
-                       echo "\t" . '<skin name="' . ENTITY::hsc($skinName) . '" type="' . ENTITY::hsc($skinObj->getContentType()) . '" includeMode="' . ENTITY::hsc($skinObj->getIncludeMode()) . '" includePrefix="' . ENTITY::hsc($skinObj->getIncludePrefix()) . '">' . "\n";\r
-                       echo "\t\t<description>" . ENTITY::hsc($skinObj->getDescription()) . "</description>\n";\r
+                       echo "\t" . '<skin name="' . Entity::hsc($skinName) . '" type="' . Entity::hsc($skinObj->getContentType()) . '" includeMode="' . Entity::hsc($skinObj->getIncludeMode()) . '" includePrefix="' . Entity::hsc($skinObj->getIncludePrefix()) . '">' . "\n";\r
+                       echo "\t\t<description>" . Entity::hsc($skinObj->getDescription()) . "</description>\n";\r
                        \r
-                       $res = sql_query('SELECT stype, scontent FROM '. sql_table('skin') .' WHERE sdesc=' . $skinId);\r
-                       while ( $partObj = sql_fetch_object($res) )\r
+                       $res = DB::getResult('SELECT stype, scontent FROM '. sql_table('skin') .' WHERE sdesc=' . $skinId);\r
+                       foreach ( $res as $row )\r
                        {\r
-                               echo "\t\t" . '<part name="',ENTITY::hsc($partObj->stype) . '">';\r
-                               echo '<![CDATA[' . $this->escapeCDATA($partObj->scontent) . ']]>';\r
+                               echo "\t\t" . '<part name="',Entity::hsc($row['stype']) . '">';\r
+                               echo '<![CDATA[' . $this->escapeCDATA($row['scontent']) . ']]>';\r
                                echo "</part>\n\n";\r
                        }\r
                        echo "\t</skin>\n\n\n";\r
@@ -717,14 +719,14 @@ class SKINEXPORT {
                {\r
                        $templateId = intval($templateId);\r
                        \r
-                       echo "\t" . '<template name="' . ENTITY::hsc($templateName) . '">' . "\n";\r
-                       echo "\t\t<description>" . ENTITY::hsc(TEMPLATE::getDesc($templateId)) . "</description>\n";\r
+                       echo "\t" . '<template name="' . Entity::hsc($templateName) . '">' . "\n";\r
+                       echo "\t\t<description>" . Entity::hsc(Template::getDesc($templateId)) . "</description>\n";\r
                        \r
-                       $res = sql_query('SELECT tpartname, tcontent FROM '. sql_table('template') .' WHERE tdesc=' . $templateId);\r
-                       while ( $partObj = sql_fetch_object($res) )\r
+                       $res = DB::getResult('SELECT tpartname, tcontent FROM '. sql_table('template') .' WHERE tdesc=' . $templateId);\r
+                       foreach ( $res as $row )\r
                        {\r
-                               echo "\t\t" . '<part name="' . ENTITY::hsc($partObj->tpartname) . '">';\r
-                               echo '<![CDATA[' . $this->escapeCDATA($partObj->tcontent) . ']]>';\r
+                               echo "\t\t" . '<part name="' . Entity::hsc($row['tpartname']) . '">';\r
+                               echo '<![CDATA[' . $this->escapeCDATA($row['tcontent']) . ']]>';\r
                                echo "</part>\n\n";\r
                        }\r
                        \r
@@ -741,3 +743,753 @@ class SKINEXPORT {
                return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata);\r
        }\r
 }\r
+=======
+<?php
+/*
+ * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
+ * Copyright (C) 2002-2009 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
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * (see nucleus/documentation/index.html#license for more info)
+ */
+/**
+ *     This class contains two classes that can be used for importing and
+ *     exporting Nucleus skins: SKINIMPORT and SKINEXPORT
+ *
+ * @license http://nucleuscms.org/license.txt GNU General Public License
+ * @copyright Copyright (C) 2002-2009 The Nucleus Group
+ * @version $Id: skinie.php 1883 2012-06-17 07:55:47Z sakamocchi $
+ */
+
+class SkinImport
+{
+       // hardcoded value (see constructor). When 1, interesting info about the
+       // parsing process is sent to the output
+       private $debug;
+       
+       // parser/file pointer
+       private $parser;
+       private $fp;
+       
+       // parset internal charset, US-ASCII/ISO-8859-1/UTF-8
+       private $parse_charset = 'UTF-8';
+       
+       // which data has been read?
+       private $metaDataRead;
+       private $allRead;
+       
+       // extracted data
+       private $skins;
+       private $templates;
+       private $info;
+       
+       // to maintain track of where we are inside the XML file
+       private $inXml;
+       private $inData;
+       private $inMeta;
+       private $inSkin;
+       private $inTemplate;
+       private $currentName;
+       private $currentPartName;
+       private $cdata;
+       
+       /**
+        * constructor initializes data structures
+        */
+       public function __construct()
+       {
+               // disable magic_quotes_runtime if it's turned on
+               //set_magic_quotes_runtime(0);
+               if ( version_compare(PHP_VERSION, '5.3.0', '<') )
+               {
+                       ini_set('magic_quotes_runtime', '0');
+               }
+               
+               // debugging mode?
+               $this->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)), '<br />';
+                       }
+               }
+               
+               // 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)
+       {
+               global $manager;
+               
+               $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 = "<p>"._SKINIE_INVALID_NAMES_DETECTED."</p>\n";
+                       $inames_error .= "<ul>";
+                       foreach( $invalidSkinNames as $sName )
+                       {
+                               $inames_error .= "<li>".Entity::hsc($sName)."</li>";
+                       }
+                       foreach( $invalidTemplateNames as $sName )
+                       {
+                               $inames_error .= "<li>".Entity::hsc($sName)."</li>";
+                       }
+                       $inames_error .= "</ul>";
+                       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 =& $manager->getSkin($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: ', Entity::hsc($name), '<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 . Entity::hsc($name) . '<br />';
+                               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_element($parser, $name)
+       {
+               if ( $this->debug )
+               {
+                       echo 'END: ' . Entity::hsc($name) . '<br />';
+               }
+               
+               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());
+               }
+               else
+               {
+                       $charset_data = $this->get_character_data();
+               }
+               
+               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 . Entity::hsc($name) . '<br />';
+                               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: ' . Entity::hsc($data) . '<br />';
+               }
+               $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)
+       {
+               global $manager;
+               
+               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 "<nucleusskin>\n";
+               
+               // meta
+               echo "\t<meta>\n";
+               // skins
+               foreach ( $this->skins as $skinId => $skinName )
+               {
+                       echo "\t\t" . '<skin name="' . Entity::hsc($skinName) . '" />' . "\n";
+               }
+               // templates
+               foreach ( $this->templates as $templateId => $templateName )
+               {
+                       echo "\t\t" . '<template name="' . Entity::hsc($templateName) . '" />' . "\n";
+               }
+               // extra info
+               if ( $this->info )
+               {
+                       echo "\t\t<info><![CDATA[" . $this->info . "]]></info>\n";
+               }
+               echo "\t</meta>\n\n\n";
+               
+               // contents skins
+               foreach ($this->skins as $skinId => $skinName)
+               {
+                       $skinId = (integer) $skinId;
+                       $skinObj =& $manager->getSkin($skinId);
+                       
+                       echo "\t" . '<skin name="' . Entity::hsc($skinName) . '" type="' . Entity::hsc($skinObj->getContentType()) . '" includeMode="' . Entity::hsc($skinObj->getIncludeMode()) . '" includePrefix="' . Entity::hsc($skinObj->getIncludePrefix()) . '">' . "\n";
+                       echo "\t\t<description>" . Entity::hsc($skinObj->getDescription()) . "</description>\n";
+                       
+                       $res = DB::getResult('SELECT stype, scontent FROM '. sql_table('skin') .' WHERE sdesc=' . $skinId);
+                       foreach ( $res as $row )
+                       {
+                               echo "\t\t" . '<part name="',Entity::hsc($row['stype']) . '">';
+                               echo '<![CDATA[' . $this->escapeCDATA($row['scontent']) . ']]>';
+                               echo "</part>\n\n";
+                       }
+                       echo "\t</skin>\n\n\n";
+               }
+               
+               // contents templates
+               foreach ( $this->templates as $templateId => $templateName )
+               {
+                       $templateId = intval($templateId);
+                       
+                       echo "\t" . '<template name="' . Entity::hsc($templateName) . '">' . "\n";
+                       echo "\t\t<description>" . Entity::hsc(Template::getDesc($templateId)) . "</description>\n";
+                       
+                       $res = DB::getResult('SELECT tpartname, tcontent FROM '. sql_table('template') .' WHERE tdesc=' . $templateId);
+                       foreach ( $res as $row )
+                       {
+                               echo "\t\t" . '<part name="' . Entity::hsc($row['tpartname']) . '">';
+                               echo '<![CDATA[' . $this->escapeCDATA($row['tcontent']) . ']]>';
+                               echo "</part>\n\n";
+                       }
+                       
+                       echo "\t</template>\n\n\n";
+               }
+               echo '</nucleusskin>';
+       }
+       
+       /**
+        * Escapes CDATA content so it can be included in another CDATA section
+        */
+       private function escapeCDATA($cdata)
+       {
+               return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata);
+       }
+}
+>>>>>>> skinnable-master