3 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
4 * Copyright (C) 2002-2012 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)
12 * This class contains two classes that can be used for importing and
13 * exporting Nucleus skins: SKINIMPORT and SKINEXPORT
18 // hardcoded value (see constructor). When 1, interesting info about the
19 // parsing process is sent to the output
22 // parser/file pointer
26 // which data has been read?
35 // to maintain track of where we are inside the XML file
48 * constructor initializes data structures
50 function SKINIMPORT() {
51 // disable magic_quotes_runtime if it's turned on
52 set_magic_quotes_runtime(0);
63 xml_parser_free($this->parser);
68 // which data has been read?
69 $this->metaDataRead = 0;
72 // to maintain track of where we are inside the XML file
77 $this->inTemplate = 0;
78 $this->currentName = '';
79 $this->currentPartName = '';
81 // character data pile
84 // list of skinnames and templatenames (will be array of array)
85 $this->skins = array();
86 $this->templates = array();
88 // extra info included in the XML files (e.g. installation notes)
92 $this->parser = xml_parser_create();
93 xml_set_object($this->parser, $this);
94 xml_set_element_handler($this->parser, 'startElement', 'endElement');
95 xml_set_character_data_handler($this->parser, 'characterData');
96 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
101 * Reads an XML file into memory
106 * Set to 1 when only the metadata needs to be read (optional, default 0)
108 * [2004/08/04] Modified by Japanese Package Release Team according to the URL below,
109 * http://japan.nucleuscms.org/bb/viewtopic.php?t=4416
110 * [2004/08/04] Modified by dakarma
111 * Took this out since it messes up good XML if it has skins/templates
112 * with CDATA sections. need to investigate consequences.
113 * see bug [ 999914 ] Import fails (multiple skins in XML/one of them with CDATA)
115 function readFile($filename, $metaOnly = 0) {
117 $this->fp = @fopen($filename, 'r');
119 return _SKINIE_ERROR_FAILEDOPEN_FILEURL;
123 $tempbuffer = fread($this->fp,filesize($filename));
126 // backwards compatibility with the non-wellformed skinbackup.xml files
127 // generated by v2/v3 (when CDATA sections were present in skins)
128 // split up those CDATA sections into multiple ones
129 $tempbuffer = preg_replace_callback(
130 "/(<!\[CDATA\[[^]]*?<!\[CDATA\[[^]]*)((?:\]\].*?<!\[CDATA.*?)*)(\]\])(.*\]\])/ms",
133 'return $matches[1] . preg_replace("/(\]\])(.*?<!\[CDATA)/ms","]]]]><![CDATA[$2",$matches[2])."]]]]><![CDATA[".$matches[4];'
139 if (function_exists('mb_convert_encoding') && (strtoupper(_CHARSET) != 'ISO-8859-1')) {
140 mb_detect_order("ASCII, EUC-JP, UTF-8, JIS, SJIS, EUC-CN, ISO-8859-1");
141 $temp_encode = mb_detect_encoding($tempbuffer);
147 while ( ($buffer = fgets($this->fp, 4096) ) && (!$metaOnly || ($metaOnly && !$this->metaDataRead))) {
149 $buffer = mb_convert_encoding($buffer, 'UTF-8', $temp_encode);
151 $err = xml_parse( $this->parser, $buffer, feof($this->fp) );
152 if (!$err && $this->debug) {
153 echo _ERROR . ': ' . xml_error_string(xml_get_error_code($this->parser)) . '<br />';
162 * Returns the list of skin names
164 function getSkinNames() {
165 return array_keys($this->skins);
169 * Returns the list of template names
171 function getTemplateNames() {
172 return array_keys($this->templates);
176 * Returns the extra information included in the XML file
183 * Writes the skins and templates to the database
185 * @param $allowOverwrite
186 * set to 1 when allowed to overwrite existing skins with the same name
189 function writeToDatabase($allowOverwrite = 0) {
190 $existingSkins = $this->checkSkinNameClashes();
191 $existingTemplates = $this->checkTemplateNameClashes();
193 // if not allowed to overwrite, check if any nameclashes exists
194 if (!$allowOverwrite) {
195 if ((sizeof($existingSkins) > 0) || (sizeof($existingTemplates) > 0)) {
196 return _SKINIE_NAME_CLASHES_DETECTED;
200 foreach ($this->skins as $skinName => $data) {
201 // 1. if exists: delete all part data, update desc data
202 // if not exists: create desc
203 if (in_array($skinName, $existingSkins)) {
204 $skinObj = SKIN::createFromName($skinName);
206 // delete all parts of the skin
207 $skinObj->deleteAllParts();
209 // update general info
210 $skinObj->updateGeneralInfo(
212 $data['description'],
214 $data['includeMode'],
215 $data['includePrefix']
218 $skinid = SKIN::createNew(
220 $data['description'],
222 $data['includeMode'],
223 $data['includePrefix']
225 $skinObj = new SKIN($skinid);
229 foreach ($data['parts'] as $partName => $partContent) {
230 $skinObj->update($partName, $partContent);
234 foreach ($this->templates as $templateName => $data) {
235 // 1. if exists: delete all part data, update desc data
236 // if not exists: create desc
237 if (in_array($templateName, $existingTemplates)) {
238 $templateObj = TEMPLATE::createFromName($templateName);
240 // delete all parts of the template
241 $templateObj->deleteAllParts();
243 // update general info
244 $templateObj->updateGeneralInfo($templateName, $data['description']);
246 $templateid = TEMPLATE::createNew($templateName, $data['description']);
247 $templateObj = new TEMPLATE($templateid);
251 foreach ($data['parts'] as $partName => $partContent) {
252 $templateObj->update($partName, $partContent);
260 * returns an array of all the skin nameclashes (empty array when no name clashes)
262 function checkSkinNameClashes() {
265 foreach ($this->skins as $skinName => $data) {
266 if (SKIN::exists($skinName)) {
267 array_push($clashes, $skinName);
275 * returns an array of all the template nameclashes
276 * (empty array when no name clashes)
278 function checkTemplateNameClashes() {
281 foreach ($this->templates as $templateName => $data) {
282 if (TEMPLATE::exists($templateName)) {
283 array_push($clashes, $templateName);
291 * Called by XML parser for each new start element encountered
293 function startElement($parser, $name, $attrs) {
294 foreach($attrs as $key=>$value) {
295 $attrs[$key] = htmlspecialchars($value, ENT_QUOTES);
299 echo 'START: ' . htmlspecialchars($name, ENT_QUOTES) . '<br />';
313 if (!$this->inMeta) {
315 $this->currentName = $attrs['name'];
316 $this->skins[$this->currentName]['type'] = $attrs['type'];
317 $this->skins[$this->currentName]['includeMode'] = $attrs['includeMode'];
318 $this->skins[$this->currentName]['includePrefix'] = $attrs['includePrefix'];
319 $this->skins[$this->currentName]['parts'] = array();
321 $this->skins[$attrs['name']] = array();
322 $this->skins[$attrs['name']]['parts'] = array();
326 if (!$this->inMeta) {
327 $this->inTemplate = 1;
328 $this->currentName = $attrs['name'];
329 $this->templates[$this->currentName]['parts'] = array();
331 $this->templates[$attrs['name']] = array();
332 $this->templates[$attrs['name']]['parts'] = array();
339 $this->currentPartName = $attrs['name'];
342 echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . htmlspecialchars($name, ENT_QUOTES) . '<br />';
346 // character data never contains other tags
347 $this->clearCharacterData();
352 * Called by the XML parser for each closing tag encountered
354 function endElement($parser, $name) {
356 echo 'END: ' . htmlspecialchars($name, ENT_QUOTES) . '<br />';
366 $this->metaDataRead = 1;
369 $this->info = $this->getCharacterData();
371 if (!$this->inMeta) {
376 if (!$this->inMeta) {
377 $this->inTemplate = 0;
382 $this->skins[$this->currentName]['description'] = $this->getCharacterData();
384 $this->templates[$this->currentName]['description'] = $this->getCharacterData();
389 $this->skins[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
391 $this->templates[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
395 echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . htmlspecialchars($name, ENT_QUOTES) . '<br />';
398 $this->clearCharacterData();
403 * Called by XML parser for data inside elements
405 function characterData ($parser, $data) {
407 echo 'NEW DATA: ' . htmlspecialchars($data, ENT_QUOTES) . '<br />';
409 $this->cdata .= $data;
413 * Returns the data collected so far
415 function getCharacterData() {
416 // echo $this->cdata;
417 if ( (strtoupper(_CHARSET) == 'UTF-8')
418 or (strtoupper(_CHARSET) == 'ISO-8859-1')
419 or (!function_exists('mb_convert_encoding')) ) {
422 return mb_convert_encoding($this->cdata, _CHARSET ,'UTF-8');
427 * Clears the data buffer
429 function clearCharacterData() {
434 * Static method that looks for importable XML files in subdirs of the given dir
436 function searchForCandidates($dir) {
437 $candidates = array();
439 $dirhandle = opendir($dir);
440 while ($filename = readdir($dirhandle)) {
441 if (@is_dir($dir . $filename) && ($filename != '.') && ($filename != '..')) {
442 $xml_file = $dir . $filename . '/skinbackup.xml';
443 if (file_exists($xml_file) && is_readable($xml_file)) {
444 $candidates[$filename] = $filename; //$xml_file;
447 // backwards compatibility
448 $xml_file = $dir . $filename . '/skindata.xml';
449 if (file_exists($xml_file) && is_readable($xml_file)) {
450 $candidates[$filename] = $filename; //$xml_file;
454 closedir($dirhandle);
471 * Constructor initializes data structures
473 function SKINEXPORT() {
474 // list of templateIDs to export
475 $this->templates = array();
477 // list of skinIDs to export
478 $this->skins = array();
480 // extra info to be in XML file
485 * Adds a template to be exported
489 * @result false when no such ID exists
491 function addTemplate($id) {
492 if (!TEMPLATE::existsID($id)) {
497 $this->templates[$id] = TEMPLATE::getNameFromId($id);
503 * Adds a skin to be exported
507 * @result false when no such ID exists
509 function addSkin($id) {
510 if (!SKIN::existsID($id)) {
514 $this->skins[$id] = SKIN::getNameFromId($id);
520 * Sets the extra info to be included in the exported file
522 function setInfo($info) {
528 * Outputs the XML contents of the export file
531 * set to 0 if you don't want to send out headers
532 * (optional, default 1)
534 function export($setHeaders = 1) {
536 // make sure the mimetype is correct, and that the data does not show up
537 // in the browser, but gets saved into and XML file (popup download window)
538 header('Content-Type: text/xml');
539 header('Content-Disposition: attachment; filename="skinbackup.xml"');
540 header('Expires: 0');
541 header('Pragma: no-cache');
544 echo "<nucleusskin>\n";
549 foreach ($this->skins as $skinId => $skinName) {
550 $skinName = htmlspecialchars($skinName, ENT_QUOTES);
551 if (strtoupper(_CHARSET) != 'UTF-8') {
552 $skinName = mb_convert_encoding($skinName, 'UTF-8', _CHARSET);
554 echo "\t\t" . '<skin name="' . htmlspecialchars($skinName, ENT_QUOTES) . '" />' . "\n";
557 foreach ($this->templates as $templateId => $templateName) {
558 $templateName = htmlspecialchars($templateName, ENT_QUOTES);
559 if (strtoupper(_CHARSET) != 'UTF-8') {
560 $templateName = mb_convert_encoding($templateName, 'UTF-8', _CHARSET);
562 echo "\t\t" . '<template name="' . htmlspecialchars($templateName, ENT_QUOTES) . '" />' . "\n";
566 if (strtoupper(_CHARSET) != 'UTF-8') {
567 $skin_info = mb_convert_encoding($this->info, 'UTF-8', _CHARSET);
569 $skin_info = $this->info;
571 echo "\t\t<info><![CDATA[" . $skin_info . "]]></info>\n";
573 echo "\t</meta>\n\n\n";
576 foreach ($this->skins as $skinId => $skinName) {
577 $skinId = intval($skinId);
578 $skinObj = new SKIN($skinId);
579 $skinName = htmlspecialchars($skinName, ENT_QUOTES);
580 $contentT = htmlspecialchars($skinObj->getContentType(), ENT_QUOTES);
581 $incMode = htmlspecialchars($skinObj->getIncludeMode(), ENT_QUOTES);
582 $incPrefx = htmlspecialchars($skinObj->getIncludePrefix(), ENT_QUOTES);
583 $skinDesc = htmlspecialchars($skinObj->getDescription(), ENT_QUOTES);
584 if (strtoupper(_CHARSET) != 'UTF-8') {
585 $skinName = mb_convert_encoding($skinName, 'UTF-8', _CHARSET);
586 $contentT = mb_convert_encoding($contentT, 'UTF-8', _CHARSET);
587 $incMode = mb_convert_encoding($incMode, 'UTF-8', _CHARSET);
588 $incPrefx = mb_convert_encoding($incPrefx, 'UTF-8', _CHARSET);
589 $skinDesc = mb_convert_encoding($skinDesc, 'UTF-8', _CHARSET);
592 echo "\t" . '<skin name="' . $skinName . '" type="' . $contentT . '" includeMode="' . $incMode . '" includePrefix="' . $incPrefx . '">' . "\n";
594 echo "\t\t" . '<description>' . $skinDesc . '</description>' . "\n";
602 . ' sdesc = ' . $skinId;
603 $res = sql_query($que);
604 while ($partObj = sql_fetch_object($res)) {
605 $type = htmlspecialchars($partObj->stype, ENT_QUOTES);
606 $cdata = $this->escapeCDATA($partObj->scontent);
607 if (strtoupper(_CHARSET) != 'UTF-8') {
608 $type = mb_convert_encoding($type, 'UTF-8', _CHARSET);
609 $cdata = mb_convert_encoding($cdata, 'UTF-8', _CHARSET);
611 echo "\t\t" . '<part name="' . $type . '">';
612 echo '<![CDATA[' . $cdata . ']]>';
616 echo "\t</skin>\n\n\n";
619 // contents templates
620 foreach ($this->templates as $templateId => $templateName) {
621 $templateId = intval($templateId);
622 $templateName = htmlspecialchars($templateName, ENT_QUOTES);
623 $templateDesc = htmlspecialchars(TEMPLATE::getDesc($templateId), ENT_QUOTES);
624 if (strtoupper(_CHARSET) != 'UTF-8') {
625 $templateName = mb_convert_encoding($templateName, 'UTF-8', _CHARSET);
626 $templateDesc = mb_convert_encoding($templateDesc, 'UTF-8', _CHARSET);
629 echo "\t" . '<template name="' . $templateName . '">' . "\n";
631 echo "\t\t" . '<description>' . $templateDesc . "</description>\n";
637 . sql_table('template')
639 . ' tdesc = ' . $templateId;
640 $res = sql_query($que);
641 while ($partObj = sql_fetch_object($res)) {
642 $type = htmlspecialchars($partObj->tpartname, ENT_QUOTES);
643 $cdata = $this->escapeCDATA($partObj->tcontent);
644 if (strtoupper(_CHARSET) != 'UTF-8') {
645 $type = mb_convert_encoding($type, 'UTF-8', _CHARSET);
646 $cdata = mb_convert_encoding($cdata, 'UTF-8', _CHARSET);
648 echo "\t\t" . '<part name="' . $type . '">';
649 echo '<![CDATA[' . $cdata . ']]>';
650 echo '</part>' . "\n\n";
653 echo "\t</template>\n\n\n";
656 echo '</nucleusskin>';
660 * Escapes CDATA content so it can be included in another CDATA section
662 function escapeCDATA($cdata)
664 return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata);