3 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
4 * Copyright (C) 2002-2011 The Nucleus Group
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 * (see nucleus/documentation/index.html#license for more info)
13 * This class contains two classes that can be used for importing and
14 * exporting Nucleus skins: SKINIMPORT and SKINEXPORT
16 * @license http://nucleuscms.org/license.txt GNU General Public License
17 * @copyright Copyright (C) 2002-2011 The Nucleus Group
19 * @version $NucleusJP: skinie.php,v 1.9.2.1 2007/09/05 07:46:30 kimitake Exp $
24 // hardcoded value (see constructor). When 1, interesting info about the
25 // parsing process is sent to the output
28 // parser/file pointer
32 // which data has been read?
41 // to maintain track of where we are inside the XML file
54 * constructor initializes data structures
56 function SKINIMPORT() {
57 // disable magic_quotes_runtime if it's turned on
58 set_magic_quotes_runtime(0);
69 xml_parser_free($this->parser);
74 // which data has been read?
75 $this->metaDataRead = 0;
78 // to maintain track of where we are inside the XML file
83 $this->inTemplate = 0;
84 $this->currentName = '';
85 $this->currentPartName = '';
87 // character data pile
90 // list of skinnames and templatenames (will be array of array)
91 $this->skins = array();
92 $this->templates = array();
94 // extra info included in the XML files (e.g. installation notes)
98 $this->parser = xml_parser_create();
99 xml_set_object($this->parser, $this);
100 xml_set_element_handler($this->parser, 'startElement', 'endElement');
101 xml_set_character_data_handler($this->parser, 'characterData');
102 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
107 * Reads an XML file into memory
112 * Set to 1 when only the metadata needs to be read (optional, default 0)
114 * [2004/08/04] Modified by Japanese Package Release Team according to the URL below,
115 * http://japan.nucleuscms.org/bb/viewtopic.php?t=4416
116 * [2004/08/04] Modified by dakarma
117 * Took this out since it messes up good XML if it has skins/templates
118 * with CDATA sections. need to investigate consequences.
119 * see bug [ 999914 ] Import fails (multiple skins in XML/one of them with CDATA)
121 function readFile($filename, $metaOnly = 0) {
123 $this->fp = @fopen($filename, 'r');
125 return _SKINIE_ERROR_FAILEDOPEN_FILEURL;
129 $tempbuffer = fread($this->fp,filesize($filename));
132 // backwards compatibility with the non-wellformed skinbackup.xml files
133 // generated by v2/v3 (when CDATA sections were present in skins)
134 // split up those CDATA sections into multiple ones
135 $tempbuffer = preg_replace_callback(
136 "/(<!\[CDATA\[[^]]*?<!\[CDATA\[[^]]*)((?:\]\].*?<!\[CDATA.*?)*)(\]\])(.*\]\])/ms",
139 'return $matches[1] . preg_replace("/(\]\])(.*?<!\[CDATA)/ms","]]]]><![CDATA[$2",$matches[2])."]]]]><![CDATA[".$matches[4];'
145 if (function_exists('mb_convert_encoding') && (strtoupper(_CHARSET) != 'ISO-8859-1')) {
146 mb_detect_order("ASCII, EUC-JP, UTF-8, JIS, SJIS, EUC-CN, ISO-8859-1");
147 $temp_encode = mb_detect_encoding($tempbuffer);
153 while ( ($buffer = fgets($this->fp, 4096) ) && (!$metaOnly || ($metaOnly && !$this->metaDataRead))) {
155 $buffer = mb_convert_encoding($buffer, 'UTF-8', $temp_encode);
157 $err = xml_parse( $this->parser, $buffer, feof($this->fp) );
158 if (!$err && $this->debug) {
159 echo _ERROR . ': ' . xml_error_string(xml_get_error_code($this->parser)) . '<br />';
168 * Returns the list of skin names
170 function getSkinNames() {
171 return array_keys($this->skins);
175 * Returns the list of template names
177 function getTemplateNames() {
178 return array_keys($this->templates);
182 * Returns the extra information included in the XML file
189 * Writes the skins and templates to the database
191 * @param $allowOverwrite
192 * set to 1 when allowed to overwrite existing skins with the same name
195 function writeToDatabase($allowOverwrite = 0) {
196 $existingSkins = $this->checkSkinNameClashes();
197 $existingTemplates = $this->checkTemplateNameClashes();
199 // if not allowed to overwrite, check if any nameclashes exists
200 if (!$allowOverwrite) {
201 if ((sizeof($existingSkins) > 0) || (sizeof($existingTemplates) > 0)) {
202 return _SKINIE_NAME_CLASHES_DETECTED;
206 foreach ($this->skins as $skinName => $data) {
207 // 1. if exists: delete all part data, update desc data
208 // if not exists: create desc
209 if (in_array($skinName, $existingSkins)) {
210 $skinObj = SKIN::createFromName($skinName);
212 // delete all parts of the skin
213 $skinObj->deleteAllParts();
215 // update general info
216 $skinObj->updateGeneralInfo(
218 $data['description'],
220 $data['includeMode'],
221 $data['includePrefix']
224 $skinid = SKIN::createNew(
226 $data['description'],
228 $data['includeMode'],
229 $data['includePrefix']
231 $skinObj = new SKIN($skinid);
235 foreach ($data['parts'] as $partName => $partContent) {
236 $skinObj->update($partName, $partContent);
240 foreach ($this->templates as $templateName => $data) {
241 // 1. if exists: delete all part data, update desc data
242 // if not exists: create desc
243 if (in_array($templateName, $existingTemplates)) {
244 $templateObj = TEMPLATE::createFromName($templateName);
246 // delete all parts of the template
247 $templateObj->deleteAllParts();
249 // update general info
250 $templateObj->updateGeneralInfo($templateName, $data['description']);
252 $templateid = TEMPLATE::createNew($templateName, $data['description']);
253 $templateObj = new TEMPLATE($templateid);
257 foreach ($data['parts'] as $partName => $partContent) {
258 $templateObj->update($partName, $partContent);
266 * returns an array of all the skin nameclashes (empty array when no name clashes)
268 function checkSkinNameClashes() {
271 foreach ($this->skins as $skinName => $data) {
272 if (SKIN::exists($skinName)) {
273 array_push($clashes, $skinName);
281 * returns an array of all the template nameclashes
282 * (empty array when no name clashes)
284 function checkTemplateNameClashes() {
287 foreach ($this->templates as $templateName => $data) {
288 if (TEMPLATE::exists($templateName)) {
289 array_push($clashes, $templateName);
297 * Called by XML parser for each new start element encountered
299 function startElement($parser, $name, $attrs) {
300 foreach($attrs as $key=>$value) {
301 $attrs[$key] = htmlspecialchars($value, ENT_QUOTES);
305 echo 'START: ' . htmlspecialchars($name, ENT_QUOTES) . '<br />';
319 if (!$this->inMeta) {
321 $this->currentName = $attrs['name'];
322 $this->skins[$this->currentName]['type'] = $attrs['type'];
323 $this->skins[$this->currentName]['includeMode'] = $attrs['includeMode'];
324 $this->skins[$this->currentName]['includePrefix'] = $attrs['includePrefix'];
325 $this->skins[$this->currentName]['parts'] = array();
327 $this->skins[$attrs['name']] = array();
328 $this->skins[$attrs['name']]['parts'] = array();
332 if (!$this->inMeta) {
333 $this->inTemplate = 1;
334 $this->currentName = $attrs['name'];
335 $this->templates[$this->currentName]['parts'] = array();
337 $this->templates[$attrs['name']] = array();
338 $this->templates[$attrs['name']]['parts'] = array();
345 $this->currentPartName = $attrs['name'];
348 echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . htmlspecialchars($name, ENT_QUOTES) . '<br />';
352 // character data never contains other tags
353 $this->clearCharacterData();
358 * Called by the XML parser for each closing tag encountered
360 function endElement($parser, $name) {
362 echo 'END: ' . htmlspecialchars($name, ENT_QUOTES) . '<br />';
372 $this->metaDataRead = 1;
375 $this->info = $this->getCharacterData();
377 if (!$this->inMeta) {
382 if (!$this->inMeta) {
383 $this->inTemplate = 0;
388 $this->skins[$this->currentName]['description'] = $this->getCharacterData();
390 $this->templates[$this->currentName]['description'] = $this->getCharacterData();
395 $this->skins[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
397 $this->templates[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
401 echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . htmlspecialchars($name, ENT_QUOTES) . '<br />';
404 $this->clearCharacterData();
409 * Called by XML parser for data inside elements
411 function characterData ($parser, $data) {
413 echo 'NEW DATA: ' . htmlspecialchars($data, ENT_QUOTES) . '<br />';
415 $this->cdata .= $data;
419 * Returns the data collected so far
421 function getCharacterData() {
422 // echo $this->cdata;
423 if ( (strtoupper(_CHARSET) == 'UTF-8')
424 or (strtoupper(_CHARSET) == 'ISO-8859-1')
425 or (!function_exists('mb_convert_encoding')) ) {
428 return mb_convert_encoding($this->cdata, _CHARSET ,'UTF-8');
433 * Clears the data buffer
435 function clearCharacterData() {
440 * Static method that looks for importable XML files in subdirs of the given dir
442 function searchForCandidates($dir) {
443 $candidates = array();
445 $dirhandle = opendir($dir);
446 while ($filename = readdir($dirhandle)) {
447 if (@is_dir($dir . $filename) && ($filename != '.') && ($filename != '..')) {
448 $xml_file = $dir . $filename . '/skinbackup.xml';
449 if (file_exists($xml_file) && is_readable($xml_file)) {
450 $candidates[$filename] = $filename; //$xml_file;
453 // backwards compatibility
454 $xml_file = $dir . $filename . '/skindata.xml';
455 if (file_exists($xml_file) && is_readable($xml_file)) {
456 $candidates[$filename] = $filename; //$xml_file;
460 closedir($dirhandle);
477 * Constructor initializes data structures
479 function SKINEXPORT() {
480 // list of templateIDs to export
481 $this->templates = array();
483 // list of skinIDs to export
484 $this->skins = array();
486 // extra info to be in XML file
491 * Adds a template to be exported
495 * @result false when no such ID exists
497 function addTemplate($id) {
498 if (!TEMPLATE::existsID($id)) {
503 $this->templates[$id] = TEMPLATE::getNameFromId($id);
509 * Adds a skin to be exported
513 * @result false when no such ID exists
515 function addSkin($id) {
516 if (!SKIN::existsID($id)) {
520 $this->skins[$id] = SKIN::getNameFromId($id);
526 * Sets the extra info to be included in the exported file
528 function setInfo($info) {
534 * Outputs the XML contents of the export file
537 * set to 0 if you don't want to send out headers
538 * (optional, default 1)
540 function export($setHeaders = 1) {
542 // make sure the mimetype is correct, and that the data does not show up
543 // in the browser, but gets saved into and XML file (popup download window)
544 header('Content-Type: text/xml');
545 header('Content-Disposition: attachment; filename="skinbackup.xml"');
546 header('Expires: 0');
547 header('Pragma: no-cache');
550 echo "<nucleusskin>\n";
555 foreach ($this->skins as $skinId => $skinName) {
556 $skinName = htmlspecialchars($skinName, ENT_QUOTES);
557 if (strtoupper(_CHARSET) != 'UTF-8') {
558 $skinName = mb_convert_encoding($skinName, 'UTF-8', _CHARSET);
560 echo "\t\t" . '<skin name="' . htmlspecialchars($skinName, ENT_QUOTES) . '" />' . "\n";
563 foreach ($this->templates as $templateId => $templateName) {
564 $templateName = htmlspecialchars($templateName, ENT_QUOTES);
565 if (strtoupper(_CHARSET) != 'UTF-8') {
566 $templateName = mb_convert_encoding($templateName, 'UTF-8', _CHARSET);
568 echo "\t\t" . '<template name="' . htmlspecialchars($templateName, ENT_QUOTES) . '" />' . "\n";
572 if (strtoupper(_CHARSET) != 'UTF-8') {
573 $skin_info = mb_convert_encoding($this->info, 'UTF-8', _CHARSET);
575 $skin_info = $this->info;
577 echo "\t\t<info><![CDATA[" . $skin_info . "]]></info>\n";
579 echo "\t</meta>\n\n\n";
582 foreach ($this->skins as $skinId => $skinName) {
583 $skinId = intval($skinId);
584 $skinObj = new SKIN($skinId);
585 $skinName = htmlspecialchars($skinName, ENT_QUOTES);
586 $contentT = htmlspecialchars($skinObj->getContentType(), ENT_QUOTES);
587 $incMode = htmlspecialchars($skinObj->getIncludeMode(), ENT_QUOTES);
588 $incPrefx = htmlspecialchars($skinObj->getIncludePrefix(), ENT_QUOTES);
589 $skinDesc = htmlspecialchars($skinObj->getDescription(), ENT_QUOTES);
590 if (strtoupper(_CHARSET) != 'UTF-8') {
591 $skinName = mb_convert_encoding($skinName, 'UTF-8', _CHARSET);
592 $contentT = mb_convert_encoding($contentT, 'UTF-8', _CHARSET);
593 $incMode = mb_convert_encoding($incMode, 'UTF-8', _CHARSET);
594 $incPrefx = mb_convert_encoding($incPrefx, 'UTF-8', _CHARSET);
595 $skinDesc = mb_convert_encoding($skinDesc, 'UTF-8', _CHARSET);
598 echo "\t" . '<skin name="' . $skinName . '" type="' . $contentT . '" includeMode="' . $incMode . '" includePrefix="' . $incPrefx . '">' . "\n";
600 echo "\t\t" . '<description>' . $skinDesc . '</description>' . "\n";
608 . ' sdesc = ' . $skinId;
609 $res = sql_query($que);
610 while ($partObj = sql_fetch_object($res)) {
611 $type = htmlspecialchars($partObj->stype, ENT_QUOTES);
612 $cdata = $this->escapeCDATA($partObj->scontent);
613 if (strtoupper(_CHARSET) != 'UTF-8') {
614 $type = mb_convert_encoding($type, 'UTF-8', _CHARSET);
615 $cdata = mb_convert_encoding($cdata, 'UTF-8', _CHARSET);
617 echo "\t\t" . '<part name="' . $type . '">';
618 echo '<![CDATA[' . $cdata . ']]>';
622 echo "\t</skin>\n\n\n";
625 // contents templates
626 foreach ($this->templates as $templateId => $templateName) {
627 $templateId = intval($templateId);
628 $templateName = htmlspecialchars($templateName, ENT_QUOTES);
629 $templateDesc = htmlspecialchars(TEMPLATE::getDesc($templateId), ENT_QUOTES);
630 if (strtoupper(_CHARSET) != 'UTF-8') {
631 $templateName = mb_convert_encoding($templateName, 'UTF-8', _CHARSET);
632 $templateDesc = mb_convert_encoding($templateDesc, 'UTF-8', _CHARSET);
635 echo "\t" . '<template name="' . $templateName . '">' . "\n";
637 echo "\t\t" . '<description>' . $templateDesc . "</description>\n";
643 . sql_table('template')
645 . ' tdesc = ' . $templateId;
646 $res = sql_query($que);
647 while ($partObj = sql_fetch_object($res)) {
648 $type = htmlspecialchars($partObj->tpartname, ENT_QUOTES);
649 $cdata = $this->escapeCDATA($partObj->tcontent);
650 if (strtoupper(_CHARSET) != 'UTF-8') {
651 $type = mb_convert_encoding($type, 'UTF-8', _CHARSET);
652 $cdata = mb_convert_encoding($cdata, 'UTF-8', _CHARSET);
654 echo "\t\t" . '<part name="' . $type . '">';
655 echo '<![CDATA[' . $cdata . ']]>';
656 echo '</part>' . "\n\n";
659 echo "\t</template>\n\n\n";
662 echo '</nucleusskin>';
666 * Escapes CDATA content so it can be included in another CDATA section
668 function escapeCDATA($cdata)
670 return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata);