3 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
4 * Copyright (C) 2002-2009 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-2009 The Nucleus Group
18 * @version $Id: skinie.php 1624 2012-01-09 11:36:20Z sakamocchi $
22 // hardcoded value (see constructor). When 1, interesting info about the
23 // parsing process is sent to the output
26 // parser/file pointer
30 // which data has been read?
39 // to maintain track of where we are inside the XML file
50 * constructor initializes data structures
54 // disable magic_quotes_runtime if it's turned on
55 //set_magic_quotes_runtime(0);
56 if ( version_compare(PHP_VERSION, '5.3.0', '<') )
58 ini_set('magic_quotes_runtime', '0');
71 xml_parser_free($this->parser);
77 // which data has been read?
78 $this->metaDataRead = 0;
81 // to maintain track of where we are inside the XML file
86 $this->inTemplate = 0;
87 $this->currentName = '';
88 $this->currentPartName = '';
90 // character data pile
93 // list of skinnames and templatenames (will be array of array)
94 $this->skins = array();
95 $this->templates = array();
97 // extra info included in the XML files (e.g. installation notes)
101 $this->parser = xml_parser_create();
102 xml_set_object($this->parser, $this);
103 xml_set_element_handler($this->parser, 'startElement', 'endElement');
104 xml_set_character_data_handler($this->parser, 'characterData');
105 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
109 * Reads an XML file into memory
114 * Set to 1 when only the metadata needs to be read (optional, default 0)
116 function readFile($filename, $metaOnly = 0)
119 $this->fp = @fopen($filename, 'r');
122 return _SKINIE_ERROR_FAILEDOPEN_FILEURL;
130 while ( !feof($this->fp) )
132 $tempbuffer .= fread($this->fp, 4096);
137 [2004-08-04] dekarma - Took this out since it messes up good XML if it has skins/templates
138 with CDATA sections. need to investigate consequences.
139 see bug [ 999914 ] Import fails (multiple skins in XML/one of them with CDATA)
141 // backwards compatibility with the non-wellformed skinbackup.xml files
142 // generated by v2/v3 (when CDATA sections were present in skins)
143 // split up those CDATA sections into multiple ones
144 $tempbuffer = preg_replace_callback(
145 "/(<!\[CDATA\[[^]]*?<!\[CDATA\[[^]]*)((?:\]\].*?<!\[CDATA.*?)*)(\]\])(.*\]\])/ms",
148 'return $matches[1] . preg_replace("/(\]\])(.*?<!\[CDATA)/ms","]]]]><![CDATA[$2",$matches[2])."]]]]><![CDATA[".$matches[4];'
154 fwrite($temp, $tempbuffer);
157 while ( ($buffer = fread($temp, 4096) )
158 && (!$metaOnly || ($metaOnly && !$this->metaDataRead)) )
160 $err = xml_parse( $this->parser, $buffer, feof($temp) );
161 if ( !$err && $this->debug )
163 echo 'ERROR: ', xml_error_string(xml_get_error_code($this->parser)), '<br />';
173 * Returns the list of skin names
175 function getSkinNames()
177 return array_keys($this->skins);
181 * Returns the list of template names
183 function getTemplateNames()
185 return array_keys($this->templates);
189 * Returns the extra information included in the XML file
197 * Writes the skins and templates to the database
199 * @param $allowOverwrite
200 * set to 1 when allowed to overwrite existing skins with the same name
203 function writeToDatabase($allowOverwrite = 0)
205 $existingSkins = $this->checkSkinNameClashes();
206 $existingTemplates = $this->checkTemplateNameClashes();
207 $invalidSkinNames = $this->checkSkinNamesValid();
208 $invalidTemplateNames = $this->checkTemplateNamesValid();
210 // if there are invalid skin or template names, stop executioin and return and error
211 if ( (sizeof($invalidSkinNames) > 0) || (sizeof($invalidTemplateNames) > 0) )
213 $inames_error = "<p>"._SKINIE_INVALID_NAMES_DETECTED."</p>\n";
214 $inames_error .= "<ul>";
215 foreach( $invalidSkinNames as $sName )
217 $inames_error .= "<li>".i18n::hsc($sName)."</li>";
219 foreach( $invalidTemplateNames as $sName )
221 $inames_error .= "<li>".i18n::hsc($sName)."</li>";
223 $inames_error .= "</ul>";
224 return $inames_error;
227 // if not allowed to overwrite, check if any nameclashes exists
228 if ( !$allowOverwrite )
230 if ( (sizeof($existingSkins) > 0) || (sizeof($existingTemplates) > 0) )
232 return _SKINIE_NAME_CLASHES_DETECTED;
236 foreach ( $this->skins as $skinName => $data )
238 // 1. if exists: delete all part data, update desc data
239 // if not exists: create desc
240 if ( in_array($skinName, $existingSkins) )
242 $skinObj = SKIN::createFromName($skinName);
244 // delete all parts of the skin
245 $skinObj->deleteAllParts();
247 // update general info
248 $skinObj->updateGeneralInfo(
250 $data['description'],
252 $data['includeMode'],
253 $data['includePrefix']
258 $skinid = SKIN::createNew(
260 $data['description'],
262 $data['includeMode'],
263 $data['includePrefix']
265 $skinObj = new SKIN($skinid);
269 foreach ( $data['parts'] as $partName => $partContent )
271 $skinObj->update($partName, $partContent);
275 foreach ( $this->templates as $templateName => $data )
277 // 1. if exists: delete all part data, update desc data
278 // if not exists: create desc
279 if ( in_array($templateName, $existingTemplates) )
281 $templateObj = TEMPLATE::createFromName($templateName);
283 // delete all parts of the template
284 $templateObj->deleteAllParts();
286 // update general info
287 $templateObj->updateGeneralInfo($templateName, $data['description']);
291 $templateid = TEMPLATE::createNew($templateName, $data['description']);
292 $templateObj = new TEMPLATE($templateid);
296 foreach ( $data['parts'] as $partName => $partContent )
298 $templateObj->update($partName, $partContent);
304 * returns an array of all the skin nameclashes (empty array when no name clashes)
306 function checkSkinNameClashes()
310 foreach ( $this->skins as $skinName => $data )
312 if ( SKIN::exists($skinName) )
314 array_push($clashes, $skinName);
321 * returns an array of all the template nameclashes
322 * (empty array when no name clashes)
324 function checkTemplateNameClashes()
328 foreach ( $this->templates as $templateName => $data )
330 if ( TEMPLATE::exists($templateName) )
332 array_push($clashes, $templateName);
339 * returns an array of all the invalid skin names (empty array when no invalid names )
341 function checkSkinNamesValid()
345 foreach ( $this->skins as $skinName => $data )
347 if ( !isValidSkinName($skinName) )
349 array_push($notValid, $skinName);
356 * returns an array of all the invalid template names (empty array when no invalid names )
358 function checkTemplateNamesValid()
362 foreach ( $this->templates as $templateName => $data )
364 if ( !isValidTemplateName($templateName) )
366 array_push($notValid, $templateName);
373 * Called by XML parser for each new start element encountered
375 function startElement($parser, $name, $attrs)
377 foreach( $attrs as $key=>$value )
379 $attrs[$key] = i18n::hsc($value);
384 echo 'START: ', i18n::hsc($name), '<br />';
399 if ( !$this->inMeta )
402 $this->currentName = $attrs['name'];
403 $this->skins[$this->currentName]['type'] = $attrs['type'];
404 $this->skins[$this->currentName]['includeMode'] = $attrs['includeMode'];
405 $this->skins[$this->currentName]['includePrefix'] = $attrs['includePrefix'];
406 $this->skins[$this->currentName]['parts'] = array();
410 $this->skins[$attrs['name']] = array();
411 $this->skins[$attrs['name']]['parts'] = array();
415 if ( !$this->inMeta )
417 $this->inTemplate = 1;
418 $this->currentName = $attrs['name'];
419 $this->templates[$this->currentName]['parts'] = array();
423 $this->templates[$attrs['name']] = array();
424 $this->templates[$attrs['name']]['parts'] = array();
431 $this->currentPartName = $attrs['name'];
434 echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . i18n::hsc($name) . '<br />';
437 // character data never contains other tags
438 $this->clearCharacterData();
442 * Called by the XML parser for each closing tag encountered
444 function endElement($parser, $name)
448 echo 'END: ' . i18n::hsc($name) . '<br />';
459 $this->metaDataRead = 1;
462 $this->info = $this->getCharacterData();
464 if ( !$this->inMeta )
470 if ( !$this->inMeta )
472 $this->inTemplate = 0;
478 $this->skins[$this->currentName]['description'] = $this->getCharacterData();
482 $this->templates[$this->currentName]['description'] = $this->getCharacterData();
488 $this->skins[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
492 $this->templates[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
496 echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . i18n::hsc($name) . '<br />';
499 $this->clearCharacterData();
503 * Called by XML parser for data inside elements
505 function characterData ($parser, $data)
509 echo 'NEW DATA: ' . i18n::hsc($data) . '<br />';
511 $this->cdata .= $data;
515 * Returns the data collected so far
517 function getCharacterData()
523 * Clears the data buffer
525 function clearCharacterData()
531 * Static method that looks for importable XML files in subdirs of the given dir
533 function searchForCandidates($dir)
535 $candidates = array();
537 $dirhandle = opendir($dir);
538 while ( $filename = readdir($dirhandle) )
540 if ( @is_dir($dir . $filename) && ($filename != '.') && ($filename != '..') )
542 $xml_file = $dir . $filename . '/skinbackup.xml';
543 if ( file_exists($xml_file) && is_readable($xml_file) )
546 $candidates[$filename] = $filename;
549 // backwards compatibility
550 $xml_file = $dir . $filename . '/skindata.xml';
551 if ( file_exists($xml_file) && is_readable($xml_file) )
554 $candidates[$filename] = $filename;
558 closedir($dirhandle);
569 * Constructor initializes data structures
571 function SKINEXPORT()
573 // list of templateIDs to export
574 $this->templates = array();
576 // list of skinIDs to export
577 $this->skins = array();
579 // extra info to be in XML file
584 * Adds a template to be exported
588 * @result false when no such ID exists
590 function addTemplate($id)
592 if ( !TEMPLATE::existsID($id) )
597 $this->templates[$id] = TEMPLATE::getNameFromId($id);
602 * Adds a skin to be exported
606 * @result false when no such ID exists
608 function addSkin($id)
610 if ( !SKIN::existsID($id) )
615 $this->skins[$id] = SKIN::getNameFromId($id);
620 * Sets the extra info to be included in the exported file
622 function setInfo($info)
628 * Outputs the XML contents of the export file
631 * set to 0 if you don't want to send out headers
632 * (optional, default 1)
634 function export($setHeaders = 1)
638 // make sure the mimetype is correct, and that the data does not show up
639 // in the browser, but gets saved into and XML file (popup download window)
640 header('Content-Type: text/xml');
641 header('Content-Disposition: attachment; filename="skinbackup.xml"');
642 header('Expires: 0');
643 header('Pragma: no-cache');
646 echo "<nucleusskin>\n";
651 foreach ( $this->skins as $skinId => $skinName )
653 echo "\t\t" . '<skin name="' . i18n::hsc($skinName) . '" />' . "\n";
656 foreach ( $this->templates as $templateId => $templateName )
658 echo "\t\t" . '<template name="' . i18n::hsc($templateName) . '" />' . "\n";
663 echo "\t\t<info><![CDATA[" . $this->info . "]]></info>\n";
665 echo "\t</meta>\n\n\n";
668 foreach ($this->skins as $skinId => $skinName)
670 $skinId = intval($skinId);
671 $skinObj = new SKIN($skinId);
673 echo "\t" . '<skin name="' . i18n::hsc($skinName) . '" type="' . i18n::hsc($skinObj->getContentType()) . '" includeMode="' . i18n::hsc($skinObj->getIncludeMode()) . '" includePrefix="' . i18n::hsc($skinObj->getIncludePrefix()) . '">' . "\n";
674 echo "\t\t<description>" . i18n::hsc($skinObj->getDescription()) . "</description>\n";
676 $res = sql_query('SELECT stype, scontent FROM '. sql_table('skin') .' WHERE sdesc=' . $skinId);
677 while ( $partObj = sql_fetch_object($res) )
679 echo "\t\t" . '<part name="',i18n::hsc($partObj->stype) . '">';
680 echo '<![CDATA[' . $this->escapeCDATA($partObj->scontent) . ']]>';
683 echo "\t</skin>\n\n\n";
686 // contents templates
687 foreach ( $this->templates as $templateId => $templateName )
689 $templateId = intval($templateId);
691 echo "\t" . '<template name="' . i18n::hsc($templateName) . '">' . "\n";
692 echo "\t\t<description>" . i18n::hsc(TEMPLATE::getDesc($templateId)) . "</description>\n";
694 $res = sql_query('SELECT tpartname, tcontent FROM '. sql_table('template') .' WHERE tdesc=' . $templateId);
695 while ( $partObj = sql_fetch_object($res) )
697 echo "\t\t" . '<part name="' . i18n::hsc($partObj->tpartname) . '">';
698 echo '<![CDATA[' . $this->escapeCDATA($partObj->tcontent) . ']]>';
702 echo "\t</template>\n\n\n";
704 echo '</nucleusskin>';
708 * Escapes CDATA content so it can be included in another CDATA section
710 function escapeCDATA($cdata)
712 return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata);